11 KiB
11 KiB
需求15/16/18/19/20/21 技术方案
需求15:套餐下架后允许续费
业务规则
- 下架套餐(
shelf_status=2)不可被新购 - 下架套餐可以续费(已在使用该套餐的客户)
- 续费仅支持客户自己购买(不允许代理代购下架套餐给新客户)
后端
当前逻辑:下架套餐在购买时被拦截。
修改:在订单创建校验中,区分"新购"和"续费"场景:
// internal/service/order/service.go 或 client_order/service.go
func (s *Service) validatePackageAvailability(
ctx context.Context,
pkg *model.Package,
assetID uint,
isRenewal bool,
) error {
if pkg.Status == constants.StatusDisabled { // 0=禁用,定义在 pkg/constants/constants.go
return errors.New(errors.CodeForbidden, "套餐已禁用")
}
if pkg.ShelfStatus == constants.ShelfStatusOff && !isRenewal { // 2=下架
return errors.New(errors.CodeForbidden, "套餐已下架,不可新购")
}
return nil
}
isRenewal 判断:查当前资产是否有该套餐的历史生效记录:
func (s *Service) isRenewal(ctx context.Context, assetType string, assetID uint, packageID uint) bool {
count, _ := s.packageUsageStore.CountByAssetAndPackage(ctx, assetType, assetID, packageID)
return count > 0
}
前端
C端续费页面:下架套餐不在"新购"列表中展示,但在"续费"入口中仍可展示。
后台代购时:下架套餐的"代购"按钮禁用,tooltip 提示"套餐已下架,不可代购"。
需求16:代理分销码与佣金提现
业务规则
DST-001:新建代理时自动建立分销归属关系(指定发展人)
DST-002:员工可作为代理发展人进行标识
二维码:代理或员工生成推广二维码 → 扫码进入H5填写信息 → 提交成为代理申请 → 平台审批
数据库变更
-- 1. Shop 表新增发展人字段
ALTER TABLE tb_shop
ADD COLUMN referrer_type VARCHAR(20) DEFAULT NULL
COMMENT '发展人类型 shop=代理介绍 admin=员工介绍',
ADD COLUMN referrer_id BIGINT DEFAULT NULL
COMMENT '发展人ID(shop_id 或 tb_account.id)',
ADD COLUMN referrer_name VARCHAR(50) DEFAULT NULL
COMMENT '发展人姓名快照';
-- 2. 分销码表
CREATE TABLE tb_distribution_code (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
code VARCHAR(32) NOT NULL,
owner_type VARCHAR(20) NOT NULL, -- shop=代理 | admin=员工
owner_id BIGINT NOT NULL,
owner_name VARCHAR(50) NOT NULL DEFAULT '',
qr_code_url TEXT,
use_count INT NOT NULL DEFAULT 0,
status INT NOT NULL DEFAULT 1, -- 1=有效 0=禁用
expires_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX idx_distribution_code ON tb_distribution_code(code) WHERE deleted_at IS NULL;
-- 3. 代理申请表(H5扫码提交)
CREATE TABLE tb_agent_application (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
apply_no VARCHAR(30) NOT NULL,
distribution_code VARCHAR(32) NOT NULL,
referrer_type VARCHAR(20) NOT NULL,
referrer_id BIGINT NOT NULL,
applicant_name VARCHAR(50) NOT NULL,
applicant_phone VARCHAR(20) NOT NULL,
shop_name VARCHAR(100) NOT NULL,
province VARCHAR(50),
city VARCHAR(50),
district VARCHAR(50),
address VARCHAR(255),
status INT NOT NULL DEFAULT 1, -- 1=待审批 2=已通过 3=已拒绝
reject_reason TEXT,
reviewed_by BIGINT,
reviewed_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX idx_agent_application_no ON tb_agent_application(apply_no) WHERE deleted_at IS NULL;
API 设计
生成分销码:
POST /admin/distribution-codes
body: { owner_type: "admin"|"shop", owner_id: 123 }
H5 扫码获取分销码信息:
GET /app/distribution-codes/{code}
H5 提交代理申请:
POST /app/agent-applications
body: { distribution_code, applicant_name, applicant_phone, shop_name, ... }
后台代理申请列表/审批:
GET /admin/agent-applications?status=1
POST /admin/agent-applications/{id}/approve
POST /admin/agent-applications/{id}/reject
body: { reject_reason: "..." }
审批通过时:自动创建 tb_shop + tb_account,设置 referrer_type 和 referrer_id。
佣金提现(DST-003~007):
现有 commission 框架基础上新增材料上传字段,提现申请时一次性上传所有材料(对象存储 Key 列表)。
需求18:多人审批(APR-001~009)
依赖
APR-009(企微审批对接)= Phase 2。
实现要点
| 编号 | 需求 | 实现 |
|---|---|---|
| APR-001~003 | 充值/退款多级审核 | 见需求20/21 |
| APR-004 | 审核环节:部门领导→财务 | 审批流固定两步:step=1 部门领导,step=2 财务 |
| APR-005 | 待审核有消息提示 | 站内消息 NotifyTypeApprovalPending |
| APR-006 | 上一级完成后才提示下一级 | ApprovalFlowAggregate.Approve() 触发 ApprovalStepAdvancedEvent |
| APR-007 | 通过后通知申请人 | ApprovalCompletedEvent handler |
| APR-008 | 驳回/退回后通知申请人含原因 | ApprovalRejectedEvent / ApprovalReturnedEvent handler |
| APR-009 | 对接企微 | Phase 2 |
业务表与审批流的关联
充值单、退款单接入审批流,各自新增 approval_flow_id 字段:
ALTER TABLE tb_refund_request ADD COLUMN approval_flow_id BIGINT DEFAULT NULL
COMMENT '当前审批流ID(已退回后重新提交会更新为新流程ID)';
ALTER TABLE tb_agent_recharge_record ADD COLUMN approval_flow_id BIGINT DEFAULT NULL
COMMENT '当前审批流ID(已退回后重新提交会更新为新流程ID)';
接入协议详见 审批流文档 - 接入协议。
需求19:批量订购套餐(BPO-001~008)
业务流程
- 员工进入批量订购页面
- 选择代理 + 上传 Excel(字段:资产类型/资产标识/套餐编码/套餐名称/支付方式)
- 系统解析并校验每一行
- 支付方式:
offline=线下支付(默认成功)/agent_wallet=代理钱包支付 - 代理钱包支付时从代理余额扣款(含信用额度)
- 线下支付时标记为已支付(不走钱包)
- 校验失败的行展示在页面,带失败原因
- 线下支付需上传整批次的支付凭证
数据库变更
CREATE TABLE tb_bulk_purchase_task (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
task_no VARCHAR(30) NOT NULL,
shop_id BIGINT NOT NULL,
operator_id BIGINT NOT NULL,
payment_method VARCHAR(20) NOT NULL, -- offline | agent_wallet
voucher_keys JSONB, -- 线下支付凭证列表
total_count INT NOT NULL DEFAULT 0,
success_count INT NOT NULL DEFAULT 0,
fail_count INT NOT NULL DEFAULT 0,
status INT NOT NULL DEFAULT 1, -- 1=处理中 2=已完成
failed_items JSONB -- 失败明细
);
CREATE UNIQUE INDEX idx_bulk_purchase_task_no ON tb_bulk_purchase_task(task_no) WHERE deleted_at IS NULL;
失败明细存 JSONB(
failed_items),与现有 DeviceImportTask 模式一致,无需单独明细表。
API 设计
下载 Excel 模板:
GET /admin/bulk-purchases/template
上传并提交:
POST /admin/bulk-purchases
Content-Type: multipart/form-data
字段:
shop_id: 123
payment_method: offline | agent_wallet
voucher_keys: ["key1","key2"] (offline 时必填)
file: <Excel文件>
查询任务状态:
GET /admin/bulk-purchases/{task_id}
需求20:退款审批
依赖
基于 审批流基础设施。
退款单现有状态(不变)
1=待审批 2=已通过 3=已拒绝 4=已退回
退款单本身的状态含义不变,审批进度由 tb_approval_flow 管理,两者通过 approval_flow_id 关联。
流程
提交退款申请(POST /admin/refund-requests)
→ 创建 RefundRequest(status=1 待审批)
→ 调 SubmitApprovalUseCase 创建 ApprovalFlow(biz_type=refund)
→ 把 approval_flow_id 写入 RefundRequest
审批人操作(POST /admin/approvals/{flow_id}/approve|reject|return)
→ ApprovalCompletedEvent → RefundRequest.status=2(已通过)→ 触发实际退款
→ ApprovalRejectedEvent → RefundRequest.status=3(已拒绝)
→ ApprovalReturnedEvent → RefundRequest.status=4(已退回)→ 通知提交人可修改
退回后重新提交
PUT /admin/refund-requests/{id} 仅 status=4 时可编辑退款单内容
POST /admin/refund-requests/{id}/resubmit
→ 校验 status=4
→ 新建 ApprovalFlow
→ 更新 approval_flow_id,status 回到 1(待审批)
需求21:充值审核流程
充值单现有状态
tb_agent_recharge_record:1=待支付 2=已支付 3=已完成 4=已关闭 5=已退款
员工线下充值走审批流,需在现有状态基础上新增"已退回"状态:
-- status 说明更新(不改原有值语义,追加新状态)
-- 6=已退回(审批人退回给提交人修改)
充值业务的状态语义:
1=待支付:创建未支付(线下充值等待审批时也停在这里,由 approval_flow_id 判断是否在审批中)2=已支付:支付成功(线下充值审批通过后跳到已完成,不经过此状态)3=已完成:充值到账4=已关闭:取消/超时5=已退款:退款6=已退回:审批人退回给提交人修改
流程
代理自行充值(不走审批):
代理提交充值申请 → 系统生成收款码 → 代理扫码支付 → 回调自动充值到钱包
员工线下代充值(走审批):
POST /admin/agent-recharge-records(payment_method=offline)
→ 创建 AgentRechargeRecord(status=1)
→ 调 SubmitApprovalUseCase 创建 ApprovalFlow(biz_type=recharge)
→ 把 approval_flow_id 写入记录
审批通过 → AgentRechargeRecord.status=3(已完成)→ 实际充值到钱包
审批驳回 → AgentRechargeRecord.status=4(已关闭)
审批退回 → AgentRechargeRecord.status=6(已退回)→ 通知提交人可修改
退回后重新提交
PUT /admin/agent-recharge-records/{id} 仅 status=6 时可编辑
POST /admin/agent-recharge-records/{id}/resubmit
→ 校验 status=6
→ 新建 ApprovalFlow
→ 更新 approval_flow_id,status 回到 1(待支付/待审批)