Files
backend/backend/app/admin/service/points_service.py
2025-10-18 10:54:08 +08:00

180 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Optional
from backend.app.admin.crud.points_crud import points_dao, points_log_dao
from backend.app.admin.model.points import Points
from backend.database.db import async_db_session
class PointsService:
@staticmethod
async def get_user_points(user_id: int) -> Optional[Points]:
"""
获取用户积分账户信息(会检查并清空过期积分)
"""
async with async_db_session.begin() as db:
# 获取当前积分余额(清空前)
points_account_before = await points_dao.get_by_user_id(db, user_id)
balance_before = points_account_before.balance if points_account_before else 0
# 检查并清空过期积分
expired_cleared = await points_dao.check_and_clear_expired_points(db, user_id)
# 如果清空了过期积分,记录日志
if expired_cleared and balance_before > 0:
await points_log_dao.add_log(db, {
"user_id": user_id,
"action": "expire_clear",
"amount": balance_before, # 记录清空前的积分数量
"balance_after": 0,
"details": {"message": "过期积分已清空", "cleared_amount": balance_before}
})
return await points_dao.get_by_user_id(db, user_id)
@staticmethod
async def get_user_balance(user_id: int) -> int:
"""
获取用户积分余额(会检查并清空过期积分)
"""
async with async_db_session.begin() as db:
# 获取当前积分余额(清空前)
points_account_before = await points_dao.get_by_user_id(db, user_id)
balance_before = points_account_before.balance if points_account_before else 0
# 检查并清空过期积分
expired_cleared = await points_dao.check_and_clear_expired_points(db, user_id)
# 如果清空了过期积分,记录日志
if expired_cleared and balance_before > 0:
await points_log_dao.add_log(db, {
"user_id": user_id,
"action": "expire_clear",
"balance_before": balance_before,
"balance_after": 0,
"details": {"message": "过期积分已清空", "cleared_amount": balance_before}
})
return await points_dao.get_balance(db, user_id)
@staticmethod
async def add_points(user_id: int, amount: int, extend_expiration: bool = False, related_id: Optional[int] = None, details: Optional[dict] = None) -> bool:
"""
为用户增加积分
Args:
user_id: 用户ID
amount: 增加的积分数量
extend_expiration: 是否自动延期过期时间
related_id: 关联ID可选
details: 附加信息(可选)
Returns:
bool: 是否成功
"""
if amount <= 0:
raise ValueError("积分数量必须大于0")
async with async_db_session.begin() as db:
# 获取当前余额以记录日志
points_account = await points_dao.get_by_user_id(db, user_id)
if not points_account:
points_account = await points_dao.create_user_points(db, user_id)
current_balance = points_account.balance
# 原子性增加积分(可能延期过期时间)
result = await points_dao.add_points_atomic(db, user_id, amount, extend_expiration)
if not result:
return False
# 准备日志详情
log_details = details or {}
if extend_expiration:
log_details["expiration_extended"] = True
log_details["extension_days"] = 30
# 记录积分变动日志
new_balance = current_balance + amount
await points_log_dao.add_log(db, {
"user_id": user_id,
"action": "earn",
"amount": amount,
"balance_after": new_balance,
"related_id": related_id,
"details": log_details
})
return True
@staticmethod
async def deduct_points(user_id: int, amount: int, related_id: Optional[int] = None, details: Optional[dict] = None) -> bool:
"""
扣减用户积分(会检查并清空过期积分)
Args:
user_id: 用户ID
amount: 扣减的积分数量
related_id: 关联ID可选
details: 附加信息(可选)
Returns:
bool: 是否成功
"""
if amount <= 0:
raise ValueError("积分数量必须大于0")
async with async_db_session.begin() as db:
# 获取当前积分余额(清空前)
points_account_before = await points_dao.get_by_user_id(db, user_id)
if not points_account_before:
return False
balance_before = points_account_before.balance
# 检查并清空过期积分
expired_cleared = await points_dao.check_and_clear_expired_points(db, user_id)
# 如果清空了过期积分,记录日志
if expired_cleared and balance_before > 0:
await points_log_dao.add_log(db, {
"user_id": user_id,
"action": "expire_clear",
"amount": balance_before, # 记录清空前的积分数量
"balance_after": 0,
"details": {"message": "过期积分已清空", "cleared_amount": balance_before}
})
# 重新获取账户信息(可能已被清空)
points_account = await points_dao.get_by_user_id(db, user_id)
if not points_account or points_account.balance < amount:
return False
current_balance = points_account.balance
# 原子性扣减积分
result = await points_dao.deduct_points_atomic(db, user_id, amount)
if not result:
return False
# 记录积分变动日志
new_balance = current_balance - amount
await points_log_dao.add_log(db, {
"user_id": user_id,
"action": "spend",
"amount": amount,
"balance_after": new_balance,
"related_id": related_id,
"details": details
})
return True
@staticmethod
async def initialize_user_points(user_id: int) -> Points:
"""
为新用户初始化积分账户
"""
async with async_db_session.begin() as db:
points_account = await points_dao.get_by_user_id(db, user_id)
if not points_account:
points_account = await points_dao.create_user_points(db, user_id)
return points_account