Files
shanxia2.0/openspec/changes/zkxw-engineering-standards/specs/permission-conventions/spec.md
T
2026-06-01 08:21:21 +08:00

7.7 KiB
Raw Blame History

ADDED Requirements

Requirement: 角色与菜单授权

系统 SHALL 通过 sys_role、sys_role_menu 等模型维护角色与菜单的关联;角色管理界面 SHALL 支持为角色配置可访问菜单树,且勾选子菜单时 SHALL 自动包含必要父菜单(与《shanxia2.0需求 V0.3》§2.1.5 描述一致)。

Scenario: 勾选子菜单

  • WHEN 管理员在角色菜单配置弹窗中仅勾选叶子菜单
  • THEN 保存后角色 SHALL 同时获得该叶子到根路径上的父菜单权限,避免出现「有子无父」的不可达状态

Requirement: 数据权限范围

角色 SHALL 具有 data_scope 字段,取值语义为:全部、本部门、本部门及以下、仅本人、自定义等(与需求 §2.1.5 及表注释一致);当为自定义时,系统 SHALL 使用 sys_role_data_scope_detail 维护 scope_type(部门/用户)与 scope_value。

Scenario: 配置自定义数据权限

  • WHEN 管理员将角色数据权限设为自定义并在弹窗中选择部门与用户
  • THEN 持久化 SHALL 写入 sys_role_data_scope_detail,且运行时查询 SHALL 按明细过滤

Requirement: 多角色权限合并

单个用户允许绑定多个角色;其菜单权限与数据权限 SHALL 分别取所有的角色的权限并集(与需求 §2.1.5「特别注意」一致)。

Scenario: 两角色菜单合并

  • WHEN 用户同时具有角色 R1(菜单集合 M1)与角色 R2(菜单集合 M2)
  • THEN 用户可见菜单 SHALL 为 M1 ∪ M2

Scenario: 两角色数据范围合并

  • WHEN 用户同时具有角色 R1 与 R2,各自数据权限范围不同
  • THEN 可访问数据 SHALL 为两角色数据权限的并集(在实现上与产品确认:取更宽或按需求文档并集语义)

Requirement: Web 与小程序菜单树

菜单管理 SHALL 同时支撑 Web 端与小程序端菜单;系统 SHALL 预置两棵顶层虚拟根(需求中的 parent_id=0 的「web 端菜单」「移动端菜单」),后续菜单 SHALL 挂载其下(与需求 §2.1.6)。

Scenario: 新增移动端菜单

  • WHEN 管理员新增小程序端一级菜单
  • THEN 该节点 SHALL 挂在移动端根节点之下,且 visible/status 字段符合规范枚举

Requirement: Web 管理端菜单注册与侧栏展示(三件套同步)

web-admin 新增或变更可在侧栏点击访问的页面菜单时,SHALL 同时维护下列三处,并在同一迭代/提交中交付。仅执行 docs/sql/ 中的 sys_menu INSERT 不足以在主页侧栏显示菜单。

序号 位置 作用
1 数据库 sys_menu(脚本放 docs/sql/) 菜单主数据;sys_role_menu 授权;perms 供非超管过滤;系统管理「菜单管理」展示
2 前端静态路由 web-admin/src/router/modules/sqxp.ts(经 index.ts → asyncRoutes) 当前默认侧栏与动态路由注册的数据源(见下节访问模式)
3 后端静态菜单树 backend/app/api/menu.py 的 STATIC_MENU_TREE 与 sqxp.ts 字段对齐,供后端菜单模式、/web/menu/list 及文档对照;SHALL 与 sqxp.ts 同步维护

访问模式(VITE_ACCESS_MODE,见 web-admin/.env / .env.development)

  • frontend(本仓库默认):登录后 MenuProcessor(src/router/core/MenuProcessor.ts)从 asyncRoutes / sqxp.ts 构建侧栏并 register 路由;不读取 sys_menu 树渲染侧栏。
  • backend:侧栏来自 GET /api/web/menu/list,响应体为 menu.py 的 STATIC_MENU_TREE。

运行时可见性(frontend 模式)

  • super_admin:展示 sqxp.ts 中全部静态菜单(不按 meta.roles 裁剪)。
  • admin 及其他非超管:在静态菜单基础上,按 GET /api/web/auth/menu-codes 返回的 menuCodes(来源于用户角色关联的 sys_menu.perms)过滤;menuCodes 与路由 meta.menuCode SHALL 一致(MenuProcessor 会做 snake_case 规范化)。
  • 非超管角色若未在「角色管理」中勾选新菜单(或未写入 sys_role_menu),即使 sqxp.ts 已注册路由,侧栏也 SHALL NOT 显示该项。

字段对齐(页面菜单 menu_type = C)

sys_menu sqxp.ts / STATIC_MENU_TREE 说明
id id 建议一致,便于角色授权与排错
parent_id 父节点 id / children 嵌套 目录 M 仅 SQL + 静态树父级;sqxp.ts 用 children
path path 子路由为相对路径(如 customer),非 /crm/customer
component component 如 /crm/customer/index,对应 src/views 下页面
perms meta.menuCode 列表权限标识,如 crm:customer:list
menu_name meta.title 侧栏展示文案
visible / status — '1' 启用;库内维护,静态路由默认可见

按钮权限(menu_type = F)

  • 仅写入 sys_menu(parent_id 指向页面菜单 C),perms 如 crm:customer:add。
  • SHALL NOT 写入 sqxp.ts / STATIC_MENU_TREE 的 children(按钮不出现在侧栏)。

一级目录菜单图标

  • sqxp.ts / STATIC_MENU_TREE 中一级菜单 meta.icon SHALL 在 web-admin/src/utils/ui/ep-icon-map.ts 注册(见 ui-conventions 反例 EC-UI-002)。

参考实现

  • CRM 客户域:docs/sql/crm-customer-menu.sql + sqxp.ts 中 CRM 管理 下 customer / customer-pool 子路由 + menu.py 同结构节点。

Scenario: 新增业务页面菜单(frontend 模式)

  • WHEN 新增可在侧栏访问的业务页(如 CRM 管理 → 客户管理)
  • THEN 开发者 SHALL:
    1. 在 docs/sql/ 编写 sys_menu INSERT(页面 C + 所需按钮 F;tenant_no、父菜单 id 与现网一致)
    2. 在 web-admin/src/router/modules/sqxp.ts 对应父级 children 追加路由(path、component、meta.menuCode 与 SQL 一致)
    3. 在 backend/app/api/menu.py 的 STATIC_MENU_TREE 追加同构节点
    4. 为非 super_admin 角色在「角色管理」勾选新菜单(或脚本维护 sys_role_menu)
    5. 部署后要求用户重新登录或触发路由初始化,以注册新动态路由

Scenario: 仅执行 sys_menu SQL 未改 sqxp.ts

  • WHEN 开发者只执行了 docs/sql/*-menu.sql,未修改 sqxp.ts
  • THEN 在 VITE_ACCESS_MODE=frontend 下侧栏 SHALL NOT 出现新菜单;系统管理「菜单管理」中可能可见该记录——此为缺陷状态,SHALL 补全 sqxp.ts 与 menu.py

Scenario: 废弃/删除菜单

  • WHEN 废弃或删除一个菜单项
  • THEN 开发者 SHALL:
    1. 在 docs/sql/ 中将对应 sys_menu 软删除(del_flag=1,SHALL NOT 物理删除)
    2. 从 sqxp.ts 移除对应路由节点
    3. 从 menu.py 的 STATIC_MENU_TREE 移除对应节点
    4. 若父目录下无剩余子菜单,父目录 SHALL 一并软删除
    5. 清理或调整相关 sys_role_menu 关联(按需)

Scenario: 代码评审检查项

  • WHEN 评审涉及菜单变更的 PR
  • THEN 评审者 SHALL 确认:docs/sql/ 脚本、sqxp.ts、menu.py 三者 id / path / component / menuCode↔perms 一致;sqxp.ts 已修改(frontend 模式);按钮 F 未误入静态路由树;一级 meta.icon 已在 ep-icon-map.ts 注册

Requirement: 超级管理员与租户

租户管理 SHALL 仅对超级系统管理员开放(与需求 §2.1.1);其他系统管理 SHALL 在租户上下文中执行,与 api-conventions 中租户隔离一致。

Scenario: 非 superadmin 访问租户管理

  • WHEN 非超级管理员请求租户管理接口或页面
  • THEN 系统 SHALL 拒绝访问