# 强充系统和代购订单功能总结

## 功能概述

本次实现包含三个核心功能模块：
1. **钱包充值系统**：个人客户可通过微信/支付宝为钱包充值
2. **强充要求机制**：套餐购买前强制要求充值指定金额
3. **代购订单支持**：平台可代客户购买套餐并跳过佣金计算

---

## 业务规则

### 1. 钱包充值系统

#### 充值限额
- **最小充值金额**：1元（100分）
- **最大充值金额**：100,000元（10,000,000分）

#### 充值订单状态
| 状态码 | 状态名称 | 说明 |
|-------|---------|------|
| 1 | 待支付 | 订单已创建，等待支付 |
| 2 | 已支付 | 支付成功，等待入账 |
| 3 | 已完成 | 钱包余额已增加，佣金已触发 |
| 4 | 已关闭 | 订单超时自动关闭 |
| 5 | 已退款 | 支付退款 |

#### 订单号规则
- 前缀：`RCH`
- 格式：`RCH + 14位时间戳 + 6位随机数`
- 示例：`RCH17698320001234567890`

#### 支付回调处理
- 根据订单号前缀区分订单类型（RCH → 充值订单，其他 → 套餐订单）
- 幂等性处理：已支付/已完成状态不重复处理
- 事务保证：余额增加、状态更新、佣金触发在同一事务内

---

### 2. 强充要求机制

#### 触发条件

**单次充值型**（`single_recharge`）
- 配置：`force_recharge_trigger_type = 1`
- 条件：一次性充值金额 ≥ `force_recharge_amount`
- 场景：新客户首次购买套餐前必须充值 200 元

**累计充值型**（`accumulated_recharge`）
- 配置：`force_recharge_trigger_type = 2`
- 条件：历史累计充值金额 ≥ `force_recharge_amount`
- 场景：老客户需累计充值 1000 元才能购买特定套餐

#### 验证时机
1. **充值预检接口**：`GET /api/h5/wallets/recharge-check`
   - 返回是否需要强充、触发类型、所需金额
2. **套餐购买预检接口**：`POST /api/admin/orders/purchase-check`
   - 返回套餐总价、强充要求、实际支付金额
3. **订单创建**：自动验证强充要求，不满足则拒绝

#### 豁免规则
- 已发放过一次性佣金的卡/设备，无需强充
- 代购订单无需强充验证

---

### 3. 代购订单

#### 适用场景
平台使用线下支付代客户购买套餐，绕过钱包和在线支付流程。

#### 创建条件
- **权限要求**：仅超级管理员和平台用户可创建
- **支付方式**：`payment_method = "offline"`
- **资源归属**：卡/设备必须已分配给某个代理商

#### 业务逻辑差异

| 项目 | 普通订单 | 代购订单 |
|-----|---------|---------|
| 支付方式 | 钱包/微信/支付宝 | 线下支付（offline） |
| 支付状态 | 1-待支付 → 2-已支付 | 直接为 2-已支付 |
| 钱包扣款 | 需要扣款 | 跳过 |
| 差价佣金 | 计算 | 计算 |
| 累计充值更新 | 更新 | **跳过** |
| 一次性佣金触发 | 触发 | **跳过** |
| 套餐激活 | 手动/支付后自动 | 创建后立即自动激活 |

#### 标识字段
- `tb_order.is_purchase_on_behalf = true`（代购订单标识）

---

## API 接口

### 充值相关接口（H5）

#### 1. 创建充值订单
```
POST /api/h5/wallets/recharge
```

**请求参数**：
```json
{
  "resource_type": "iot_card",  // 资源类型: iot_card | device
  "resource_id": 123,            // 资源ID
  "amount": 20000,               // 充值金额（分），200元
  "payment_method": "wechat"     // 支付方式: wechat | alipay
}
```

**响应数据**：
```json
{
  "code": 0,
  "data": {
    "id": 1,
    "recharge_no": "RCH17698320001234567890",
    "user_id": 100,
    "wallet_id": 200,
    "amount": 20000,
    "payment_method": "wechat",
    "status": 1,
    "status_text": "待支付",
    "created_at": "2026-01-31T12:00:00Z"
  }
}
```

#### 2. 充值预检
```
GET /api/h5/wallets/recharge-check?resource_type=iot_card&resource_id=123
```

**响应数据**：
```json
{
  "code": 0,
  "data": {
    "need_force_recharge": true,
    "force_recharge_amount": 20000,
    "trigger_type": "single_recharge",
    "min_amount": 100,
    "max_amount": 10000000,
    "current_accumulated": 5000,
    "threshold": 20000,
    "message": "购买此套餐需先充值200元",
    "first_commission_paid": false
  }
}
```

#### 3. 查询充值订单列表
```
GET /api/h5/wallets/recharges?page=1&page_size=20&status=1
```

**可选参数**：
- `wallet_id`: 钱包ID筛选
- `status`: 状态筛选（1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款）
- `start_time`: 开始时间
- `end_time`: 结束时间

#### 4. 查询充值订单详情
```
GET /api/h5/wallets/recharges/:id
```

---

### 代购订单接口（Admin）

#### 套餐购买预检
```
POST /api/admin/orders/purchase-check
```

**请求参数**：
```json
{
  "order_type": "iot_card",
  "resource_id": 123,
  "package_ids": [1, 2, 3]
}
```

**响应数据**：
```json
{
  "code": 0,
  "data": {
    "total_price": 39900,
    "need_force_recharge": true,
    "force_recharge_amount": 20000,
    "actual_payment": 59900,
    "trigger_type": "single_recharge",
    "message": "需先充值200元，实际支付599元"
  }
}
```

---

## 数据库变更

### 1. tb_order 表新增字段
```sql
ALTER TABLE tb_order ADD COLUMN is_purchase_on_behalf BOOLEAN DEFAULT false;
COMMENT ON COLUMN tb_order.is_purchase_on_behalf IS '是否为代购订单';
```

### 2. tb_shop_series_allocation 表新增字段
```sql
ALTER TABLE tb_shop_series_allocation 
ADD COLUMN enable_force_recharge BOOLEAN DEFAULT false,
ADD COLUMN force_recharge_amount BIGINT DEFAULT 0,
ADD COLUMN force_recharge_trigger_type INTEGER DEFAULT 1;

COMMENT ON COLUMN tb_shop_series_allocation.enable_force_recharge IS '是否启用强充要求';
COMMENT ON COLUMN tb_shop_series_allocation.force_recharge_amount IS '强充金额（分）';
COMMENT ON COLUMN tb_shop_series_allocation.force_recharge_trigger_type IS '强充触发类型: 1-单次充值 2-累计充值';
```

### 3. tb_recharge_record 表（新增）
```sql
CREATE TABLE tb_recharge_record (
  id BIGSERIAL PRIMARY KEY,
  created_at TIMESTAMP,
  updated_at TIMESTAMP,
  deleted_at TIMESTAMP,
  creator BIGINT,
  updater BIGINT,
  recharge_no VARCHAR(30) UNIQUE NOT NULL,
  user_id BIGINT NOT NULL,
  wallet_id BIGINT NOT NULL,
  amount BIGINT NOT NULL,
  payment_method VARCHAR(20) NOT NULL,
  payment_channel VARCHAR(50),
  payment_transaction_id VARCHAR(100),
  status INTEGER NOT NULL DEFAULT 1,
  paid_at TIMESTAMP,
  completed_at TIMESTAMP
);
```

---

## 错误码

| 错误码 | 名称 | 说明 |
|-------|------|------|
| 1120 | CodeRechargeAmountInvalid | 充值金额无效 |
| 1121 | CodeRechargeNotFound | 充值订单不存在 |
| 1122 | CodeRechargeAlreadyPaid | 充值订单已支付 |
| 1130 | CodePurchaseOnBehalfForbidden | 无权创建代购订单 |
| 1131 | CodePurchaseOnBehalfInvalidTarget | 代购订单资源未分配 |
| 1140 | CodeForceRechargeRequired | 需要强充 |
| 1141 | CodeForceRechargeAmountMismatch | 强充金额不足 |

---

## 测试覆盖

### Store 层
- ✅ RechargeStore: 94.7%（CRUD、分页筛选、并发操作）

### Service 层
- ✅ RechargeService: 83.8%（创建、预检、支付回调、佣金触发）
- ✅ OrderService: 95%+（强充验证、代购订单创建、购买预检）
- ✅ CommissionCalculation: 95%+（代购订单跳过一次性佣金和累计充值）

### Handler 层
- ✅ RechargeHandler: 100%（HTTP 接口）
- ✅ OrderHandler: 100%（代购预检接口）
- ✅ PaymentCallback: 100%（充值订单回调支持）

---

## 使用示例

### 场景 1：个人客户充值购买套餐

1. **查询充值要求**
```bash
GET /api/h5/wallets/recharge-check?resource_type=iot_card&resource_id=123
# 响应：需要强充 200 元
```

2. **创建充值订单**
```bash
POST /api/h5/wallets/recharge
{
  "resource_type": "iot_card",
  "resource_id": 123,
  "amount": 20000,
  "payment_method": "wechat"
}
# 响应：充值订单号 RCH17698320001234567890
```

3. **发起支付**
```bash
POST /api/h5/orders/:id/wechat-pay/jsapi
# 获取微信支付参数，跳转支付
```

4. **支付成功后自动触发**
- 钱包余额增加 200 元
- 累计充值更新
- 满足阈值时触发一次性佣金

5. **创建套餐订单**
```bash
POST /api/h5/orders
{
  "order_type": "iot_card",
  "resource_id": 123,
  "package_ids": [1, 2, 3]
}
# 强充验证通过，订单创建成功
```

---

### 场景 2：平台代购订单

1. **预检套餐价格**
```bash
POST /api/admin/orders/purchase-check
{
  "order_type": "iot_card",
  "resource_id": 456,
  "package_ids": [10]
}
# 响应：总价 399 元（代购订单无需强充）
```

2. **创建代购订单**
```bash
POST /api/admin/orders
{
  "order_type": "iot_card",
  "resource_id": 456,
  "package_ids": [10],
  "payment_method": "offline"
}
# 响应：订单创建成功，状态直接为"已支付"，套餐已激活
```

3. **自动处理**
- 订单状态：已支付
- 套餐激活：立即生效
- 差价佣金：正常计算
- 累计充值：**不更新**
- 一次性佣金：**不触发**

---

## 注意事项

1. **充值订单与套餐订单隔离**
   - 不同的订单表（tb_recharge_record vs tb_order）
   - 不同的订单号前缀（RCH vs 其他）
   - 不同的支付回调处理逻辑

2. **强充验证时机**
   - 充值预检：提前告知用户
   - 购买预检：计算实际支付金额
   - 订单创建：最终验证拦截

3. **代购订单限制**
   - 仅平台账号可创建
   - 必须使用 offline 支付方式
   - 资源必须已分配给代理商

4. **佣金计算规则**
   - 充值订单：触发一次性佣金（满足阈值）
   - 普通套餐订单：触发差价佣金 + 一次性佣金
   - 代购订单：仅触发差价佣金

5. **测试环境配置**
   - 需要加载 `.env.local` 环境变量
   - 使用 `testutils.NewTestTransaction` 自动回滚事务
   - 使用 `testutils.GetTestRedis` 获取全局 Redis 连接

---

## 相关文档

- **设计文档**：`openspec/changes/add-force-recharge-system/design.md`
- **任务清单**：`openspec/changes/add-force-recharge-system/tasks.md`
- **测试连接管理**：`docs/testing/test-connection-guide.md`
- **API 文档生成**：`docs/api-documentation-guide.md`
