diff --git a/deploy/postgres/README.md b/deploy/postgres/README.md index 04e7222..920d9f9 100644 --- a/deploy/postgres/README.md +++ b/deploy/postgres/README.md @@ -6,6 +6,7 @@ - `create_databases.sql`:创建四个业务库与对应角色(可多次执行,存在则跳过)。 - `bootstrap.ps1`:PowerShell 包装脚本,调用 `psql` 执行 SQL。 +- `seed_tenant_seckill_permissions.sql`:补齐租户端秒杀活动权限码、菜单权限与角色授权映射(可重复执行)。 ## 前置条件 @@ -38,6 +39,20 @@ cd deploy/postgres - 如需修改密码或新增库,编辑 `create_databases.sql` 后重新运行脚本。 - 若在本地拉起测试库,可把 `Host` 指向 `localhost`,其余参数保持一致。 +## 秒杀权限补齐 + +在 `takeout_identity_db` 执行: + +```powershell +psql -h -p -U identity_user -d takeout_identity_db -f .\seed_tenant_seckill_permissions.sql +``` + +脚本会完成: + +1. 新增/修正 `tenant:marketing:seckill:*` 权限码。 +2. 更新秒杀菜单 (`/marketing/seckill/index`) 的 `RequiredPermissions`、`MetaPermissions` 与 `AuthListJson`。 +3. 参考满减权限映射,补齐角色模板与租户角色的秒杀权限授权。 + ## 常见问题 | 问题 | 处理方式 | diff --git a/deploy/postgres/seed_tenant_seckill_permissions.sql b/deploy/postgres/seed_tenant_seckill_permissions.sql new file mode 100644 index 0000000..944d44c --- /dev/null +++ b/deploy/postgres/seed_tenant_seckill_permissions.sql @@ -0,0 +1,263 @@ +-- 文件职责:补齐 Tenant 端秒杀活动权限与菜单权限绑定(可重复执行)。 +-- 执行范围:takeout_identity 数据库(Portal=1,Tenant 端)。 + +BEGIN; + +-- 1) 新增/修正营销中心秒杀权限码 +WITH seckill_permissions(code, name, sort_order) AS ( + VALUES + ('tenant:marketing:seckill:view', '查看秒杀活动', 29), + ('tenant:marketing:seckill:create', '创建秒杀活动', 30), + ('tenant:marketing:seckill:update', '编辑秒杀活动', 31), + ('tenant:marketing:seckill:delete', '删除秒杀活动', 32), + ('tenant:marketing:seckill:status', '切换秒杀状态', 33) +), +missing AS ( + SELECT + s.code, + s.name, + s.sort_order, + ROW_NUMBER() OVER (ORDER BY s.sort_order, s.code) AS rn + FROM seckill_permissions s + LEFT JOIN public.permissions p + ON p."Code" = s.code + WHERE p."Id" IS NULL +), +base AS ( + SELECT COALESCE(MAX("Id"), 820000000000000000) AS max_id + FROM public.permissions +) +INSERT INTO public.permissions +( + "Id", "Name", "Code", "Description", "CreatedAt", "UpdatedAt", "DeletedAt", + "CreatedBy", "UpdatedBy", "DeletedBy", "ParentId", "SortOrder", "Type", "Portal" +) +SELECT + base.max_id + missing.rn, + missing.name, + missing.code, + NULL, + NOW(), + NULL, + NULL, + NULL, + NULL, + NULL, + 820000000000000005, + missing.sort_order, + 'leaf', + 1 +FROM missing +CROSS JOIN base; + +WITH seckill_permissions(code, name, sort_order) AS ( + VALUES + ('tenant:marketing:seckill:view', '查看秒杀活动', 29), + ('tenant:marketing:seckill:create', '创建秒杀活动', 30), + ('tenant:marketing:seckill:update', '编辑秒杀活动', 31), + ('tenant:marketing:seckill:delete', '删除秒杀活动', 32), + ('tenant:marketing:seckill:status', '切换秒杀状态', 33) +) +UPDATE public.permissions p +SET + "Name" = s.name, + "ParentId" = 820000000000000005, + "SortOrder" = s.sort_order, + "Type" = 'leaf', + "Portal" = 1, + "DeletedAt" = NULL, + "DeletedBy" = NULL, + "UpdatedAt" = NOW() +FROM seckill_permissions s +WHERE p."Code" = s.code; + +-- 2) 绑定秒杀菜单的访问权限与按钮权限 +UPDATE public.menu_definitions +SET + "RequiredPermissions" = 'tenant:marketing:seckill:view', + "MetaPermissions" = 'tenant:marketing:seckill:view,tenant:marketing:seckill:create,tenant:marketing:seckill:update,tenant:marketing:seckill:delete,tenant:marketing:seckill:status', + "AuthListJson" = '[{"title":"创建","authMark":"tenant:marketing:seckill:create"},{"title":"编辑","authMark":"tenant:marketing:seckill:update"},{"title":"删除","authMark":"tenant:marketing:seckill:delete"},{"title":"停启用","authMark":"tenant:marketing:seckill:status"}]', + "UpdatedAt" = NOW() +WHERE + "Portal" = 1 + AND ("Path" = 'seckill' OR "Component" = '/marketing/seckill/index'); + +-- 3) 给角色模板补齐秒杀权限(按满减权限映射) +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:marketing:full-reduction:view', 'tenant:marketing:seckill:view'), + ('tenant:marketing:full-reduction:create', 'tenant:marketing:seckill:create'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:update'), + ('tenant:marketing:full-reduction:delete', 'tenant:marketing:seckill:delete'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:status') +), +candidates AS ( + SELECT DISTINCT + rtp."RoleTemplateId", + mapping.target_code + FROM public.role_template_permissions rtp + INNER JOIN code_mapping mapping + ON mapping.source_code = rtp."PermissionCode" + WHERE rtp."DeletedAt" IS NULL +), +missing AS ( + SELECT + c."RoleTemplateId", + c.target_code, + ROW_NUMBER() OVER (ORDER BY c."RoleTemplateId", c.target_code) AS rn + FROM candidates c + LEFT JOIN public.role_template_permissions existing + ON existing."RoleTemplateId" = c."RoleTemplateId" + AND existing."PermissionCode" = c.target_code + WHERE existing."Id" IS NULL +), +base AS ( + SELECT COALESCE(MAX("Id"), 820000000000000000) AS max_id + FROM public.role_template_permissions +) +INSERT INTO public.role_template_permissions +( + "Id", "RoleTemplateId", "PermissionCode", "CreatedAt", "UpdatedAt", "DeletedAt", + "CreatedBy", "UpdatedBy", "DeletedBy" +) +SELECT + base.max_id + missing.rn, + missing."RoleTemplateId", + missing.target_code, + NOW(), + NULL, + NULL, + NULL, + NULL, + NULL +FROM missing +CROSS JOIN base; + +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:marketing:full-reduction:view', 'tenant:marketing:seckill:view'), + ('tenant:marketing:full-reduction:create', 'tenant:marketing:seckill:create'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:update'), + ('tenant:marketing:full-reduction:delete', 'tenant:marketing:seckill:delete'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:status') +) +UPDATE public.role_template_permissions target +SET + "DeletedAt" = NULL, + "DeletedBy" = NULL, + "UpdatedAt" = NOW() +FROM public.role_template_permissions source +INNER JOIN code_mapping mapping + ON mapping.source_code = source."PermissionCode" +WHERE + source."RoleTemplateId" = target."RoleTemplateId" + AND target."PermissionCode" = mapping.target_code; + +-- 4) 给租户角色补齐秒杀权限(按满减权限映射) +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:marketing:full-reduction:view', 'tenant:marketing:seckill:view'), + ('tenant:marketing:full-reduction:create', 'tenant:marketing:seckill:create'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:update'), + ('tenant:marketing:full-reduction:delete', 'tenant:marketing:seckill:delete'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:status') +), +source_rows AS ( + SELECT DISTINCT + rp."RoleId", + rp."TenantId", + rp."Portal", + mapping.target_code + FROM public.role_permissions rp + INNER JOIN public.permissions source_permission + ON source_permission."Id" = rp."PermissionId" + INNER JOIN code_mapping mapping + ON mapping.source_code = source_permission."Code" + WHERE rp."DeletedAt" IS NULL + AND rp."Portal" = 1 +), +candidates AS ( + SELECT DISTINCT + s."RoleId", + s."TenantId", + s."Portal", + target_permission."Id" AS target_permission_id + FROM source_rows s + INNER JOIN public.permissions target_permission + ON target_permission."Code" = s.target_code +), +missing AS ( + SELECT + c."RoleId", + c."TenantId", + c."Portal", + c.target_permission_id, + ROW_NUMBER() OVER ( + ORDER BY c."TenantId", c."RoleId", c.target_permission_id + ) AS rn + FROM candidates c + LEFT JOIN public.role_permissions existing + ON existing."RoleId" = c."RoleId" + AND existing."PermissionId" = c.target_permission_id + AND existing."Portal" = c."Portal" + AND ( + (existing."TenantId" IS NULL AND c."TenantId" IS NULL) + OR existing."TenantId" = c."TenantId" + ) + WHERE existing."Id" IS NULL +), +base AS ( + SELECT COALESCE(MAX("Id"), 830000000000000000) AS max_id + FROM public.role_permissions +) +INSERT INTO public.role_permissions +( + "Id", "RoleId", "PermissionId", "CreatedAt", "UpdatedAt", "DeletedAt", + "CreatedBy", "UpdatedBy", "DeletedBy", "TenantId", "Portal" +) +SELECT + base.max_id + missing.rn, + missing."RoleId", + missing.target_permission_id, + NOW(), + NULL, + NULL, + NULL, + NULL, + NULL, + missing."TenantId", + missing."Portal" +FROM missing +CROSS JOIN base; + +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:marketing:full-reduction:view', 'tenant:marketing:seckill:view'), + ('tenant:marketing:full-reduction:create', 'tenant:marketing:seckill:create'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:update'), + ('tenant:marketing:full-reduction:delete', 'tenant:marketing:seckill:delete'), + ('tenant:marketing:full-reduction:update', 'tenant:marketing:seckill:status') +) +UPDATE public.role_permissions target +SET + "DeletedAt" = NULL, + "DeletedBy" = NULL, + "UpdatedAt" = NOW() +FROM public.role_permissions source +INNER JOIN public.permissions source_permission + ON source_permission."Id" = source."PermissionId" +INNER JOIN code_mapping mapping + ON mapping.source_code = source_permission."Code" +INNER JOIN public.permissions target_permission + ON target_permission."Code" = mapping.target_code +WHERE + source."Portal" = 1 + AND target."Portal" = source."Portal" + AND target."RoleId" = source."RoleId" + AND target."PermissionId" = target_permission."Id" + AND ( + (target."TenantId" IS NULL AND source."TenantId" IS NULL) + OR target."TenantId" = source."TenantId" + ); + +COMMIT;