2026-09-01-process-route-builtin-template-design.md 6.5 KB

工艺路线内置模版复制与只读保护 — 设计文档

日期:2026-09-01
状态:已实现(待库执行 SQL 与浏览器验证)
范围:工艺库管理 · 工艺路线 Tab;biz_process_route 字段;写接口守卫;SQL 深拷贝脚本
不改:派发任务选路线逻辑(内置路线仍可选);原 4 条源路线权限不变


1. 背景与目标

将下列 4 条工艺路线各复制一份作为「内置模版」:

源路线名称 源路线编码
造血干样本处理 CC样本处理
干细胞培养 MSC培养
UBMC冻存 UBMC冻存
PBMC冻存 PBMC冻存

复制结果:

目标名称 目标编码
造血干样本处理(内置模版) CC样本处理-BUILTIN
干细胞培养(内置模版) MSC培养-BUILTIN
UBMC冻存(内置模版) UBMC冻存-BUILTIN
PBMC冻存(内置模版) PBMC冻存-BUILTIN

约束:

  • 备注固定为「内置模版」
  • 仅 super_admin 可编辑/删除/维护步骤
  • 非超管:禁止改路线头、步骤及试剂/耗材/产出/数据采集、禁止删除;派发任务时仍可选
  • is_current 与对应源路线一致
  • 提供可在测试/生产执行的幂等 SQL

2. 已确认产品决策

项 决策
命名 名称加「(内置模版)」;编码加 -BUILTIN
限制范围 禁改头信息 + 全部步骤维护 + 禁删;派发可选
识别 字段 is_builtin='1' + 备注「内置模版」
当前版本 与源 is_current 一致
可修改角色 仅 super_admin(admin 不可改内置)

3. 数据模型

3.1 字段

biz_process_route 增加:

列 类型 默认 说明
is_builtin CHAR(1) NOT NULL '0' 1=内置模版,0=普通

位置:业务字段区(建议在 remark 之前或之后,与现有列顺序风格一致)。

配套:

  • SQLAlchemy BizProcessRoute.is_builtin
  • _route_row_to_dict 增加 isBuiltin: "0"|"1"
  • 详情接口同样返回

3.2 迁移 SQL

docs/sql/2026-09-01-process-route-add-is-builtin.sql:

ALTER TABLE biz_process_route
  ADD COLUMN is_builtin CHAR(1) NOT NULL DEFAULT '0'
  COMMENT '是否内置模版(0-否 1-是)' AFTER remark;

(若环境 MySQL 对 AFTER 敏感,可省略 AFTER。)


4. 复制规则(深拷贝)

对每个源路线(按 tenant_no + route_code + del_flag=0 定位;若同编码多版本,取 is_current='1',若无则取 update_time 最新一条):

  1. 幂等:若同租户已存在 route_code = 源编码 + '-BUILTIN' 且未删除 → 跳过该条。
  2. 插入新 biz_process_route:
    • 新 UUID
    • route_name / route_code 按上表
    • product_type_id、version、equipment_ids、equipment_name、order_num 同源
    • is_current 同源
    • remark = '内置模版'
    • is_builtin = '1'
    • 公共字段按惯例写入 create_time/update_time/del_flag=0 等
  3. 复制该路线下全部 del_flag=0 的 biz_process_route_step(含 sop_content 等全文)
  4. 对每步复制:
    • biz_process_route_step_reagent
    • biz_process_route_step_consumable
    • biz_process_route_step_output
    • biz_process_route_step_data_collection
  5. 步骤与子表均新 UUID;route_id/step_id 指向新记录

脚本:docs/sql/2026-09-01-process-route-builtin-templates.sql
实现方式:推荐存储过程或分步 INSERT…SELECT + 临时映射表(旧 id → 新 id),保证可在 MySQL 5.7 执行。脚本头部注释说明:先执行 add-is-builtin,再执行本复制脚本。


5. 后端权限

5.1 判定

is_super_admin(user) := user.role == "super_admin"

与现有 menu_guard.SUPER_ADMIN_ROLE / 前端 isSuperAdmin 一致。

5.2 守卫

新增辅助(建议放在 process_route CRUD 或小工具模块):

assert_route_writable(db, route_id, user) 
  → 若路线 is_builtin=='1' 且非 super_admin → raise ValueError("内置模版仅超级管理员可修改")

按 step_id 写操作时:先解析步骤所属 route_id,再调用同上。

5.3 必须拦截的接口

接口 说明
PUT /process-routes/{id} 更新路线头
PUT /process-routes/set-current 设当前版本
DELETE /process-routes/{id} 删除路线
POST .../step/add 加步骤
POST .../step/copy 复制步骤
PUT .../step/update 更新步骤
PUT .../step/reorder 重排
DELETE .../step/{id} 删步骤
`PUT .../step/reagent consumable

列表/详情/派发侧拉启用路线:不拦截。

批量删除若存在:对选中项中含内置的,整批拒绝或逐条跳过——推荐整批拒绝并提示含内置模版。

普通用户不能通过更新接口把 is_builtin 改为 0(字段不开放给 UpdateBody;仅 SQL/超管运维脚本维护)。


6. 前端(ProcessRouteTab)

  • 列表/详情使用 isBuiltin === '1'
  • userStore.isSuperAdmin === false 时:
    • 隐藏或禁用:编辑、删除、批量删除中的内置行勾选(或勾选后删除时过滤并提示)
    • 「路线图配置」:允许打开只读,或直接禁用入口——推荐禁用入口并 Tooltip「内置模版仅超级管理员可配置」(避免只读改造过大);若产品更希望可查看步骤,可改为只读打开(实现阶段按工作量二选一,默认禁入口)
    • 「设为当前」若在 UI 上对内置可见,同样仅超管可点
  • 超管:行为与现网一致,可完整维护

备注列展示「内置模版」即可(SQL 已写入)。


7. 错误与边界

场景 行为
非超管改内置 400,文案明确
源路线不存在 SQL 跳过并记录(或脚本注释人工检查)
目标编码已存在 幂等跳过
派发选内置路线 允许,克隆任务步骤逻辑不变

8. 实现清单

# 项
1 ALTER + Model + isBuiltin 序列化
2 写接口 assert_route_writable
3 ProcessRouteTab 按钮/入口禁用
4 幂等深拷贝 SQL
5 字段一致性(若 UpdateBody 曾暴露字段则排除 isBuiltin)

9. 非目标

  • 不修改源 4 条路线
  • 不限制派发选用
  • 不把 admin 视为可改内置
  • 不做跨租户自动复制(按各环境库内实际 tenant_no 匹配源数据执行)