Files
junhong_cmp_fiber/docs/add-force-recharge-system/功能总结.md
huang 62708892ec
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m2s
文档
2026-01-31 13:06:30 +08:00

10 KiB
Raw Blame History

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

功能概述

本次实现包含三个核心功能模块:

  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

请求参数

{
  "resource_type": "iot_card",  // 资源类型: iot_card | device
  "resource_id": 123,            // 资源ID
  "amount": 20000,               // 充值金额200元
  "payment_method": "wechat"     // 支付方式: wechat | alipay
}

响应数据

{
  "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

响应数据

{
  "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

请求参数

{
  "order_type": "iot_card",
  "resource_id": 123,
  "package_ids": [1, 2, 3]
}

响应数据

{
  "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 表新增字段

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 表新增字段

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 表(新增)

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. 查询充值要求
GET /api/h5/wallets/recharge-check?resource_type=iot_card&resource_id=123
# 响应:需要强充 200 元
  1. 创建充值订单
POST /api/h5/wallets/recharge
{
  "resource_type": "iot_card",
  "resource_id": 123,
  "amount": 20000,
  "payment_method": "wechat"
}
# 响应:充值订单号 RCH17698320001234567890
  1. 发起支付
POST /api/h5/orders/:id/wechat-pay/jsapi
# 获取微信支付参数,跳转支付
  1. 支付成功后自动触发
  • 钱包余额增加 200 元
  • 累计充值更新
  • 满足阈值时触发一次性佣金
  1. 创建套餐订单
POST /api/h5/orders
{
  "order_type": "iot_card",
  "resource_id": 123,
  "package_ids": [1, 2, 3]
}
# 强充验证通过,订单创建成功

场景 2平台代购订单

  1. 预检套餐价格
POST /api/admin/orders/purchase-check
{
  "order_type": "iot_card",
  "resource_id": 456,
  "package_ids": [10]
}
# 响应:总价 399 元(代购订单无需强充)
  1. 创建代购订单
POST /api/admin/orders
{
  "order_type": "iot_card",
  "resource_id": 456,
  "package_ids": [10],
  "payment_method": "offline"
}
# 响应:订单创建成功,状态直接为"已支付",套餐已激活
  1. 自动处理
  • 订单状态:已支付
  • 套餐激活:立即生效
  • 差价佣金:正常计算
  • 累计充值:不更新
  • 一次性佣金:不触发

注意事项

  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