docs: plan production pre-transit sample menu copy.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
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 与权限码全文一致。
|
||||
Reference in new issue
Block a user