Files
f9-web-ai-test/工作进度/工作进度-3.md
T
yanpeng200212 a8890c81e9 新增质量模块(工艺执行书 / 样茧试缫)与报表中心
质量模块:
- 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫
- 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度
- 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包
- 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件
- 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据

报表中心:
- 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册)

其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录
2026-10-22 10:30:37 +08:00

11 KiB
Raw Blame History

工作进度-3:部门管理补齐管理/职责类型绑定 + 角色授权「勾了不生效」根治

时间:2026-10-20(承接工作进度-2) 环境:后端 .NET 8 + FreeSql + MediatR(Windows),前端 Vue3 + Element Plus + Vite 范围:①部门管理页支持绑定 ManageType / DutyType;②定位并修复角色管理「分配权限保存后不生效、该角色登录看不到菜单」


一、本轮目标

  1. 部门管理页面支持给部门绑定 Department_ManageType_Enum 与 Department_DutyType_Enum
  2. 修复「角色管理 → 分配权限 → 勾选菜单 → 确认保存 → 重新打开仍是未勾选、该角色账号也加载不到菜单」
  3. 补齐工作进度-2 遗留第 1 条:部门增删改后刷新部门缓存
  4. 顺手修掉部门编辑会把「禁用」状态改回「启用」的连带问题

二、部门管理(ManageType / DutyType 绑定)

2.1 定位

后端原本就支持两个字段:

层 文件 说明
实体 Domain/Entities/SYS/Departments/Department.cs manage_type / duty_type 私有 set
命令 CQRS/SYS/Departments/Commands/Department_CreateCommand.cs / _UpdateCommand.cs 均带两个枚举参数
读模型 Dtos/SYS/Departments/DepartmentReadDto_View.cs 已返回 ManageType / DutyType

问题全在前端 web/src/views/sys/department/index.vue:

项 原状态
表单 只有「类型」= ManageType;DutyType 藏在 form 里固定为 5,没有任何控件
列表 只有管理类型列,看不到职责类型
上级部门 平铺下拉,看不出层级;clearable 还会把值置空,与 long ParentId 不匹配
缓存 保存后不刷新 utils/departments.js 的部门缓存

注:菜单里生效的是 /sys/department → sys/department/index;/system/department → system/department/index 是死代码(它调用的 /api/department 已被下线,菜单也被 seeder 清掉)。

2.2 改动

web/src/views/sys/department/index.vue(重写)

  • 表单新增「职责类型」下拉;「管理类型」改用共享枚举,删除本地硬编码的 manageTypeMap
  • 列表改树形表格(row-key + tree-props),新增「管理类型」「职责类型」两个标签列,新增「添加子部门」
  • 上级部门改 el-tree-select,编辑时排除自身及子孙(防成环)
  • 保存 / 删除 / 启停后调用 clearDepCache(),报表部门控件立即生效
  • parentId 按字符串回传(部门 Id 是雪花 Id,Number() 会丢精度)

web/src/utils/departments.js

  • DUTY 补上缺失的 后缫整理: 115
  • 新增 enumOptions(map)(枚举 → 下拉选项,按数值升序)与 enumLabel(map, value)(值 → 中文名)

Infrastructure/FreeSqls/Repositories/SYS/Departments/DepartmentWriteRepository.cs

  • UpdateAsync 增加 .IgnoreColumns(x => new { x.IsActive, x.IsDeleted, x.CreatedAt, x.CreatedBy })
  • 原因:Department_UpdateCommand 用 new Department(...) 重建对象,构造函数里 IsActive = true,SetSource 会把「禁用」的部门一编辑就改回「启用」;启用状态本应由独立的 /active 命令维护

2.3 实测

POST  /api/sys/department  {manageType:15(车间), dutyType:25(前缫)}   → 创建成功
PUT   /api/sys/department/{id} {dutyType:115(后缫整理)}              → 修改成功
读回  => manageType=15  dutyType=115  isActive=False                 ✅

运行时(无头 Edge,/sys/department):

TABLE_ROWS=38
ROW0=123 / 安泰茧丝绸 / 部门 / 其他 / 0 / 启用 / 添加子部门 编辑 禁用 删除
FORM_ITEMS=编码|部门名称|上级部门|管理类型|职责类型|排序|描述
SELECT_1(管理类型)=公司,部门,车间,科室,岗位,虚拟
SELECT_2(职责类型)=其他,仓储,选剥茧,煮茧,前缫,后缫,设备办,副产品,质检,丝绵,人力资源,丝业中心,前缫管理,后缫检验,后缫整理

三、角色授权「勾了不生效」根治(本轮重点)

3.1 现象

角色管理 → 分配权限 → 勾选菜单 → 确认保存(提示"权限保存成功")
→ 再次点分配权限:之前勾的菜单仍是未勾选
→ 用该角色的账号登录:菜单也加载不出来

3.2 根因:雪花 Id 被前端 Number() 转成 double 后精度丢失

真实菜单 id : 134905585693888512
Number() 后 : 134905585693888510   ← 少 2
Number.isSafeInteger(...) = false

web/src/views/sys/role/index.vue 的 savePermType() 里写的是 resourceId: Number(key)。后端本来已经用 LongToStringJsonConverter 把 long 序列化成字符串(避免 JS 精度问题),前端又把它转回数字,等于白折腾。

环节 表现
保存 sys_rbac_permissions.resource_id 写成 ...510,菜单表里不存在这个 id
重新打开 setCheckedKeys(['...510']) 在树里找不到节点 → 勾不上 → 显示未勾选
登录 GetUserMenusAsync 拿 ...510 查菜单表 → 查不到 → 菜单不出现
再点确认 回显是空的 → 删除逻辑把这些权限彻底删掉

只有 id 很小的老数据(11/12/13…)不暴露此问题。第一轮用「普通用户」角色验证时,恰好勾的都是小 id 菜单,所以误判成"功能正常"——这是本次排查的教训。

3.3 修复(4 处)

# 文件 改动
1 web/src/views/sys/role/index.vue resourceId: Number(key) → resourceId: key(字符串原样回传)。菜单 / 部门 / 报表三类授权共用此函数,一并修好
2 Application/CQRS/SYS/RBAC/Roles/Commands/RolePermission_AssignCommand.cs 新建菜单/操作类权限单元时,用菜单自己声明的 Permission(如 base:worktype:list)作为 Code;原来只有消息订阅会补 Code,其余会退化成 menu:{id}:visble,导致 v-permission 永远匹配不上。为此注入 IRepository<Menu>
3 Infrastructure/Data/DbSeeder.cs(FixMenuPermissions) 增加自愈:启动时删除 ResourceId 不对应任何菜单的失效权限单元及其 role_permission 关联,修复历史脏数据
4 web/src/views/sys/department/index.vue parentId 同样改为字符串回传(同类隐患)

后端 LongToStringJsonConverter 的 Read 已兼容字符串,所以前端传字符串可以直接绑定 long(实测 parentId:"1"、resourceId:"134905585693888512" 均正常)。

3.4 验证

修复前(前缫主任,勾「工种管理」):

API_AFTER(18)=...,42,134905585693888510     ← 尾数错
REOPEN_CHECKED=17  MISSING_ON_REOPEN=工种管理

修复后:

TICKED=工种管理 now=18
API_AFTER(18)=11,12,17,13,14,18,15,16,19,20,21,22,23,24,30,31,42,134905585693888512
REOPEN_CHECKED=18
PERSISTED=YES(工种管理)                                          ✅

用该角色真实账号(余华 / 13547542219)登录:

LOGIN code=200 roles=1000
MENU_TREE:
- 考勤管理 [/attendance]        → 考勤打卡 / 我的考勤 / 请假申请 / 考勤记录 / 请假审批
- 基础数据 [/base]              → 工艺庄口 / 生丝规格 / 生丝定级指标 / 机组车台 / 工种管理  ← 新加的已生效
- 生产-前缫 [/prod/qianxian]    → 落丝记录
- 消息中心 [/notification]      → 我的消息 / 消息订阅
PERMS 含 base:worktype:list

重启后端时的自愈日志侧证:前缫主任 菜单权限中「不在菜单树内」的资源 Id 由 1 条降为 0;后缫检验员 / 后缫主任 的菜单权限数由 10 → 8(清掉 2 条无效单元)。


四、验证手段(沿用工作进度-2)

无 Node/npm 全局环境,前端用项目内置 node:

:: .run-logs\start-web.bat 内部就是这两步
set PATH=C:\Users\32453\.workbuddy\binaries\node\versions\22.22.2-6;%PATH%
cd /d d:\f9webaitest\f9-web-ai-test\web
node node_modules\vite\bin\vite.js

后端:dotnet run --project backend\F9WebAiTest.Api\F9WebAiTest.Api.csproj

SPA 运行时验证(无头 Edge):

# 1) 诊断页放 web/public/__diag_xxx.html:login 写 localStorage → history.replaceState 改路由
#    → 等 el-table 出现 → 模拟点击 → 把断言写进 <div class="L">,用完即删
msedge --headless=new --user-data-dir=$prof --virtual-time-budget=150000 --disable-gpu --dump-dom "http://localhost:5173/__diag_xxx.html"

要点:

  • --virtual-time-budget 会让 setTimeout 与 fetch 都按虚拟时钟推进,可放心写 await sleep()
  • 想直接渲染某路由,在模块脚本之前插一段 history.replaceState({}, '', '/sys/role')(本项目是 createWebHistory,不是 hash 路由)
  • 诊断页里的内联 <script> 必须语法正确——本轮曾因多一个 catch 导致整段不执行、表现为"页面没反应",排查成本很高
  • 统计勾选状态要用 :scope > .el-tree-node__content .el-checkbox,否则父节点会把子节点的 is-checked 也算进来

五、当前状态

项 状态
部门新增/编辑绑定管理类型、职责类型 正常,列表以标签展示 ✅
部门树形列表 / 添加子部门 / 上级树选择(排除自身子孙) 正常 ✅
部门编辑不再覆盖启用状态 已修 ✅
部门缓存(报表部门控件)随部门增删改刷新 已接入 clearDepCache() ✅(工作进度-2 遗留 1 关闭)
角色授权:勾选 → 保存 → 回显 → 该角色登录加载菜单 全链路打通(含雪花 Id 菜单)✅

六、遗留与后续建议

  1. 同类 Number() 隐患(新数据 id 变大后会踩同一个坑,尚未动)
    • web/src/views/base/chejianbanci/index.vue(depId)
    • web/src/views/base/jizuchetai/index.vue(departmentId)
    • web/src/views/base/employee/index.vue(depId / worktypeId,含 depIds.map(Number))
    • web/src/views/prod/lsjl/index.vue(depOptions 的 id: Number(o.key))
  2. 超级管理员 RolePermission 重复:sys_rbac_role_permissions 中 admin 有 481 条、只对应 49 个不同资源(历史 seeding 重复插入)。不影响鉴权(admin 直接放行),但角色列表「菜单权限」数字虚高,可去重。
  3. 部门权限无消费方:授权会写 department:{id}:visble,但前后端没有任何地方按此做数据/可见性过滤,勾选当前无实际效果——要么接上数据范围,要么先从 UI 移除。
  4. 「操作」权限维度未启用:Resource_Type_Enum.操作 = 2 与 Permission_Type_Enum.Enable = 2 已定义但未使用;保存时 permissionType 硬编码为 1(Visble)。按钮类节点混在「菜单权限」树里,无法单独授予「可操作」。
  5. 新菜单的权限单元:现由 FixMenuPermissions 在启动时补建,AssignRolePermission 兜底时也会按菜单的 Permission 补 Code;建议后续把「建 Permission」直接并入菜单 CRUD,避免依赖启动时机。
  6. 死代码:web/src/views/system/{department,role,menu,employee} 与路由表里的 system/*/index 已无菜单指向(/system% 菜单被 seeder 删除,对应后端接口也已下线),可整批删掉。