bindbox-game/.trae/documents/抽奖流程解耦与支持N次抽奖改造方案.md
邹方成 45815bfb7d chore: 清理无用文件与优化代码结构
refactor(utils): 修复密码哈希比较逻辑错误
feat(user): 新增按状态筛选优惠券接口
docs: 添加虚拟发货与任务中心相关文档
fix(wechat): 修正Code2Session上下文传递问题
test: 补充订单折扣与积分转换测试用例
build: 更新配置文件与构建脚本
style: 清理多余的空行与注释
2025-12-18 17:35:55 +08:00

72 lines
4.0 KiB
Markdown
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.

## 执行逻辑(目标态)
### 参与下单(仅创建订单,不开奖)
- 接口:`POST /api/app/lottery/join`
- 入参:`activity_id``issue_id``count`(默认1≤N)、可选 `client_nonce`
- 流程:
- 查询活动单价 `price_draw`;计算 `total_amount = price_draw * count`
- 查询积分余额;计算需积分 `needPts = ceil(total_amount / 10)`;实际扣减 `usePts = min(balance, needPts)`
- 写订单:`draw_count = count``PointsAmount = usePts * 10``ActualAmount = total_amount - PointsAmount``Status=1`
-`ActualAmount == 0` → 将订单置为已支付(`Status=2``PaidAt=now`),但不开奖
- 返回:`order_no``draw_mode``count`、金额与抵扣细节、`queued`
### 预下单(微信 JSAPI
- 接口:`POST /api/app/pay/wechat/jsapi/preorder`
- 校验:订单属于当前用户且 `Status=1`
- 返回:`wx.requestPayment` 参数;记录 `PaymentPreorders`
### 支付回调(入账)
- 接口:`POST /api/pay/wechat/notify`
- 行为:验签→记录交易→更新订单 `Status=2(PAID)``PaidAt`;幂等事件处理
- 触发:即时模式下调用 `DrawProcessor`;计划模式下等待调度
### 抽奖处理器(按订单维度执行)
- 输入:`order_id/order_no/activity_id/issue_id/draw_mode/draw_count`
- 即时模式:
- 读取已抽次数 `n = count(ActivityDrawLogs where order_id)`
- 循环 `i=n+1..draw_count`: `SelectItem``GrantReward``Create(ActivityDrawLogs{draw_index=i})`
- `completed == draw_count` → 订单标记 `SETTLED`
- 计划模式:
-`scheduled_time` 统一处理;参与不足自动退款(先积分、后微信金额),保留现有逻辑
- 幂等:以上循环按“每订单已存在日志条数”补齐,事务化执行
### 统一结果轮询(基于 order_no
- 接口:`GET /api/app/lottery/result?order_no=...`
- 返回:
- `status`: `pending|paid_waiting|settled|refunded`
- `draw_mode``count``completed``results[{reward_id,reward_name,level,draw_index}]`
- `receipt``issue_id/seed_version/timestamp/nonce/signature/algorithm`
- `nextPollMs`建议2s-5s
## 改造点与文件定位
- 修改 `JoinLottery`internal/api/activity/lottery_app.go
- 增加 `count` 入参;仅下单与积分抵扣;免支付不开奖
- 保持 `WechatJSAPIPreorder` 不变internal/api/user/pay_wechat_app.go
- 扩展回调:在 `WechatNotify` 支付入账后触发即时模式订单的 `DrawProcessor`internal/api/pay/wechat_notify.go
- 定时调度按订单 `draw_count` 抽取或退款internal/service/activity/scheduler.go
- 新增统一查询接口 `GET /api/app/lottery/result`:新增 handlerinternal/api/activity/lottery_result_app.go挂载到 APP 认证组internal/router/router.go
## 数据库变更
- `orders` 新增:`draw_count INT NOT NULL DEFAULT 1`(可选:`draw_mode` 冗余)
- `activity_draw_logs` 可选新增:`draw_index INT NULL`(记录第几次抽取)
- 迁移默认值:历史订单填充 `draw_count = 1`
## 边界与异常
- 积分充足:免支付→订单直接 `PAID`;由处理器负责开奖
- 积分不够/无积分:需支付→入账后再开奖
- 计划不足:自动退款(积分与金额),订单 `REFUNDED`
- 并发与幂等:以 `order_id` + 已有日志条数控制,所有扣减与发奖在事务中执行
## 前端调用契约
- 流程:`join` → (可选)`preorder` → 支付 → 轮询 `result``settled|refunded`
- 显示:用 `results` 数组展示 N 次抽奖结果;根据 `status` 渲染进度与状态
## 测试与验收
- 单元:积分计算(充足/不够/为0、N 抽即时/计划模式日志与发奖一致
- 集成:支付回调幂等、计划不足退款、统一查询状态流转
- 验收:同一 `order_no` 在两模式下的查询端点返回一致结构与正确状态机;库存扣减准确;退款日志完整
## 注意事项
- `client_nonce`(可选):用于防重复参与提交的客户端幂等键;不影响支付与开奖
- 安全JWT校验、签名凭证不泄露种子所有金额与积分变更审计入库