7.9 KiB
7.9 KiB
样本接收 — 实验室入库编码
日期:2026-06-03
状态:已实现
关联:web-admin/src/views/quality-inspection/sample-reception/index.vue、backend/app/crud/sample_reception.py、backend/app/crud/biz_no_generator.py、docs/superpowers/specs/2026-05-19-unified-sample-reception-design.md
1. 已确认业务规则
| 项 | 规则 |
|---|---|
| 字段名称 | 实验室入库编码 |
| 编码格式 | RK + YYYYMMDD + 3 位序号(左补零),例:RK20260603001 |
| 日期段 | YYYYMMDD = 样本接收时间的日期部分(非服务器当天、非打开弹窗当天) |
| 序号规则 | 同一租户、同一接收日期下递增;默认自动生成;用户可在提交前手动修改 |
| 并发 | 自动分配须原子占号,避免多人同时拿到相同默认序号 |
| 锁定 | 保存成功后编码只读,编辑已接收记录时不可修改 |
| 结论条件 | 仅接收(js) 生成并保存入库编码;拒收(bj) 不生成、不保存、不展示该字段 |
| 适用范围 | 单条接收、批量接收、查看详情、已接收列表(仅 js 有值) |
2. 方案(已选 A)
| 方案 | 说明 | 结论 |
|---|---|---|
| A. 预占 + 提交校验 | 打开弹窗/改接收日期时调 allocate;提交校验格式、日期一致性与唯一性 | 采用 |
| B. 仅提交时分配 | 预览码不锁号 | 不满足并发要求 |
| C. 前端生成 | 无服务端原子序号 | 不采用 |
3. 数据模型
3.1 表变更
biz_sample_reception 新增:
ALTER TABLE biz_sample_reception
ADD COLUMN lab_storage_code VARCHAR(32) NULL
COMMENT '实验室入库编码 RK+接收日期YYYYMMDD+3位序号'
AFTER sample_code;
索引(软删除场景):
CREATE UNIQUE INDEX uk_sr_tenant_lab_code
ON biz_sample_reception (tenant_no, lab_storage_code, del_flag);
说明:
del_flag=0时租户内唯一;历史软删记录保留原编码,不参与业务冲突。
3.2 序号表复用
复用现有 biz_biz_no_sequence,prefix = 'RK',biz_date = YYYYMMDD(来自接收时间日期)。
- 序号宽度:3 位,上限 999/日/租户
- 与
allocate_next_biz_no()相同:INSERT ... ON DUPLICATE KEY UPDATE last_seq = LAST_INSERT_ID(last_seq + 1)
3.3 手动编码与序号同步
用户手动输入 RK20260603088 且提交成功时,在同一事务内:
last_seq = MAX(last_seq, 88)
避免后续自动分配仍从较小序号开始导致撞号。
分配前亦取:
next_seq = MAX(last_seq, MAX(已存在编码末三位)) + 1
(限定同一 tenant_no + prefix + biz_date)
4. 后端 API
4.1 预分配编码
POST /web/admin/sample-receptions/allocate-lab-storage-codes
Body
| 字段 | 类型 | 说明 |
|---|---|---|
receptionDate |
string | 必填,YYYY-MM-DD,由前端从「样本接收时间」截取 |
count |
int | 默认 1,批量接收为 N,上限 50 |
Response
{ "codes": ["RK20260603001", "RK20260603002"] }
逻辑
- 校验
receptionDate格式 - 对
biz_date = receptionDate.replace('-','')原子递增count次 - 返回连续编码列表
权限:与样本接收提交一致(require_admin_or_super_admin 或现有接收写权限)。
4.2 创建 / 更新接收
SampleReceptionCreateBody / SampleReceptionUpdateBody 增加:
labStorageCode(仅acceptConclusion=js时必填;bj时忽略且存NULL;更新不可改)
创建校验(acceptConclusion=js)
labStorageCode非空- 正则:
^RK\d{8}\d{3}$ - 编码中
YYYYMMDD必须等于receptionTime的日期部分 - 租户内
del_flag=0唯一 - 成功后同步
biz_biz_no_sequence.last_seq(见 §3.3)
创建校验(acceptConclusion=bj)
- 不得传入有效
labStorageCode(传入非空则 400,或一律忽略并置NULL) - 不调用 allocate、不占用 RK 序号
更新
- 忽略客户端传入的
labStorageCode变更(或 API 不接受该字段),保持原值
4.3 列表与详情
_reception_to_dict / 列表项增加 labStorageCode;已接收列表关键词搜索纳入该字段。
5. 前端交互
5.1 单条接收弹窗
- 默认「是否接收」= 接收(
js);此时:- 打开弹窗 → 默认接收时间 = 当前时间 → 调
allocate(receptionDate, count=1)→ 填入「实验室入库编码」
- 打开弹窗 → 默认接收时间 = 当前时间 → 调
- 用户切换为 拒收(
bj):- 隐藏「实验室入库编码」表单项
- 清空本地编码值;不再占用/提交编码
- 用户从
bj切回js:按当前接收时间日期重新allocate(1)(除非编辑已保存的js记录) - 用户修改样本接收时间且日期变化(且当前为
js):- 若编码未被手动编辑 → 自动按新日期重新
allocate(1)并替换 - 若已手动编辑 → 不自动改码;提交时若日期与编码不一致,提示错误
- 若编码未被手动编辑 → 自动按新日期重新
- 编码输入框:新建
js时可编辑;编辑已保存js记录时disabled;bj记录不展示 - 字段位置:「接收登记」区,与接收温度/体积同级(
form-grid-3) - label 提示:
<span class="form-label-tip">RK+接收日期+序号,保存后不可修改</span>
5.2 批量接收
- 打开弹窗 → 每条 entry 默认
receptionTime = now、acceptConclusion = js - 仅
acceptConclusion=js的 entry 参与 allocate:按接收日期分组,allocate(count=组内 js 条数) - 每条
jsentry 增加labStorageCode;改接收时间日期时,同 §5.1 规则 - entry 切为
bj→ 隐藏该条编码字段并清空;切回js→ 为该条单独 re-allocate - 提交:
js携带labStorageCode;bj不传或传空
5.3 查看详情 / 已接收列表
- 详情:
js展示「实验室入库编码」;bj不展示该字段(或整块接收信息不含此项) - 已接收 Tab:
js显示编码;bj显示- - 筛选关键词 placeholder 增加「入库编码」(仅匹配有编码的
js记录)
6. 并发与异常
| 场景 | 处理 |
|---|---|
| 两人同时打开弹窗 | allocate 返回不同序号 |
| 用户改码为已存在编码 | 提交 400:「实验室入库编码已存在」 |
| 当日序号 > 999 | allocate / 提交返回明确错误 |
| 批量部分失败 | 已成功记录保留编码;失败项可重试(新 allocate) |
| 取消弹窗未提交 | 已占序号允许空号,不回滚 sequence |
js 改 bj 再提交 |
已 allocate 的序号允许空号;落库 lab_storage_code=NULL |
7. 影响文件(实现清单)
| 层级 | 文件 |
|---|---|
| SQL | docs/sql/sample_reception_add_lab_storage_code.sql、docs/sql/zkxw.sql |
| 后端 | models.py、crud/biz_no_generator.py(或 sample_reception_lab_code.py)、crud/sample_reception.py、api/sample_reception.py |
| 前端 | api/sample-reception.ts、views/quality-inspection/sample-reception/index.vue |
| 关联只读 | 协议查看弹窗中样本接收区块(若展示接收信息) |
| 校验 | scripts/check_field_consistency.py |
8. 非目标
- 事后修改已保存的入库编码
- 跨租户共享序号
- 拒收(
bj)生成或保存入库编码
9. 测试要点
- 同日连续接收 3 条 →
001/002/003 - 接收时间日期为昨天 → 编码日期段为昨天,序号独立计数
- 两人并发打开单条接收 → 默认编码不同
- 手动改为已存在编码 → 提交失败
- 手动改为
...088后,下一条自动分配 > 088 - 保存后编辑弹窗 → 编码 disabled
- 批量 5 条同日期且均为
js→ 连续 5 码;混合两个日期 → 各日期独立序号 - 单条先选
js占号后改bj提交 → 无入库编码;库中lab_storage_code为 NULL - 批量中 3 条
js+ 2 条bj→ 仅 3 条 allocate 并保存编码