Files
junhong_cmp_fiber/migrations/000214_add_agent_distribution_registration.up.sql
break 575d056f54
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat(代理分销提现): 落地扫码注册、提现资料资格与企微终审提现
AUG26-008。

- 迁移 000214–000217:tb_shop 全局唯一且不可修改的随机分销码(含存量回填)、
  tb_agent_distribution_registration 待审批注册记录、tb_withdrawal_qualification 资料版本、
  tb_commission_withdrawal_request_attempt 审批尝试记录,以及提现申请的 latest_*/异常标记列;
  不修改既有迁移,down 在存在本 Change 业务事实或新类型场景行时拒绝破坏性回滚。
- 公开接口 POST /api/c/v1/agent-distribution-registrations:无认证,复用既有短信验证码校验、
  消费与限流;无效分销码、停用上级、验证码无效或已消费统一返回「分销码不可用」且不落库,
  审批通过前不创建店铺、账号或钱包。
- 审批通过才在同一事务内建启用店铺、代理主账号、钱包、上级层级与业务员快照,驳回不建实体,
  重复回调不重复建实体,提交后清理上级下级缓存。
- 提现资料资格按不可变版本保存,替换合同或法人身份证即新增版本并同事务失效旧有效版本;
  超管作废原因必填;代理停用与店铺删除联动失效。
- 提现每次提交或重提新增不可变审批尝试记录并冻结金额;企业微信通过仅一次从冻结扣减、
  保持状态 2 并写 paid_at(不使用状态 4),驳回/cancelled/deleted 仅一次释放,
  通过后撤销不回滚、不重新冻结、只写正交异常标记;加锁顺序统一为申请→尝试→钱包。
- 本地人工终审对已关联审批实例的申请返回状态冲突,approval_instance_id 为空的存量申请保持既有行为,
  不新增任何配置开关。
- 补齐审批业务类型注册点全集:业务类型与场景字段常量、场景 DTO 两处枚举与中文描述、
  场景字段白名单/合法类型/中文名、数据库 CHECK、Worker 决策消费者与装配、审批审计资源映射,
  以及三个新审计资源与 13 个审计动作;失败/拒绝审计改为必达。
- 新增后台路由与 OpenAPI:资格提交/查询/作废、提现申请/重提/详情、店铺详情返回只读分销码。
- 归档本 Change:主 Spec 新增 agent-distribution-withdrawal 能力(5 个 Requirement)。

验证(junhong_cmp_test + Redis DB 6,显式 DB_*,未重置整库):
- 迁移 up → version 217 且 dirty=false → down 3 → up 回 217,fixture 复核残留为 0。
- 受控状态机脚手架 227 项通过 / 0 项失败,覆盖 18 组场景(幂等与乱序回调、资金冻结/释放/重提、
  退款回扣 × 在途提现并发、负向场景拒绝审计与 14 个动作码审计真实落库)。
- gofmt 空、go build/go vet 通过、gendocs 与工作区逐字节一致、context-health 通过、
  openspec validate --strict 通过、doctor healthy;自动化测试按项目决策为 N/A。

运行期前置(未完成,非代码交付物):由超管经 PUT /api/admin/wecom/scenes/{business_type} 为
agent_distribution_approval、withdrawal_qualification_approval、commission_withdrawal_approval
配置启用场景与模板控件映射;未配置时相应提交失败关闭。
2026-09-14 09:45:13 +08:00

82 lines
5.6 KiB
SQL
Raw 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.
-- 代理分销码与公开扫码注册:店铺全局唯一分销码、待审批注册记录。
-- 分销码由应用层随机生成且不可修改,条件唯一索引兜底;不提供人工指定或编辑入口。
-- 注册只落待审批记录,审批通过前不创建店铺、账号、钱包或上下级归属。
-- 不使用数据库外键,关联以 ID 保存并由应用层显式校验。
ALTER TABLE tb_shop
ADD COLUMN IF NOT EXISTS distribution_code VARCHAR(32) NOT NULL DEFAULT '';
COMMENT ON COLUMN tb_shop.distribution_code IS '全局唯一随机分销码,创建时生成且不可修改;空字符串表示历史数据未生成';
CREATE UNIQUE INDEX IF NOT EXISTS uk_shop_distribution_code
ON tb_shop (distribution_code)
WHERE distribution_code <> '' AND deleted_at IS NULL;
CREATE TABLE tb_agent_distribution_registration (
id BIGSERIAL PRIMARY KEY,
distribution_code VARCHAR(32) NOT NULL,
parent_shop_id BIGINT NOT NULL,
phone VARCHAR(20) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
shop_name VARCHAR(100) NOT NULL,
shop_code VARCHAR(50) NOT NULL,
username VARCHAR(50) NOT NULL,
contact_name VARCHAR(50) NOT NULL DEFAULT '',
province VARCHAR(50) NOT NULL DEFAULT '',
city VARCHAR(50) NOT NULL DEFAULT '',
district VARCHAR(50) NOT NULL DEFAULT '',
address VARCHAR(255) NOT NULL DEFAULT '',
status SMALLINT NOT NULL DEFAULT 0,
approval_instance_id BIGINT,
reject_reason VARCHAR(500) NOT NULL DEFAULT '',
decided_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_agent_distribution_registration_code CHECK (distribution_code <> ''),
CONSTRAINT chk_agent_distribution_registration_parent CHECK (parent_shop_id > 0),
CONSTRAINT chk_agent_distribution_registration_phone CHECK (phone <> ''),
CONSTRAINT chk_agent_distribution_registration_password CHECK (password_hash <> ''),
CONSTRAINT chk_agent_distribution_registration_shop CHECK (
shop_name <> '' AND shop_code <> '' AND username <> ''
),
CONSTRAINT chk_agent_distribution_registration_status CHECK (status IN (0, 1, 2)),
CONSTRAINT chk_agent_distribution_registration_instance CHECK (
approval_instance_id IS NULL OR approval_instance_id > 0
)
);
-- 同一手机号可存在多条记录:驳回后再次扫码是新记录、新审批实例。
CREATE INDEX idx_agent_distribution_registration_parent
ON tb_agent_distribution_registration (parent_shop_id, status, id);
CREATE INDEX idx_agent_distribution_registration_phone
ON tb_agent_distribution_registration (phone, id DESC);
CREATE UNIQUE INDEX uk_agent_distribution_registration_instance
ON tb_agent_distribution_registration (approval_instance_id)
WHERE approval_instance_id IS NOT NULL;
COMMENT ON TABLE tb_agent_distribution_registration IS '代理扫码注册待审批记录,审批通过后才创建店铺与账号';
COMMENT ON COLUMN tb_agent_distribution_registration.id IS '主键注册记录ID同时作为通用审批业务ID';
COMMENT ON COLUMN tb_agent_distribution_registration.distribution_code IS '提交时使用的上级店铺分销码快照,审批通过时校验其归属店铺仍启用';
COMMENT ON COLUMN tb_agent_distribution_registration.parent_shop_id IS '上级店铺ID审批通过后作为新店铺的直接上级';
COMMENT ON COLUMN tb_agent_distribution_registration.phone IS '注册手机号,审批通过时写入代理主账号;不在此记录明文密码';
COMMENT ON COLUMN tb_agent_distribution_registration.password_hash IS '注册时生成的 bcrypt 密码哈希,审批通过时直接写入新建账号';
COMMENT ON COLUMN tb_agent_distribution_registration.shop_name IS '申请店铺名称快照';
COMMENT ON COLUMN tb_agent_distribution_registration.shop_code IS '申请店铺编号快照,审批通过时按既有唯一约束校验';
COMMENT ON COLUMN tb_agent_distribution_registration.username IS '申请代理主账号用户名快照,审批通过时按既有唯一约束校验';
COMMENT ON COLUMN tb_agent_distribution_registration.contact_name IS '联系人姓名快照';
COMMENT ON COLUMN tb_agent_distribution_registration.province IS '省份快照';
COMMENT ON COLUMN tb_agent_distribution_registration.city IS '城市快照';
COMMENT ON COLUMN tb_agent_distribution_registration.district IS '区县快照';
COMMENT ON COLUMN tb_agent_distribution_registration.address IS '详细地址快照';
COMMENT ON COLUMN tb_agent_distribution_registration.status IS '状态0 待审批、1 已通过、2 已驳回';
COMMENT ON COLUMN tb_agent_distribution_registration.approval_instance_id IS '关联的通用审批实例ID创建后不可修改';
COMMENT ON COLUMN tb_agent_distribution_registration.reject_reason IS '企业微信最终驳回原因,通过时为空';
COMMENT ON COLUMN tb_agent_distribution_registration.decided_at IS '审批终态到达时间';
COMMENT ON COLUMN tb_agent_distribution_registration.creator IS '创建人用户ID0 表示系统';
COMMENT ON COLUMN tb_agent_distribution_registration.updater IS '最近更新人用户ID0 表示系统';
COMMENT ON COLUMN tb_agent_distribution_registration.created_at IS '创建时间';
COMMENT ON COLUMN tb_agent_distribution_registration.updated_at IS '最近更新时间';