Files
shanxia2.0/docs/superpowers/specs/2026-10-09-pl-plan-copy-seq-design.md
T
wuzilongandCursor 811e6a129f feat(pl): allocate copy_seq_no on first driver plan share-to-group
Add ensure-copy-seq API and miniprogram clipboard prefix so each plan
gets a tenant-scoped fixed serial on first copy; cover allocation with
unit tests and refresh the backend graph.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 12:36:58 +08:00

5.8 KiB
Raw Blame History

运输计划「复制发群」固定序号设计

日期: 2026-10-09
状态: 已确认
范围: pl_plan.copy_seq_no + POST /api/transport/driver-plan/{planId}/ensure-copy-seq + 小程序司机运输计划单「复制发群」文案
前置: docs/superpowers/specs/2026-09-28-mini-pl-driver-plan-copy-wechat-design.md

1. 目标

在司机运输计划单「复制发群」纯文本中,于「计划编号」上方增加固定流水「序号」。租户内永不回收;历史按创建时间回填;新建计划不在创建时发号,首次点击复制时发号并落库。

2. 已确认决策

# 项 决策
D1 号段范围 按 tenant_no 永久累加 1、2、3…
D2 回收 作废 / 软删 不回收号;回填与 MAX 均含 del_flag=1
D3 展示面 仅「复制发群」文案;列表/详情/Web 管理端不展示
D4 发号时机 创建时 NULL;首次「复制发群」调用后端 ensure 时分配并写库
D5 历史 迁移按租户 create_time ASC, id ASC 一次性补号;含软删
D6 无号兜底 文案写 序号:—(正常流程点复制会先 ensure,几乎用不到)
D7 格式 序号:{n},纯数字不补零
D8 详情副作用 禁止在普通 GET 详情里发号;专用 POST ensure-copy-seq

3. 非目标

  • 不在 Web 管理端 / 小程序列表卡片 / 回执页 UI 展示该序号
  • 不改变装货方、送货方、实际装/送货量等既有复制规则
  • 不按日/月重置号段
  • 创建计划路径(create_plan / create_plan_full / create_from_contract 等)不分配 copy_seq_no

4. 数据模型

4.1 字段

pl_plan 新增:

列 类型 空 说明
copy_seq_no INT YES 复制发群序号(租户内固定流水);未首次复制前为 NULL(历史回填后有值)

建议索引:KEY idx_pl_plan_tenant_copy_seq (tenant_no, copy_seq_no)(便于 MAX)。

4.2 历史回填

  • 分组:tenant_no
  • 排序:create_time ASC, id ASC
  • 范围:含 del_flag = 1
  • 赋值:组内从 1 起连续编号写入 copy_seq_no
  • 幂等:已非空的行不覆盖(便于脚本重跑)

4.3 运行时发号

在事务内:

  1. 按与司机详情相同的可见性解析计划;不可见则失败。
  2. SELECT … FROM pl_plan WHERE id=? FOR UPDATE(或等价行锁)。
  3. 若 copy_seq_no 已有值 → 原样返回。
  4. 否则计算
    next = COALESCE(MAX(copy_seq_no) WHERE tenant_no = ?, 含软删), 0) + 1
    写回当前行并 commit,返回 next。

同一计划并发首次复制只得到一个号;不同计划不撞号。

5. API

POST /api/transport/driver-plan/{planId}/ensure-copy-seq

项 约定
鉴权 JWT;与 GET /transport/driver-plan/{planId} 一致(须关联驾押人员且可看该计划)
成功体 { "copySeqNo": <int> }(camelCase / by_alias 与现网一致)
失败 未关联人员 / 计划不可见 → 与详情同等 404 或业务错误码
加密排除 前缀已在 /api/transport/driver-plan,一般无需改配置

详情 GET 可不强制返回 copySeqNo;小程序以 ensure 结果为准合并进复制上下文。

6. 复制文案

在既有模板最上方增加一行:

序号:12
计划编号:TP2026xxxx
装货方:…
送货方#1:…
…

规则:

  • 有号:序号:{copySeqNo}
  • 无号:序号:—
  • 其余字段规则不变(见 2026-09-28 设计)

7. 小程序交互

列表「复制发群」

  1. showLoading
  2. GET /transport/driver-plan/{planId} 取详情
  3. POST .../ensure-copy-seq → 将 copySeqNo 合并进详情对象(如 page.copySeqNo)
  4. buildDriverPlanCopyText → setClipboardData
  5. hideLoading;成功「已复制」;任一步失败「复制失败」/「计划无效」

说明:若 ensure 失败,仍可用详情尝试复制(序号行为 —),或整单失败——推荐整单失败并 toast「复制失败」,避免发出无序号的半成品(与「点复制就会有号」预期一致)。实现时采用:ensure 失败则中止复制。

回执保存成功 →「复制发群」

同样:ensure → 合并 → 复制;成功仍 navigateBack;失败 toast 且留在回执页。

8. 实现结构

位置 职责
docs/sql/migrations/2026-10-09-pl-plan-copy-seq-no.sql(名可微调) ALTER + 回填
backend/app/models/pl_models.py PlPlan.copy_seq_no
backend/app/services/pl_driver_plan_service.py(或邻近 service) ensure_copy_seq_no
backend/app/api/pl/driver_plan.py 新路由
backend/app/schemas/pl/… 响应模型(如需)
miniprogram/api/pl.js driverPlanApi.ensureCopySeq
miniprogram/utils/plDriverPlanCopy.js 首行序号
miniprogram/pages/pl/driver-plan/list.vue handleCopy 调 ensure
miniprogram/pages/pl/driver-plan/detail.vue 保存成功复制路径调 ensure

9. 测试要点

  • 历史回填后:最旧计划为 1,同租户连续;软删行也占号
  • 新计划创建后 copy_seq_no 为 NULL;首次复制后为 MAX+1 且再次复制不变
  • 两客户端同时首次复制同一计划:只出现一个号
  • 两计划几乎同时首次复制:号不重复
  • 复制文案首行为 序号:n,其后为原「计划编号」块
  • ensure 失败时不写入剪贴板(或明确 toast),列表/详情 UI 仍无序号展示
  • 不可见计划 / 未绑定驾押人员:ensure 与 GET 详情一致失败

10. 与加油登记序号的差异

加油登记 seq_no 为按 create_time 现算、不落库。本需求为 落库固定流水,且新单在首次复制时分配,不可套用加油登记的纯计算方案。