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

208 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工作进度-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:
```batch
:: .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):
```powershell
# 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 删除,对应后端接口也已下线),可整批删掉。