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 (
- [x]) 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
| 文件 | 职责 |
|---|---|
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 |
Files:
web-admin/src/views/production/pre-transit-sample/index.vueCreate: web-admin/src/views/production/pre-transit-sample/PreTransitProtocolCardList.vue
[x] Step 1: 复制目录
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 行)。
index.vue 注释与组件名将文件头注释改为:
<!-- 生产管理 — 预产及在途样本 -->
将:
defineOptions({ name: 'QualityInspectionPreTransitSample' })
改为:
defineOptions({ name: 'ProductionPreTransitSample' })
(keep-alive 需唯一组件名;勿与质检侧同名。)
其余逻辑、@/api/pre-transit-sample import、弹窗路径保持不变。PreTransitProtocolCardList.vue 仅改文件头注释为「生产管理 — …」即可,相对 import ./ 无需改。
rg -n "quality-inspection/pre-transit" web-admin/src/views/production/pre-transit-sample || true
Expected: 无匹配。
git add web-admin/src/views/production/pre-transit-sample
git commit -m "$(cat <<'EOF'
feat: copy pre-transit sample views under production.
EOF
)"
Files:
Modify: web-admin/src/router/modules/sqxp.ts(ProductionManage.children,库位管理节点之前)
[x] Step 1: 在库位管理前插入路由
定位 name: 'ProductionStorageFacility'(约 L682)。在其之前插入:
{
id: '95200000000000000000000000000001',
name: 'ProductionPreTransitSample',
path: 'pre-transit-sample',
component: '/production/pre-transit-sample/index',
meta: {
title: '预产及在途样本',
keepAlive: true,
menuCode: 'production:preTransitSample:list'
}
},
保持其后的 ProductionStorageFacility 块不变。
rg -n "ProductionPreTransitSample|ProductionStorageFacility" web-admin/src/router/modules/sqxp.ts
Expected: ProductionPreTransitSample 行号小于 ProductionStorageFacility(在同一 children 内)。
git add web-admin/src/router/modules/sqxp.ts
git commit -m "$(cat <<'EOF'
feat: register production pre-transit sample route.
EOF
)"
Files:
Modify: backend/app/api/pre_transit_sample.py
[x] Step 1: 改 import 与两处 Depends
将:
from app.core.menu_guard import require_menu_code
改为:
from app.core.menu_guard import require_any_menu_code
将 list_in_transit_samples 与 list_pre_delivery_samples 中的:
user: dict = Depends(require_menu_code("biz:preTransitSample:list")),
均改为:
user: dict = Depends(
require_any_menu_code(
"biz:preTransitSample:list",
"production:preTransitSample:list",
)
),
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。
git add backend/app/api/pre_transit_sample.py
git commit -m "$(cat <<'EOF'
fix: allow production perm on shared pre-transit APIs.
EOF
)"
Files:
docs/sql/alter_sys_menu_add_production_pre_transit_sample.sqldocs/sql/zkxw.sql(插入两条 sys_menu)Modify: 待同步到生产的sql脚本(追加可执行片段)
[x] Step 1: 写迁移脚本
创建 docs/sql/alter_sys_menu_add_production_pre_transit_sample.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
);
zkxw.sql 插入两条菜单在 sys_menu 插入段中、95000000000000000000000000000003(洁净服管理)之前或附近按 id 排序插入(建议紧挨 951… 段之前插入 952…):
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:
rg "9520000000000000000000000000000" docs/sql/zkxw.sql
Expected: 恰好两行(C + F)。
待同步到生产的sql脚本在文件末尾追加注释 + 与 Step 1 相同的可执行 SQL(或 SOURCE/-- 见 docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql 并粘贴全文,与仓库现有「待同步」习惯一致:粘贴全文便于运维一次执行)。
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
)"
Files:
运行:./scripts/update-graph.sh backend 与 ./scripts/update-graph.sh frontend(或 all)
[x] Step 1: 更新知识图谱
./scripts/update-graph.sh all
若图谱产物有变更则提交:
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
)"
docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql。/#/production-group/production/pre-transit-sample:双分区列表可加载;与质检侧同筛选结果一致。/#/quality-group/quality-manage/pre-transit-sample 仍正常。将本 plan 中已完成 Task 的 - [x] 改为 - [x](若执行 agent 负责跟踪)。
| 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 与权限码全文一致。