|
|
@@ -0,0 +1,182 @@
|
|
|
+-- 汇安融扫码收款(收银台)—— 菜单与权限(2026-10-06)
|
|
|
+--
|
|
|
+-- 【本脚本尚未执行】执行由用户做。执行前先跑文末「只读核对」第 1 条。
|
|
|
+--
|
|
|
+-- ── 本脚本干什么 ──────────────────────────────────────────────────────
|
|
|
+-- 在**已存在**的「汇安融通道」目录(`route_name='payment-har'`,`har-menu.sql` 所建)下,新增:
|
|
|
+-- · 1 行页面(type=2)「扫码收款」 —— 收银台路由
|
|
|
+-- · 1 行按钮(type=3)「发起收款」 —— 取码权限点
|
|
|
+-- 并把这两行授权给 role_id = 1(管理员角色)。
|
|
|
+-- ★ 与乐收赢那份(`lsy-cashier-menu.sql`)**故意不同**:那份把页面/按钮/目录一并授给
|
|
|
+-- role 2(租户角色);这份**只授 role 1**。理由见下「★ 授权给谁」,附现成的放开段。
|
|
|
+--
|
|
|
+-- ── 表结构约定(照抄踩过的坑)────────────────────────────────────────
|
|
|
+-- · `component_path` 是**相对 `src/views/` 的路径**:不带 `views/` 前缀、不带 `.vue` 后缀。
|
|
|
+-- 本脚本的值与页面文件对齐:`frontend/src/views/module_payment/har/cashier/index.vue`。
|
|
|
+-- 写错会拼成查不到的模块名 ⇒ **静默落到 error/404.vue**(不是报错)。
|
|
|
+-- · 目录那一行是**查**目录、不是插目录(HAR 目录已由 `har-menu.sql` 建好)。
|
|
|
+-- · 目录行本身也必须在 `sys_role_menus` 里(`UserService.buildMenuTree` 只会把 parent
|
|
|
+-- 同在集合内的节点挂上树,父不在集合里的子节点被**直接丢弃**,不报错)。
|
|
|
+-- —— 对 role 1 而言这一行执行前就有(`har-menu.sql` 授过);本脚本仍带 NOT EXISTS 兜一道。
|
|
|
+--
|
|
|
+-- ── ★★ 锚 `route_name`(身份),不锚 `name`(标签)──────────────────────
|
|
|
+-- 同 `lsy-cashier-menu.sql:133-137` 的实测理由:那一侧的目录被**人手工改过名**
|
|
|
+-- (2026-09-23 叫「收付款」、09-28 实测已叫「银联收单」,全仓零命中)。
|
|
|
+-- 本目录的 `route_name` 由 `har-menu.sql:19` 写入 = `payment-har`,此后无任何脚本改过它。
|
|
|
+-- 若这里按名字(如「汇安融通道」)查,一旦有人改过标签,`dir` 为空 ⇒ **全脚本静默空跑**。
|
|
|
+--
|
|
|
+-- ── ★ permission 必须与 @PreAuthorize 逐字相同 ──────────────────────────
|
|
|
+-- 差一个字符就是 403,而且**超管测不出来**(`PermissionCheck` 对 `ROLE_SUPERUSER` 直接放行)。
|
|
|
+-- 本脚本两个 permission 逐字取自 **2026-10-06 从源码现取**(未抄任何文档):
|
|
|
+-- HarCashierController.java @PreAuthorize("@perm.hasAny('module_payment:har:cashier:create')") —— POST /qrcode
|
|
|
+-- HarCashierController.java @PreAuthorize("@perm.hasAny('module_payment:har:cashier:list')") —— GET /{orderNo}
|
|
|
+-- 类级映射 `@RequestMapping("/payment/har/cashier")`(同文件)。
|
|
|
+-- ★ 取值口径是「现取」,不是「抄一次就完」—— 控制器改了这里必须重取。
|
|
|
+-- ★ 钉子:`HarCashierControllerTest#endpoints_carryTheirExactPathsAndPermissionLiterals`
|
|
|
+-- 把这两个字面量与注解钉在一起;控制器改了而这里没改,是「菜单给了、点进去 403」。
|
|
|
+--
|
|
|
+-- ── ★★ route_name 与前端组件名必须逐字相同 ────────────────────────────
|
|
|
+-- `<keep-alive :include>` 匹配的是**组件名**,而 tags-view 推进 `cachedViews` 的是**路由名**
|
|
|
+-- (`tags-view.store.ts`)—— 对不上时 vue 不报错,只是**静默不缓存**(乐收赢踩过,`bf09016`)。
|
|
|
+-- 本行 `route_name='payment-har-cashier'` ⇒ 新页面 `defineOptions({ name: "payment-har-cashier" })`,
|
|
|
+-- 两头都已按此写。
|
|
|
+-- ★ 既存的三个 HAR 页面(商户/门店/订单)**没有写 defineOptions** ⇒ 它们的 `keep_alive=true`
|
|
|
+-- 今天静默失效。那是**既有缺陷、不在本批次范围**,未顺手改(用户令:不相关的不动)。
|
|
|
+-- 本页只保证自己自洽。
|
|
|
+--
|
|
|
+-- ── ★★ 授权给谁:只授 role 1;放开段留给裁定 ──────────────────────────
|
|
|
+-- 2026-10-05 的裁定口径(当日复量):**role 2 在 HAR 子树下只有 276/279,目录 275 不在手**
|
|
|
+-- ⇒ 租户看不见 HAR(与「HAR 未上线」一致)。本页照此只授 role 1。
|
|
|
+-- 为什么不「先授 role 2 页面、等将来再说」:`buildMenuTree` 父节点缺席 ⇒ 子节点被静默丢弃,
|
|
|
+-- 只授页面/按钮**等于没授**(角色 2 什么都看不到);而放开视野的最小动作是**连目录 275
|
|
|
+-- 一起授**,那会让 17 个租户用户看到「汇安融通道」目录 —— 是否现在这么做不是本脚本能定的
|
|
|
+-- (har-menu.sql 对 role 2 的门店/订单授权同样要等这个裁定)。⇒ 现成放开段见脚本末尾。
|
|
|
+--
|
|
|
+-- ★ 如实写代价:本页写权限 `module_payment:har:cashier:create` 是一条**新的资金入口**
|
|
|
+-- (真的能产生收款的单)。只授 role 1 ⇒ 今天只有管理员与超管能用;role 2 何时放开见上。
|
|
|
+--
|
|
|
+-- ── 幂等(与 lsy-cashier-menu.sql 同形,两种相反情形都不报错)──────────
|
|
|
+-- 1) 页面行已存在 ⇒ `page` 为空 ⇒ 页面与按钮都不插(`buttons` 是 `FROM page`)且不报错。
|
|
|
+-- 2) 目录不存在 ⇒ `dir` 为空 ⇒ 三行都不插,且不报错。执行前先跑文末第 1 条只读核对。
|
|
|
+--
|
|
|
+-- ── "order" ──────────────────────────────────────────────────────────
|
|
|
+-- 用 `MAX("order") + 1` **现算**,不写死数字(HAR 目录下现有子行 1/2/3,本行应拿到 4;
|
|
|
+-- 有人手工调过序时不撞车。`sys_menu` 对 (parent_id, "order") 没有唯一约束 ⇒ 撞了不报错、
|
|
|
+-- 只是两行同序)。
|
|
|
+--
|
|
|
+-- ── ★ 已知未验证点(如实)────────────────────────────────────────────
|
|
|
+-- HAR 的 `pay_url` 至今**没有人真扫过**:通道四接口在测试环境打过,但主扫收款链接从未被
|
|
|
+-- 一部真实手机扫过。上线后需用真实小额(如 1 元)实弹验证一次「取码 → 顾客扫 → 变绿」。
|
|
|
+
|
|
|
+WITH dir AS (
|
|
|
+ SELECT id FROM sys_menu
|
|
|
+ WHERE route_name = 'payment-har' AND type = 1 AND status = '0' AND parent_id IS NULL
|
|
|
+),
|
|
|
+page AS (
|
|
|
+ INSERT INTO sys_menu (name, type, "order", permission, icon, route_name, route_path, component_path,
|
|
|
+ redirect, hidden, keep_alive, always_show, affix, title, params,
|
|
|
+ uuid, parent_id, status, created_time, updated_time)
|
|
|
+ SELECT '扫码收款', 2,
|
|
|
+ (SELECT COALESCE(MAX("order"), 0) + 1 FROM sys_menu WHERE parent_id = dir.id),
|
|
|
+ 'module_payment:har:cashier:list', NULL,
|
|
|
+ 'payment-har-cashier', '/payment/har/cashier',
|
|
|
+ 'module_payment/har/cashier/index',
|
|
|
+ NULL, false, true, false, false, '扫码收款', NULL,
|
|
|
+ gen_random_uuid(), dir.id, '0', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
|
|
+ FROM dir
|
|
|
+ WHERE NOT EXISTS (SELECT 1 FROM sys_menu
|
|
|
+ WHERE name = '扫码收款' AND parent_id = dir.id AND status = '0')
|
|
|
+ RETURNING id
|
|
|
+),
|
|
|
+buttons AS (
|
|
|
+ INSERT INTO sys_menu (name, type, "order", permission, icon, route_name, route_path, component_path,
|
|
|
+ redirect, hidden, keep_alive, always_show, affix, title, params,
|
|
|
+ uuid, parent_id, status, created_time, updated_time)
|
|
|
+ SELECT v.name, 3, v.ord, v.perm, NULL, NULL, NULL, NULL,
|
|
|
+ NULL, false, false, false, false, v.name, NULL,
|
|
|
+ gen_random_uuid(), page.id, '0', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
|
|
+ FROM page,
|
|
|
+ (VALUES
|
|
|
+ ('发起收款', 1, 'module_payment:har:cashier:create')
|
|
|
+ ) AS v(name, ord, perm)
|
|
|
+ RETURNING id
|
|
|
+)
|
|
|
+-- 页面行与按钮行**都要**授权:菜单树按 sys_role_menus 过滤,只授页面会让按钮权限缺席 ⇒ 403。
|
|
|
+-- 目录行一并带上 NOT EXISTS 兜底(role 1 执行前就持有,兜的是「被谁删过」这种情况)。
|
|
|
+INSERT INTO sys_role_menus (role_id, menu_id)
|
|
|
+SELECT r.role_id, x.id
|
|
|
+ FROM (SELECT id FROM page
|
|
|
+ UNION ALL SELECT id FROM buttons
|
|
|
+ UNION ALL SELECT d.id FROM dir d) AS x
|
|
|
+ CROSS JOIN (VALUES (1)) AS r(role_id)
|
|
|
+ WHERE NOT EXISTS (SELECT 1 FROM sys_role_menus rm
|
|
|
+ WHERE rm.role_id = r.role_id AND rm.menu_id = x.id);
|
|
|
+
|
|
|
+
|
|
|
+-- ── 放开段(**不要**和上面一起跑;等「HAR 对租户开放」的裁定)──────────
|
|
|
+-- 裁定为「开放」时,单独跑这一段即可(与上面同形,只是把 role 2 补上;
|
|
|
+-- 目录 275 这一行是必须的 —— 父不在集合里,子节点会被 buildMenuTree 静默丢弃)。
|
|
|
+--
|
|
|
+-- INSERT INTO sys_role_menus (role_id, menu_id)
|
|
|
+-- SELECT r.role_id, x.id
|
|
|
+-- FROM (SELECT id FROM sys_menu
|
|
|
+-- WHERE (route_name = 'payment-har-cashier' AND type = 2)
|
|
|
+-- OR (permission = 'module_payment:har:cashier:create' AND type = 3)
|
|
|
+-- OR (route_name = 'payment-har' AND type = 1)) AS x
|
|
|
+-- CROSS JOIN (VALUES (2)) AS r(role_id)
|
|
|
+-- WHERE NOT EXISTS (SELECT 1 FROM sys_role_menus rm
|
|
|
+-- WHERE rm.role_id = r.role_id AND rm.menu_id = x.id);
|
|
|
+--
|
|
|
+-- ★ 收回(万一放开后又决定收回):删 role 2 对这三行的授权即可 ——
|
|
|
+-- DELETE FROM sys_role_menus
|
|
|
+-- WHERE role_id = 2
|
|
|
+-- AND menu_id IN (SELECT id FROM sys_menu
|
|
|
+-- WHERE route_name IN ('payment-har', 'payment-har-cashier')
|
|
|
+-- OR permission = 'module_payment:har:cashier:create');
|
|
|
+-- ★ 收回**目录那一行**(route_name='payment-har')才是让整个目录从角色 2 视野消失的那一下。
|
|
|
+
|
|
|
+
|
|
|
+-- ── 只读核对(执行后手工跑一遍;第 1 条**执行前**也先跑)────────────────
|
|
|
+--
|
|
|
+-- 1) 目录在不在(**为空则本脚本什么都不会插,且不报错**)
|
|
|
+-- SELECT id, name, title, type, route_name, route_path FROM sys_menu
|
|
|
+-- WHERE route_name = 'payment-har' AND type = 1 AND status = '0' AND parent_id IS NULL;
|
|
|
+-- 期望:**1 行**(2026-10-05 复量时该目录 id=275)。查的是 `route_name` 而不是 `name`(同 CTE 的理由)。
|
|
|
+--
|
|
|
+-- 2) 页面行
|
|
|
+-- SELECT id, name, type, "order", permission, route_name, route_path, component_path,
|
|
|
+-- keep_alive, uuid
|
|
|
+-- FROM sys_menu WHERE name = '扫码收款' AND status = '0'
|
|
|
+-- AND parent_id = (SELECT id FROM sys_menu
|
|
|
+-- WHERE route_name = 'payment-har' AND type = 1 AND status = '0' AND parent_id IS NULL);
|
|
|
+-- 期望:**1 行**,type=2,permission='module_payment:har:cashier:list',
|
|
|
+-- route_name='payment-har-cashier',route_path='/payment/har/cashier',
|
|
|
+-- component_path='module_payment/har/cashier/index',
|
|
|
+-- keep_alive=true,uuid 非空(36 字符)。
|
|
|
+-- ★ 若出现**2 行**:`name` 撞了乐收赢那份脚本的「扫码收款」(它在另一个父目录下,正常;
|
|
|
+-- 本查询已按 parent 限定,若仍 2 行说明同一目录里真重复了,先查清再动)。
|
|
|
+--
|
|
|
+-- 3) 按钮行(**漏了 ⇒ 非超管点「发起收款」必 403,而超管测不出来**)
|
|
|
+-- SELECT m.id, m.name, m.type, m."order", m.permission
|
|
|
+-- FROM sys_menu m JOIN sys_menu p ON m.parent_id = p.id
|
|
|
+-- WHERE p.name = '扫码收款' AND m.type = 3 AND m.status = '0'
|
|
|
+-- AND p.route_name = 'payment-har-cashier'
|
|
|
+-- ORDER BY m."order";
|
|
|
+-- 期望:**1 行**,permission 逐字为 'module_payment:har:cashier:create'。
|
|
|
+--
|
|
|
+-- 4) 授权行
|
|
|
+-- SELECT rm.role_id, m.id, m.name, m.type, m.permission
|
|
|
+-- FROM sys_role_menus rm JOIN sys_menu m ON m.id = rm.menu_id
|
|
|
+-- WHERE m.status = '0'
|
|
|
+-- AND (m.route_name IN ('payment-har-cashier', 'payment-har')
|
|
|
+-- OR m.permission = 'module_payment:har:cashier:create')
|
|
|
+-- ORDER BY rm.role_id, m.type, m.id;
|
|
|
+-- 期望:**3 行**(role 1:目录 + 扫码收款 + 发起收款)。role 2 **一行都没有** ——
|
|
|
+-- 那是本脚本的授权范围决定,不是漏授(理由见文件头)。
|
|
|
+--
|
|
|
+-- 5) 端到端(**唯一能证明权限串没写错的一条**):用一个**非超管**的账号登录,
|
|
|
+-- 确认两个 `module_payment:har:cashier:*` 都出现在
|
|
|
+-- `MenuMapper.selectPermissionsByUserId` 的返回里。
|
|
|
+-- ★ 超管账号**证明不了任何事** —— 它对 ROLE_SUPERUSER 直接放行。
|
|
|
+-- ★ 今天 role 2 的成员看不到本页(目录未授),这一步要用**非超管但持有 role 1**的账号,
|
|
|
+-- 或先跑放开段再用租户账号验。
|