Files
zkxw/docs/superpowers/specs/2026-09-01-production-task-set-name-placeholder-design.md
T
2026-09-01 11:32:49 +08:00

11 KiB
Raw Blame History

生产任务组合模版 — 任务名称动态参数与条目区间距

日期:2026-09-01
状态:已实现(待浏览器联调)
范围:【生产任务组合模版】新增/编辑弹窗 UI;【生产工单】/【我的生产任务】按模版派发时任务名称占位符正向替换;「保存为模版」反向还原
派发重点场景:两页中【细胞存储】【科研捐赠】生产工单(有样本接收与实验室入库编码)
不改:单条派发、描述/备注字段、{pro_no} 别名


1. 背景与目标

1.1 问题

  1. 模版新增/编辑弹窗中,「添加条目」按钮与条目列表间距过紧。
  2. 同套模版在不同生产工单上复用时,任务名称需要带上该工单对应的实验室入库编码;当前 task_name 原样落库,无法按工单动态生成。
  3. 「保存为模版」若直接写入已替换的具体编码,模版无法跨工单复用。

1.2 目标

  1. 「任务条目」区:按钮与列表增加间距;区域上方增加一行可用动态参数说明。
  2. 约定唯一占位符 {lab_storage_code},语义对齐【样本接收】中的实验室入库编码(biz_sample_reception.lab_storage_code)。
  3. 【生产工单】与【我的生产任务】中,对【细胞存储】【科研捐赠】等工单按模版派发时:将模版任务名中的 {lab_storage_code} 替换为该工单对应的实验室入库编码,再创建任务、完成派发。
  4. 「保存为模版」时,对任务名称做反向还原后再写入模版。

1.3 已确认产品决策

项 决策
占位符字面量 {lab_storage_code}(大小写敏感;不实现 {pro_no})
数据来源 样本接收「实验室入库编码」;经生产工单关联解析
正向空值 无编码时替换为空串({lab_storage_code}样本处理 → 样本处理)
作用字段 仅任务名称 taskName / task_name
说明文案位置 模版维护页「任务条目」区域上方一行
正向替换时机 后端 create_tasks_from_set(按模版派发)统一替换;两页共用 DispatchTaskDialog
派发适用 【细胞存储】【科研捐赠】工单:用工单关联样本接收的入库编码赋值;无编码则置空仍派发
保存为模版 反向还原:任务名中的当前入库编码 → {lab_storage_code} 再写入模版
入口页 派发与保存均在【生产工单】/【我的生产任务】共用组件,行为一致

2. 概念与数据流

模版条目 task_name(可含 {lab_storage_code})
        │
        ▼  按模版派发 create_tasks_from_set(正向)
生产工单 ──► 解析 lab_storage_code(reception_id / sample_code)
        │
        ▼  替换占位符
生产任务 task_name(具体编码)
        │
        ▼  保存为模版 create_set_from_work_order(反向)
模版条目 task_name(编码还原为 {lab_storage_code})
  • 模版库:保存可含占位符的模板文案。
  • 生产任务:保存替换后的最终名称。
  • 「保存为模版」:将当前工单任务名中出现的实验室入库编码,还原为 {lab_storage_code} 后写入模版,便于跨工单复用。

3. 占位符规范

3.1 白名单

占位符 含义 解析
{lab_storage_code} 实验室入库编码 见 §4
  • 仅替换白名单键;未知 {xxx} 原样保留。
  • 同一名称内可多次出现,全部替换。
  • 不支持嵌套、条件表达式或过滤器。

3.2 正向示例

模版任务名称 入库编码 生成结果
{lab_storage_code}样本处理 LS20260301001 LS20260301001样本处理
{lab_storage_code}样本处理 (空) 样本处理
常规分装 任意 常规分装
{unknown}处理 任意 {unknown}处理(未知键不替换)

3.3 反向示例

任务名称 入库编码 还原结果
LS001样本处理 LS001 {lab_storage_code}样本处理
前LS001后LS001 LS001 前{lab_storage_code}后{lab_storage_code}
常规分装 LS001 常规分装
LS001样本处理 (空) LS001样本处理(不还原)

4. 后端解析与替换

4.1 解析 lab_storage_code

在按模版派发 / 保存为模版路径中,对当前工单:

  1. 若 work_order.reception_id 非空:查本租户未删除的 BizSampleReception,取 lab_storage_code(trim)。
  2. 若仍为空且工单有 sample_code:复用既有按样本编码取入库编码的映射能力(与 production_task 详情补全 labStorageCode 一致,如 get_lab_storage_code_map_by_sample_codes)。
  3. 仍为空 → 视为无编码(正向替换空串;反向不还原)。

临时工单等无接收记录场景:按无编码处理,不阻断操作。

4.2 正向替换函数

apply_task_name_placeholders(template: str, *, lab_storage_code: str) -> str
  • 对 template 中所有 {lab_storage_code} 替换为传入值(已 trim;空则空串)。
  • 在 create_tasks_from_set 写入 create_production_task 的 task_name 之前调用。
  • 无论条目来自库内模版还是前端提交的 items[] 覆盖,均走同一替换。

4.3 反向还原函数

revert_task_name_placeholders(task_name: str, *, lab_storage_code: str) -> str

规则:

  1. lab_storage_code 经 trim 后为空 → 不改动 task_name。
  2. 否则:将名称中所有与该编码完全相等的连续子串替换为字面量 {lab_storage_code}(普通字符串替换,非正则通配)。
  3. 若名称中已含 {lab_storage_code},且又含同一编码文本,仅替换编码文本。
  4. 仅处理任务名称;描述、备注不还原。

接入:

  • 后端 create_set_from_work_order:落库前对每条 task_name 执行还原(编码解析同 §4.1)。无论 items 由服务端从任务加载还是前端提交,均执行一次。
  • 前端 打开「保存为模版」时:用当前工单入库编码对预览条目做同样还原,弹窗内显示占位符并可再编辑。

两端算法一致;以后端落库结果为准。前端已还原后再提交时:名称中已无具体编码 → 后端再还原为幂等(无匹配则不变)。

4.4 API / 表结构

  • 不改表结构、不改请求/响应字段契约。
  • 按模版派发响应中的新建任务 taskName 为正向替换后的值。
  • 保存为模版详情中的条目 taskName 为反向还原后的值。

4.5 单条派发

POST /production-tasks 不做占位符正向替换。若用户在单条派发名称中手写 {lab_storage_code},将按字面保存。


5. 前端

5.1 生产任务组合模版页 production-task-set/index.vue

间距

  • .set-items-block 内工具栏与 .inner-table-wrap 之间增加垂直间距(建议工具栏 margin-bottom: 12px,或父级 gap: 12px + flex 列布局)。

说明文案

  • 放在「任务条目」内容区顶部、「添加条目」工具栏之上。

  • 文案建议:

    任务名称支持动态参数:{lab_storage_code}(实验室入库编码)。按模版派发时按工单关联样本接收自动替换;无入库编码则置空。保存为模版时,会将任务名中的入库编码还原为该占位符。

  • 样式:次要说明(12px / muted);可用 .form-tip 或等价 class。

查看弹窗

  • 展示模版原文(可含占位符);不做示例替换预览。

5.2 保存为模版 SaveProductionTaskSetDialog.vue

  • 【生产工单】与【我的生产任务】共用,同一套反向还原,无分端特殊分支。
  • 扩展 mapWorkOrderTasksToSetItems:接收可选 labStorageCode,映射时对 taskName 做反向还原。
  • 打开弹窗的调用方传入当前工单实验室入库编码(与 DispatchTaskDialog 的 headerLabStorageCode 同源:row.labStorageCode 或 reception.labStorageCode)。
  • 保存弹窗本次不强制加参数说明文案(维护页已有说明);间距可与维护页对齐,非必须。

5.3 派发弹窗 DispatchTaskDialog.vue

  • 【生产工单】与【我的生产任务】共用;【细胞存储】【科研捐赠】Tab 下选择「任务组合模版」派发时,走 POST /production-tasks/from-set。
  • 替换在后端完成:解析该工单 lab_storage_code,对每条任务名中的 {lab_storage_code} 赋值后建任务;前端不必先本地替换再提交(提交覆盖 items 时后端仍会正向替换)。
  • 派发成功后关闭弹窗并刷新任务列表,列表中任务名应为已代入入库编码的最终名称。
  • 本次不强制在派发弹窗加占位符说明与间距(维护页已有说明)。

6. 错误处理与边界

场景 行为
正向:无入库编码 占位符 → 空串,继续创建
正向:未知 {xxx} 原样保留
正向:替换后任务名为空 沿用现有「任务名称必填」语义,避免无名任务
正向:名称超长 与现有 create_production_task / VARCHAR(200) 一致
反向:无入库编码 不还原,保留任务原名
反向:名称不含该编码 不改动
反向:编码为空串 不还原(避免误替换)

7. 测试要点

  • 纯函数正向:有编码 / 空编码 / 无占位符 / 多次出现 / 未知键。
  • 纯函数反向:有编码命中 / 多次命中 / 无编码不还原 / 名称不含编码。
  • create_tasks_from_set:有接收编码 → 任务名正确;无编码 → 空串;前端 items 仍含占位符 → 同样替换。
  • create_set_from_work_order:任务名含入库编码 → 模版为占位符;无编码 → 原文;前端已还原再提交 → 幂等正确。
  • 前端:维护页说明与间距;保存弹窗(两入口)打开后有编码时任务名显示占位符。

8. 实现范围清单

# 项 位置
1 工具栏与列表间距 + 说明文案 web-admin/.../production-task-set/index.vue
2 正向/反向纯函数 + 单测 backend/app/(建议可测 utils)
3 create_tasks_from_set 正向替换 backend/app/crud/production_task_set.py
4 create_set_from_work_order 反向还原 同上
5 前端映射还原 + 传入 labStorageCode production-task-set.ts、SaveProductionTaskSetDialog.vue 及打开处

9. 非目标

  • {pro_no} 或其它别名
  • 描述、备注字段的占位符
  • 单条派发路径正向替换
  • 派发弹窗内「替换后预览」专用 UI
  • 按部分匹配/模糊匹配还原(仅精确子串等于完整入库编码)