2026-06-23-pl-site-inventory-link-design.md 15 KB

出入库单 · 工厂现场人员免登录链接 — 设计文档

日期: 2026-06-23
状态: 已批准(2026-06-23)
前置: docs/运输计划管理-需求说明文档.md(§6 流程、BR-11)、docs/superpowers/specs/2026-06-20-pl-arrange-dibang-design.md(Phase 2 现场页)、docs/superpowers/specs/2026-06-21-pl-arrange-transport-refine-design.md(调整运输与地磅 revoke)
界面模式: 公共移动端页参考 views/public/pl/transport/index.vue(Vant 4 + PublicMobileShell);管理端复制链接参考安排成功态「复制司机链接」

目标

为 地磅出入库单(db_inbound_outbound,入库/出库)生成 工厂现场人员免登录链接。现场人员打开链接后可查看单据摘要,上传附件并填写备注;计划专员在安排运输成功后可复制链接发给现场(微信等)。


已确认决策(2026-06-23)

# 决策 说明
S1 免登录 token 链接(方案 A) 与司机链接一致:token 即凭证,不使用门户 JWT
S2 链接粒度:每出入库单一条 一产品一行地磅单 → 一条 site_access_token + 一条 URL
S3 首期能力边界 现场仅可:查看摘要 + 上传附件 + 填写/更新备注;不可在公共页修改数量、车牌、地磅重量等核心字段
S4 覆盖式保存 备注与附件 URL 列表可多次保存,以最新一次为准(与司机回执页一致)
S5 token 生成时机 任意创建 db_inbound_outbound 时均生成 token:运输地磅 bridge、地磅模块手工新建、计划生成出入库等;历史数据 token 为空时由补全脚本或首次访问管理端「复制链接」时 lazy 生成
S6 调整运输联动 出入库单被软删 → 旧 token 404;重建的新单生成 新 token(不保留旧链接)
S7 修订 BR-33 原「现场人员扫码需登录」改为:现场人员链接免登录;司机与现场均为 token 链接,管理端内操作仍走 JWT
S8 已完成不可补传 status=已完成 时公共页只读:可查看摘要与历史回执,禁止 POST feedback / 上传附件
S9 全量出入库单均有链接 不限于运输联动;地磅模块手工新建的出入库单同样在创建时生成 site_access_token
S10 未完成可删改附件 待入库 / 待出库 等可编辑态:Uploader 支持删除已传项并重新上传;保存时整表覆盖(与司机页一致)
S11 复制链接 UX 多单时同时提供「复制全部现场链接」(按产品换行拼接)与「复制本条」
S12 计划作废展示 与司机页一致:链接仍可打开,展示「运输计划已作废」空态,禁止提交
S13 附件类型不限制 公共上传不校验 MIME/扩展名;仅保留单文件大小上限(与门户统一上传策略一致)

非目标(本期不做)

  • 现场人员门户账号 / 短信验证码 / PIN 二次校验
  • 地磅硬件对接;公共页填写磅重、毛皮净重
  • 公共页创建/编辑地磅单(db_weighing_slip)
  • 系统消息 / 微信模板自动推送(本期仅「复制链接」)
  • 直销单(plan_type=direct)— 本就不生成出入库单
  • 运输 N:N 拼车下一链接聚合多单(见 S2)

一、业务语义

1.1 角色与单据

角色 链接 关联实体 主要动作
司机 /public/pl/transport/{token} pl_transport 运输回执、附件、备注
工厂现场人员 /public/db/inventory/{token} db_inbound_outbound 出入库附件、现场备注

1.2 触发链路

路径 A:运输安排 + 地磅联动

计划专员:安排运输 + 勾选「生成地磅计划」
    → pl_dibang_bridge 创建 db_inbound_outbound(每产品一行)
    → 每行生成 site_access_token
    → arrange 响应 inventoryRecords[] 含 siteUrl
    → 成功弹窗:复制司机链接 + 复制现场链接 + 复制磅单二维码

路径 B:地磅模块手工新建出入库单(S9)

专员:/#/db/inventory 新建入库/出库单
    → POST create inventory
    → 后端创建时 _ensure_site_access_token
    → 列表/详情「复制现场链接」(P3)

现场使用(共通)

现场人员:打开 siteUrl → 上传附件、填备注 → 保存(未完成态可删改附件,S10)
计划专员:计划查看 / 地磅 Tab / 出入库列表查看现场回执

1.3 与「调整运输」的关系

沿用 2026-06-21 的 revoke 规则:

  • 再次调整且存在历史 pl_transport_db_link → 先 revoke_regeneratable_dibang_for_transport
  • 可作废的出入库单:del_flag=1 → 其 site_access_token 仍保留在库中但公共 GET 因 del_flag 返回 404
  • 新建出入库单 → 新 token;专员须重新发送链接
  • 已完成 / 已关联磅单不可作废 → 调整运输整体 阻断(与现逻辑一致)

1.4 公共页只读条件

条件 公共页行为
del_flag=1 404 链接无效
status 为 已完成 可查看摘要与历史回执;禁止上传附件与修改备注(不可补传,S8)
关联运输计划已作废 链接仍可 GET;页面展示「运输计划已作废」空态(S12,对齐司机页);禁止 POST / 上传

二、现场免登录页(公共路由)

2.1 路由

在 web-admin/src/router/routes/staticRoutes.ts 注册(不需登录):

{
  path: '/public/db/inventory/:token',
  name: 'PublicDbInventorySite',
  component: () => import('@views/public/db/inventory/index.vue'),
  meta: { title: '出入库单', isHideTab: true, isPublicMobile: true }
}

2.2 技术栈

项 选择
组件库 Vant 4(与司机页相同 chunk 策略)
布局 PublicMobileShell
样式 Fresh Mint --primary #10b981
HTTP 新建 utils/http/public-db.ts(或扩展 public-pl.ts 为通用 public-api.ts),无 JWT、无 API 加密

2.3 页面能力

区域 内容
单据摘要(只读) 出入库单号(batch_no)、业务类型(入库/出库)、日期、往来单位、产品/规格、数量(吨)、车牌、地磅计划号
附件上传 多文件(上限 9,与司机页一致);不限制文件类型(S13);MinIO;未完成态可删除已传项(S10)
现场备注 文本域,site_feedback_remark,max 500 字
历史回显 再次打开:展示已保存附件、site_feedback_remark、site_feedback_time
底部操作 「保存回执」;已完成/作废态隐藏或禁用

交互:进入即可操作,无登录、无验证码(S1)。

参考:views/public/pl/transport/index.vue、simple-dibang-xiaoshou/mobile-h5/src/views/inventory/fill.vue(仅借鉴卡片布局,不引入其「完善数量/车牌后锁定」逻辑 — 本期字段已由专员在安排运输时写入)。


三、数据模型

3.1 db_inbound_outbound 新增字段

ALTER TABLE db_inbound_outbound
  ADD COLUMN site_access_token VARCHAR(64) NULL COMMENT '现场人员免登录访问令牌' AFTER remark,
  ADD COLUMN site_feedback_remark VARCHAR(500) NULL COMMENT '现场回执备注' AFTER site_access_token,
  ADD COLUMN site_feedback_images TEXT NULL COMMENT '现场回执附件URLs(JSON数组)' AFTER site_feedback_remark,
  ADD COLUMN site_feedback_time DATETIME NULL COMMENT '现场回执保存时间' AFTER site_feedback_images;

CREATE UNIQUE INDEX uk_db_inbound_outbound_site_token
  ON db_inbound_outbound(site_access_token);
字段 说明
remark 保留:系统/专员备注(如「运输计划 XXX」),公共页只读展示
site_feedback_remark 现场人员填写,与 remark 分离
site_feedback_images JSON 字符串数组,与 pl_transport.feedback_images 同结构
site_access_token secrets.token_urlsafe(32)[:64];所有 db_inbound_outbound INSERT 路径调用 _ensure_site_access_token

生成 token 的代码路径(均需覆盖)

路径 模块
运输安排地磅 bridge pl_dibang_bridge_service._create_db_inventory
地磅手工新建 backend/app/api/db/inventory.py → create / 批量创建
地磅计划生成出入库 backend/app/api/db/plans.py 或等价 create 逻辑

抽取公共函数:backend/app/services/db_inventory_site_token.py(或 app/utils/db_site_token.py)→ _ensure_site_access_token(row).

同步:docs/sql/ 增量脚本 + shanxia2.0.sql 快照。

3.2 ORM

backend/app/models/db_models.py → DbInboundOutbound 增加上述四列。


四、后端 API

4.1 现场公共 API(免登录)

新建 backend/app/api/db/public_inventory.py(或 app/api/public_db_inventory.py),前缀 /api/public/db/inventory:

方法 路径 说明
GET /{token} 单据摘要 + 已保存现场回执
POST /{token}/feedback 保存 siteFeedbackRemark、siteFeedbackImages
POST /{token}/attachments multipart 上传 → MinIO,biz_type=db_inventory_site_feedback

校验:

db.query(DbInboundOutbound).filter(
    DbInboundOutbound.site_access_token == token,
    DbInboundOutbound.del_flag == 0,
).first()
# 不存在 → 404「链接无效或已失效」

须加入 API_ENCRYPT_EXCLUDE_PATHS:

/api/public/db/inventory

4.2 响应 Schema(GET 示例)

{
  "batchNo": "CRKDH0000005",
  "businessType": "出库",
  "date": "2026-06-20",
  "partnerCompany": "安徽哇哈哈有限责任公司",
  "product": "试剂氨水",
  "specification": "AR",
  "quantity": 1,
  "vehicleNo": "皖P13123",
  "planNo": "SX-20260620203540001",
  "status": "待出库",
  "remark": "运输计划 SXDDH20260620000003",
  "siteFeedbackRemark": "现场确认无误",
  "siteFeedbackImages": ["db/inventory_site/202606/..."],
  "siteFeedbackTime": "2026-06-23 10:00:00",
  "readOnly": false,
  "voided": false
}

readOnly: status == "已完成"(S8);voided: 关联 pl_plan.follow_status=cancelled(S12,仍可 GET 展示作废空态)。

4.3 扩展 arrange / bridge 响应

TransportInventoryLinkRead 增加:

site_access_token: Optional[str] = Field(alias="siteAccessToken")
site_url: Optional[str] = Field(alias="siteUrl")

URL 构建(与司机链接一致):

f"{PUBLIC_WEB_ORIGIN}/#/public/db/inventory/{site_access_token}"

在 pl_dibang_bridge_service._create_db_inventory 及 inventory.py 等所有创建路径内:创建后 _ensure_site_access_token(inventory).

GET /transport/plan/{id}/arrange-info 的 inventoryRecords 同样返回 siteUrl(便于「调整运输」后复制)。

4.4 管理端只读扩展

  • InboundOutboundRead(backend/app/schemas/db/inventory.py)增加 siteFeedbackRemark、siteFeedbackImages、siteFeedbackTime;列表/详情不返回 siteAccessToken(防泄露)
  • 新增管理端接口(可选 P1.5):GET /db/inventory/{id}/site-url — 仅登录用户可复制链接(用于事后补发)

五、前端(管理端)

5.1 安排运输成功态(PlPlanArrangeDialog)

在现有「复制司机链接 / 复制磅单二维码」旁(S11):

场景 UI
inventoryRecords.length === 1 「复制现场链接」
inventoryRecords.length > 1 「复制全部现场链接」(产品名:url 换行拼接)+ 每条记录旁「复制本条」

5.2 计划查看 · 地磅 Tab(PlPlanViewTabs)

在出入库卡片增加:

  • 现场备注、附件缩略图/下载
  • 上次保存时间
  • 按钮「复制现场链接」(调 arrange-info 或 inventory site-url API)

5.3 地磅出入库列表(P3,S9)

操作列「复制现场链接」:所有未删除出入库单均可用(含手工新建);已完成单仍可复制链接供查看(公共页只读)。


六、安全与约束

项 规则
Token 熵 与 driver_access_token 相同生成方式
暴露面 列表 API 不返回 token;仅 arrange 成功、arrange-info、管理员 site-url 接口
上传 不限制 MIME/扩展名(S13);单文件大小上限与门户统一上传策略一致;最多 9 个附件 URL
租户 上传路径带 tenant_no;OSS biz_type=db_inventory_site_feedback
已完成单 公共 POST / 上传均返回 400「单据已完成,无法修改回执」(S8:不允许补传)

七、流程图

sequenceDiagram
  participant UI as 安排运输弹窗
  participant API as POST arrange
  participant BR as pl_dibang_bridge
  participant DB as db_inbound_outbound
  participant Site as 现场 H5

  UI->>API: arrange + generateDibangPlan
  API->>BR: sync_dibang_for_transport
  loop 每个产品行
    BR->>DB: INSERT + site_access_token
  end
  API-->>UI: inventoryRecords[].siteUrl
  UI->>Site: 专员复制链接 → 微信发送
  Site->>DB: GET/POST public feedback + attachments

八、验收标准

# 场景 预期
V1 出库单安排 + 勾地磅,1 个产品 返回 siteUrl;手机浏览器免登录打开;可上传附件与备注
V2 2 个产品 2 条不同 siteUrl;互不影响
V3 保存后刷新链接 回显备注与附件
V4 调整运输重建地磅单 旧链接 404;新链接可用
V5 出入库单已完成 公共页只读或禁止 POST
V6 计划作废 公共页展示作废空态;POST 拒绝(S12)
V7 管理端列表 不泄露 token;查看 Tab 可见现场回执
V8 未勾地磅 arrange 无 inventoryRecords;手工新建单仍有 siteUrl(S9)
V9 地磅模块手工新建出入库单 创建后即有 token;列表可「复制现场链接」
V10 未完成态删附件后保存 列表不再含已删附件(S10)

九、实施分期

Phase 内容
P1 DDL + ORM + 全路径 token 生成 + 公共 GET/POST + Vant 页
P2 安排成功态复制链接(含复制全部)+ PlPlanViewTabs 回显
P3 出入库列表复制链接 + GET site-url 补发 + 历史 token 补全 + 文档修订 BR-33

十、参考路径

说明 路径
司机公共页(模板) web-admin/src/views/public/pl/transport/index.vue
司机公共 API backend/app/api/pl/public_transport.py
地磅 bridge 创建出入库 backend/app/services/pl_dibang_bridge_service.py
安排弹窗成功态 web-admin/src/components/business/pl-plan-arrange-dialog/index.vue
磅单二维码 web-admin/src/components/business/db/DbPoundSlipQrcodeDialog.vue
原 H5 参考 simple-dibang-xiaoshou/mobile-h5/src/views/inventory/fill.vue