Files
junhong_cmp_fiber/docs/7月迭代/需求15-16-18-19-20-21-复杂需求.md
2026-07-13 12:01:18 +09:00

11 KiB
Raw Blame History

需求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 '发展人IDshop_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_typereferrer_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

业务流程

  1. 员工进入批量订购页面
  2. 选择代理 + 上传 Excel字段资产类型/资产标识/套餐编码/套餐名称/支付方式)
  3. 系统解析并校验每一行
  4. 支付方式:offline=线下支付(默认成功) / agent_wallet=代理钱包支付
  5. 代理钱包支付时从代理余额扣款(含信用额度)
  6. 线下支付时标记为已支付(不走钱包)
  7. 校验失败的行展示在页面,带失败原因
  8. 线下支付需上传整批次的支付凭证

数据库变更

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;

失败明细存 JSONBfailed_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
  → 创建 RefundRequeststatus=1 待审批)
  → 调 SubmitApprovalUseCase 创建 ApprovalFlowbiz_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_idstatus 回到 1待审批

需求21充值审核流程

充值单现有状态

tb_agent_recharge_record1=待支付 2=已支付 3=已完成 4=已关闭 5=已退款

员工线下充值走审批流,需在现有状态基础上新增"已退回"状态

-- status 说明更新(不改原有值语义,追加新状态)
-- 6=已退回(审批人退回给提交人修改)

充值业务的状态语义:

  • 1=待支付:创建未支付(线下充值等待审批时也停在这里,由 approval_flow_id 判断是否在审批中)
  • 2=已支付:支付成功(线下充值审批通过后跳到已完成,不经过此状态)
  • 3=已完成:充值到账
  • 4=已关闭:取消/超时
  • 5=已退款:退款
  • 6=已退回:审批人退回给提交人修改

流程

代理自行充值(不走审批)

代理提交充值申请 → 系统生成收款码 → 代理扫码支付 → 回调自动充值到钱包

员工线下代充值(走审批)

POST /admin/agent-recharge-recordspayment_method=offline
  → 创建 AgentRechargeRecordstatus=1
  → 调 SubmitApprovalUseCase 创建 ApprovalFlowbiz_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_idstatus 回到 1待支付/待审批)