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

4.0 KiB
Raw Blame History

执行逻辑(目标态)

参与下单(仅创建订单,不开奖)

  • 接口:POST /api/app/lottery/join
  • 入参:activity_idissue_idcount(默认1≤N)、可选 client_nonce
  • 流程:
    • 查询活动单价 price_draw;计算 total_amount = price_draw * count
    • 查询积分余额;计算需积分 needPts = ceil(total_amount / 10);实际扣减 usePts = min(balance, needPts)
    • 写订单:draw_count = countPointsAmount = usePts * 10ActualAmount = total_amount - PointsAmountStatus=1
    • ActualAmount == 0 → 将订单置为已支付(Status=2PaidAt=now),但不开奖
    • 返回:order_nodraw_modecount、金额与抵扣细节、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: SelectItemGrantRewardCreate(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_modecountcompletedresults[{reward_id,reward_name,level,draw_index}]
    • receiptissue_id/seed_version/timestamp/nonce/signature/algorithm
    • nextPollMs建议2s-5s

改造点与文件定位

  • 修改 JoinLotteryinternal/api/activity/lottery_app.go
    • 增加 count 入参;仅下单与积分抵扣;免支付不开奖
  • 保持 WechatJSAPIPreorder 不变internal/api/user/pay_wechat_app.go
  • 扩展回调:在 WechatNotify 支付入账后触发即时模式订单的 DrawProcessorinternal/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 → 支付 → 轮询 resultsettled|refunded
  • 显示:用 results 数组展示 N 次抽奖结果;根据 status 渲染进度与状态

测试与验收

  • 单元:积分计算(充足/不够/为0、N 抽即时/计划模式日志与发奖一致
  • 集成:支付回调幂等、计划不足退款、统一查询状态流转
  • 验收:同一 order_no 在两模式下的查询端点返回一致结构与正确状态机;库存扣减准确;退款日志完整

注意事项

  • client_nonce(可选):用于防重复参与提交的客户端幂等键;不影响支付与开奖
  • 安全JWT校验、签名凭证不泄露种子所有金额与积分变更审计入库