| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- """
- 充值服务
- """
- import json
- from datetime import datetime, timedelta
- from typing import Tuple
- from sqlalchemy.orm import Session
- from app.models.user import User
- from app.utils.timezone import beijing_now_naive
- from app.models.card import Card
- from app.models.recharge_record import RechargeRecord
- from app.models.recharge_record_his import RechargeRecordHis
- from app.models.software import Software
- from app.models.user_software_subscription import UserSoftwareSubscription
- from app.core.logger import logger
- # 默认软件代码
- DEFAULT_SOFTWARE_CODE = "buff-xp-steam-sh"
- class RechargeService:
- """充值服务"""
-
- def __init__(self, db: Session):
- self.db = db
-
- def recharge(self, username: str, password: str, card_code: str,
- machine_code: str = None, software_code: str = None) -> Tuple[bool, str, dict]:
- """
- 卡密充值。按 software_code 更新该用户在该软件下的订阅(user_software_subscription)。
- Args:
- username: 用户名
- password: 密码
- card_code: 卡密代码
- machine_code: 机器码(可选)
- software_code: 软件特征码(可选,默认 buff-xp-steam-sh)
- Returns:
- (success, message, data)
- """
- try:
- sw_code = (software_code or "").strip() or DEFAULT_SOFTWARE_CODE
- software = self.db.query(Software).filter(
- Software.code == sw_code,
- Software.is_deleted == 0
- ).first()
- if not software:
- return False, "软件不存在", {}
- user = self.db.query(User).filter(
- User.username == username,
- User.is_deleted == 0
- ).first()
- if not user:
- return False, "用户不存在", {}
- from app.services.auth_service import verify_password
- if not verify_password(password, user.password):
- return False, "密码错误", {}
- if machine_code and user.machine_code and user.machine_code != machine_code:
- return False, "机器码不匹配", {}
- card = self.db.query(Card).filter(
- Card.card_code == card_code,
- Card.is_deleted == 0
- ).first()
- if not card:
- return False, "卡密不存在", {}
- if card.status == 1:
- if card.user_id == user.id:
- return False, "该卡密已激活到您的账户", {}
- return False, "卡密已被其他用户使用", {}
- if card.status == 2:
- return False, "卡密已过期", {}
- if card.software_code != sw_code:
- return False, "卡密类型不匹配", {}
- # 该用户在该软件下的订阅(用于取充值前到期时间)
- sub = self.db.query(UserSoftwareSubscription).filter(
- UserSoftwareSubscription.user_id == user.id,
- UserSoftwareSubscription.software_id == software.id,
- UserSoftwareSubscription.is_deleted == 0
- ).first()
- expire_time_before = sub.expire_time if sub else None
- if not sub:
- sub = UserSoftwareSubscription(
- user_id=user.id,
- software_id=software.id,
- card_id=None,
- expire_time=None,
- is_deleted=0
- )
- self.db.add(sub)
- self.db.flush()
- now = beijing_now_naive()
- # 新到期时间 = 在「当前到期时间」基础上累加卡密天数(若已过期或未激活则从当前时间起算)
- # 例:当前 2026-03-01 10:09:10,充 10 天 → 2026-03-11 10:09:10;再充 10 天 → 2026-03-21 10:09:10
- if expire_time_before and expire_time_before > now:
- base_time = expire_time_before
- else:
- base_time = now
- expire_time_after = base_time + timedelta(days=card.days)
- card.status = 1
- card.user_id = user.id
- card.activated_at = now
- card.expire_time = expire_time_after
- sub.card_id = card.id
- sub.expire_time = expire_time_after
- # 兼容:默认软件时同步写 user 表,便于未传 software_code 的旧逻辑
- if sw_code == DEFAULT_SOFTWARE_CODE:
- user.card_id = card.id
- user.expire_time = expire_time_after
- recharge_record = RechargeRecord(
- user_id=user.id,
- card_id=card.id,
- software_code=card.software_code,
- days=card.days,
- expire_time_before=expire_time_before,
- expire_time_after=expire_time_after
- )
- self.db.add(recharge_record)
- record_data = {
- "user_id": user.id,
- "card_id": card.id,
- "software_code": card.software_code,
- "days": card.days,
- "expire_time_before": expire_time_before.isoformat() if expire_time_before else None,
- "expire_time_after": expire_time_after.isoformat()
- }
- recharge_record_his = RechargeRecordHis(
- user_id=user.id,
- card_id=card.id,
- software_code=card.software_code,
- days=card.days,
- expire_time_before=expire_time_before,
- expire_time_after=expire_time_after,
- action_type="INSERT",
- changed_fields=json.dumps(record_data, ensure_ascii=False),
- operator="system",
- record_id=0
- )
- self.db.add(recharge_record_his)
- self.db.commit()
- recharge_record_his.record_id = recharge_record.id
- self.db.commit()
- days_remaining = (expire_time_after - beijing_now_naive()).days
- logger.info(f"卡密充值成功: 用户{username}, 卡密{card_code}, software={sw_code}, 新到期时间={expire_time_after}")
- return True, "充值成功", {
- "expire_time": expire_time_after.isoformat(),
- "days_remaining": days_remaining
- }
- except Exception as e:
- self.db.rollback()
- logger.exception(f"卡密充值失败: {e}")
- return False, f"充值失败: {str(e)}", {}
-
- def validate_card(self, card_code: str) -> Tuple[bool, str, dict]:
- """
- 验证卡密
-
- Args:
- card_code: 卡密代码
-
- Returns:
- (is_valid, message, card_info)
- """
- try:
- card = self.db.query(Card).filter(
- Card.card_code == card_code,
- Card.is_deleted == 0
- ).first()
-
- if not card:
- return False, "卡密不存在", {}
-
- if card.status == 0:
- # 未激活的卡密
- software = self.db.query(Software).filter(
- Software.code == card.software_code,
- Software.is_active == 1,
- Software.is_deleted == 0
- ).first()
-
- return True, "卡密可用", {
- "software_name": software.name if software else card.software_code,
- "days": card.days,
- "discount": card.discount
- }
-
- if card.status == 1:
- return False, "卡密已被使用", {}
-
- if card.status == 2:
- return False, "卡密已过期", {}
-
- return False, "卡密状态异常", {}
-
- except Exception as e:
- logger.exception(f"验证卡密失败: {e}")
- return False, f"验证失败: {str(e)}", {}
|