## ADDED Requirements

### Requirement: 钱包实体定义

系统 SHALL 定义钱包(Wallet)实体，统一管理用户钱包和代理钱包，支持余额管理、充值、扣款等操作。

**核心概念**：
- **用户钱包**：普通用户和企业用户的钱包，用于购买套餐
- **代理钱包**：代理商的钱包，支持预充值，可用成本价购买套餐

**实体字段**：
- `id`：钱包 ID（主键，BIGINT）
- `user_id`：用户 ID（BIGINT，关联 tb_account.id）
- `wallet_type`：钱包类型（VARCHAR(20)，枚举值："user"-用户钱包 | "agent"-代理钱包）
- `balance`：余额（BIGINT，单位：分，默认 0）
- `frozen_balance`：冻结余额（BIGINT，单位：分，默认 0，用于订单待支付、提现申请中等场景）
- `currency`：币种（VARCHAR(10)，默认 "CNY"）
- `status`：钱包状态（INT，1-正常 2-冻结 3-关闭）
- `version`：版本号（INT，默认 0，乐观锁字段，用于防止并发扣款）
- `creator`：创建人 ID（BIGINT）
- `updater`：更新人 ID（BIGINT）
- `created_at`：创建时间（TIMESTAMP，自动填充）
- `updated_at`：更新时间（TIMESTAMP，自动填充）
- `deleted_at`：删除时间（TIMESTAMP，可空，软删除）

**唯一约束**：`(user_id, wallet_type, currency)` 在 `deleted_at IS NULL` 条件下唯一

**可用余额计算**：可用余额 = balance - frozen_balance

#### Scenario: 创建用户钱包

- **WHEN** 用户（ID 为 2001）首次充值
- **THEN** 系统创建钱包记录，`user_id` 为 2001，`wallet_type` 为 "user"，`balance` 为 0，`status` 为 1（正常）

#### Scenario: 创建代理钱包

- **WHEN** 代理商（ID 为 123）首次充值
- **THEN** 系统创建钱包记录，`user_id` 为 123，`wallet_type` 为 "agent"，`balance` 为 0，`status` 为 1（正常）

#### Scenario: 计算可用余额

- **WHEN** 用户钱包余额为 10000 分（100 元），冻结余额为 3000 分（30 元）
- **THEN** 系统计算可用余额为 7000 分（70 元）

---

### Requirement: 钱包明细记录

系统 SHALL 记录所有钱包余额变动，包括充值、扣款、退款、分佣、提现等操作，确保完整的审计追踪。

**实体字段**：
- `id`：明细 ID（主键，BIGINT）
- `wallet_id`：钱包 ID（BIGINT，关联 tb_wallet.id）
- `user_id`：用户 ID（BIGINT，关联 tb_account.id）
- `transaction_type`：交易类型（VARCHAR(20)，枚举值："recharge"-充值 | "deduct"-扣款 | "refund"-退款 | "commission"-分佣 | "withdrawal"-提现）
- `amount`：变动金额（BIGINT，单位：分，正数为增加，负数为减少）
- `balance_before`：变动前余额（BIGINT，单位：分）
- `balance_after`：变动后余额（BIGINT，单位：分）
- `status`：交易状态（INT，1-成功 2-失败 3-处理中）
- `reference_type`：关联业务类型（VARCHAR(50)，如 "order" | "commission" | "withdrawal" | "topup"）
- `reference_id`：关联业务 ID（BIGINT）
- `remark`：备注（TEXT）
- `metadata`：扩展信息（JSONB，如手续费、支付方式等）
- `creator`：创建人 ID（BIGINT）
- `created_at`：创建时间（TIMESTAMP，自动填充）
- `updated_at`：更新时间（TIMESTAMP，自动填充）
- `deleted_at`：删除时间（TIMESTAMP，可空，软删除）

#### Scenario: 充值创建明细记录

- **WHEN** 用户（ID 为 2001）充值 10000 分（100 元）
- **THEN** 系统创建钱包明细记录，`transaction_type` 为 "recharge"，`amount` 为 10000，`balance_before` 为 0，`balance_after` 为 10000，`status` 为 1（成功）

#### Scenario: 购买套餐扣款创建明细记录

- **WHEN** 用户（ID 为 2001）使用钱包支付购买套餐，金额 3000 分（30 元）
- **THEN** 系统创建钱包明细记录，`transaction_type` 为 "deduct"，`amount` 为 -3000，`balance_before` 为 10000，`balance_after` 为 7000，`reference_type` 为 "order"，`reference_id` 为订单 ID

#### Scenario: 分佣发放创建明细记录

- **WHEN** 代理（ID 为 123）的分佣 5000 分（50 元）审批通过并发放
- **THEN** 系统创建钱包明细记录，`transaction_type` 为 "commission"，`amount` 为 5000，`balance_before` 为 20000，`balance_after` 为 25000，`reference_type` 为 "commission"，`reference_id` 为分佣记录 ID

---

### Requirement: 充值记录管理

系统 SHALL 记录所有充值操作，包括充值订单号、金额、支付方式、支付状态等信息。

**实体字段**：
- `id`：充值记录 ID（主键，BIGINT）
- `user_id`：用户 ID（BIGINT，关联 tb_account.id）
- `wallet_id`：钱包 ID（BIGINT，关联 tb_wallet.id）
- `recharge_no`：充值订单号（VARCHAR(50)，唯一）
- `amount`：充值金额（BIGINT，单位：分）
- `payment_method`：支付方式（VARCHAR(20)，枚举值："alipay"-支付宝 | "wechat"-微信 | "bank"-银行转账 | "offline"-线下）
- `payment_channel`：支付渠道（VARCHAR(50)）
- `payment_transaction_id`：第三方支付交易号（VARCHAR(100)）
- `status`：充值状态（INT，1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款）
- `paid_at`：支付时间（TIMESTAMP，可空）
- `completed_at`：完成时间（TIMESTAMP，可空）
- `creator`：创建人 ID（BIGINT）
- `updater`：更新人 ID（BIGINT）
- `created_at`：创建时间（TIMESTAMP，自动填充）
- `updated_at`：更新时间（TIMESTAMP，自动填充）
- `deleted_at`：删除时间（TIMESTAMP，可空，软删除）

#### Scenario: 创建充值订单

- **WHEN** 用户（ID 为 2001）发起充值 10000 分（100 元），选择支付宝支付
- **THEN** 系统创建充值记录，生成唯一的 `recharge_no`，`amount` 为 10000，`payment_method` 为 "alipay"，`status` 为 1（待支付）

#### Scenario: 充值支付完成

- **WHEN** 用户完成支付宝支付
- **THEN** 系统将充值记录状态从 1（待支付）变更为 2（已支付），记录 `paid_at` 时间和 `payment_transaction_id`

#### Scenario: 充值到账

- **WHEN** 充值记录状态为 2（已支付），系统处理充值到账
- **THEN** 系统将钱包余额增加 10000 分，创建钱包明细记录，将充值记录状态变更为 3（已完成），记录 `completed_at` 时间

---

### Requirement: 钱包余额操作

系统 SHALL 支持钱包余额的充值、扣款、退款、冻结、解冻等操作，使用乐观锁防止并发问题。

**操作类型**：
- **充值**：增加钱包余额
- **扣款**：减少钱包余额（如购买套餐）
- **退款**：增加钱包余额（如订单退款）
- **冻结**：将部分余额转为冻结状态（如订单待支付）
- **解冻**：将冻结余额转回可用余额（如订单取消）

**并发控制**：
- 使用 `version` 字段实现乐观锁
- 每次更新余额时，检查 `version` 是否匹配
- 如果 `version` 不匹配，说明有并发更新，操作失败并重试

#### Scenario: 钱包充值

- **WHEN** 用户钱包当前余额为 10000 分，充值 5000 分
- **THEN** 系统将钱包余额更新为 15000 分，`version` 从 1 变更为 2，创建钱包明细记录

#### Scenario: 钱包扣款

- **WHEN** 用户钱包当前余额为 15000 分，购买套餐扣款 3000 分
- **THEN** 系统检查可用余额（15000 - 0 = 15000）≥ 3000，将钱包余额更新为 12000 分，`version` 从 2 变更为 3，创建钱包明细记录

#### Scenario: 余额不足扣款失败

- **WHEN** 用户钱包当前余额为 2000 分，购买套餐需要扣款 3000 分
- **THEN** 系统检查可用余额（2000 - 0 = 2000）< 3000，拒绝扣款，返回错误信息"余额不足"

#### Scenario: 并发扣款乐观锁生效

- **WHEN** 用户钱包当前余额为 10000 分，version 为 1，两个并发请求同时扣款 3000 分和 5000 分
- **THEN** 第一个请求成功，余额变为 7000 分，version 变为 2；第二个请求因 version 不匹配失败，需重新读取最新余额（7000 分）后重试

#### Scenario: 冻结余额

- **WHEN** 用户创建订单 10001，订单金额 3000 分，选择钱包支付
- **THEN** 系统将钱包的 `frozen_balance` 增加 3000 分，可用余额减少 3000 分

#### Scenario: 解冻余额

- **WHEN** 用户取消订单 10001，订单金额 3000 分
- **THEN** 系统将钱包的 `frozen_balance` 减少 3000 分，可用余额增加 3000 分

---

### Requirement: 钱包数据校验

系统 SHALL 对钱包数据进行校验，确保数据完整性和一致性。

**校验规则**：
- `user_id`：必填，≥ 1
- `wallet_type`：必填，枚举值 "user" | "agent"
- `balance`：必填，≥ 0
- `frozen_balance`：必填，≥ 0，≤ balance
- `currency`：必填，长度 1-10 字符
- `status`：必填，枚举值 1-3
- `version`：必填，≥ 0

#### Scenario: 创建钱包时 user_id 无效

- **WHEN** 创建钱包，`user_id` 为 0
- **THEN** 系统拒绝创建，返回错误信息"用户 ID 无效"

#### Scenario: 创建钱包时 wallet_type 无效

- **WHEN** 创建钱包，`wallet_type` 为 "invalid"
- **THEN** 系统拒绝创建，返回错误信息"钱包类型无效"

#### Scenario: 冻结余额超过总余额

- **WHEN** 钱包余额为 10000 分，尝试冻结 15000 分
- **THEN** 系统拒绝操作，返回错误信息"冻结余额不能超过总余额"
