## 执行逻辑（目标态）

### 参与下单（仅创建订单，不开奖）
- 接口：`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`：新增 handler（internal/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校验、签名凭证不泄露种子；所有金额与积分变更审计入库
