# Capability: 钱包充值

## Purpose

本 capability 定义钱包充值功能，允许个人客户为卡/设备钱包充值，支持强充验证、第三方支付和充值后的累计充值更新与一次性佣金触发。
## Requirements
### Requirement: 创建钱包充值订单

系统 SHALL 允许个人客户创建钱包充值订单。创建前 MUST 验证强充要求，强充场景下充值金额必须等于要求的强充金额。

#### Scenario: 无强充要求时自由充值
- **WHEN** 个人客户为卡/设备创建充值订单，该卡/设备无强充要求，充值金额 100 元
- **THEN** 系统创建充值订单，状态为待支付，金额 10000 分

#### Scenario: 首次充值强充
- **WHEN** 卡关联系列配置为首次充值触发，阈值 100 元，客户尝试充值 100 元
- **THEN** 系统验证通过，创建充值订单，金额 10000 分

#### Scenario: 首次充值金额不符
- **WHEN** 卡关联系列配置为首次充值触发，阈值 100 元，客户尝试充值 50 元
- **THEN** 系统返回错误 "必须充值100元"

#### Scenario: 累计充值启用强充
- **WHEN** 卡关联系列配置为累计充值触发，启用强充，强充金额 100 元，客户尝试充值 100 元
- **THEN** 系统验证通过，创建充值订单

#### Scenario: 累计充值强充金额不符
- **WHEN** 卡关联系列配置为累计充值触发，启用强充，强充金额 100 元，客户尝试充值 50 元
- **THEN** 系统返回错误 "必须充值100元"

#### Scenario: 累计充值未启用强充
- **WHEN** 卡关联系列配置为累计充值触发，未启用强充，客户充值任意金额
- **THEN** 系统创建充值订单

#### Scenario: 充值订单号唯一
- **WHEN** 创建充值订单
- **THEN** 系统生成唯一充值单号，格式为 RCH + 14位时间戳 + 6位随机数

---

### Requirement: 查询充值订单列表

系统 SHALL 提供充值订单列表查询，支持按状态筛选、时间范围筛选。

#### Scenario: 查询个人客户的充值订单
- **WHEN** 个人客户查询充值订单列表
- **THEN** 系统返回该客户的所有充值订单

#### Scenario: 按状态筛选
- **WHEN** 客户指定充值状态筛选（待支付/已支付/已完成）
- **THEN** 系统只返回匹配状态的充值订单

#### Scenario: 分页查询
- **WHEN** 查询充值订单列表
- **THEN** 系统使用分页返回，默认每页 20 条，最大 100 条

---

### Requirement: 查询充值订单详情

系统 SHALL 允许个人客户查询充值订单详情。

#### Scenario: 查询自己的充值订单
- **WHEN** 客户查询自己的充值订单详情
- **THEN** 系统返回订单信息（充值单号、金额、支付方式、状态、时间等）

#### Scenario: 查询他人充值订单
- **WHEN** 客户尝试查询不属于自己的充值订单
- **THEN** 系统返回 "充值订单不存在" 错误

---

### Requirement: 充值支付（微信/支付宝）

系统 SHALL 支持通过微信支付和支付宝支付完成充值。

#### Scenario: 微信 JSAPI 支付
- **WHEN** 客户在微信内选择充值，使用微信支付
- **THEN** 系统调用微信支付 JSAPI 接口，返回支付参数

#### Scenario: 微信 H5 支付
- **WHEN** 客户在浏览器内选择充值，使用微信支付
- **THEN** 系统调用微信支付 H5 接口，返回支付跳转 URL

#### Scenario: 支付宝支付
- **WHEN** 客户选择支付宝支付充值
- **THEN** 系统调用支付宝接口，返回支付参数

---

### Requirement: 充值支付回调处理

系统 SHALL 处理微信和支付宝的支付回调，验证签名，更新充值订单状态，增加钱包余额。

#### Scenario: 微信支付回调成功
- **WHEN** 收到微信支付成功回调，验证签名通过
- **THEN** 系统更新充值订单状态为已支付
- **AND** 增加对应钱包余额
- **AND** 创建钱包交易记录
- **AND** 返回成功响应给微信

#### Scenario: 支付宝回调成功
- **WHEN** 收到支付宝支付成功回调，验证签名通过
- **THEN** 系统更新充值订单状态为已支付
- **AND** 增加对应钱包余额
- **AND** 创建钱包交易记录

#### Scenario: 签名验证失败
- **WHEN** 收到支付回调，签名验证失败
- **THEN** 系统记录错误日志，不处理订单，返回失败响应

#### Scenario: 重复回调幂等处理
- **WHEN** 收到同一充值订单的重复支付回调
- **THEN** 系统检查订单状态，如果已支付则直接返回成功，不重复处理

---

### Requirement: 充值成功更新累计充值金额

充值支付成功后系统 SHALL 更新卡/设备的累计充值金额（AccumulatedRecharge）。

#### Scenario: 充值成功累加充值金额
- **WHEN** 卡钱包充值 100 元成功，当前累计充值 200 元
- **THEN** 系统更新卡的累计充值为 300 元（200 + 100）

#### Scenario: 设备充值成功累加充值金额
- **WHEN** 设备钱包充值 200 元成功，当前累计充值 500 元
- **THEN** 系统更新设备的累计充值为 700 元（500 + 200）

#### Scenario: 使用原子操作更新
- **WHEN** 更新累计充值金额
- **THEN** 系统使用 SQL 原子操作或 GORM 乐观锁确保并发安全

---

### Requirement: 充值成功触发一次性佣金判断

充值支付成功后系统 SHALL 检查是否达到一次性佣金阈值，如果达到则触发佣金计算。

#### Scenario: 首次充值达到阈值
- **WHEN** 卡配置为首次充值触发，阈值 100 元，客户充值 100 元
- **THEN** 系统触发一次性佣金计算，发放佣金

#### Scenario: 累计充值达到阈值
- **WHEN** 卡配置为累计充值触发，阈值 1000 元，累计充值已达到 1000 元
- **THEN** 系统触发一次性佣金计算，发放佣金

#### Scenario: 未达阈值不触发
- **WHEN** 充值后累计充值未达到阈值
- **THEN** 系统不触发一次性佣金计算

#### Scenario: 已发放过不重复触发
- **WHEN** 卡的一次性佣金已发放过（first_commission_paid = true）
- **THEN** 系统不触发一次性佣金计算

---

### Requirement: 充值订单状态流转

充值订单状态 SHALL 按以下流程流转：待支付 → 已支付 → 已完成。

#### Scenario: 正常流转
- **WHEN** 创建充值订单 → 支付成功 → 钱包余额增加完成
- **THEN** 订单状态依次为：1（待支付）→ 2（已支付）→ 3（已完成）

#### Scenario: 超时未支付
- **WHEN** 充值订单创建 30 分钟后仍未支付
- **THEN** 系统标记订单为已关闭（状态 4）

---

### Requirement: 充值金额限制

系统 SHALL 限制单次充值金额范围。

#### Scenario: 充值金额范围
- **WHEN** 创建充值订单
- **THEN** 充值金额必须在 1 元到 100000 元之间

#### Scenario: 充值金额过小
- **WHEN** 客户尝试充值 0.5 元
- **THEN** 系统返回错误 "充值金额不能小于1元"

#### Scenario: 充值金额过大
- **WHEN** 客户尝试充值 200000 元
- **THEN** 系统返回错误 "单次充值金额不能超过100000元"

### Requirement: 充值回调事务一致性

`HandlePaymentCallback` 内的 `UpdateStatusWithOptimisticLock` 与 `UpdatePaymentInfo` MUST 使用同一个事务内 `tx` 执行，保证充值状态与支付信息的原子性。

#### Scenario: 回调处理中状态更新与支付信息更新同事务
- **WHEN** 收到支付成功回调并进入 `HandlePaymentCallback`
- **THEN** 系统 MUST 在同一事务 `tx` 内执行 `UpdateStatusWithOptimisticLock`
- **THEN** 系统 MUST 在同一事务 `tx` 内执行 `UpdatePaymentInfo`

#### Scenario: 事务失败整体回滚
- **WHEN** 回调处理中任一步骤失败
- **THEN** 系统 MUST 回滚该事务，保证订单状态与支付信息不出现部分成功

---

### Requirement: Store 方法签名支持事务参数

系统 MUST 调整充值相关 Store 方法签名，支持显式传入 `*gorm.DB tx` 参数，以保证事务边界可控。

#### Scenario: Service 传入事务句柄
- **WHEN** Service 在事务上下文调用 Store 更新充值记录
- **THEN** Store 方法 MUST 接收并使用传入的 `tx` 执行数据库操作

---

### Requirement: 充值回调采用两阶段处理

系统 MUST 将强充场景的充值回调改为两阶段：第一阶段同步事务内完成入账与状态更新，第二阶段异步执行自动购买。第一阶段 SHALL 包含：更新充值状态、钱包加款、累计充值更新、首充佣金判断。第二阶段 SHALL 通过 Asynq 任务执行钱包扣款、创建套餐订单、激活套餐。该改造适用于客户端触发的强充路径，且不影响非强充充值主流程。

#### Scenario: 强充回调同步入账成功并触发异步任务
- **WHEN** 强充充值支付回调验签成功
- **THEN** 系统在事务内完成钱包入账与充值单状态更新
- **AND** 入队 `AutoPurchaseAfterRecharge` 异步任务

---

### Requirement: 充值记录新增 auto_purchase_status 状态追踪

系统 MUST 在 `AssetRechargeRecord` 增加 `auto_purchase_status` 字段，用于追踪强充后二阶段自动购买状态。状态集 SHALL 至少包括：`pending`、`success`、`failed`。创建强充充值单时 MUST 初始化为 `pending`；异步购买成功后 MUST 更新为 `success`；重试耗尽后 MUST 更新为 `failed`。

#### Scenario: 强充充值单创建时默认 pending
- **WHEN** 系统创建与套餐联动的强充充值单
- **THEN** 充值记录 `auto_purchase_status` 初始化为 `pending`

---

### Requirement: 异步自动购买失败处理规范

系统 SHALL 对 `AutoPurchaseAfterRecharge` 失败场景执行统一处理：任务 MUST 自动重试（最多 3 次）；全部失败后 MUST 记录错误日志并将 `auto_purchase_status` 置为 `failed`；用户资金 SHALL 保留在钱包中，允许后续手动购买，不得回滚已成功的充值入账。

#### Scenario: 异步任务最终失败
- **WHEN** 自动购买任务连续失败并达到最大重试次数
- **THEN** 系统将 `auto_purchase_status` 标记为 `failed`
- **AND** 钱包余额保持可用，用户可手动下单

