场景
----
扫码出库/借库的作业可能很长(一张单几十项),工人常需中途暂停去处理
更紧急的单据。改造前切换单据会清空已扫内容,刷新/退出页面则全部丢失。
由于库存在申请审批通过时已**预占**,暂停期间货不会被他人抢走 ——
因此草稿只记录「扫到哪了」,**不涉及任何库存操作**。即使草稿丢失也只是
需要重扫,不会造成库存错乱。
隔离粒度
--------
按 (user_id, biz_type, request_id) 一人一单:每个人扫自己的草稿,互不影响;
同一人可同时持有多张单据的草稿(正是「暂停 A 去出 B」的场景)。
user_id 一律取自 JWT,不接受入参覆盖,故不可能读写他人草稿。
为什么整单存一个 JSON(而非每条明细一行)
------------------------------------------
1. 保存是「全量覆盖」语义,逐行存无增量更新的收益;
2. 恢复时需要物料名称/规格/库位等展示字段,逐行方案只能回查申请单的
items_json —— 而历史单据的 items_json 不含 stock_id,回查会错配。
整单快照把展示字段一并存下,恢复零依赖,对老单据同样可靠。
接口
----
GET /api/v1/scan-draft 读取草稿
POST /api/v1/scan-draft 保存(全量覆盖;空清单则删除)
DELETE /api/v1/scan-draft 清除(提交成功后调用)
GET /api/v1/scan-draft/overview 各单据进度,供下拉徽标
实现要点
--------
· items_json 列是 jsonb,模型必须用 db.JSON —— 用 db.Text 会让 psycopg2
拿到 Python list/dict 时无法适配,报 "can't adapt type 'dict'",而异常
被接口的 except 吞掉后 POST 仍返回"成功",问题极难发现(开发中实际踩到);
· 概览接口做防御:残留的空草稿不参与展示。
实测:保存/读回、跨单据隔离(B 读 A 的草稿为 0 项)、全量覆盖、
提交后清除,全部符合预期。
43 lines
2.2 KiB
PL/PgSQL
43 lines
2.2 KiB
PL/PgSQL
-- =============================================================================
|
||
-- 扫码草稿表(scan_draft)
|
||
--
|
||
-- 背景
|
||
-- 扫码出库/借库的作业过程可能很长(一张单几十项),工人常需中途暂停去处理
|
||
-- 更紧急的单据。改造前切换单据会直接清空已扫内容,刷新/退出页面更是全部丢失。
|
||
--
|
||
-- 库存已在申请审批通过时**预占**,暂停期间不会被他人抢走 —— 因此草稿只需
|
||
-- 记录「扫到哪了」,不涉及任何库存操作,纯状态保存。
|
||
--
|
||
-- 隔离粒度:(user_id, biz_type, request_id)
|
||
-- 一人一单 —— 每个人扫自己的草稿,互不影响;同一人可同时持有多张单据的草稿。
|
||
--
|
||
-- 为什么整单存一个 JSON(而非每条明细一行)
|
||
-- 1. 保存是「全量覆盖」语义,逐行存并无增量更新的收益;
|
||
-- 2. 恢复时需要物料名称/规格/库位等展示字段,逐行存方案只能回查申请单
|
||
-- 的 items_json —— 而历史单据的 items_json 不含 stock_id,回查会错配。
|
||
-- 整单快照把展示字段一并存下,恢复时零依赖、对老单据同样可靠。
|
||
--
|
||
-- 执行: docker exec -i inventory_db psql -U test -d inventory_system < 本文件
|
||
-- =============================================================================
|
||
BEGIN;
|
||
|
||
DROP TABLE IF EXISTS scan_draft;
|
||
|
||
CREATE TABLE scan_draft (
|
||
id SERIAL PRIMARY KEY,
|
||
user_id integer NOT NULL,
|
||
biz_type varchar(20) NOT NULL, -- outbound / borrow
|
||
request_id integer NOT NULL, -- 关联的申请单ID
|
||
request_no varchar(100), -- 单号快照,便于展示免联表
|
||
-- 整单快照:[{stock_id, source_table, sku, name, spec_model,
|
||
-- warehouse_location, available_quantity, out_quantity, price}, ...]
|
||
items_json jsonb NOT NULL DEFAULT '[]'::jsonb,
|
||
updated_at timestamp without time zone NOT NULL DEFAULT timezone('Asia/Shanghai', now()),
|
||
created_at timestamp without time zone NOT NULL DEFAULT timezone('Asia/Shanghai', now()),
|
||
CONSTRAINT uq_scan_draft UNIQUE (user_id, biz_type, request_id)
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS ix_scan_draft_lookup ON scan_draft(user_id, biz_type);
|
||
|
||
COMMIT;
|