本文说明 DGJ2 PC、APP、OPS 管理端中的菜单展示、页面元素、接口权限、服务站能力、应用开通、白名单和财务锁站机制。核心原则是:页面可见、按钮可见、接口可调用、站点具备业务能力是四件不同的事。

适用于新增菜单/按钮/接口、给子账号授权、OPS 控制服务站能力、应用开通后菜单未出现、权限修改不生效、采购/销售被锁、财务逾期仅能查看资金菜单等场景。

1. 业务目标

  • 明确旧权限体系与新 ACL 体系并存时的读取和写入位置。
  • 解释服务站、用户、应用、白名单和页面资源之间的交集关系。
  • 从“没有菜单”“没有按钮”“接口无权”“业务被禁止”反查到准确控制点。
  • 新增功能时同时补齐资源、OPS、用户授权、接口校验和回归验证。
  • 避免仅修改前端显示或单张表,造成权限绕过或配置表面生效。
  • 为财务逾期锁站、采购/销售黑名单提供可执行排查流程。

2. 权限模型总览

flowchart LR
    RES["资源默认配置\nt_acl_sys_resource"] --> MENU["站点菜单过滤"]
    OPS["OPS菜单规则\nold/new"] --> MENU
    APP["应用开通关系"] --> MENU
    WL["白名单/地区规则"] --> MENU
    USER["用户资源规则"] --> MENU
    MENU --> PAGE["PC菜单与页面元素"]
    USER --> API["运行时接口鉴权"]
    CACHE["USER_RIGHTS缓存"] --> API
    ABILITY["采购/销售能力黑名单"] --> BIZ["业务提交校验"]
    LOCK["财务逾期锁站"] --> OPS
    LOCK --> ACCOUNT["PC/APP/PDA账户状态"]

最终可用能力可近似理解为:

站点可见菜单
= 资源默认开启
∩ OPS允许
∩ 应用已开通
∩ 白名单允许
∩ 用户已授权
∩ 父级仍有可见子项

接口可调用
= 登录态有效
∩ 用户为主账号或拥有对应resource_id
∩ 业务控制器实际调用鉴权
∩ 站点业务能力未被额外禁用

3. 先区分四类问题

问题层用户现象主要数据源首查代码
菜单左侧没有入口Resource、OPS、应用、用户规则ResourceService::getMenuList
页面元素页面存在但按钮/字段不显示Resource type=2/4、前端 rightsgetPageElementList、getWebRightList
接口手工请求返回无权限ResourceRule、USER_RIGHTSCommon_model::checkpurview
业务能力页面和接口都在,但提交采购/销售失败Redis 能力黑名单、锁站状态InvPoFactory、InvSaService

不要把“菜单隐藏”等同于“接口禁止”。如果控制器没有调用权限校验,仅隐藏前端入口不能构成安全边界。

4. 新旧权限体系并存

DGJ2 处于从旧 SYS_MENU + SYS_ADMIN.lever 向新 ACL_SYS_RESOURCE + ACL_SYS_RESOURCE_RULE 迁移后的兼容阶段。

维度旧体系新体系
资源表t_sys_menut_acl_sys_resource
用户授权t_sys_admin.lever 逗号分隔 IDt_acl_sys_resource_rule 一行一个资源
OPS 站点规则t_sys_ops_menu_rulet_sys_ops_menu_rule_new
PC 菜单SYS_MENU.platform=pcResource platform=pc,type=1
APP 菜单SYS_MENU.platform=app、HomeRight仍主要兼容旧数据
接口检查module -> SYS_MENU.id -> levercode -> Resource.id -> ResourceRule
缓存主要直接读账号字段USER_RIGHTS:{uid},TTL 3600 秒

canAccessV2() 决定 Common_model 运行时采用哪套权限检查。排查时必须先确认当前用户进入的是旧分支还是新分支。

flowchart TD
    REQ["checkpurview(module/code)"] --> EMPTY{"权限码为空?"}
    EMPTY -- 是 --> PASS0["直接通过"]
    EMPTY -- 否 --> ADMIN["查询SYS_ADMIN"]
    ADMIN --> MAIN{"roleid=0?"}
    MAIN -- 是 --> PASS1["主账号通过"]
    MAIN -- 否 --> V2{"canAccessV2()?"}
    V2 -- 否 --> OLD["SYS_MENU.module -> id\n检查SYS_ADMIN.lever"]
    V2 -- 是 --> NEW["Resource.code -> id\n检查ResourceRule/Redis"]
    OLD --> RESULT{"命中?"}
    NEW --> RESULT
    RESULT -- 是 --> PASS2["通过"]
    RESULT -- 否 --> DENY["无页面管理权"]

5. 代码入口地图

职责文件核心方法
PC 首页组装application/controllers/v2/Desktop.phpindex
新资源服务application/service/v2/ResourceService.phpgetMenuList、getWebRightList
通用接口鉴权application/models/Common_model.phpcheckpurview、checkPermissionV2
子账号授权接口application/controllers/v2/Right.phpqueryalluserright、addrights2Outuser
资源创建接口application/controllers/v2/Resource.phpcreate
OPS 站点菜单入口application/controllers/inner/StationManage.phpgetMenuConfig、setMenuConfig
OPS 菜单规则application/Services/MenuRule/MenuRuleSer.phpgetOpsMenuRuleList、setOpsMenuRule
采购/销售能力同上getAbilityRuleList、setAbilityRule
财务锁站同上lockMenu、unLockMenu
通用白名单 APIapplication/controllers/inner/sys/OpsWhitelist.phpindex、batchSet
通用白名单服务application/Services/System/OpsWhitelistSer.phpgetWhitelistPage、batchSetWhitelist
菜单同步任务application/controllers/tasks/MenuRule.phpsyncOpsMenuRule、refreshSysMenu、refreshUserLever
ACL 迁移脚本application/controllers/tasks/AclMigrate.php多阶段迁移方法

6. 核心表字典

6.1 新 ACL 资源表

常量物理表关键字段用途
ACL_SYS_RESOURCEt_acl_sys_resourceid,parent_id,type,title,module,code,platform,status,is_delete,display,path菜单、按钮、接口、前端权限定义
ACL_SYS_RESOURCE_RULEt_acl_sys_resource_rulesid,uid,resource_id,platform,code,is_delete子账号拥有的资源

资源类型来自 ResourceEnums:

type名称典型用途
1菜单左侧导航和菜单树
2页面元素按钮、字段、操作入口
3接口后端调用权限定义
4Web Right前端按 rights[code] 控制的能力

通用状态:status=1 启用、0 关闭;is_delete=0 有效、1 逻辑删除;display=1 显示、0 隐藏。

6.2 旧菜单与账号表

常量物理表关键字段用途
SYS_ADMINt_sys_adminuid,sid,roleid,lever,storeLever,storeDefault,status,isDelete登录账号、旧权限、门店数据范围
SYS_MENUt_sys_menuid,parentId,module,platform,path,level,isDelete旧 PC/APP 菜单
SYS_MENU_DATAt_sys_menu_dataid,surname 等旧数据权限辅助配置
SYS_MENU_CONFIGt_sys_menu_configsid,content 等历史菜单配置源

roleid=0 在当前通用权限逻辑中视为主账号,跳过用户资源过滤;子账号必须命中授权。

6.3 OPS 站点控制表

常量物理表关键字段用途
SYS_OPS_MENU_RULEt_sys_ops_menu_rulesid,pc_rule_json,app_rule_json,update_user_*旧 PC 与 APP OPS 菜单规则
SYS_OPS_MENU_RULE_NEWt_sys_ops_menu_rule_newsid,pc_rule_json,before_lock_menu,lock_status,update_user_*新 PC 规则与财务锁站快照
SYS_OPS_WHITELISTt_sys_ops_whitelistsid,finance_version,create_user,update_user,*_time可扩展的站点开通配置

旧、新 OPS 表必须一起理解:setOpsMenuRule 会把 pcMenuList/appMenuList 写旧表,把 newPcMenuList 写新表。

6.4 应用和特殊控制

菜单组装还会读取应用定义、应用资源关系、站点应用开通关系及特殊权限控制。相关模型包括:

  • ApplicationModel
  • ApplicationResourceRelModel
  • ApplicationOpenModel
  • SpecialAuthorityControlModel
  • HomeRightModel

应用相关菜单必须同时满足“资源存在、站点已开应用、用户有权”。

7. 新 PC 菜单过滤顺序

ResourceService::getMenuList 的真实顺序非常重要:

  1. 从 Resource 读取 PC、菜单类型、启用、未删除、默认显示的资源。
  2. 执行地区/运营/报价/对账等白名单过滤。
  3. 执行 t_sys_ops_menu_rule_new 站点菜单过滤。
  4. 执行应用开通关系过滤。
  5. 执行用户资源权限过滤。
  6. 子账号额外隐藏权限设置和系统新增入口。
  7. 转为树结构。
  8. 如果一个父节点所有子项都隐藏,父节点也隐藏。
flowchart TD
    A["Resource有效PC菜单"] --> B["filterWhiteListRule"]
    B --> C["filterOpsMenuRuleNew"]
    C --> D["filterApplicationResourceRule"]
    D --> E["filterUserResourceRule"]
    E --> F["特殊账号规则"]
    F --> G["list_to_tree"]
    G --> H["父级无可见子项则隐藏"]
    H --> OUT["Desktop.menus"]

这意味着只查最后一层用户授权并不够。前面任一层把 display 置为 0,后续用户授权不会自动恢复。

8. PC 首页返回的三类权限数据

v2/Desktop::index 一次加载三组数据:

返回字段服务方法前端用途
menusgetMenuList左侧菜单和路由
pageElementgetPageElementList应用相关页面元素显示
rightsgetWebRightList按权限码控制按钮/操作

rights 最终是类似 {CODE: true} 的映射,并会通过 CODE_CHANGE_REVERSE 兼容旧权限码。因此排查前端按钮时要同时搜索新旧 code。

9. 用户授权写入流程

子账号授权入口:v2/Right::addrights2Outuser。

sequenceDiagram
    participant UI as 权限设置页
    participant C as v2/Right
    participant S as ResourceService
    participant RR as ResourceRule
    participant A as SYS_ADMIN
    participant R as Redis
    UI->>C: ruleIds/noRuleIds + APP权限 + 门店范围
    C->>C: checkpurview(STAFF_ADD)
    C->>S: createPcResourceRule
    S->>A: 更新storeLever/storeDefault
    S->>RR: 恢复、插入或逻辑删除资源规则
    S->>R: DEL USER_RIGHTS:{uid}
    C->>S: createAppResourceRule
    S->>A: 更新旧lever字段
    C-->>UI: 操作成功

9.1 PC 资源规则的细节

  • ruleIds 会自动补齐所有父资源 ID,避免只有子项没有父菜单。
  • 已逻辑删除的相同规则会恢复 is_delete=0。
  • 新规则批量插入。
  • noRuleIds 指定的规则逻辑删除为 is_delete=1。
  • 最后删除 USER_RIGHTS:{uid} 缓存。
  • 门店范围同步到 SYS_ADMIN.storeLever/storeDefault,并可能触发 IM 群成员变更。

9.2 APP 资源规则的细节

APP 仍通过 SYS_ADMIN.lever 维护:新增 ID 合并,取消 ID 从集合剔除,再写回逗号分隔字符串。

9.3 授权请求示例

以下仅表达字段语义,URL 以前端实际路由配置为准:

POST /v2/right/addrights2Outuser
Content-Type: application/json

{
  "JXCSID": 10001,
  "userId": 20001,
  "ruleIds": "101,102,103",
  "noRuleIds": "201,202",
  "appRightid": "419,422",
  "appNoRightid": "430",
  "storeLever": "10,11",
  "storeDefault": "10",
  "status": 1
}

约束:设置门店范围时必须有一个默认门店,storeDefault 不能包含多个 ID。

代码审计提示:createPcResourceRule 当前仍引用历史变量 $userName 进行参数检查和日志记录,但方法签名没有该参数。现网如未暴露告警,可能受 PHP 错误级别影响;后续修改该公共方法时应先补测试,不要在无回归的情况下顺手调整。

10. 运行时接口鉴权

典型控制器会显式调用:

$this->common_model->checkpurview('STAFF_ADD');

新体系检查过程:

  1. 用 ResourceEnums::CODE_CHANGE 把旧 code 映射成新 code。
  2. 从 Redis 读取 USER_RIGHTS:{uid}。
  3. 缓存不存在时查 t_acl_sys_resource_rule 的有效 resource_id。
  4. 把结果缓存 3600 秒。
  5. 用 Resource code 找资源 ID。
  6. 判断资源 ID 是否在用户权限列表中。

module 参数也可以是数组,数组语义是“任一权限命中即可通过”。

10.1 三种通用检查方法

方法无权行为适用场景
checkpurview页面跳转/JSON 错误传统控制器入口
checkpurdata返回 1/0是否展示敏感数据
checkPurviewWithException抛异常Service/API 统一异常处理

10.2 重要风险

如果控制器只依赖前端 rights 隐藏按钮,没有调用任何后端鉴权方法,用户仍可能直接请求接口。新增写接口时必须检查同模块既有入口的后端校验模式。

11. USER_RIGHTS 缓存

项值
Redis 枚举RedisKeys::USER_RIGHTS
逻辑格式user_rights:{uid},实际带项目统一前缀
内容JSON 数组,元素为 resource_id
TTL3600 秒
正常失效点createPcResourceRule 写权限后主动删除
flowchart LR
    CHECK["接口鉴权"] --> EXISTS{"缓存存在?"}
    EXISTS -- 是 --> JSON["解析resource_id数组"]
    EXISTS -- 否 --> DB["查询有效ResourceRule"]
    DB --> SET["缓存3600秒"]
    SET --> JSON
    JSON --> MATCH{"目标资源ID存在?"}
    MATCH -- 是 --> YES["允许"]
    MATCH -- 否 --> NO["拒绝"]

如果通过脚本或 SQL 直接改 ResourceRule,没有走服务方法,旧缓存可能继续生效一小时。生产排查应先证明数据库与缓存是否一致,不能直接清空整个 Redis。

12. OPS 菜单配置

12.1 查询站点配置

逻辑入口:inner/StationManage::getMenuConfig。

POST /inner/stationManage/getMenuConfig
Content-Type: application/json

{
  "sid": 10001
}

返回分三组:

{
  "menuConfig": {
    "pcMenuList": [],
    "newPcMenuList": [],
    "appMenuList": []
  },
  "accountConfig": {},
  "abilityConfig": {
    "poAbilityNo": false,
    "saleAbilityNo": false
  }
}

12.2 设置站点配置

POST /inner/stationManage/setMenuConfig
Content-Type: application/json

{
  "sid": 10001,
  "config": {
    "menuConfig": {
      "pcMenuList": [],
      "newPcMenuList": [],
      "appMenuList": []
    },
    "abilityConfig": {
      "poAbilityNo": 0,
      "saleAbilityNo": 0
    }
  }
}

操作者信息由可信内部请求上下文提供,文档不记录真实请求凭证。接口在数据库事务中写账号、菜单和能力配置,并增加安全操作日志。

13. 新旧 OPS 规则的生成方式

13.1 旧 PC 规则

数据基础来自 MenusService::getMenus,再叠加旧表 pc_rule_json 的 display 配置。

13.2 新 PC 规则

数据基础来自 Resource:

type=菜单
status=启用
is_delete=否
platform=pc
display=显示

规则按 module_id 建立临时映射,再把 display 回填到资源树。应用中心会动态加入应用数据;部分应用默认隐藏,需明确开通。

13.3 APP 规则

数据基础来自 HomeRightModel,排除 PDA/HOMEPAGE 后,按 module 构造首页、查找、数据统计、配送等分组,再叠加旧表 app_rule_json。

14. 应用开通与菜单

应用菜单不是普通菜单授权:

flowchart LR
    DEF["应用定义"] --> REL["应用-资源关系"]
    OPEN["站点应用开通"] --> REL
    REL --> FILTER["ResourceService应用过滤"]
    USER["用户资源授权"] --> FILTER
    FILTER --> SHOW["菜单/页面元素显示"]

排查应用菜单时依次确认:

  1. 应用定义有效。
  2. Resource 已绑定到应用。
  3. 站点存在有效应用开通记录。
  4. OPS 没有隐藏该资源。
  5. 子账号拥有对应资源。
  6. 应用页面元素规则也处于开启状态。

15. 通用 OPS 白名单

当前 SysOpsWhitelistModel::COLUMN_DEFAULT 明确定义的可配置字段为:

字段值默认行为
finance_version1 老版、2 新版无记录或非 1 时按新版 2

通用白名单表按“可扩展字段”设计,但服务和 Validator 只接受 COLUMN_DEFAULT 中登记的字段。新增列时必须同步模型默认值、DDL、校验、列表和业务读取方。

15.1 白名单查询

支持站点、城市、财务版本、开通时间范围和分页。列表以正常主账号站点为底表,左连接白名单,因此未配置站点也会按默认值显示。

15.2 批量设置

POST /inner/sys/opsWhitelist/batchSet
Content-Type: application/json

{
  "sids": [10001, 10002],
  "config_column": "finance_version",
  "config_value": 2
}

模型会:

  • 已存在 sid:按记录 ID 批量更新,并写更新人。
  • 不存在 sid:合并所有默认值后批量插入,并写创建人。
  • 无效 config_column:Validator 拒绝。

16. 地区和运营白名单

ResourceService::filterWhiteListRule 还读取多个 Redis/业务白名单:

控制结果排查方向
地区简化菜单仅保留销售、采购或两者会话 areaCode 与地区 Redis Hash
精细化运营白名单服务站/修理厂画像、库存分析菜单站点是否在运营白名单
报价规则版本切换菜单标题、table_code、hrefQuoteManager 的 V2 判断
对账单白名单可能切换对账菜单配置Statement 白名单服务
少量特殊功能代码级站点例外搜索目标权限 code 的硬编码判断

代码中存在少量历史硬编码站点例外。公共文档不列真实站点标识;维护时应优先迁移到有审计记录的配置或白名单表。

17. 采购与销售能力黑名单

OPS 的 abilityConfig 不是菜单控制,而是业务提交控制:

字段Redis Hash1 的业务含义
poAbilityNoHASH_PURCHASE_STATION_BLACKLIST禁止采购能力
saleAbilityNoHASH_SALE_STATION_BLACKLIST禁止销售能力

setAbilityRule 的写法:值为 1 时 HSET sid 1,值为 0 时 HDEL sid。

读取点包括:

  • 采购:application/Services/InvPoFactory.php
  • 销售:application/service/scm/InvSaService.php
  • OPS 展示:MenuRuleSer::getAbilityRuleList
  • 缓存管理:application/controllers/inner/CacheManage.php
sequenceDiagram
    participant OPS as OPS配置
    participant MS as MenuRuleSer
    participant R as Redis Hash
    participant B as 采购/销售Service
    OPS->>MS: abilityConfig
    alt 禁止能力
        MS->>R: HSET hash sid 1
    else 恢复能力
        MS->>R: HDEL hash sid
    end
    B->>R: HGET hash sid
    R-->>B: 1或空
    B-->>B: 拒绝或继续业务

因此“菜单能看到但采购提交失败”应优先查能力 Hash,而不是 ResourceRule。

18. 财务逾期锁站

支付中心逾期状态变化会调用 MenuRuleSer::lockMenu/unLockMenu。

18.1 锁定

  1. 从 Resource 获取所有有效 PC 菜单。
  2. 仅保留 module=money 为显示,其余隐藏。
  3. 把锁定前 pc_rule_json 保存到 before_lock_menu。
  4. 写 lock_status=1。
  5. 锁定 PDA、APP、商城相关账号。
  6. 记录财务锁定安全日志。
  7. 通过 WebSocket 通知站点强制下线。

18.2 解锁

  1. 要求站点存在新 OPS 配置。
  2. 要求当前 lock_status=1。
  3. 用 before_lock_menu 恢复 pc_rule_json。
  4. 清空快照并写 lock_status=0。
  5. 解锁 PDA、APP、商城账号。
  6. 记录财务解锁日志。
stateDiagram-v2
    [*] --> Unlocked
    Unlocked: lock_status=0
    Unlocked --> Locked: 逾期锁定
    Locked: lock_status=1\n仅显示资金菜单
    Locked --> Unlocked: 逾期解除
    Locked --> Locked: 重复锁定抛异常
    Unlocked --> Unlocked: 重复解锁抛异常

18.3 主要风险

  • 首次锁定时快照来自动态菜单结果,必须确认是完整规则。
  • 锁定过程中数据库、账号服务、WebSocket 不是天然分布式事务。
  • before_lock_menu 被覆盖或为空时,解锁可能无法恢复原菜单。
  • 直接修改 pc_rule_json 可能破坏锁定快照。
  • 重复锁/解锁当前会抛状态异常,上游需理解重试行为。

19. 菜单同步和迁移任务

任务作用风险
syncOpsMenuRule菜单源与 SYS_MENU_CONFIG 合并写旧 OPS 规则可能批量覆盖站点 JSON
refreshSysMenu根据 APP HomeRight 刷新旧 SYS_MENU固定 ID/重复执行风险需验 DDL
refreshUserLever把 PC 对应 APP 菜单合并进用户 lever可能扩大旧 APP 权限
AclMigrate旧菜单、用户规则迁移到新 ACL迁移脚本步骤多,不应在线随意重跑
syncLoginStatus按站点状态同步 PC/APP/PDA 关闭标记影响整站登录

执行任何批量任务前必须备份受影响记录、统计目标数量、限定环境,并准备回滚数据。

20. 新增菜单的标准流程

flowchart TD
    A["确定菜单层级/路由/权限码"] --> B["新增Resource资源"]
    B --> C["确认parent_id/path/level/sort"]
    C --> D["如属应用则绑定应用资源关系"]
    D --> E["更新OPS规则展示"]
    E --> F["给测试子账号授权"]
    F --> G["控制器增加后端鉴权"]
    G --> H["清权限缓存/重新登录"]
    H --> I["主账号、子账号、白名单内外回归"]

资源最少需要确认:

字段要求
parent_id指向正确父资源
platformPC 当前为 pc
module前端菜单模块标识,保持同层约定
title展示名称
type菜单/页面元素/接口/Web Right
code全局稳定权限码,接口校验使用
table_code前端页面/表格标识,按项目约定
href实际页面路由
level/path/sort树层级与顺序
status/is_delete/display启用、有效、默认显示

21. 新增按钮或接口权限

新增写操作建议至少有两类资源:

  1. 前端按钮/Web Right:控制正常 UI 是否显示。
  2. 后端接口权限:控制直接请求是否允许。

检查清单:

  • 新 code 是否唯一。
  • 旧前端是否仍使用历史 code,是否需要兼容映射。
  • 权限设置页能否看到并勾选资源。
  • 子账号授权是否自动带父资源。
  • 控制器是否在业务执行前校验。
  • 主账号和系统内部回调是否需要不同授权策略。
  • 查询、导出、编辑、删除是否应拆分权限。

22. SQL 排查模板

所有 SQL 默认为只读;表字段以目标环境 DDL 为准。

22.1 查账号和新旧权限分支

SELECT uid, sid, username, roleid, status, isDelete,
       lever, storeLever, storeDefault
FROM t_sys_admin
WHERE uid = :uid;

判断:roleid=0 通常为主账号;子账号继续查 ResourceRule 或 lever。

22.2 按权限码查资源

SELECT id, parent_id, type, title, module, code,
       platform, status, is_delete, display, path, href
FROM t_acl_sys_resource
WHERE code = :resource_code;

重点检查是否重复 code、是否逻辑删除、是否默认隐藏、父资源是否有效。

22.3 查完整父链

SELECT id, parent_id, title, module, code, level, path,
       status, is_delete, display
FROM t_acl_sys_resource
WHERE FIND_IN_SET(id, :resource_path)
   OR id = :resource_id
ORDER BY level, sort;

22.4 查用户资源

SELECT rr.sid, rr.uid, rr.resource_id, rr.code, rr.platform,
       rr.is_delete, r.title, r.module, r.type, r.status, r.display
FROM t_acl_sys_resource_rule rr
LEFT JOIN t_acl_sys_resource r ON r.id = rr.resource_id
WHERE rr.sid = :sid
  AND rr.uid = :uid
ORDER BY rr.is_delete, rr.resource_id;

22.5 查站点 OPS 规则

SELECT sid, pc_rule_json, app_rule_json,
       update_user_name, update_user_code
FROM t_sys_ops_menu_rule
WHERE sid = :sid;

SELECT sid, pc_rule_json, before_lock_menu, lock_status,
       update_user_name, update_user_code
FROM t_sys_ops_menu_rule_new
WHERE sid = :sid;

不要在生产查询结果中传播完整 JSON、人员信息或真实站点标识。

22.6 查通用白名单

SELECT sid, finance_version, create_user, update_user,
       create_time, update_time, modify_time
FROM t_sys_ops_whitelist
WHERE sid = :sid;

无记录不等于老版;当前 Service 会默认返回财务新版。

22.7 查旧菜单

SELECT id, parentId, name, module, platform, path,
       level, status, isDelete
FROM t_sys_menu
WHERE module = :module
ORDER BY platform, level;

23. Redis 排查原则

不要在文档或工单记录 Redis 密码、地址和完整 key 前缀。使用项目已有安全连接方式。

需要核对的逻辑键:

USER_RIGHTS:{uid}
HASH_PURCHASE_STATION_BLACKLIST[sid]
HASH_SALE_STATION_BLACKLIST[sid]
HASH_SIDS_PROVINCE_SIMPLE_MENU[areaCode]
HASH_REFINED_OPERATIONS_WHITE_LIST[sid]

只读检查顺序:

  1. 确认连接环境。
  2. 获取逻辑 key 的实际项目前缀。
  3. 查看类型和 TTL。
  4. 仅读取目标 uid/sid field。
  5. 与数据库记录比对。
  6. 如需删除单用户缓存,记录操作前后值和原因。

24. 常用代码搜索

# 查权限码在哪些页面、按钮和接口使用
rg -n "TARGET_RESOURCE_CODE" application public assets

# 查后端是否真正鉴权
rg -n "checkpurview|checkPermissionV2|checkPurviewWithException" application/controllers application/service application/Services

# 查菜单组装与过滤顺序
rg -n "getMenuList|filterWhiteListRule|filterOpsMenuRuleNew|filterUserResourceRule" application/service/v2/ResourceService.php

# 查新旧资源映射
rg -n "CODE_CHANGE|CODE_CHANGE_REVERSE" application/KzData/Enums application

# 查OPS菜单和财务锁站
rg -n "setOpsMenuRule|lockMenu|unLockMenu|before_lock_menu|lock_status" application

# 查采购/销售能力黑名单
rg -n "HASH_PURCHASE_STATION_BLACKLIST|HASH_SALE_STATION_BLACKLIST" application

# 查通用白名单读取方
rg -n "OpsWhitelistSer|getFinanceVersion|SYS_OPS_WHITELIST" application

25. 故障一:主账号有菜单,子账号没有

flowchart TD
    A["主账号有/子账号无"] --> B{"Resource默认有效?"}
    B -- 否 --> R1["修资源配置"]
    B -- 是 --> C{"站点OPS/应用允许?"}
    C -- 否 --> R2["修站点级配置"]
    C -- 是 --> D{"ResourceRule有效?"}
    D -- 否 --> R3["走授权接口补规则"]
    D -- 是 --> E{"USER_RIGHTS含resource_id?"}
    E -- 否 --> R4["删除单用户缓存再验证"]
    E -- 是 --> F{"父资源均可见?"}
    F -- 否 --> R5["补父链/修父级display"]
    F -- 是 --> R6["检查前端缓存和路由"]

补权限时不要只插叶子资源,标准服务会自动补父资源。

26. 故障二:菜单可见但接口无权限

按以下顺序:

  1. 记录接口实际传入 checkpurview 的 code/module。
  2. 查 CODE_CHANGE 是否发生映射。
  3. 查 Resource 中目标 code 是否唯一且有效。
  4. 查 ResourceRule 是否属于同一 sid + uid。
  5. 查 USER_RIGHTS 是否为旧值。
  6. 确认用户会话 uid 与页面展示账号一致。
  7. 确认接口没有校验另一个更细粒度 code。

常见原因是菜单资源和接口资源不是同一个 ID,或者前端按一个 Web Right 显示按钮,而后端检查另一个接口 code。

27. 故障三:有按钮但直接请求可越权

这是安全问题,不是显示问题。

  1. 确认按钮资源是否只是 type=2/4。
  2. 搜索控制器是否调用权限检查。
  3. 使用无权限子账号验证直接请求。
  4. 在业务执行前增加后端权限校验。
  5. 回归正常账号、主账号、内部系统回调。
  6. 检查批量、导入、导出等旁路接口。

28. 故障四:OPS 设置后页面不变

检查项预期
请求是否成功事务提交且返回成功
写入哪张表旧 PC/APP 写旧表,新 PC 写新表
JSON 是否有效可解析、节点 id/module/display 完整
当前页面体系Desktop 是否走 v2 ResourceService
应用/用户过滤后续过滤可能继续隐藏
财务锁状态锁定时新 PC JSON 被锁站规则覆盖
前端状态重新拉 Desktop 数据或重新登录

OPS 菜单当前主要数据库直读,没有统一菜单缓存;“不生效”更多来自写错新旧表、后续过滤或浏览器未刷新。

29. 故障五:采购或销售突然不可用

  1. 查对应能力 Redis Hash 的目标 sid field。
  2. 查 OPS 最近是否设置 abilityConfig。
  3. 查财务是否触发锁站和账户锁定。
  4. 查站点账号状态、登录关闭字段。
  5. 查具体业务 Service 的拒绝日志。
  6. 菜单仍存在时不要误判为权限资源故障。

恢复能力应走 OPS 服务写入,使安全日志和操作来源完整;不要直接删除整个 Hash。

30. 故障六:财务解锁后菜单不完整

  1. 查 lock_status 是否从 1 变为 0。
  2. 查解锁前 before_lock_menu 是否非空且 JSON 有效。
  3. 比较当前 pc_rule_json 与快照。
  4. 检查锁定期间是否有 OPS 菜单变更覆盖快照。
  5. 查 APP/PDA/商城账号是否都已解锁。
  6. 查用户 ResourceRule 和应用开通是否另有变化。
  7. 重新登录获取完整 Desktop 数据。

仅恢复菜单 JSON 不代表账号和业务能力全部恢复。

31. 故障七:白名单内外表现相反

优先确认语义:系统中同时存在“白名单”“黑名单”“版本默认值”“地区简化菜单”。

配置空值语义
finance_version默认新版 2
采购/销售能力 Hash无 field 表示不禁止
运营白名单无 field 表示不展示目标菜单
地区简化菜单无匹配类型时不做该项筛选
OPS pc_rule_json无对应节点时使用资源默认 display

排查时必须写清“存在时是允许还是禁止”,避免把黑名单当白名单。

32. 数据修复门槛

权限数据修复前至少满足:

  • 已确认环境、sid、uid、资源 code 和 resource_id。
  • 已确认旧/新权限分支。
  • 已导出目标账号、资源规则、OPS JSON 的修复前快照。
  • 已评估父资源、应用关系、门店范围和 IM 副作用。
  • 已确认用户缓存的单点失效方式。
  • 已准备主账号和最小权限子账号验收。

禁止做法:

  • 清空整库 Redis。
  • 批量把所有资源 display 改为 1。
  • 给子账号复制主账号全量权限以临时绕过。
  • 覆盖 OPS JSON 而不保存原值。
  • 在财务锁定状态下直接改 pc_rule_json。
  • 只修页面显示,不验证接口权限。

33. 完整回归矩阵

维度用例
账号主账号、已授权子账号、未授权子账号、禁用账号
平台新 PC、旧 PC 兼容、APP、PDA
资源一级菜单、三级菜单、按钮、查询、编辑、导出、接口
站点默认站点、OPS 隐藏站点、应用未开站点、白名单内外
能力采购允许/禁止、销售允许/禁止
财务未锁、锁定、重复锁、解锁、重复解锁
缓存无缓存、旧缓存、写权限后缓存删除、TTL 后重建
门店无分店、多个分店、默认门店变更
兼容旧 code、新 code、CODE_CHANGE 双向映射
安全隐藏按钮直调接口、越权 sid/uid、批量/导入/导出旁路

34. 新功能上线验收

34.1 配置层

  • Resource 字段、父链、path、sort、code 正确。
  • OPS 新旧规则展示符合目标平台。
  • 应用资源关系和站点开通正确。
  • 白名单默认值和内外表现正确。
  • 没有把真实账号或站点写死在新增逻辑中。

34.2 用户层

  • 主账号可见并可操作。
  • 已授权子账号可见并可操作。
  • 未授权子账号不可见且直调接口被拒绝。
  • 权限取消后缓存立即失效。
  • 父菜单不会残留空壳,也不会误隐藏其他子项。

34.3 业务层

  • 菜单、按钮和接口使用同一业务语义的权限码。
  • 采购/销售能力禁用时提交被拒绝。
  • 恢复能力后无需人工改业务单即可继续正常操作。
  • 财务锁定仅保留资金菜单,相关账号被锁并强制下线。
  • 解锁恢复原菜单、账号和业务能力。

34.4 审计层

  • 权限变更有系统日志。
  • OPS 变更记录操作者。
  • 锁站/解锁有账户安全日志。
  • 批量任务有目标数量、开始结束和异常日志。

35. 改动风险分级

级别改动风险
高Common_model 鉴权、ResourceService 过滤顺序影响全站菜单和接口
高Resource/ResourceRule 批量迁移可能全量扩大或收缩权限
高lockMenu/unLockMenu菜单、账号、WebSocket 多系统副作用
高AclMigrate、菜单同步任务批量覆盖历史配置
中修复 createPcResourceRule 历史变量可能改变现有告警、日志或异常分支
中新增资源或 code 映射影响目标模块及旧前端兼容
中OPS 菜单 JSON影响单站或一组站点菜单
中能力黑名单直接阻断采购/销售
低单用户授权调整影响单账号,但仍需清缓存和审计

36. 证据来源

结论代码证据
新 PC 菜单六层过滤application/service/v2/ResourceService.php::getMenuList
Resource 四种类型与状态application/KzData/Enums/ResourceEnums.php
主账号跳过、子账号检查application/models/Common_model.php::checkpurview
USER_RIGHTS TTL 3600Common_model::checkPermissionV2
写权限后删除缓存ResourceService::createPcResourceRule
新旧 code 兼容ResourceEnums::CODE_CHANGE/CODE_CHANGE_REVERSE
OPS 同写新旧规则MenuRuleSer::setOpsMenuRule
采购/销售黑名单MenuRuleSer::setAbilityRule
财务锁定仅留资金MenuRuleSer::getLockMenu/lockMenu/unLockMenu
通用白名单默认新版SysOpsWhitelistModel::COLUMN_DEFAULT、OpsWhitelistSer::getFinanceVersion
白名单字段受校验限制SysOpsWhitelistValidate::checkConfigColumn
旧菜单刷新和迁移任务application/controllers/tasks/MenuRule.php、AclMigrate.php

37. 待环境确认项

  • 线上 canAccessV2() 对不同站点和版本的实际分流条件。
  • Resource 与 ResourceRule 的唯一索引、外键及历史重复数据。
  • 前端对 menus/pageElement/rights 的本地缓存和刷新时机。
  • OPS 内部接口真实网关路由、认证和审计保留周期。
  • 应用开通关系的线上状态枚举和失效机制。
  • 财务锁站的消息重试、WebSocket 失败补偿和跨系统一致性。
  • 历史硬编码站点例外是否仍有效,能否配置化下线。
  • 菜单同步任务当前调度状态、最近运行时间和可重入性。

环境未确认项不能写成确定事实;上线前应通过 DDL、真实配置、只读查询和最小权限账号验证补齐。

请求-日志-数据变更追踪卡

多入口请求链路

场景调用方与入口请求载荷/上下文Controller/ConsumerService/Provider汇合点最终业务事实
PC 菜单加载登录后桌面请求用户、sid、角色、组织v2/Desktop.phpv2/ResourceService.php用户-角色-资源关系返回当前用户可见菜单与按钮
权限配置OPS/管理页资源、规则、角色、菜单配置v2/Right.php、v2/Resource.phpResource/MenuRule Serviceresource/menu ID权限关系和菜单规则落库
白名单维护内部系统OPS 用户/接口/站点范围inner/sys/OpsWhitelist.phpOpsWhitelistSer.php白名单业务键命中白名单的例外放行事实
规则迁移CLI task旧规则批次、用户/菜单范围tasks/MenuRule.php、AclMigrate.php迁移 Service旧新规则 ID新旧权限数据按批迁移

日志证据矩阵

链路段日志来源可检索锚点成功信号失败信号与下一段关联方式
登录与菜单Web 日志/Desktoprequest_id、用户 ID、sid、URI菜单树含目标资源未登录、菜单为空、站点上下文错用户 ID 查角色和规则
鉴权判定BaseController/Resource Servicecontroller/method、resource ID、用户/角色allow 且进入业务方法deny/资源未登记resource ID 关联菜单和规则表
配置写入Right/Resource/MenuRule Service操作人、资源/角色 ID规则 commit,重新查询生效duplicate、父子菜单错误、缓存旧规则 ID 与菜单树回查
白名单/迁移OpsWhitelistSer/Task白名单键、task 批次、用户 ID例外命中或迁移数一致范围过宽、重复迁移、部分失败业务键/批次查新旧表

环节数据变更台账

步骤代码位置事务读取事实写入表/缓存/MQ字段或数量变化回查证据
资源登记v2/Resource.php权限事务Controller/method、父资源ACL_SYS_RESOURCE、ACL_SYS_RESOURCE_RULEresource/rule insert 或启停 old -> new路由、资源 ID、规则行
菜单授权Right/MenuRule Service权限事务用户角色、菜单配置SYS_MENU*、SYS_OPS_MENU_RULE(_NEW)角色-菜单关系增删;排序/状态更新用户菜单树与表关系
白名单OpsWhitelistSer独立事务对象是否已存在、范围SYS_OPS_WHITELISTinsert/delete/enable;仅窄范围例外对象键、操作人、更新时间
读取生效Desktop/ResourceService只读;缓存边界需确认资源、规则、菜单、白名单可能的权限缓存DB 不变;计算 allow/deny 与菜单树最小权限账号实测 + request ID
迁移AclMigrate/MenuRule task每批事务旧规则、目标是否存在新规则表旧关系 -> 新关系;重复批次不重复 insert批次计数、新旧集合差

子模块追踪:menu-load PC 菜单加载与过滤

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
加载PC 首页,userId/sidrequest ID、用户与站点application/controllers/v2/Desktop.php -> application/service/v2/ResourceService.php主子账号、ACL、旧菜单、OPS/应用/锁站查询只读 不写;逐层过滤菜单树request ID + userId/sid + resource code从第一处过滤原因修复,不硬加最终菜单
生效授权后重新加载userId、缓存键application/controllers/inner/CacheManage.phpDB 授权与 USER_RIGHTS 缓存事务外 cache old -> deleted/latest,权限 DB 不变userId + key + hit/missDB 正确只清受影响缓存;TTL 需环境确认

子模块追踪:user-grant 用户授权写入

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
授权管理员勾选 PC/App 资源operator、userId、resource IDsapplication/controllers/v2/Right.php -> application/service/v2/ResourceService.php操作者权限、资源和父子关系、原集合权限本地事务 old grants -> new grantsrequest ID + operator/userId + count越权/非法资源零写入;批量失败整批回滚
回查保存后按钮仍旧userId、resource codeapplication/controllers/v2/Resource.php授权表、资源规则、缓存查询只读 不写;必要时失效缓存userId + resource code + cache stateDB 缺失重走授权接口,不手插孤儿规则

子模块追踪:runtime-auth 运行时接口鉴权

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
鉴权直接请求受控 actionuserId、sid、URI、permission codeapplication/core/BaseController.php -> application/KzData/Enums/ResourceEnums.phpsession、资源码、用户授权、站点限制鉴权查询只读 不写;拒绝时业务零写入request ID + URI + userId/sid + code菜单可见不等于接口有权;缺鉴权是代码风险
执行鉴权通过后业务动作billNo、当前状态application/service/scm/InvSaService.php对象归属和业务前置条件领域本地事务才执行 status old -> newrequest ID + permission + billNo越权尝试不得有副作用;回归按钮和直接 URL

子模块追踪:rights-cache USER_RIGHTS 缓存

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
读写菜单/鉴权读取权限userId、cache keyapplication/service/v2/ResourceService.phpRedis 命中、DB 授权、资源状态miss 后事务外 none -> rights snapshot,DB 不变userId + key + hit/miss + count区分空缓存和无权限;Redis 环境事实待验证
失效授权/迁移/资源变更userId/批次application/controllers/inner/CacheManage.php受影响用户和旧缓存事务外 delete cache,授权 DB 不写batch/userId + deleted count只清受影响键;失败重试不重复改授权

子模块追踪:ops-menu OPS 菜单规则配置

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
配置OPS 按站点设置菜单operator、sid、menu/resource codesapplication/Services/MenuRule/MenuRuleSer.php新旧规则、站点与菜单映射配置本地事务 old rules -> new rules,同步规则表request ID + sid + rule count生成失败整批回滚;新旧表双读验收
过滤PC/App 加载菜单sid、app/typeapplication/controllers/inner/StationManage.php站点规则、应用开通、用户授权查询只读 不写;取多层交集request ID + sid + matched rules页面旧时查缓存/应用开通,不重复保存

子模块追踪:app-menu 应用开通与菜单

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
开停应用站点应用配置sid、app code、menu setapplication/Services/MenuRule/MenuRuleSer.php应用状态、资源映射、已有规则本地事务 app disabled/enabled old -> new 并更新菜单关系request ID + sid + app code停用后敏感接口也须拒绝;失败整批回滚
回查已开通但无入口userId、sid、resource codeapplication/controllers/v2/Desktop.php应用、OPS 规则、授权、缓存查询只读 不写request ID + sid/userId + app code找第一处过滤原因,禁止绕过权限硬显示

子模块追踪:ops-whitelist OPS、地区与运营白名单

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
设置批量增删白名单operator、rule code、sid/area 集合application/controllers/inner/sys/OpsWhitelist.php -> application/Services/System/OpsWhitelistSer.php规则定义、原集合、站点地区白名单本地事务 old set -> new setrequest ID + rule code + item count空集合和反选语义须确认;失败整批回滚
命中页面/业务判断例外sid、area、rule codeapplication/Services/System/OpsWhitelistSer.php当前规则和站点属性查询只读 不写;返回 allow/denyrequest ID + sid + rule + matched row表现相反先查 include/exclude 与缓存

子模块追踪:business-blacklist 采购销售黑名单与财务锁站

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
拦截锁定采购/销售创建或逾期任务sid、能力类型、逾期单application/Services/InvPoFactory.php、application/service/scm/InvSaService.php黑名单、逾期未还和锁因业务校验只读;锁站本地事务 unlocked -> lockedrequest/task ID + sid + rule/repaymentNo拦截业务零写入;多个锁因分别保留
解锁移出名单或还清sid、有效锁因集合application/service/scm/InvSaService.php全部黑名单和逾期事实条件满足才本地事务 locked -> unlockedsid + action + remaining reasons不因单笔还款清其他锁;菜单缓存另失效

子模块追踪:acl-migration 菜单同步与 ACL 迁移

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
迁移CLI ACL/MenuRule 任务batch、resource/menu codeapplication/controllers/tasks/AclMigrate.php、application/controllers/tasks/MenuRule.php旧菜单、新资源、映射、已迁移标记每批本地事务 upsert old ACL -> new ACL,重跑零新增task + batch + insert/update counts先小批;中断保留失败码,成功项不重复
验收新旧菜单和鉴权对比sample users、resource codesapplication/service/v2/ResourceService.php新旧授权、父链、鉴权和缓存查询只读 不写;回滚按批次恢复映射batch + sample + diff count历史脏数据单列,不自动吞掉未知资源