Files
team-api/migrations/000020_v0_2_17_version.sql

240 lines
13 KiB
SQL
Raw Permalink 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.
-- +goose Up
-- 管理后台角色权限管理:引入可自定义的角色实体
--
-- 背景:此前管理后台只有 super_admin / admin 两种身份,权限点直接绑在用户上
-- (sys_admin_role_perms 名为 role 实为 user_perms),无法复用、无法按岗位批量调整,
-- 结果是全团队共用超管账号。本迁移补齐「角色」这一层。
--
-- 角色模型:
-- · 超级管理员不入本表 —— 它由 sys_admin_users.role='super_admin' 唯一判定并在鉴权时短路,
-- 特权判定只保留一个真相源;即便角色被删光,超管仍可登录并重建角色体系。
-- · 三个预置角色(管理员/运营/技术支持)只是安装时的初始数据,不是系统契约:
-- 用户可以改权限、改名、禁用,也可以直接删掉自建一套。
-- · 有效权限 = ∪(已启用角色的权限) ∪ 用户特批权限(sys_admin_role_perms),只做并集不做否定。
--
-- 本迁移另含两笔独立变更:删除帮助中心不再使用的全文检索索引;新增 member:manage 权限点并回填存量授权。
-- ── 角色定义 ──
CREATE TABLE IF NOT EXISTS sys_admin_roles (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(50) NOT NULL,
name VARCHAR(50) NOT NULL,
description VARCHAR(255) NOT NULL DEFAULT '',
is_builtin BOOLEAN NOT NULL DEFAULT FALSE,
is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
sort INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT uk_sys_admin_roles_code UNIQUE (code)
);
COMMENT ON TABLE sys_admin_roles IS '管理后台角色定义(超级管理员不入表,由 sys_admin_users.role 判定)';
COMMENT ON COLUMN sys_admin_roles.id IS '主键ID';
COMMENT ON COLUMN sys_admin_roles.code IS '角色标识(小写字母/数字/下划线,创建后不可修改,是权限缓存与审计日志的稳定标识;保留字 super_admin 不可占用)';
COMMENT ON COLUMN sys_admin_roles.name IS '角色显示名称(可修改)';
COMMENT ON COLUMN sys_admin_roles.description IS '角色说明,在分配界面展示';
COMMENT ON COLUMN sys_admin_roles.is_builtin IS '是否系统预置:仅用于标识来源与支撑「恢复默认权限」,不构成删除保护(预置角色同样可改可删)';
COMMENT ON COLUMN sys_admin_roles.is_enabled IS '是否启用:禁用后该角色的权限对全部关联用户立即失效,无需解除关联';
COMMENT ON COLUMN sys_admin_roles.sort IS '排序权重(升序)';
COMMENT ON COLUMN sys_admin_roles.created_at IS '创建时间';
COMMENT ON COLUMN sys_admin_roles.updated_at IS '更新时间';
-- ── 角色 → 权限点 ──
CREATE TABLE IF NOT EXISTS sys_admin_role_permissions (
id BIGSERIAL PRIMARY KEY,
role_id BIGINT NOT NULL,
permission_point VARCHAR(100) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT uk_sys_admin_role_permissions UNIQUE (role_id, permission_point)
);
CREATE INDEX IF NOT EXISTS idx_sys_admin_role_permissions_role ON sys_admin_role_permissions (role_id);
COMMENT ON TABLE sys_admin_role_permissions IS '角色权限点关联';
COMMENT ON COLUMN sys_admin_role_permissions.id IS '主键ID';
COMMENT ON COLUMN sys_admin_role_permissions.role_id IS '角色ID(逻辑关联 sys_admin_roles.id,无外键,删除角色时由业务层级联清理)';
COMMENT ON COLUMN sys_admin_role_permissions.permission_point IS '权限点标识(如 tenant:create、channel:edit)';
COMMENT ON COLUMN sys_admin_role_permissions.created_at IS '创建时间';
COMMENT ON COLUMN sys_admin_role_permissions.updated_at IS '更新时间';
-- ── 用户 → 角色(多对多,覆盖兼岗场景) ──
CREATE TABLE IF NOT EXISTS sys_admin_user_roles (
id BIGSERIAL PRIMARY KEY,
admin_user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT uk_sys_admin_user_roles UNIQUE (admin_user_id, role_id)
);
CREATE INDEX IF NOT EXISTS idx_sys_admin_user_roles_user ON sys_admin_user_roles (admin_user_id);
CREATE INDEX IF NOT EXISTS idx_sys_admin_user_roles_role ON sys_admin_user_roles (role_id);
COMMENT ON TABLE sys_admin_user_roles IS '管理员用户与角色关联(多对多:一个账号可同时担任多个角色,有效权限取并集)';
COMMENT ON COLUMN sys_admin_user_roles.id IS '主键ID';
COMMENT ON COLUMN sys_admin_user_roles.admin_user_id IS '管理员用户ID(逻辑关联 sys_admin_users.id,无外键)';
COMMENT ON COLUMN sys_admin_user_roles.role_id IS '角色ID(逻辑关联 sys_admin_roles.id,无外键)';
COMMENT ON COLUMN sys_admin_user_roles.created_at IS '创建时间';
COMMENT ON COLUMN sys_admin_user_roles.updated_at IS '更新时间';
-- ── 存量表语义调整:sys_admin_role_perms 由「用户权限」改为「用户特批权限」 ──
-- 数据不迁移、不删除。它现在表示「在角色之外额外授予该用户的权限」,
-- 保留「临时给某人单加一个权限」的口子,不必为一个权限点新建角色。
COMMENT ON TABLE sys_admin_role_perms IS '管理员用户特批权限(角色之外的额外授权,与角色权限取并集)';
COMMENT ON COLUMN sys_admin_role_perms.admin_user_id IS '关联的管理员用户ID';
COMMENT ON COLUMN sys_admin_role_perms.permission_point IS '特批的权限点标识(独立于角色,角色禁用不影响特批)';
-- ── 预置角色种子 ──
-- ON CONFLICT DO NOTHING 有两层作用:① 迁移可重复执行;
-- ② 用户改过或删过的预置角色,重跑迁移不会被覆盖或复活。
INSERT INTO sys_admin_roles (code, name, description, is_builtin, is_enabled, sort) VALUES
('admin', '管理员', '二把手:能碰钱(退款)与系统配置(支付、汇率、存储、邮件),不能管理账号与角色', TRUE, TRUE, 10),
('operator', '运营', '平台日常运营:渠道、模型、租户、套餐、内容的完整权限,不涉及资金与系统设置', TRUE, TRUE, 20),
('support', '技术支持', '排查客户问题:除财务数据外全站只读,唯一写权限是工单、反馈与帮助中心', TRUE, TRUE, 30)
ON CONFLICT (code) DO NOTHING;
-- 管理员:除 user:*(账号与角色)、system:update/plugin 外的全部权限
INSERT INTO sys_admin_role_permissions (role_id, permission_point)
SELECT r.id, unnest(ARRAY[
'dashboard:view',
'channel:view','channel:create','channel:edit','channel:test','channel:delete',
'model:view','model:create','model:edit','model:delete',
'tenant:view','tenant:create','tenant:edit','tenant:suspend','tenant:delete','tenant:close',
'member:view','member:import','member:manage','member:model_scope',
'plan:view','plan:create','plan:edit','plan:delete',
'billing:view','billing:export','billing:refund',
'order:view','order:refund',
'promo:view','promo:create','promo:edit',
'redemption:view','redemption:create','redemption:edit',
'invoice:view','invoice:manage',
'operation:view','operation:edit',
'support:view','support:reply','support:edit',
'monitor:view','monitor:edit',
'task:view','task:edit',
'file:view','file:delete','file:cleanup',
'audit:view','audit:export',
'system:view','system:edit'
])
FROM sys_admin_roles r WHERE r.code = 'admin'
ON CONFLICT (role_id, permission_point) DO NOTHING;
-- 运营:业务配置的完整权限;不给退款、审计、系统设置、账号管理
INSERT INTO sys_admin_role_permissions (role_id, permission_point)
SELECT r.id, unnest(ARRAY[
'dashboard:view',
'channel:view','channel:create','channel:edit','channel:test','channel:delete',
'model:view','model:create','model:edit','model:delete',
'tenant:view','tenant:create','tenant:edit','tenant:suspend',
'member:view','member:import','member:manage','member:model_scope',
'plan:view','plan:create','plan:edit','plan:delete',
'billing:view',
'order:view',
'promo:view','promo:create','promo:edit',
'redemption:view','redemption:create','redemption:edit',
'invoice:view','invoice:manage',
'operation:view','operation:edit',
'support:view','support:reply','support:edit',
'monitor:view','monitor:edit',
'task:view','task:edit',
'file:view','file:delete','file:cleanup'
])
FROM sys_admin_roles r WHERE r.code = 'operator'
ON CONFLICT (role_id, permission_point) DO NOTHING;
-- 技术支持:除财务面(billing/order/promo/redemption/invoice)外全站只读 + 工单完整权限。
-- 排查主力工具不在财务面:请求审计日志走 audit:view,错误日志与渠道错误监控走 monitor:view。
INSERT INTO sys_admin_role_permissions (role_id, permission_point)
SELECT r.id, unnest(ARRAY[
'dashboard:view',
'channel:view',
'model:view',
'tenant:view',
'member:view',
'plan:view',
'operation:view',
'support:view','support:reply','support:edit',
'monitor:view',
'task:view',
'file:view',
'audit:view'
])
FROM sys_admin_roles r WHERE r.code = 'support'
ON CONFLICT (role_id, permission_point) DO NOTHING;
-- 移除不再使用的配置项
DELETE FROM sys_options
WHERE "key" IN (
-- 安全
'max_sessions_per_user',
'admin_max_sessions',
'new_device_notification',
'password_min_length',
-- 性能
'cache_enabled',
'cache_ttl_seconds',
'channel_affinity_ttl',
'auto_test_interval',
-- 审计/数据治理
'audit_retention_days',
'operation_log_retention_days',
'data_deletion_completion_days',
-- 沙箱
'sandbox_enabled',
'sandbox_default_quota'
);
-- ── 帮助中心搜索:删除不再使用的全文检索索引 ──
-- 搜索已从 to_tsvector 全文检索改为 ILIKE 模糊匹配:PostgreSQL 'simple' 分词配置不做中文分词,
-- 连续中文被切成单个长 token,中文关键词几乎无法命中(帮助文档以中文内容为主)。
-- 该 GIN 索引不再被查询使用,删除以释放空间。
DROP INDEX IF EXISTS idx_spt_articles_search;
-- ── member:manage 权限点与存量回填 ──
-- 成员禁用/启用/重置密码/解锁四个写接口此前挂在只读的 member:view 上(internal/middleware/rbac.go),
-- 「技术支持」这类只读定位的角色可重置任意租户成员的密码 —— 改密等于接管该成员账号,
-- 进而可用其 API Key 消耗租户余额,档位与动作不匹配,已改为要求 member:manage。
--
-- 上方预置角色种子已直接带上 member:manage;下面两条回填覆盖「执行过早期版本本迁移
-- (种子未含该权限点)」的部署:给【已持有 member:import 的角色/用户】补 member:manage ——
-- member:import 本就在 member 模块的操作档,持有它的角色已经能创建成员,补上「管理成员」
-- 不扩大其实际能力边界;仅持 member:view 的只读角色(如预置 support)不补。
-- 幂等:ON CONFLICT DO NOTHING,可重复执行。
INSERT INTO sys_admin_role_permissions (role_id, permission_point)
SELECT DISTINCT rp.role_id, 'member:manage'
FROM sys_admin_role_permissions rp
WHERE rp.permission_point = 'member:import'
ON CONFLICT (role_id, permission_point) DO NOTHING;
-- 用户特批权限同理:特批过 member:import 的账号一并补 member:manage,
-- 否则依赖特批权限工作的账号会在升级后突然失去成员管理能力。
INSERT INTO sys_admin_role_perms (admin_user_id, permission_point)
SELECT DISTINCT p.admin_user_id, 'member:manage'
FROM sys_admin_role_perms p
WHERE p.permission_point = 'member:import'
ON CONFLICT DO NOTHING;
-- +goose Down
-- 移除 member:manage 权限点。回滚后 rbac.go 若仍要求 member:manage,相关接口对非超管一律 403,
-- 因此代码与迁移需要同步回退。
DELETE FROM sys_admin_role_permissions WHERE permission_point = 'member:manage';
DELETE FROM sys_admin_role_perms WHERE permission_point = 'member:manage';
-- 恢复帮助中心全文检索索引(仅回滚迁移时使用,此时搜索已退回 to_tsvector 方案)
CREATE INDEX idx_spt_articles_search ON spt_articles USING gin (to_tsvector('simple'::regconfig, (((COALESCE(title, ''::character varying))::text || ' '::text) || COALESCE(content, ''::text))));
-- 删除角色体系三张表。存量用户权限不受影响:它们本就存放在 sys_admin_role_perms,
-- 回滚后 HasPermission 退回「仅查用户特批权限」的旧语义。
DROP TABLE IF EXISTS sys_admin_user_roles;
DROP TABLE IF EXISTS sys_admin_role_permissions;
DROP TABLE IF EXISTS sys_admin_roles;
COMMENT ON TABLE sys_admin_role_perms IS '管理员角色权限关联';
COMMENT ON COLUMN sys_admin_role_perms.admin_user_id IS '关联的管理员用户ID';
COMMENT ON COLUMN sys_admin_role_perms.permission_point IS '权限点标识(如 tenant:create、channel:edit)';