recharge_service.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. """
  2. 充值服务
  3. """
  4. import json
  5. from datetime import datetime, timedelta
  6. from typing import Tuple
  7. from sqlalchemy.orm import Session
  8. from app.models.user import User
  9. from app.utils.timezone import beijing_now_naive
  10. from app.models.card import Card
  11. from app.models.recharge_record import RechargeRecord
  12. from app.models.recharge_record_his import RechargeRecordHis
  13. from app.models.software import Software
  14. from app.models.user_software_subscription import UserSoftwareSubscription
  15. from app.core.logger import logger
  16. # 默认软件代码
  17. DEFAULT_SOFTWARE_CODE = "buff-xp-steam-sh"
  18. class RechargeService:
  19. """充值服务"""
  20. def __init__(self, db: Session):
  21. self.db = db
  22. def recharge(self, username: str, password: str, card_code: str,
  23. machine_code: str = None, software_code: str = None) -> Tuple[bool, str, dict]:
  24. """
  25. 卡密充值。按 software_code 更新该用户在该软件下的订阅(user_software_subscription)。
  26. Args:
  27. username: 用户名
  28. password: 密码
  29. card_code: 卡密代码
  30. machine_code: 机器码(可选)
  31. software_code: 软件特征码(可选,默认 buff-xp-steam-sh)
  32. Returns:
  33. (success, message, data)
  34. """
  35. try:
  36. sw_code = (software_code or "").strip() or DEFAULT_SOFTWARE_CODE
  37. software = self.db.query(Software).filter(
  38. Software.code == sw_code,
  39. Software.is_deleted == 0
  40. ).first()
  41. if not software:
  42. return False, "软件不存在", {}
  43. user = self.db.query(User).filter(
  44. User.username == username,
  45. User.is_deleted == 0
  46. ).first()
  47. if not user:
  48. return False, "用户不存在", {}
  49. from app.services.auth_service import verify_password
  50. if not verify_password(password, user.password):
  51. return False, "密码错误", {}
  52. if machine_code and user.machine_code and user.machine_code != machine_code:
  53. return False, "机器码不匹配", {}
  54. card = self.db.query(Card).filter(
  55. Card.card_code == card_code,
  56. Card.is_deleted == 0
  57. ).first()
  58. if not card:
  59. return False, "卡密不存在", {}
  60. if card.status == 1:
  61. if card.user_id == user.id:
  62. return False, "该卡密已激活到您的账户", {}
  63. return False, "卡密已被其他用户使用", {}
  64. if card.status == 2:
  65. return False, "卡密已过期", {}
  66. if card.software_code != sw_code:
  67. return False, "卡密类型不匹配", {}
  68. # 该用户在该软件下的订阅(用于取充值前到期时间)
  69. sub = self.db.query(UserSoftwareSubscription).filter(
  70. UserSoftwareSubscription.user_id == user.id,
  71. UserSoftwareSubscription.software_id == software.id,
  72. UserSoftwareSubscription.is_deleted == 0
  73. ).first()
  74. expire_time_before = sub.expire_time if sub else None
  75. if not sub:
  76. sub = UserSoftwareSubscription(
  77. user_id=user.id,
  78. software_id=software.id,
  79. card_id=None,
  80. expire_time=None,
  81. is_deleted=0
  82. )
  83. self.db.add(sub)
  84. self.db.flush()
  85. now = beijing_now_naive()
  86. # 新到期时间 = 在「当前到期时间」基础上累加卡密天数(若已过期或未激活则从当前时间起算)
  87. # 例:当前 2026-03-01 10:09:10,充 10 天 → 2026-03-11 10:09:10;再充 10 天 → 2026-03-21 10:09:10
  88. if expire_time_before and expire_time_before > now:
  89. base_time = expire_time_before
  90. else:
  91. base_time = now
  92. expire_time_after = base_time + timedelta(days=card.days)
  93. card.status = 1
  94. card.user_id = user.id
  95. card.activated_at = now
  96. card.expire_time = expire_time_after
  97. sub.card_id = card.id
  98. sub.expire_time = expire_time_after
  99. # 兼容:默认软件时同步写 user 表,便于未传 software_code 的旧逻辑
  100. if sw_code == DEFAULT_SOFTWARE_CODE:
  101. user.card_id = card.id
  102. user.expire_time = expire_time_after
  103. recharge_record = RechargeRecord(
  104. user_id=user.id,
  105. card_id=card.id,
  106. software_code=card.software_code,
  107. days=card.days,
  108. expire_time_before=expire_time_before,
  109. expire_time_after=expire_time_after
  110. )
  111. self.db.add(recharge_record)
  112. record_data = {
  113. "user_id": user.id,
  114. "card_id": card.id,
  115. "software_code": card.software_code,
  116. "days": card.days,
  117. "expire_time_before": expire_time_before.isoformat() if expire_time_before else None,
  118. "expire_time_after": expire_time_after.isoformat()
  119. }
  120. recharge_record_his = RechargeRecordHis(
  121. user_id=user.id,
  122. card_id=card.id,
  123. software_code=card.software_code,
  124. days=card.days,
  125. expire_time_before=expire_time_before,
  126. expire_time_after=expire_time_after,
  127. action_type="INSERT",
  128. changed_fields=json.dumps(record_data, ensure_ascii=False),
  129. operator="system",
  130. record_id=0
  131. )
  132. self.db.add(recharge_record_his)
  133. self.db.commit()
  134. recharge_record_his.record_id = recharge_record.id
  135. self.db.commit()
  136. days_remaining = (expire_time_after - beijing_now_naive()).days
  137. logger.info(f"卡密充值成功: 用户{username}, 卡密{card_code}, software={sw_code}, 新到期时间={expire_time_after}")
  138. return True, "充值成功", {
  139. "expire_time": expire_time_after.isoformat(),
  140. "days_remaining": days_remaining
  141. }
  142. except Exception as e:
  143. self.db.rollback()
  144. logger.exception(f"卡密充值失败: {e}")
  145. return False, f"充值失败: {str(e)}", {}
  146. def validate_card(self, card_code: str) -> Tuple[bool, str, dict]:
  147. """
  148. 验证卡密
  149. Args:
  150. card_code: 卡密代码
  151. Returns:
  152. (is_valid, message, card_info)
  153. """
  154. try:
  155. card = self.db.query(Card).filter(
  156. Card.card_code == card_code,
  157. Card.is_deleted == 0
  158. ).first()
  159. if not card:
  160. return False, "卡密不存在", {}
  161. if card.status == 0:
  162. # 未激活的卡密
  163. software = self.db.query(Software).filter(
  164. Software.code == card.software_code,
  165. Software.is_active == 1,
  166. Software.is_deleted == 0
  167. ).first()
  168. return True, "卡密可用", {
  169. "software_name": software.name if software else card.software_code,
  170. "days": card.days,
  171. "discount": card.discount
  172. }
  173. if card.status == 1:
  174. return False, "卡密已被使用", {}
  175. if card.status == 2:
  176. return False, "卡密已过期", {}
  177. return False, "卡密状态异常", {}
  178. except Exception as e:
  179. logger.exception(f"验证卡密失败: {e}")
  180. return False, f"验证失败: {str(e)}", {}