Files
huang 62708892ec
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m2s
文档
2026-01-31 13:06:30 +08:00

396 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 强充系统和代购订单功能总结
## 功能概述
本次实现包含三个核心功能模块:
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`