"""比价战绩里程碑 CRUD:进度查询 + 逐档领奖。 进度 = comparison_record 里 status='success' 的条数(crud_compare.count_success)。 第 N 档在"成功次数 >= N"时解锁;每档领一次,写 comparison_milestone_claim 去重(标记已领)。 档位金额来自 rewards.get_record_milestones(运营后台可改)。 ⚠️ 当前产品定暂不真发金币(后续整体删除该功能):claim 只写领取记录,不调 grant_coins、 不写 coin_transaction,coin_awarded 恒为 0,余额不变。仿一次性任务 (task.py)。 """ from __future__ import annotations from dataclasses import dataclass from sqlalchemy import select from sqlalchemy.orm import Session from app.core import rewards from app.models.comparison_milestone import ComparisonMilestoneClaim from app.repositories import comparison as crud_compare from app.repositories import wallet as crud_wallet class UnknownMilestoneError(Exception): """档位序号越界(不在 1..档位数)。""" class MilestoneLockedError(Exception): """该档还没解锁(成功比价次数不够)。""" class AlreadyClaimedError(Exception): """该档已经领过了。""" @dataclass class MilestoneState: milestone: int # 1-based 档位序号 coin: int state: str # claimed / active / locked @dataclass class MilestoneStatus: success_count: int # 累计成功比价次数(解锁进度) claimable_count: int # 当前可领(active)档数 milestones: list[MilestoneState] def _claimed_set(db: Session, user_id: int) -> set[int]: rows = db.execute( select(ComparisonMilestoneClaim.milestone).where( ComparisonMilestoneClaim.user_id == user_id ) ).scalars().all() return set(rows) def get_status(db: Session, user_id: int) -> MilestoneStatus: """各档领取状态:已领=claimed,已解锁未领=active(可领),未解锁=locked。""" success_count = crud_compare.count_success(db, user_id) claimed = _claimed_set(db, user_id) milestones_def = rewards.get_record_milestones(db) milestones: list[MilestoneState] = [] claimable = 0 for m in range(1, len(milestones_def) + 1): if m in claimed: state = "claimed" elif success_count >= m: state = "active" claimable += 1 else: state = "locked" milestones.append( MilestoneState(milestone=m, coin=milestones_def[m - 1], state=state) ) return MilestoneStatus( success_count=success_count, claimable_count=claimable, milestones=milestones, ) def claim(db: Session, user_id: int, milestone: int) -> tuple[int, int]: """领取第 milestone 档奖励。返回 (发放金币, 领奖后余额)。 越界抛 UnknownMilestoneError;未解锁抛 MilestoneLockedError;重复领抛 AlreadyClaimedError。 """ if milestone < 1 or milestone > len(rewards.get_record_milestones(db)): raise UnknownMilestoneError existing = db.execute( select(ComparisonMilestoneClaim).where( ComparisonMilestoneClaim.user_id == user_id, ComparisonMilestoneClaim.milestone == milestone, ) ).scalar_one_or_none() if existing is not None: raise AlreadyClaimedError # 解锁校验:成功比价次数必须 >= 档位序号 if crud_compare.count_success(db, user_id) < milestone: raise MilestoneLockedError # ⚠️ 比价战绩里程碑暂不真发金币(产品定,后续整体删除该功能):仍记一条领取(去重/标记已领), # 但不调 grant_coins、不写 coin_transaction,余额不变,返回发放 0 金币 + 当前余额。 db.add( ComparisonMilestoneClaim( user_id=user_id, milestone=milestone, coin_awarded=0 ) ) acc = crud_wallet.get_or_create_account(db, user_id, commit=False) db.commit() return 0, acc.coin_balance