docs: add plan-archive price permission design

Document button-level archive/price controls on the existing
transport plan archive page instead of a separate view module.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-09-03 16:16:13 +08:00
1 parent dd3bb3cf06
commit 513d3b0fb3
1 file changed
+175
@@ -0,0 +1,175 @@
# 运输计划归档:只读可见 + 隐藏单价金额 设计
日期:2026-09-03
范围:`web-admin` 【运输计划归档】页及相关查看弹窗/导出;`backend` 计划归档写接口与读/导出脱敏;`sys_menu` 按钮权限
状态:已评审(待用户确认本文档后实现)
## 1. 背景与目标
客户希望部分角色可以进入【运输计划归档】界面查看进度与数量信息,但:
1. **不能**执行【归档】/【调整】操作;
2. **不能**看到单价、金额相关数据(含运费单价/运费金额)。
### 已否决方案
- **新增独立菜单「运输计划归档查看」**:两套页面/路由/角色配置,后续维护成本高。
### 采用方案
- **在当前界面做权限处理**:复用 `pl:plan-archive:list`,用按钮权限拆分「归档操作」与「价格可见」。
## 2. 已确认决策
| 项 | 决策 |
|---|---|
| 实现路径 | 当前页权限控制,不新建模块 |
| 归档操作权限 | 复用已有 `pl:plan-archive:archive`(菜单已有,前端尚未接线) |
| 价格可见权限 | **新建** `pl:plan-archive:price` |
| 价格范围 | 产品单价/金额 **与** 运费单价/运费金额 **一并** 受 `price` 控制 |
| 导出 | 无 `price` 时去掉价相关列(不仅清空单元格) |
| ERP 勾选 | **本期不改**(只读角色若已有 list 仍可操作 ERP;若需再拆权限后续单开) |
| `edit` 按钮权限 | **本期不强制接线**(无 `archive` 即无法进入归档填写) |
| `super_admin` | 视为拥有全部相关权限 |
| 安全边界 | 前端隐藏 + 后端脱敏/写接口 403,避免仅藏 UI |
## 3. 权限模型
| perms | menu_type | 作用 | 现状 |
|---|---|---|---|
| `pl:plan-archive:list` | C(页面) | 进入归档页、看列表 | 已有 |
| `pl:plan-archive:query` | F | 查询(可与 list 一并授权) | 已有 |
| `pl:plan-archive:edit` | F | 编辑(本期不强制 UI 接线) | 已有 |
| `pl:plan-archive:archive` | F | 归档/调整入口与提交 | 菜单已有,需接线 |
| `pl:plan-archive:price` | F | 查看单价/金额(含运费价) | **新建** |
### 角色配置示例
| 角色意图 | 勾选 |
|---|---|
| 只读业务(看进度、不见价、不能归档) | `list`(+`query`) |
| 归档员 | `list` + `archive` + `price` |
| 财务看价不归档 | `list` + `price` |
历史已授权「运输计划归档」整页的角色:上线后需在角色管理中 **显式补勾** `archive` / `price`(否则会变成「能进页但不能归档且不见价」)。实现阶段应在变更说明/SQL 备注中写清迁移建议(例如:对原拥有 `PL_PLAN_ARCHIVE` 页面权限的角色默认补上 `archive`+`price`,避免现网行为回退)。
## 4. 前端行为
入口页:`web-admin/src/views/pl/plan-archive/index.vue`
权限读取:与现网一致,使用登录态 `menuCodes`(参考 `pl:plan:force-follow-status`)。
### 4.1 无 `pl:plan-archive:archive`
- 列表工具栏「归档 / 调整」按钮隐藏;
- 查看弹窗 hero「归档 / 调整」按钮隐藏;
- 不打开 `PlPlanArchiveDialog`(无入口即可;若有深链/误调也应被权限挡住)。
### 4.2 无 `pl:plan-archive:price`
| 位置 | 处理 |
|---|---|
| 列表「单价(元)」「金额(元)」列 | 不渲染;列设置中移除对应项 |
| 列表底部统计行 | 金额相关合计不展示 |
| 查看弹窗段次产品 / IO 字段 | 关闭价金额展示;**数量仍可见** |
| 查看弹窗「品牌归档明细」 | 隐藏单价、金额列 |
| 查看弹窗运费区 | 隐藏「运输单价」「运费金额」;保留是否有运费、公里数、计算方式等非价字段 |
| 归档专用导出 | 去掉单价、金额、运费单价、运费金额列 |
| 合同详情 | 保持现有 `hide-product-price` |
### 4.3 仍可见(只读角色)
计划编号/日期、单据类型、段次客户与地址、产品与型号、计划/实际数量、入出库数量、归档状态、跟进状态、车牌司机、ERP、备注等非价格字段。
### 4.4 实现备注(组件传参)
- `PlPlanViewDialog` / `PlPlanViewTabs`:`show-archive-io-fields` 需拆成「数量」与「价格」可控,或增加 `showArchivePriceFields`(无 `price` 时仅数量、无价)。
- 列表列定义与 `usePersistedListColumns`:无权限时从可见列集合中剔除 `productUnitPrice` / `productAmount`,避免本地缓存仍勾出空列。
## 5. 后端行为
权限校验模式对齐 `FORCE_FOLLOW_STATUS_PERM`:`super_admin` 放行;否则 `get_user_menu_codes` 判断。
### 5.1 写接口(无 `archive` → 403)
- `POST /api/.../transport/plan/{id}/archive`
- `POST /api/.../transport/plan/{id}/archive-draft`
文案建议:`无运输计划归档操作权限`。
### 5.2 读接口脱敏(无 `price` → 价字段置 `null`)
归档页使用的计划列表、详情(含 stops / products / brandQtys / transport 运费字段)在序列化前脱敏。
**脱敏字段(建议清单)**
- 产品/段次:`inboundPrice`、`outboundPrice`、`inboundAmount`、`outboundAmount`
- 品牌行:`unitPrice`(及若存在的金额派生字段)
- 计划/运输:`freightPrice`、`freightAmount`
- 列表汇总若暴露:`totalAmount`
**不脱敏**:各类数量(`qty` / `inboundQty` / `outboundQty` 等)、状态、客户、车牌等。
### 5.3 导出(`exportFormat=archive`)
无 `price` 时,从 `PLAN_ARCHIVE_EXPORT_COLUMNS` 中 **移除** 列:
- `unitPrice`(单价)
- `amount`(金额)
- `freightPrice`(运费单价)
- `freightAmount`(运费金额)
运费单位(`freightType`)可保留。
## 6. 数据与菜单变更
1. 在 `docs/sql/` 增加 `sys_menu` 插入脚本:
- `id`:如 `PL_PLAN_ARCHIVE_PRICE`
- `parent_id`:`PL_PLAN_ARCHIVE`
- `menu_type`:`F`
- `perms`:`pl:plan-archive:price`
- `menu_name`:建议「查看单价金额」
2. 同步更新全量快照或增量迁移说明(与现网菜单规范一致:`docs/sql` + 角色授权)。
3. **迁移建议(推荐默认)**:对已拥有 `PL_PLAN_ARCHIVE`(或 `pl:plan-archive:list`)的角色,批量补授权 `PL_PLAN_ARCHIVE_A`(archive)与新建的 price 按钮,保持上线前行为不变;新「只读」角色再单独去掉这两项。
## 7. 验收场景
| # | 角色权限 | 期望 |
|---|---|---|
| A | 仅 `list` | 能进页;无归档按钮;列表/查看/导出均无单价金额;直接调 archive 接口 403 |
| B | `list` + `archive`,无 `price` | 能归档;归档填写中不应依赖「先看价」——若弹窗内价为录入项,无 `price` 时仍应允许录入但 **打开前详情不回显他人已填价**(见下方边界);列表/查看/导出无价 |
| C | `list` + `price`,无 `archive` | 能看价;无归档按钮;写接口 403 |
| D | `list` + `archive` + `price` | 与现网完整能力一致 |
| E | `super_admin` | 与 D 相同 |
### 边界说明(场景 B)
「无 price 但有 archive」在业务上少见。约定:
- **读路径**一律脱敏(列表/详情打开时不见价);
- **归档弹窗提交**仍可填写单价并写入(写权限由 `archive` 覆盖);
- 若产品确认「能归档就必须能看价」,可在角色配置上强制 `archive` 依赖勾选 `price`(UI 提示即可,后端可不强绑)。
**推荐产品约定**:归档员角色同时勾选 `archive` + `price`;不单独发放「无价可归档」。
## 8. 非目标(本期不做)
- 新建「运输计划归档查看」菜单/路由/页面副本;
- 拆分 ERP 勾选权限;
- 运输计划主列表、运费结算、库存台账等其他模块的价格权限(仅本归档页链路);
- 字段级加密或审计日志增强。
## 9. 主要改动面(实现索引)
| 层 | 路径(示意) |
|---|---|
| 菜单 SQL | `docs/sql/` 增量 + `sys_menu` |
| 列表页 | `web-admin/src/views/pl/plan-archive/index.vue` |
| 查看弹窗/Tabs | `pl-plan-view-dialog`、`pl-plan-view-tabs`、`pl-plan-stop-product-table`、`pl-plan-archive-brand-view-block` |
| 归档写 API | `backend/app/api/pl/plan.py`(`archive` / `archive-draft`) |
| 读脱敏 / 导出 | `plan.py` 列表/详情组装 + `PLAN_ARCHIVE_EXPORT_COLUMNS` / `build_plan_archive_export_rows` |
## 10. 风险与注意
- **角色回退**:若不做「补授权」迁移,现网归档员会突然不能归档且不见价。
- **列宽缓存**:本地 `pl-plan-archive-list-columns-v*` 可能仍记住价列;无权限时须强制剔除。
- **脱敏遗漏**:详情嵌套 stops/products/brandQtys/transport 多处含价,需统一在序列化出口处理,避免漏网。