diff --git a/deploy/postgres/README.md b/deploy/postgres/README.md index 4ebe378..2ccd6c1 100644 --- a/deploy/postgres/README.md +++ b/deploy/postgres/README.md @@ -13,6 +13,8 @@ - `seed_tenant_customer_permissions.sql`:补齐租户端客户管理权限码、菜单权限与角色授权映射(可重复执行)。 - `seed_tenant_member_permissions.sql`:补齐租户端会员管理权限码、菜单权限与角色授权映射(可重复执行)。 - `seed_tenant_member_stored_card_permissions.sql`:补齐租户端会员储值卡权限码、菜单权限与角色授权映射(可重复执行)。 +- `seed_tenant_finance_permissions.sql`:补齐租户端财务中心交易流水与到账查询权限码、菜单权限与角色授权映射(可重复执行)。 +- `seed_tenant_finance_invoice_permissions.sql`:补齐租户端财务中心发票管理权限码、菜单权限与角色授权映射(可重复执行)。 ## 前置条件 @@ -143,6 +145,20 @@ psql -h -p -U identity_user -d takeout_identity_db -f .\seed_tenan 2. 更新会员储值卡菜单 (`/member/stored-card/index`) 的 `RequiredPermissions`、`MetaPermissions` 与 `AuthListJson`。 3. 按订单列表权限映射补齐会员储值卡权限的角色模板与租户角色授权。 +## 财务中心交易流水/到账权限补齐 + +在 `takeout_identity_db` 执行: + +```powershell +psql -h -p -U identity_user -d takeout_identity_db -f .\seed_tenant_finance_permissions.sql +``` + +脚本会完成: + +1. 新增/修正 `tenant:finance:transaction:*` 与 `tenant:finance:settlement:*` 权限码。 +2. 更新财务中心菜单 `TransactionFlow` (`/finance/transaction/index`) 与 `SettlementQuery` (`/finance/settlement/index`) 的 `RequiredPermissions`、`MetaPermissions` 与 `AuthListJson`。 +3. 按旧财务权限(`income/statement`)映射补齐角色模板与租户角色授权。 + ## 常见问题 | 问题 | 处理方式 | diff --git a/deploy/postgres/seed_tenant_finance_invoice_permissions.sql b/deploy/postgres/seed_tenant_finance_invoice_permissions.sql new file mode 100644 index 0000000..17eaa25 --- /dev/null +++ b/deploy/postgres/seed_tenant_finance_invoice_permissions.sql @@ -0,0 +1,299 @@ +-- 文件职责:补齐 Tenant 端财务中心发票管理权限与菜单权限绑定(可重复执行)。 +-- 执行范围:takeout_identity 数据库(Portal=1,Tenant 端)。 + +BEGIN; + +-- 1) 新增/修正发票管理权限码 +WITH invoice_permissions(code, name, sort_order) AS ( + VALUES + ('tenant:finance:invoice:view', '查看发票管理', 63), + ('tenant:finance:invoice:issue', '发票开票', 64), + ('tenant:finance:invoice:void', '发票作废', 65), + ('tenant:finance:invoice:settings', '发票设置', 66) +), +missing AS ( + SELECT + source.code, + source.name, + source.sort_order, + ROW_NUMBER() OVER (ORDER BY source.sort_order, source.code) AS rn + FROM invoice_permissions source + LEFT JOIN public.permissions existing + ON existing."Code" = source.code + WHERE existing."Id" IS NULL +), +base AS ( + SELECT COALESCE(MAX("Id"), 820000000000000000) AS max_id + FROM public.permissions +), +parent AS ( + SELECT COALESCE( + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:transaction:view' + LIMIT 1 + ), + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:order:list:view' + LIMIT 1 + ), + 820000000000000004 + ) AS parent_id +) +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, + parent.parent_id, + missing.sort_order, + 'leaf', + 1 +FROM missing +CROSS JOIN base +CROSS JOIN parent; + +WITH invoice_permissions(code, name, sort_order) AS ( + VALUES + ('tenant:finance:invoice:view', '查看发票管理', 63), + ('tenant:finance:invoice:issue', '发票开票', 64), + ('tenant:finance:invoice:void', '发票作废', 65), + ('tenant:finance:invoice:settings', '发票设置', 66) +), +parent AS ( + SELECT COALESCE( + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:transaction:view' + LIMIT 1 + ), + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:order:list:view' + LIMIT 1 + ), + 820000000000000004 + ) AS parent_id +) +UPDATE public.permissions target +SET + "Name" = source.name, + "ParentId" = parent.parent_id, + "SortOrder" = source.sort_order, + "Type" = 'leaf', + "Portal" = 1, + "DeletedAt" = NULL, + "DeletedBy" = NULL, + "UpdatedAt" = NOW() +FROM invoice_permissions source +CROSS JOIN parent +WHERE target."Code" = source.code; + +-- 2) 绑定发票管理菜单权限 +UPDATE public.menu_definitions +SET + "RequiredPermissions" = 'tenant:finance:invoice:view', + "MetaPermissions" = 'tenant:finance:invoice:view,tenant:finance:invoice:issue,tenant:finance:invoice:void,tenant:finance:invoice:settings', + "AuthListJson" = '[{"title":"开票","authMark":"tenant:finance:invoice:issue"},{"title":"作废","authMark":"tenant:finance:invoice:void"},{"title":"设置","authMark":"tenant:finance:invoice:settings"}]', + "UpdatedAt" = NOW() +WHERE + "Portal" = 1 + AND ( + "Name" = 'InvoiceMgmt' + OR "Path" = 'invoice' + OR "Component" = '/finance/invoice/index' + ); + +-- 3) 给角色模板补齐发票管理权限(按交易流水权限映射) +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:finance:transaction:view', 'tenant:finance:invoice:view'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:issue'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:void'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:settings') +), +candidates AS ( + SELECT DISTINCT + source."RoleTemplateId", + mapping.target_code + FROM public.role_template_permissions source + INNER JOIN code_mapping mapping + ON mapping.source_code = source."PermissionCode" + WHERE source."DeletedAt" IS NULL +), +missing AS ( + SELECT + candidate."RoleTemplateId", + candidate.target_code, + ROW_NUMBER() OVER ( + ORDER BY candidate."RoleTemplateId", candidate.target_code + ) AS rn + FROM candidates candidate + LEFT JOIN public.role_template_permissions existing + ON existing."RoleTemplateId" = candidate."RoleTemplateId" + AND existing."PermissionCode" = candidate.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:finance:transaction:view', 'tenant:finance:invoice:view'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:issue'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:void'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:settings') +) +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:finance:transaction:view', 'tenant:finance:invoice:view'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:issue'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:void'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:settings') +), +source_rows AS ( + SELECT DISTINCT + source."RoleId", + source."TenantId", + source."Portal", + mapping.target_code + 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" + WHERE source."DeletedAt" IS NULL + AND source."Portal" = 1 +), +candidates AS ( + SELECT DISTINCT + source_row."RoleId", + source_row."TenantId", + source_row."Portal", + target_permission."Id" AS target_permission_id + FROM source_rows source_row + INNER JOIN public.permissions target_permission + ON target_permission."Code" = source_row.target_code +), +missing AS ( + SELECT + candidate."RoleId", + candidate."TenantId", + candidate."Portal", + candidate.target_permission_id, + ROW_NUMBER() OVER ( + ORDER BY candidate."TenantId", candidate."RoleId", candidate.target_permission_id + ) AS rn + FROM candidates candidate + LEFT JOIN public.role_permissions existing + ON existing."RoleId" = candidate."RoleId" + AND existing."PermissionId" = candidate.target_permission_id + AND existing."Portal" = candidate."Portal" + AND ( + (existing."TenantId" IS NULL AND candidate."TenantId" IS NULL) + OR existing."TenantId" = candidate."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:finance:transaction:view', 'tenant:finance:invoice:view'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:issue'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:void'), + ('tenant:finance:transaction:export', 'tenant:finance:invoice:settings') +) +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; diff --git a/deploy/postgres/seed_tenant_finance_permissions.sql b/deploy/postgres/seed_tenant_finance_permissions.sql new file mode 100644 index 0000000..290ba60 --- /dev/null +++ b/deploy/postgres/seed_tenant_finance_permissions.sql @@ -0,0 +1,332 @@ +-- 文件职责:补齐 Tenant 端财务中心交易流水与到账查询权限、菜单绑定与角色授权映射(可重复执行)。 +-- 执行范围:takeout_identity 数据库(Portal=1,Tenant 端)。 + +BEGIN; + +-- 1) 新增/修正财务中心交易流水与到账查询权限码 +WITH finance_permissions(code, name, sort_order) AS ( + VALUES + ('tenant:finance:transaction:view', '查看交易流水', 51), + ('tenant:finance:transaction:detail', '查看交易流水详情', 52), + ('tenant:finance:transaction:export', '导出交易流水', 53), + ('tenant:finance:settlement:view', '查看到账查询', 54), + ('tenant:finance:settlement:export', '导出到账查询', 55) +), +missing AS ( + SELECT + source.code, + source.name, + source.sort_order, + ROW_NUMBER() OVER (ORDER BY source.sort_order, source.code) AS rn + FROM finance_permissions source + LEFT JOIN public.permissions existing + ON existing."Code" = source.code + WHERE existing."Id" IS NULL +), +base AS ( + SELECT COALESCE(MAX("Id"), 820000000000000000) AS max_id + FROM public.permissions +), +parent AS ( + SELECT COALESCE( + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:income:view' + LIMIT 1 + ), + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:statement:view' + LIMIT 1 + ), + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:transaction:view' + LIMIT 1 + ), + 820000000000000006 + ) AS parent_id +) +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, + parent.parent_id, + missing.sort_order, + 'leaf', + 1 +FROM missing +CROSS JOIN base +CROSS JOIN parent; + +WITH finance_permissions(code, name, sort_order) AS ( + VALUES + ('tenant:finance:transaction:view', '查看交易流水', 51), + ('tenant:finance:transaction:detail', '查看交易流水详情', 52), + ('tenant:finance:transaction:export', '导出交易流水', 53), + ('tenant:finance:settlement:view', '查看到账查询', 54), + ('tenant:finance:settlement:export', '导出到账查询', 55) +), +parent AS ( + SELECT COALESCE( + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:income:view' + LIMIT 1 + ), + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:statement:view' + LIMIT 1 + ), + ( + SELECT "ParentId" + FROM public.permissions + WHERE "Code" = 'tenant:finance:transaction:view' + LIMIT 1 + ), + 820000000000000006 + ) AS parent_id +) +UPDATE public.permissions target +SET + "Name" = source.name, + "ParentId" = parent.parent_id, + "SortOrder" = source.sort_order, + "Type" = 'leaf', + "Portal" = 1, + "DeletedAt" = NULL, + "DeletedBy" = NULL, + "UpdatedAt" = NOW() +FROM finance_permissions source +CROSS JOIN parent +WHERE target."Code" = source.code; + +-- 2) 绑定交易流水菜单权限 +UPDATE public.menu_definitions +SET + "RequiredPermissions" = 'tenant:finance:transaction:view', + "MetaPermissions" = 'tenant:finance:transaction:view,tenant:finance:transaction:detail,tenant:finance:transaction:export', + "AuthListJson" = '[{"title":"详情","authMark":"tenant:finance:transaction:detail"},{"title":"导出","authMark":"tenant:finance:transaction:export"}]', + "UpdatedAt" = NOW() +WHERE + "Portal" = 1 + AND ( + "Name" = 'TransactionFlow' + OR "Path" = 'transaction' + OR "Component" = '/finance/transaction/index' + ); + +-- 3) 绑定到账查询菜单权限 +UPDATE public.menu_definitions +SET + "RequiredPermissions" = 'tenant:finance:settlement:view', + "MetaPermissions" = 'tenant:finance:settlement:view,tenant:finance:settlement:export', + "AuthListJson" = '[{"title":"导出","authMark":"tenant:finance:settlement:export"}]', + "UpdatedAt" = NOW() +WHERE + "Portal" = 1 + AND ( + "Name" = 'SettlementQuery' + OR "Path" = 'settlement' + OR "Component" = '/finance/settlement/index' + ); + +-- 4) 给角色模板补齐新权限(按旧财务权限映射) +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:finance:income:view', 'tenant:finance:transaction:view'), + ('tenant:finance:income:view', 'tenant:finance:transaction:detail'), + ('tenant:finance:income:export', 'tenant:finance:transaction:export'), + ('tenant:finance:statement:view', 'tenant:finance:settlement:view'), + ('tenant:finance:statement:export', 'tenant:finance:settlement:export') +), +candidates AS ( + SELECT DISTINCT + source."RoleTemplateId", + mapping.target_code + FROM public.role_template_permissions source + INNER JOIN code_mapping mapping + ON mapping.source_code = source."PermissionCode" + WHERE source."DeletedAt" IS NULL +), +missing AS ( + SELECT + candidate."RoleTemplateId", + candidate.target_code, + ROW_NUMBER() OVER ( + ORDER BY candidate."RoleTemplateId", candidate.target_code + ) AS rn + FROM candidates candidate + LEFT JOIN public.role_template_permissions existing + ON existing."RoleTemplateId" = candidate."RoleTemplateId" + AND existing."PermissionCode" = candidate.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:finance:income:view', 'tenant:finance:transaction:view'), + ('tenant:finance:income:view', 'tenant:finance:transaction:detail'), + ('tenant:finance:income:export', 'tenant:finance:transaction:export'), + ('tenant:finance:statement:view', 'tenant:finance:settlement:view'), + ('tenant:finance:statement:export', 'tenant:finance:settlement:export') +) +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; + +-- 5) 给租户角色补齐新权限(按旧财务权限映射) +WITH code_mapping(source_code, target_code) AS ( + VALUES + ('tenant:finance:income:view', 'tenant:finance:transaction:view'), + ('tenant:finance:income:view', 'tenant:finance:transaction:detail'), + ('tenant:finance:income:export', 'tenant:finance:transaction:export'), + ('tenant:finance:statement:view', 'tenant:finance:settlement:view'), + ('tenant:finance:statement:export', 'tenant:finance:settlement:export') +), +source_rows AS ( + SELECT DISTINCT + source."RoleId", + source."TenantId", + source."Portal", + mapping.target_code + 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" + WHERE source."DeletedAt" IS NULL + AND source."Portal" = 1 +), +candidates AS ( + SELECT DISTINCT + source_row."RoleId", + source_row."TenantId", + source_row."Portal", + target_permission."Id" AS target_permission_id + FROM source_rows source_row + INNER JOIN public.permissions target_permission + ON target_permission."Code" = source_row.target_code +), +missing AS ( + SELECT + candidate."RoleId", + candidate."TenantId", + candidate."Portal", + candidate.target_permission_id, + ROW_NUMBER() OVER ( + ORDER BY candidate."TenantId", candidate."RoleId", candidate.target_permission_id + ) AS rn + FROM candidates candidate + LEFT JOIN public.role_permissions existing + ON existing."RoleId" = candidate."RoleId" + AND existing."PermissionId" = candidate.target_permission_id + AND existing."Portal" = candidate."Portal" + AND ( + (existing."TenantId" IS NULL AND candidate."TenantId" IS NULL) + OR existing."TenantId" = candidate."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:finance:income:view', 'tenant:finance:transaction:view'), + ('tenant:finance:income:view', 'tenant:finance:transaction:detail'), + ('tenant:finance:income:export', 'tenant:finance:transaction:export'), + ('tenant:finance:statement:view', 'tenant:finance:settlement:view'), + ('tenant:finance:statement:export', 'tenant:finance:settlement:export') +) +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;