points.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. from fastapi import APIRouter, Request
  2. from fastapi.responses import JSONResponse
  3. from database import SessionLocal
  4. from models.user_data import UserData
  5. from models.total import User
  6. from models.points import PointsConsumptionLog
  7. from utils.logger import logger
  8. import time
  9. router = APIRouter()
  10. REQUIRED_POINTS = 10
  11. @router.get("/points/balance")
  12. async def get_balance(request: Request):
  13. """获取用户积分余额(支持本地用户和外部用户)"""
  14. user_info = request.state.user
  15. if not user_info:
  16. return JSONResponse(status_code=401, content={"statusCode": 401, "msg": "未认证"})
  17. db = SessionLocal()
  18. try:
  19. # 优先查询本地用户
  20. user = db.query(User).filter(
  21. User.id == user_info.user_id,
  22. User.is_deleted == 0
  23. ).first()
  24. if user:
  25. return JSONResponse(content={
  26. "statusCode": 200,
  27. "msg": "success",
  28. "data": {"points": user.points or 0}
  29. })
  30. # 查询外部用户
  31. user_data = db.query(UserData).filter(
  32. UserData.accountID == user_info.account
  33. ).first()
  34. if not user_data:
  35. return JSONResponse(content={"statusCode": 404, "msg": "未找到用户数据"})
  36. return JSONResponse(content={
  37. "statusCode": 200,
  38. "msg": "success",
  39. "data": {"points": user_data.points or 0}
  40. })
  41. except Exception as e:
  42. logger.error(f"获取积分余额失败: {e}")
  43. return JSONResponse(content={"statusCode": 500, "msg": f"获取积分余额失败: {str(e)}"})
  44. finally:
  45. db.close()
  46. @router.post("/points/consume")
  47. async def consume_points(request: Request):
  48. """消费积分下载文件(每次消耗10积分,支持本地用户和外部用户)"""
  49. user_info = request.state.user
  50. if not user_info:
  51. return JSONResponse(status_code=401, content={"statusCode": 401, "msg": "未认证"})
  52. try:
  53. body = await request.json()
  54. except Exception:
  55. return JSONResponse(content={"statusCode": 400, "msg": "JSON解析失败"})
  56. file_name = body.get("file_name", "")
  57. file_url = body.get("file_url", "")
  58. db = SessionLocal()
  59. try:
  60. # 优先查询本地用户
  61. user = db.query(User).filter(
  62. User.id == user_info.user_id,
  63. User.is_deleted == 0
  64. ).first()
  65. if user:
  66. current_points = user.points or 0
  67. if current_points < REQUIRED_POINTS:
  68. return JSONResponse(content={
  69. "statusCode": 400,
  70. "msg": "积分不足,下载需要10积分",
  71. "data": {
  72. "current_points": current_points,
  73. "required_points": REQUIRED_POINTS
  74. }
  75. })
  76. new_balance = current_points - REQUIRED_POINTS
  77. user.points = new_balance
  78. now = int(time.time())
  79. log = PointsConsumptionLog(
  80. user_id=str(user.id),
  81. file_name=file_name,
  82. file_url=file_url,
  83. points_consumed=REQUIRED_POINTS,
  84. balance_after=new_balance,
  85. created_at=now,
  86. updated_at=now,
  87. )
  88. db.add(log)
  89. db.commit()
  90. return JSONResponse(content={
  91. "statusCode": 200,
  92. "msg": "success",
  93. "data": {
  94. "new_balance": new_balance,
  95. "points_consumed": REQUIRED_POINTS
  96. }
  97. })
  98. # 查询外部用户
  99. user_data = db.query(UserData).filter(
  100. UserData.accountID == user_info.account
  101. ).first()
  102. if not user_data:
  103. return JSONResponse(content={"statusCode": 404, "msg": "未找到用户数据"})
  104. current_points = user_data.points or 0
  105. if current_points < REQUIRED_POINTS:
  106. return JSONResponse(content={
  107. "statusCode": 400,
  108. "msg": "积分不足,下载需要10积分",
  109. "data": {
  110. "current_points": current_points,
  111. "required_points": REQUIRED_POINTS
  112. }
  113. })
  114. new_balance = current_points - REQUIRED_POINTS
  115. user_data.points = new_balance
  116. now = int(time.time())
  117. log = PointsConsumptionLog(
  118. user_id=user_info.account,
  119. file_name=file_name,
  120. file_url=file_url,
  121. points_consumed=REQUIRED_POINTS,
  122. balance_after=new_balance,
  123. created_at=now,
  124. updated_at=now,
  125. )
  126. db.add(log)
  127. db.commit()
  128. return JSONResponse(content={
  129. "statusCode": 200,
  130. "msg": "success",
  131. "data": {
  132. "new_balance": new_balance,
  133. "points_consumed": REQUIRED_POINTS
  134. }
  135. })
  136. except Exception as e:
  137. db.rollback()
  138. logger.error(f"消费积分失败: {e}")
  139. return JSONResponse(content={"statusCode": 500, "msg": f"消费积分失败: {str(e)}"})
  140. finally:
  141. db.close()
  142. @router.post("/points/add")
  143. async def add_points(request: Request):
  144. """增加积分(仅管理员)"""
  145. user_info = request.state.user
  146. if not user_info:
  147. return JSONResponse(status_code=401, content={"statusCode": 401, "msg": "未认证"})
  148. # 检查管理员权限
  149. if user_info.role != "admin":
  150. return JSONResponse(status_code=403, content={"statusCode": 403, "msg": "权限不足"})
  151. try:
  152. body = await request.json()
  153. except Exception:
  154. return JSONResponse(content={"statusCode": 400, "msg": "JSON解析失败"})
  155. user_id = body.get("user_id")
  156. points = body.get("points", 0)
  157. reason = body.get("reason", "")
  158. if not user_id or points <= 0:
  159. return JSONResponse(content={"statusCode": 400, "msg": "参数错误"})
  160. db = SessionLocal()
  161. try:
  162. # 优先查询本地用户
  163. user = db.query(User).filter(
  164. User.id == user_id,
  165. User.is_deleted == 0
  166. ).first()
  167. if user:
  168. user.points = (user.points or 0) + points
  169. db.commit()
  170. logger.info(f"管理员 {user_info.username} 为用户 {user_id} 添加 {points} 积分,原因: {reason}")
  171. return JSONResponse(content={
  172. "statusCode": 200,
  173. "msg": "积分添加成功",
  174. "data": {"new_balance": user.points}
  175. })
  176. # 查询外部用户
  177. user_data = db.query(UserData).filter(UserData.id == user_id).first()
  178. if not user_data:
  179. return JSONResponse(content={"statusCode": 404, "msg": "用户不存在"})
  180. user_data.points = (user_data.points or 0) + points
  181. db.commit()
  182. logger.info(f"管理员 {user_info.username} 为外部用户 {user_id} 添加 {points} 积分,原因: {reason}")
  183. return JSONResponse(content={
  184. "statusCode": 200,
  185. "msg": "积分添加成功",
  186. "data": {"new_balance": user_data.points}
  187. })
  188. except Exception as e:
  189. db.rollback()
  190. logger.error(f"添加积分失败: {e}")
  191. return JSONResponse(content={"statusCode": 500, "msg": f"添加积分失败: {str(e)}"})
  192. finally:
  193. db.close()
  194. @router.get("/points/logs")
  195. @router.get("/points/history")
  196. async def get_consumption_history(request: Request, page: int = 1, pageSize: int = 10):
  197. """获取积分消费记录(支持本地用户和外部用户)"""
  198. user_info = request.state.user
  199. if not user_info:
  200. return JSONResponse(status_code=401, content={"statusCode": 401, "msg": "未认证"})
  201. db = SessionLocal()
  202. try:
  203. # 确定用户ID(本地用户用user_id,外部用户用account)
  204. user = db.query(User).filter(
  205. User.id == user_info.user_id,
  206. User.is_deleted == 0
  207. ).first()
  208. user_identifier = str(user.id) if user else user_info.account
  209. query = db.query(PointsConsumptionLog).filter(
  210. PointsConsumptionLog.user_id == user_identifier
  211. )
  212. total = query.count()
  213. logs = query.order_by(PointsConsumptionLog.id.desc()) \
  214. .offset((page - 1) * pageSize) \
  215. .limit(pageSize) \
  216. .all()
  217. items = [
  218. {
  219. "id": log.id,
  220. "file_name": log.file_name,
  221. "file_url": log.file_url,
  222. "points_consumed": log.points_consumed,
  223. "balance_after": log.balance_after,
  224. "created_at": log.created_at,
  225. }
  226. for log in logs
  227. ]
  228. return JSONResponse(content={
  229. "statusCode": 200,
  230. "msg": "success",
  231. "data": {
  232. "list": items,
  233. "total": total,
  234. "page": page,
  235. "pageSize": pageSize,
  236. }
  237. })
  238. except Exception as e:
  239. logger.error(f"获取消费记录失败: {e}")
  240. return JSONResponse(content={"statusCode": 500, "msg": f"获取消费记录失败: {str(e)}"})
  241. finally:
  242. db.close()