auth.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. """
  2. 客户端认证接口
  3. """
  4. from fastapi import APIRouter, Depends, HTTPException, Request
  5. from sqlalchemy.orm import Session
  6. from pydantic import BaseModel
  7. from app.database import get_db
  8. from app.models.user import User
  9. from app.models.card import Card
  10. from app.models.recharge_record import RechargeRecord
  11. from app.core.security import verify_password, get_password_hash
  12. from app.core.encrypt import encryptor
  13. from app.core.logger import logger
  14. from app.config import settings
  15. from datetime import datetime, timedelta
  16. router = APIRouter()
  17. class RegisterRequest(BaseModel):
  18. """注册请求"""
  19. username: str
  20. password: str
  21. email: str
  22. card_code: str
  23. machine_code: str
  24. class LoginRequest(BaseModel):
  25. """登录请求"""
  26. username: str
  27. password: str
  28. machine_code: str
  29. class UnbindRequest(BaseModel):
  30. """解绑请求"""
  31. username: str
  32. password: str
  33. class ActivateCardRequest(BaseModel):
  34. """激活卡密请求"""
  35. user_id: int
  36. card_code: str
  37. @router.post("/register")
  38. async def register(
  39. request: Request,
  40. db: Session = Depends(get_db)
  41. ):
  42. """用户注册"""
  43. try:
  44. # 获取请求数据
  45. data = await request.json()
  46. # 解密请求数据
  47. if settings.ENCRYPT_ENABLED:
  48. encrypted_data = data.get("encrypted", "")
  49. if not encrypted_data:
  50. return {"code": 400, "msg": "缺少加密数据"}
  51. decrypted = encryptor.decrypt(encrypted_data)
  52. else:
  53. decrypted = data
  54. req = RegisterRequest(**decrypted)
  55. # 检查用户名是否存在
  56. existing_user = db.query(User).filter(User.username == req.username).first()
  57. if existing_user:
  58. return {"code": 400, "msg": "用户名已存在"}
  59. # 验证卡密
  60. card = db.query(Card).filter(
  61. Card.card_code == req.card_code,
  62. Card.status == 0
  63. ).first()
  64. if not card:
  65. return {"code": 400, "msg": "卡密无效或已被使用"}
  66. # 计算到期时间
  67. expire_time = datetime.now() + timedelta(days=card.days)
  68. # 创建用户
  69. user = User(
  70. username=req.username,
  71. password=get_password_hash(req.password),
  72. email=req.email,
  73. machine_code=req.machine_code,
  74. card_id=card.id,
  75. expire_time=expire_time,
  76. status=1,
  77. created_at=datetime.now(),
  78. updated_at=datetime.now()
  79. )
  80. db.add(user)
  81. db.flush() # 刷新以获取user.id
  82. # 更新卡密状态
  83. card.user_id = user.id
  84. card.activated_at = datetime.now()
  85. card.expire_time = expire_time
  86. card.status = 1
  87. card.updated_at = datetime.now()
  88. # 创建充值记录
  89. recharge_record = RechargeRecord(
  90. user_id=user.id,
  91. card_id=card.id,
  92. software_code=card.software_code,
  93. days=card.days,
  94. expire_time_before=None, # 新用户,之前没有到期时间
  95. expire_time_after=expire_time,
  96. created_at=datetime.now()
  97. )
  98. db.add(recharge_record)
  99. db.commit()
  100. logger.info(f"用户注册成功: {req.username}")
  101. return {"code": 200, "msg": "注册成功", "data": {"user_id": user.id}}
  102. except Exception as e:
  103. db.rollback()
  104. logger.error(f"用户注册失败: {e}")
  105. return {"code": 500, "msg": "注册失败"}
  106. @router.post("/login")
  107. async def login(
  108. request: Request,
  109. db: Session = Depends(get_db)
  110. ):
  111. """用户登录"""
  112. try:
  113. # 获取请求数据
  114. data = await request.json()
  115. # 解密请求数据
  116. if settings.ENCRYPT_ENABLED:
  117. encrypted_data = data.get("encrypted", "")
  118. if not encrypted_data:
  119. return {"code": 400, "msg": "缺少加密数据"}
  120. decrypted = encryptor.decrypt(encrypted_data)
  121. else:
  122. decrypted = data
  123. req = LoginRequest(**decrypted)
  124. # 查找用户
  125. user = db.query(User).filter(User.username == req.username).first()
  126. if not user:
  127. return {"code": 401, "msg": "用户名或密码错误"}
  128. # 验证密码
  129. if not verify_password(req.password, user.password):
  130. return {"code": 401, "msg": "用户名或密码错误"}
  131. # 验证机器码
  132. if user.machine_code != req.machine_code:
  133. return {"code": 403, "msg": "机器码不匹配,请先解绑"}
  134. # 检查是否过期
  135. if user.expire_time and user.expire_time < datetime.now():
  136. return {"code": 403, "msg": "卡密已过期"}
  137. # 更新登录信息
  138. client_ip = request.client.host if request.client else None
  139. user.last_login_time = datetime.now()
  140. user.last_login_ip = client_ip
  141. user.updated_at = datetime.now()
  142. db.commit()
  143. logger.info(f"用户登录成功: {req.username}")
  144. return {
  145. "code": 200,
  146. "msg": "登录成功",
  147. "data": {
  148. "user_id": user.id,
  149. "username": user.username,
  150. "expire_time": user.expire_time.isoformat() if user.expire_time else None
  151. }
  152. }
  153. except Exception as e:
  154. logger.error(f"用户登录失败: {e}")
  155. return {"code": 500, "msg": "登录失败"}
  156. @router.post("/unbind")
  157. async def unbind(
  158. request: Request,
  159. db: Session = Depends(get_db)
  160. ):
  161. """机器解绑"""
  162. try:
  163. # 获取请求数据
  164. data = await request.json()
  165. # 解密请求数据
  166. if settings.ENCRYPT_ENABLED:
  167. encrypted_data = data.get("encrypted", "")
  168. if not encrypted_data:
  169. return {"code": 400, "msg": "缺少加密数据"}
  170. decrypted = encryptor.decrypt(encrypted_data)
  171. else:
  172. decrypted = data
  173. req = UnbindRequest(**decrypted)
  174. # 查找用户
  175. user = db.query(User).filter(User.username == req.username).first()
  176. if not user:
  177. return {"code": 401, "msg": "用户名或密码错误"}
  178. # 验证密码
  179. if not verify_password(req.password, user.password):
  180. return {"code": 401, "msg": "用户名或密码错误"}
  181. # 解绑机器码
  182. user.machine_code = None
  183. user.updated_at = datetime.now()
  184. db.commit()
  185. logger.info(f"机器解绑成功: {req.username}")
  186. return {"code": 200, "msg": "解绑成功"}
  187. except Exception as e:
  188. db.rollback()
  189. logger.error(f"机器解绑失败: {e}")
  190. return {"code": 500, "msg": "解绑失败"}
  191. @router.post("/heartbeat")
  192. async def heartbeat(
  193. request: Request,
  194. db: Session = Depends(get_db)
  195. ):
  196. """心跳检测"""
  197. try:
  198. # 获取请求数据
  199. data = await request.json()
  200. # 解密请求数据
  201. if settings.ENCRYPT_ENABLED:
  202. encrypted_data = data.get("encrypted", "")
  203. if not encrypted_data:
  204. return {"code": 400, "msg": "缺少加密数据"}
  205. decrypted = encryptor.decrypt(encrypted_data)
  206. else:
  207. decrypted = data
  208. user_id = decrypted.get("user_id")
  209. if not user_id:
  210. return {"code": 400, "msg": "参数错误"}
  211. # 查找用户
  212. user = db.query(User).filter(User.id == user_id).first()
  213. if not user:
  214. return {"code": 404, "msg": "用户不存在"}
  215. # 检查是否过期
  216. if user.expire_time and user.expire_time < datetime.now():
  217. return {"code": 403, "msg": "卡密已过期"}
  218. return {
  219. "code": 200,
  220. "msg": "正常",
  221. "data": {
  222. "expire_time": user.expire_time.isoformat() if user.expire_time else None
  223. }
  224. }
  225. except Exception as e:
  226. logger.error(f"心跳检测失败: {e}")
  227. return {"code": 500, "msg": "心跳检测失败"}
  228. @router.post("/activate-card")
  229. async def activate_card(
  230. request: Request,
  231. db: Session = Depends(get_db)
  232. ):
  233. """激活卡密(充值)"""
  234. try:
  235. # 获取请求数据
  236. data = await request.json()
  237. # 解密请求数据
  238. if settings.ENCRYPT_ENABLED:
  239. encrypted_data = data.get("encrypted", "")
  240. if not encrypted_data:
  241. return {"code": 400, "msg": "缺少加密数据"}
  242. decrypted = encryptor.decrypt(encrypted_data)
  243. else:
  244. decrypted = data
  245. req = ActivateCardRequest(**decrypted)
  246. # 查找用户
  247. user = db.query(User).filter(User.id == req.user_id).first()
  248. if not user:
  249. return {"code": 404, "msg": "用户不存在"}
  250. # 验证卡密
  251. card = db.query(Card).filter(
  252. Card.card_code == req.card_code,
  253. Card.status == 0
  254. ).first()
  255. if not card:
  256. return {"code": 400, "msg": "卡密无效或已被使用"}
  257. # 检查软件特征码是否匹配
  258. software_code = decrypted.get("software_code", "")
  259. if card.software_code != software_code:
  260. return {"code": 400, "msg": "卡密与当前软件不匹配"}
  261. # 计算新的到期时间
  262. expire_time_before = user.expire_time
  263. if expire_time_before and expire_time_before > datetime.now():
  264. # 如果还有有效期,则累加
  265. expire_time_after = expire_time_before + timedelta(days=card.days)
  266. else:
  267. # 如果已过期或没有有效期,则从当前时间开始计算
  268. expire_time_after = datetime.now() + timedelta(days=card.days)
  269. # 更新用户到期时间
  270. user.expire_time = expire_time_after
  271. user.updated_at = datetime.now()
  272. # 更新卡密状态
  273. card.user_id = user.id
  274. card.activated_at = datetime.now()
  275. card.expire_time = expire_time_after
  276. card.status = 1
  277. card.updated_at = datetime.now()
  278. # 创建充值记录
  279. recharge_record = RechargeRecord(
  280. user_id=user.id,
  281. card_id=card.id,
  282. software_code=card.software_code,
  283. days=card.days,
  284. expire_time_before=expire_time_before,
  285. expire_time_after=expire_time_after,
  286. created_at=datetime.now()
  287. )
  288. db.add(recharge_record)
  289. db.commit()
  290. logger.info(f"用户 {user.username} 激活卡密成功: {req.card_code}")
  291. return {
  292. "code": 200,
  293. "msg": "激活成功",
  294. "data": {
  295. "expire_time": expire_time_after.isoformat(),
  296. "days": card.days
  297. }
  298. }
  299. except Exception as e:
  300. db.rollback()
  301. logger.error(f"激活卡密失败: {e}")
  302. return {"code": 500, "msg": "激活失败"}
  303. @router.get("/recharge-records")
  304. async def get_recharge_records(
  305. request: Request,
  306. db: Session = Depends(get_db)
  307. ):
  308. """获取充值记录"""
  309. try:
  310. # 获取请求参数
  311. params = dict(request.query_params)
  312. # 解密请求数据(如果有)
  313. user_id = None
  314. if settings.ENCRYPT_ENABLED:
  315. # GET请求可能通过query参数传递加密数据
  316. encrypted_data = params.get("encrypted", "")
  317. if encrypted_data:
  318. decrypted = encryptor.decrypt(encrypted_data)
  319. user_id = decrypted.get("user_id")
  320. else:
  321. user_id = params.get("user_id")
  322. if user_id:
  323. user_id = int(user_id)
  324. if not user_id:
  325. return {"code": 400, "msg": "缺少用户ID"}
  326. # 查询充值记录
  327. records = db.query(RechargeRecord).filter(
  328. RechargeRecord.user_id == user_id
  329. ).order_by(RechargeRecord.created_at.desc()).all()
  330. # 格式化返回数据
  331. records_data = []
  332. for record in records:
  333. # 获取卡密代码
  334. card_code = ""
  335. if record.card_id:
  336. card = db.query(Card).filter(Card.id == record.card_id).first()
  337. if card:
  338. card_code = card.card_code
  339. records_data.append({
  340. "id": record.id,
  341. "card_code": card_code,
  342. "days": record.days,
  343. "expire_time_before": record.expire_time_before.isoformat() if record.expire_time_before else None,
  344. "expire_time_after": record.expire_time_after.isoformat() if record.expire_time_after else None,
  345. "created_at": record.created_at.isoformat() if record.created_at else None
  346. })
  347. return {
  348. "code": 200,
  349. "msg": "成功",
  350. "data": records_data
  351. }
  352. except Exception as e:
  353. logger.error(f"获取充值记录失败: {e}")
  354. return {"code": 500, "msg": "获取充值记录失败"}