feat: 福利钱包+签到+省钱战绩+激励广告发奖+微信提现 后端
- 福利钱包: 金币/现金余额、流水、兑换 (api/v1/wallet.py, crud/wallet.py, models/wallet.py, schemas/welfare.py) - 每日签到: 连续天数 + 档位奖励 (api/v1/signin.py, crud/signin.py, models/signin.py) - 任务系统: "打开消息提醒"等任务领奖 (api/v1/tasks.py, crud/task.py, models/task.py) - 省钱战绩: 省钱汇总/战绩/店铺菜品 (api/v1/savings.py, crud/savings.py, models/savings.py) - 激励广告发奖: 穿山甲服务端回调 + 发奖规则 + 限流 (api/v1/ad.py, core/pangle.py, core/rewards.py, core/ratelimit.py, crud/ad_reward.py, schemas/ad.py, docs/ad_reward_golive_checklist.md) - 微信提现: 商家转账到零钱 V3 (core/wxpay.py); user 表加微信 openid/nickname/avatar - DB 迁移: 8 个 alembic (welfare 表/cash_transaction/openid 唯一约束/savings 店铺菜品/savings_record/ad_reward/withdraw_order+openid/user 微信字段) - 运维脚本: reconcile_withdraws(对账) + reset_signin/reset_welfare + sim_pangle_callback(模拟穿山甲回调) - 测试: test_welfare / test_withdraw / test_ad_reward - 配置: .env.example + config.py 新增福利/广告/微信支付项; main.py 挂 5 个新路由 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
"""一次性任务 endpoint。
|
||||
|
||||
路由前缀 `/api/v1/tasks`:
|
||||
GET / 任务及领取状态(如"打开消息提醒")
|
||||
POST /{task_key}/claim 领取任务奖励
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from app.api.deps import CurrentUser, DbSession
|
||||
from app.crud import task as crud_task
|
||||
from app.schemas.welfare import TaskClaimResultOut, TaskListOut, TaskOut
|
||||
|
||||
logger = logging.getLogger("shagua.tasks")
|
||||
|
||||
router = APIRouter(prefix="/api/v1/tasks", tags=["tasks"])
|
||||
|
||||
|
||||
@router.get("", response_model=TaskListOut, summary="任务及领取状态")
|
||||
def list_tasks(user: CurrentUser, db: DbSession) -> TaskListOut:
|
||||
states = crud_task.list_tasks(db, user.id)
|
||||
return TaskListOut(
|
||||
items=[
|
||||
TaskOut(task_key=s.task_key, coin=s.coin, claimed=s.claimed)
|
||||
for s in states
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{task_key}/claim",
|
||||
response_model=TaskClaimResultOut,
|
||||
summary="领取任务奖励",
|
||||
)
|
||||
def claim_task(task_key: str, user: CurrentUser, db: DbSession) -> TaskClaimResultOut:
|
||||
try:
|
||||
coin, balance = crud_task.claim_task(db, user.id, task_key)
|
||||
except crud_task.UnknownTaskError as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="unknown task") from e
|
||||
except crud_task.AlreadyClaimedError as e:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="task already claimed") from e
|
||||
|
||||
logger.info("task claimed user_id=%d key=%s coin=%d", user.id, task_key, coin)
|
||||
return TaskClaimResultOut(task_key=task_key, coin_awarded=coin, coin_balance=balance)
|
||||
Reference in New Issue
Block a user