日期: 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,入库/出库)生成 工厂现场人员免登录链接。现场人员打开链接后可查看单据摘要,上传附件并填写备注;计划专员在安排运输成功后可复制链接发给现场(微信等)。
| # | 决策 | 说明 |
|---|---|---|
| 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/扩展名;仅保留单文件大小上限(与门户统一上传策略一致) |
db_weighing_slip)plan_type=direct)— 本就不生成出入库单| 角色 | 链接 | 关联实体 | 主要动作 |
|---|---|---|---|
| 司机 | /public/pl/transport/{token} |
pl_transport |
运输回执、附件、备注 |
| 工厂现场人员 | /public/db/inventory/{token} |
db_inbound_outbound |
出入库附件、现场备注 |
路径 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 / 出入库列表查看现场回执
沿用 2026-06-21 的 revoke 规则:
pl_transport_db_link → 先 revoke_regeneratable_dibang_for_transportdel_flag=1 → 其 site_access_token 仍保留在库中但公共 GET 因 del_flag 返回 404| 条件 | 公共页行为 |
|---|---|
del_flag=1 |
404 链接无效 |
status 为 已完成 |
可查看摘要与历史回执;禁止上传附件与修改备注(不可补传,S8) |
| 关联运输计划已作废 | 链接仍可 GET;页面展示「运输计划已作废」空态(S12,对齐司机页);禁止 POST / 上传 |
在 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 }
}
| 项 | 选择 |
|---|---|
| 组件库 | Vant 4(与司机页相同 chunk 策略) |
| 布局 | PublicMobileShell |
| 样式 | Fresh Mint --primary #10b981 |
| HTTP | 新建 utils/http/public-db.ts(或扩展 public-pl.ts 为通用 public-api.ts),无 JWT、无 API 加密 |
| 区域 | 内容 |
|---|---|
| 单据摘要(只读) | 出入库单号(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(仅借鉴卡片布局,不引入其「完善数量/车牌后锁定」逻辑 — 本期字段已由专员在安排运输时写入)。
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 快照。
backend/app/models/db_models.py → DbInboundOutbound 增加上述四列。
新建 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
{
"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 展示作废空态)。
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(便于「调整运输」后复制)。
InboundOutboundRead(backend/app/schemas/db/inventory.py)增加 siteFeedbackRemark、siteFeedbackImages、siteFeedbackTime;列表/详情不返回 siteAccessToken(防泄露)GET /db/inventory/{id}/site-url — 仅登录用户可复制链接(用于事后补发)PlPlanArrangeDialog)在现有「复制司机链接 / 复制磅单二维码」旁(S11):
| 场景 | UI |
|---|---|
inventoryRecords.length === 1 |
「复制现场链接」 |
inventoryRecords.length > 1 |
「复制全部现场链接」(产品名:url 换行拼接)+ 每条记录旁「复制本条」 |
PlPlanViewTabs)在出入库卡片增加:
操作列「复制现场链接」:所有未删除出入库单均可用(含手工新建);已完成单仍可复制链接供查看(公共页只读)。
| 项 | 规则 |
|---|---|
| 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 |