feat(业务用户组): AUG26-003 业务用户组与店铺负责人分组导入
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 1h43m42s
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 1h43m42s
- 迁移 000221:新增 tb_business_user_group、tb_business_user_group_member、tb_shop_business_owner_import_task,成员一账号一行由部分唯一索引保证,店铺所属组按当前负责人实时推导,不回填历史分组。 - 用户组 CRUD、成员改组/清空归属、店铺批量交接(原子失败不部分写入)。 - 店铺负责人 CSV 导入任务:逐行独立事务、逐行明细、任务级与行级失败分离。 - 读侧推导与筛选:未分组、业务线、停用组可筛出并带停用标记。 - 补齐操作审计动作与资源、openapi 清单、发布门禁巡检表清单。 - 归档 add-shop-salesperson-groups 变更并同步 openspec/specs/business-user-group,补齐 AUG26-003 验证证据链。
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
-- 回滚业务用户组、成员归属与店铺负责人导入任务 Schema。
|
||||
-- 三张表都承载不可由 Schema 重建的业务事实:用户组与成员关系决定店铺展示口径,
|
||||
-- 导入任务保存逐行结果。存在任一记录时禁止破坏性回滚,需人工先核对并清理后再评估。
|
||||
-- 注意:成员关系清空走软删,软删行同样计入本守卫,因此解除守卫需删除成员关系记录本身。
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 同一事务内先取排他锁再检查并删表,避免检查与删表之间写入新事实。
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('tb_shop_business_owner_import_task') IS NOT NULL THEN
|
||||
LOCK TABLE tb_shop_business_owner_import_task IN ACCESS EXCLUSIVE MODE;
|
||||
IF EXISTS (SELECT 1 FROM tb_shop_business_owner_import_task LIMIT 1) THEN
|
||||
RAISE EXCEPTION 'tb_shop_business_owner_import_task 已存在导入任务事实,禁止删表回滚,请停止导入后向前修复';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF to_regclass('tb_business_user_group_member') IS NOT NULL THEN
|
||||
LOCK TABLE tb_business_user_group_member IN ACCESS EXCLUSIVE MODE;
|
||||
IF EXISTS (SELECT 1 FROM tb_business_user_group_member LIMIT 1) THEN
|
||||
RAISE EXCEPTION 'tb_business_user_group_member 已存在平台用户分组事实(含软删行,清空归属走软删),禁止删表回滚,请删除成员关系记录后向前修复';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF to_regclass('tb_business_user_group') IS NOT NULL THEN
|
||||
LOCK TABLE tb_business_user_group IN ACCESS EXCLUSIVE MODE;
|
||||
IF EXISTS (SELECT 1 FROM tb_business_user_group LIMIT 1) THEN
|
||||
RAISE EXCEPTION 'tb_business_user_group 已存在业务用户组事实(含软删行),禁止删表回滚,请删除业务用户组记录后向前修复';
|
||||
END IF;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
DROP INDEX IF EXISTS idx_shop_business_owner_import_task_status_created;
|
||||
DROP INDEX IF EXISTS uq_shop_business_owner_import_task_no;
|
||||
DROP TABLE IF EXISTS tb_shop_business_owner_import_task;
|
||||
|
||||
DROP INDEX IF EXISTS idx_business_user_group_member_group;
|
||||
DROP INDEX IF EXISTS uk_business_user_group_member_account;
|
||||
DROP TABLE IF EXISTS tb_business_user_group_member;
|
||||
|
||||
DROP INDEX IF EXISTS idx_business_user_group_status;
|
||||
DROP INDEX IF EXISTS uk_business_user_group_code;
|
||||
DROP TABLE IF EXISTS tb_business_user_group;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,134 @@
|
||||
-- 业务用户组:平台用户的业务分类、成员归属与店铺负责人 CSV 导入任务。
|
||||
-- 用户组只描述业务分类,不进入登录、角色权限或数据范围;店铺所属组始终由当前负责人实时推导,
|
||||
-- 因此本迁移不给 tb_shop 增加任何组字段,也不回填历史分组。
|
||||
-- 成员关系一账号一行,改组走 UPDATE 组 ID;清空归属走软删(与仓库统一 gorm.Model 基座一致),
|
||||
-- 未删除记录的唯一性由部分唯一索引保证,历史归属变更由审计事件承担。
|
||||
-- 导入任务独立成表,不复用设备导入任务表(后者的启动补偿按表绑定设备任务类型)。
|
||||
-- 不使用数据库外键,关联以 ID 保存并由应用层显式校验。
|
||||
|
||||
CREATE TABLE tb_business_user_group (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
code VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
business_line VARCHAR(20) NOT NULL DEFAULT '',
|
||||
sort_order BIGINT NOT NULL DEFAULT 0,
|
||||
status SMALLINT NOT NULL DEFAULT 1,
|
||||
remark VARCHAR(500) NOT NULL DEFAULT '',
|
||||
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(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
CONSTRAINT chk_business_user_group_code CHECK (code <> ''),
|
||||
CONSTRAINT chk_business_user_group_name CHECK (name <> ''),
|
||||
CONSTRAINT chk_business_user_group_business_line CHECK (business_line IN ('', 'standard', 'smart', 'other')),
|
||||
CONSTRAINT chk_business_user_group_status CHECK (status IN (0, 1)),
|
||||
CONSTRAINT chk_business_user_group_sort CHECK (sort_order >= 0)
|
||||
);
|
||||
|
||||
-- 稳定编码在未删除组内唯一;编码创建后不可修改,不存在跨组复用入口。
|
||||
CREATE UNIQUE INDEX uk_business_user_group_code
|
||||
ON tb_business_user_group (code)
|
||||
WHERE deleted_at IS NULL;
|
||||
CREATE INDEX idx_business_user_group_status
|
||||
ON tb_business_user_group (status, sort_order, id)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE TABLE tb_business_user_group_member (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
business_user_group_id BIGINT NOT NULL,
|
||||
account_id BIGINT NOT NULL,
|
||||
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(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
CONSTRAINT chk_business_user_group_member_group CHECK (business_user_group_id > 0),
|
||||
CONSTRAINT chk_business_user_group_member_account CHECK (account_id > 0)
|
||||
);
|
||||
|
||||
-- 一个账号至多一条未删除成员关系,唯一索引是「一账号至多一组」的结构性保证。
|
||||
CREATE UNIQUE INDEX uk_business_user_group_member_account
|
||||
ON tb_business_user_group_member (account_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
-- 组维度用于删除前统计成员数。
|
||||
CREATE INDEX idx_business_user_group_member_group
|
||||
ON tb_business_user_group_member (business_user_group_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE TABLE tb_shop_business_owner_import_task (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
task_no VARCHAR(50) NOT NULL,
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT '',
|
||||
storage_key VARCHAR(500) NOT NULL,
|
||||
status INTEGER NOT NULL DEFAULT 1,
|
||||
total_count INTEGER NOT NULL DEFAULT 0,
|
||||
success_count INTEGER NOT NULL DEFAULT 0,
|
||||
fail_count INTEGER NOT NULL DEFAULT 0,
|
||||
result_items JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
error_message TEXT NOT NULL DEFAULT '',
|
||||
creator_name VARCHAR(100) NOT NULL DEFAULT '',
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_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(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
CONSTRAINT chk_shop_business_owner_import_task_status CHECK (status IN (1, 2, 3, 4)),
|
||||
CONSTRAINT chk_shop_business_owner_import_task_counts CHECK (
|
||||
total_count >= 0 AND success_count >= 0 AND fail_count >= 0
|
||||
AND success_count + fail_count <= total_count
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX uq_shop_business_owner_import_task_no
|
||||
ON tb_shop_business_owner_import_task (task_no)
|
||||
WHERE deleted_at IS NULL;
|
||||
CREATE INDEX idx_shop_business_owner_import_task_status_created
|
||||
ON tb_shop_business_owner_import_task (status, created_at DESC)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
COMMENT ON TABLE tb_business_user_group IS '业务用户组,平台用户的业务分类,不进入鉴权与数据范围';
|
||||
COMMENT ON COLUMN tb_business_user_group.id IS '主键';
|
||||
COMMENT ON COLUMN tb_business_user_group.code IS '创建时必填的稳定编码,未删除组内唯一且创建后不可修改';
|
||||
COMMENT ON COLUMN tb_business_user_group.name IS '用户组名称';
|
||||
COMMENT ON COLUMN tb_business_user_group.business_line IS '所属业务线单值:standard 标品、smart 智能产品、other 其他,空值表示未设置';
|
||||
COMMENT ON COLUMN tb_business_user_group.sort_order IS '排序值,非负整数';
|
||||
COMMENT ON COLUMN tb_business_user_group.status IS '状态 0-禁用 1-启用,与 pkg/constants.StatusDisabled/StatusEnabled 一致';
|
||||
COMMENT ON COLUMN tb_business_user_group.remark IS '备注';
|
||||
COMMENT ON COLUMN tb_business_user_group.creator IS '创建人账号ID';
|
||||
COMMENT ON COLUMN tb_business_user_group.updater IS '最近更新人账号ID';
|
||||
COMMENT ON COLUMN tb_business_user_group.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN tb_business_user_group.updated_at IS '最近更新时间';
|
||||
COMMENT ON COLUMN tb_business_user_group.deleted_at IS '软删除时间,仅无成员组可删除';
|
||||
|
||||
COMMENT ON TABLE tb_business_user_group_member IS '平台用户与业务用户组的唯一归属关系';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.id IS '主键';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.business_user_group_id IS '所属业务用户组ID;改组时直接更新本列';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.account_id IS '平台用户账号ID,未删除记录内唯一';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.creator IS '创建人账号ID';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.updater IS '最近更新人账号ID';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.updated_at IS '最近更新时间';
|
||||
COMMENT ON COLUMN tb_business_user_group_member.deleted_at IS '软删除时间,清空归属走软删;唯一性由未删除记录的 partial 唯一索引保证';
|
||||
|
||||
COMMENT ON TABLE tb_shop_business_owner_import_task IS '店铺负责人 CSV 导入任务,逐行独立事务并保留逐行明细';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.id IS '主键';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.task_no IS '导入任务编号,未删除记录内唯一';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.file_name IS '上传的源 CSV 文件名';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.storage_key IS '源 CSV 对象存储Key';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.status IS '任务状态 1-待处理 2-处理中 3-已完成 4-失败';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.total_count IS '任务数据行总数,任务级失败时为 0';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.success_count IS '处理成功行数';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.fail_count IS '处理失败行数';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.result_items IS '逐行结果明细,行号自数据首行起计;任务级失败为空数组';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.error_message IS '任务级失败原因,与行级失败原因分开记录';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.creator_name IS '任务创建人名称快照';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.started_at IS '任务开始处理时间';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.completed_at IS '任务处理完成时间';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.creator IS '创建人账号ID';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.updater IS '最近更新人账号ID';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.updated_at IS '最近更新时间';
|
||||
COMMENT ON COLUMN tb_shop_business_owner_import_task.deleted_at IS '软删除时间';
|
||||
Reference in New Issue
Block a user