docs: plan production pre-transit sample menu copy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-17 11:52:36 +08:00
1 parent 5f9d62fa04
commit b8a89a81f9
1 file changed
+472
@@ -0,0 +1,472 @@
# 生产管理 — 预产及在途样本独立副本 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在【生产管理】下新增与质检侧代码独立的「预产及在途样本」页面与菜单,共用现有查询 API,并自动授权给已有生产管理子菜单的角色。
**Architecture:** 完整复制 `quality-inspection/pre-transit-sample` 到 `production/pre-transit-sample`;路由挂在 `production-group/production`、库位管理之前;权限码 `production:preTransitSample:list`;后端两列表接口改为 `require_any_menu_code`(质检码 OR 生产码);SQL 幂等插菜单并按 PRM 子菜单授权。
**Tech Stack:** Vue 3 + Element Plus(web-admin)、FastAPI menu_guard、MySQL `sys_menu` / `sys_role_menu`
**Spec:** `docs/superpowers/specs/2026-08-17-production-pre-transit-sample-design.md`
---
## File map
| 文件 | 职责 |
|------|------|
| `web-admin/src/views/production/pre-transit-sample/index.vue` | 生产侧页面壳(自质检复制) |
| `web-admin/src/views/production/pre-transit-sample/PreTransitProtocolCardList.vue` | 生产侧卡片列表(自质检复制) |
| `web-admin/src/router/modules/sqxp.ts` | 注册生产侧路由 |
| `backend/app/api/pre_transit_sample.py` | 共用 API 鉴权 OR |
| `docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql` | 菜单 + 角色授权迁移 |
| `docs/sql/zkxw.sql` | 基线插入两条 `sys_menu` |
| `待同步到生产的sql脚本` | 追加待上线 SQL 片段 |
不改:`web-admin/src/api/pre-transit-sample.ts`、质检侧 `views/quality-inspection/pre-transit-sample/**`。
**ID / 权限常量(全任务统一)**
| 常量 | 值 |
|------|-----|
| C 菜单 id | `95200000000000000000000000000001` |
| F 按钮 id | `95200000000000000000000000000002` |
| parent_id | `PRM00000000000000000000000000001` |
| perms / menuCode | `production:preTransitSample:list` |
| order_num(C) | `0` |
| 组件 path | `/production/pre-transit-sample/index` |
| 路由 Hash | `/#/production-group/production/pre-transit-sample` |
---
### Task 1: 复制前端页面到生产目录
**Files:**
- Create: `web-admin/src/views/production/pre-transit-sample/index.vue`
- Create: `web-admin/src/views/production/pre-transit-sample/PreTransitProtocolCardList.vue`
- [ ] **Step 1: 复制目录**
```bash
mkdir -p web-admin/src/views/production/pre-transit-sample
cp web-admin/src/views/quality-inspection/pre-transit-sample/index.vue \
web-admin/src/views/production/pre-transit-sample/index.vue
cp web-admin/src/views/quality-inspection/pre-transit-sample/PreTransitProtocolCardList.vue \
web-admin/src/views/production/pre-transit-sample/PreTransitProtocolCardList.vue
```
Expected: 两文件存在且行数与源文件接近(卡片约 1422 行)。
- [ ] **Step 2: 改 `index.vue` 注释与组件名**
将文件头注释改为:
```vue
<!-- 生产管理 — 预产及在途样本 -->
```
将:
```ts
defineOptions({ name: 'QualityInspectionPreTransitSample' })
```
改为:
```ts
defineOptions({ name: 'ProductionPreTransitSample' })
```
(keep-alive 需唯一组件名;勿与质检侧同名。)
其余逻辑、`@/api/pre-transit-sample` import、弹窗路径保持不变。`PreTransitProtocolCardList.vue` 仅改文件头注释为「生产管理 — …」即可,相对 import `./` 无需改。
- [ ] **Step 3: 确认无交叉引用质检目录**
```bash
rg -n "quality-inspection/pre-transit" web-admin/src/views/production/pre-transit-sample || true
```
Expected: 无匹配。
- [ ] **Step 4: Commit**
```bash
git add web-admin/src/views/production/pre-transit-sample
git commit -m "$(cat <<'EOF'
feat: copy pre-transit sample views under production.
EOF
)"
```
---
### Task 2: 注册生产侧路由
**Files:**
- Modify: `web-admin/src/router/modules/sqxp.ts`(`ProductionManage.children`,库位管理节点之前)
- [ ] **Step 1: 在库位管理前插入路由**
定位 `name: 'ProductionStorageFacility'`(约 L682)。在其**之前**插入:
```ts
{
id: '95200000000000000000000000000001',
name: 'ProductionPreTransitSample',
path: 'pre-transit-sample',
component: '/production/pre-transit-sample/index',
meta: {
title: '预产及在途样本',
keepAlive: true,
menuCode: 'production:preTransitSample:list'
}
},
```
保持其后的 `ProductionStorageFacility` 块不变。
- [ ] **Step 2: 核对顺序**
```bash
rg -n "ProductionPreTransitSample|ProductionStorageFacility" web-admin/src/router/modules/sqxp.ts
```
Expected: `ProductionPreTransitSample` 行号小于 `ProductionStorageFacility`(在同一 `children` 内)。
- [ ] **Step 3: Commit**
```bash
git add web-admin/src/router/modules/sqxp.ts
git commit -m "$(cat <<'EOF'
feat: register production pre-transit sample route.
EOF
)"
```
---
### Task 3: 后端共用 API 接受生产权限码
**Files:**
- Modify: `backend/app/api/pre_transit_sample.py`
- [ ] **Step 1: 改 import 与两处 Depends**
将:
```python
from app.core.menu_guard import require_menu_code
```
改为:
```python
from app.core.menu_guard import require_any_menu_code
```
将 `list_in_transit_samples` 与 `list_pre_delivery_samples` 中的:
```python
user: dict = Depends(require_menu_code("biz:preTransitSample:list")),
```
均改为:
```python
user: dict = Depends(
require_any_menu_code(
"biz:preTransitSample:list",
"production:preTransitSample:list",
)
),
```
- [ ] **Step 2: 静态核对**
```bash
rg -n "require_menu_code|require_any_menu_code|production:preTransitSample" backend/app/api/pre_transit_sample.py
```
Expected: 无 `require_menu_code`;两处 `require_any_menu_code`;含 `production:preTransitSample:list`。
- [ ] **Step 3: Commit**
```bash
git add backend/app/api/pre_transit_sample.py
git commit -m "$(cat <<'EOF'
fix: allow production perm on shared pre-transit APIs.
EOF
)"
```
---
### Task 4: 菜单与角色授权 SQL
**Files:**
- Create: `docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql`
- Modify: `docs/sql/zkxw.sql`(插入两条 `sys_menu`)
- Modify: `待同步到生产的sql脚本`(追加可执行片段)
- [ ] **Step 1: 写迁移脚本**
创建 `docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql`,全文如下:
```sql
-- 生产管理:预产及在途样本(独立页面 + 独立权限码)
-- 幂等:可重复执行
-- 授权:已有【生产管理】PRM 下任一有效子菜单的角色 → 授予本 C/F,并补授 PRM 祖先
SET @tenant_no = CONVERT('zkxw' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
SET @prm_id = CONVERT('PRM00000000000000000000000000001' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
SET @menu_c = CONVERT('95200000000000000000000000000001' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
SET @menu_f = CONVERT('95200000000000000000000000000002' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
-- 1) C 菜单
INSERT INTO sys_menu (
id, menu_name, parent_id, menu_type, path, component, perms,
is_frame, visible, status, tenant_no, order_num, remark,
create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
@menu_c,
'预产及在途样本',
@prm_id,
'C',
'pre-transit-sample',
'/production/pre-transit-sample/index',
'production:preTransitSample:list',
1, '1', '1', @tenant_no, 0,
'生产看板:在途样本与预产协议(协议单维度只读;与质检页代码独立)',
NULL, '', NOW(), NULL, NOW(), 0
FROM DUAL
WHERE NOT EXISTS (
SELECT 1 FROM sys_menu m
WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_c
);
UPDATE sys_menu
SET
menu_name = '预产及在途样本',
parent_id = @prm_id,
menu_type = 'C',
path = 'pre-transit-sample',
component = '/production/pre-transit-sample/index',
perms = 'production:preTransitSample:list',
visible = '1',
status = '1',
order_num = 0,
remark = '生产看板:在途样本与预产协议(协议单维度只读;与质检页代码独立)',
del_flag = 0,
update_time = NOW()
WHERE id COLLATE utf8mb4_unicode_ci = @menu_c;
-- 2) F 按钮
INSERT INTO sys_menu (
id, menu_name, parent_id, menu_type, path, component, perms,
is_frame, visible, status, tenant_no, order_num, remark,
create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
@menu_f,
'预产及在途查询',
@menu_c,
'F',
'',
'',
'production:preTransitSample:list',
1, '0', '1', @tenant_no, 1,
'生产侧预产及在途样本列表查询',
NULL, '', NOW(), NULL, NOW(), 0
FROM DUAL
WHERE NOT EXISTS (
SELECT 1 FROM sys_menu m
WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_f
);
UPDATE sys_menu
SET
menu_name = '预产及在途查询',
parent_id = @menu_c,
menu_type = 'F',
path = '',
component = '',
perms = 'production:preTransitSample:list',
visible = '0',
status = '1',
order_num = 1,
remark = '生产侧预产及在途样本列表查询',
del_flag = 0,
update_time = NOW()
WHERE id COLLATE utf8mb4_unicode_ci = @menu_f;
-- 3) 目标角色:已有 PRM 下任一有效子菜单授权
-- 3a) 恢复软删的 PRM / C / F 授权
UPDATE sys_role_menu rm
INNER JOIN sys_role r
ON r.id COLLATE utf8mb4_unicode_ci = rm.role_id COLLATE utf8mb4_unicode_ci
AND r.del_flag = 0
AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
SET
rm.del_flag = 0,
rm.update_time = NOW(),
rm.remark = 'grant production pre-transit to PRM roles'
WHERE rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
AND rm.del_flag = 1
AND rm.menu_id COLLATE utf8mb4_unicode_ci IN (@prm_id, @menu_c, @menu_f)
AND EXISTS (
SELECT 1
FROM sys_role_menu rm2
INNER JOIN sys_menu m2
ON m2.id COLLATE utf8mb4_unicode_ci = rm2.menu_id COLLATE utf8mb4_unicode_ci
AND m2.del_flag = 0
AND m2.parent_id COLLATE utf8mb4_unicode_ci = @prm_id
WHERE rm2.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
AND rm2.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
AND rm2.del_flag = 0
);
-- 3b) 插入缺失授权(PRM + C + F)
INSERT INTO sys_role_menu (
id, role_id, menu_id, tenant_no, order_num, remark,
create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
UPPER(MD5(CONCAT(r.id, '|', x.menu_id))),
r.id,
x.menu_id,
@tenant_no,
0,
'grant production pre-transit to PRM roles',
'admin',
'',
NOW(),
NULL,
NOW(),
0
FROM sys_role r
INNER JOIN (
SELECT @prm_id AS menu_id
UNION ALL SELECT @menu_c
UNION ALL SELECT @menu_f
) x
WHERE r.del_flag = 0
AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
AND EXISTS (
SELECT 1
FROM sys_role_menu rm2
INNER JOIN sys_menu m2
ON m2.id COLLATE utf8mb4_unicode_ci = rm2.menu_id COLLATE utf8mb4_unicode_ci
AND m2.del_flag = 0
AND m2.parent_id COLLATE utf8mb4_unicode_ci = @prm_id
WHERE rm2.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
AND rm2.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
AND rm2.del_flag = 0
)
AND EXISTS (
SELECT 1 FROM sys_menu m
WHERE m.id COLLATE utf8mb4_unicode_ci = x.menu_id
AND m.del_flag = 0
)
AND NOT EXISTS (
SELECT 1 FROM sys_role_menu rm
WHERE rm.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
AND rm.menu_id COLLATE utf8mb4_unicode_ci = x.menu_id
AND rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
AND rm.del_flag = 0
);
```
- [ ] **Step 2: 基线 `zkxw.sql` 插入两条菜单**
在 `sys_menu` 插入段中、`95000000000000000000000000000003`(洁净服管理)之前或附近按 id 排序插入(建议紧挨 `951…` 段之前插入 `952…`):
```sql
INSERT INTO `sys_menu` (`id`, `menu_name`, `parent_id`, `menu_type`, `path`, `component`, `perms`, `is_frame`, `visible`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('95200000000000000000000000000001', '预产及在途样本', 'PRM00000000000000000000000000001', 'C', 'pre-transit-sample', '/production/pre-transit-sample/index', 'production:preTransitSample:list', 1, '1', '1', 'zkxw', 0, '生产看板:在途样本与预产协议(协议单维度只读;与质检页代码独立)', NULL, '', '2026-08-17 12:00:00', NULL, '2026-08-17 12:00:00', 0);
INSERT INTO `sys_menu` (`id`, `menu_name`, `parent_id`, `menu_type`, `path`, `component`, `perms`, `is_frame`, `visible`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('95200000000000000000000000000002', '预产及在途查询', '95200000000000000000000000000001', 'F', '', '', 'production:preTransitSample:list', 1, '0', '1', 'zkxw', 1, '生产侧预产及在途样本列表查询', NULL, '', '2026-08-17 12:00:00', NULL, '2026-08-17 12:00:00', 0);
```
核对无重复 id:
```bash
rg "9520000000000000000000000000000" docs/sql/zkxw.sql
```
Expected: 恰好两行(C + F)。
- [ ] **Step 3: 追加到 `待同步到生产的sql脚本`**
在文件末尾追加注释 + 与 Step 1 相同的可执行 SQL(或 `SOURCE`/`-- 见 docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql` 并粘贴全文,与仓库现有「待同步」习惯一致:粘贴全文便于运维一次执行)。
- [ ] **Step 4: Commit**
```bash
git add docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql \
docs/sql/zkxw.sql \
待同步到生产的sql脚本
git commit -m "$(cat <<'EOF'
docs(sql): add production pre-transit sample menu and grants.
EOF
)"
```
---
### Task 5: 图谱更新与手工验收
**Files:**
- 运行:`./scripts/update-graph.sh backend` 与 `./scripts/update-graph.sh frontend`(或 `all`)
- [ ] **Step 1: 更新知识图谱**
```bash
./scripts/update-graph.sh all
```
若图谱产物有变更则提交:
```bash
git add backend/graphify-out web-admin/graphify-out
git status --short
# 若有变更:
git commit -m "$(cat <<'EOF'
chore: update graphify after production pre-transit sample.
EOF
)"
```
- [ ] **Step 2: 本地手工验收清单**
1. 在本地/测试库执行 `docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql`。
2. 重新登录 web-admin(或刷新菜单权限缓存)。
3. 打开 `/#/production-group/production/pre-transit-sample`:双分区列表可加载;与质检侧同筛选结果一致。
4. 侧栏【生产管理】中「预产及在途样本」在【库位管理】上方。
5. 质检路由 `/#/quality-group/quality-manage/pre-transit-sample` 仍正常。
6. (可选)角色管理中确认已有 PRM 子菜单的角色已勾选新菜单;仅有生产基础信息、无 PRM 子菜单的角色未自动获得。
- [ ] **Step 3: 完成标记**
将本 plan 中已完成 Task 的 `- [ ]` 改为 `- [x]`(若执行 agent 负责跟踪)。
---
## Spec coverage (self-review)
| Spec 要求 | Task |
|-----------|------|
| 完整复制 Vue 到 production | Task 1 |
| 独立组件名 / 不引用质检目录 | Task 1 |
| 路由在库位管理前、独立 menuCode | Task 2 |
| 共用 API + OR 权限码 | Task 3 |
| 菜单 ID 952…、SQL 幂等、PRM 角色授权、zkxw/待同步 | Task 4 |
| 图谱 + 验收 | Task 5 |
| 不改质检页、不新建业务查询 API | 各 Task 未触及 |
无 TBD/占位符;ID 与权限码全文一致。