日期:2026-05-27
方案:B — 提交时后端生成
状态:待评审
采集协议单录入/管理、捐赠协议单录入/管理 4 个界面,新增弹窗中协议号当前为下拉选择号池(biz_collection_protocol_no)。需改为按时间序列自动编号,生成后不可修改。
| 项 | 决策 |
|---|---|
| 实现方案 | 方案 B:保存(create)时由后端原子生成,前端新增态不预占号 |
| 采集协议前缀 | XBCC |
| 捐赠协议前缀 | JZCC |
| 日期段 | YYYYMMDD(服务器本地时区,建议 Asia/Shanghai) |
| 序号 | 6 位左补零,当日从 000001 递增 |
| 示例 | 采集:XBCC20260527000001;捐赠:JZCC20260527000003 |
| 序号范围 | 按前缀独立计数(XBCC 与 JZCC 各自维护当日序号,互不影响) |
| 不可修改 | 创建后协议号只读;编辑/更新接口拒绝修改 |
{PREFIX}{YYYYMMDD}{SEQ6}
PREFIX 采集 = XBCC | 捐赠 = JZCC
YYYYMMDD 创建时刻服务器日期
SEQ6 同租户、同前缀、同日期下递增,000001–999999
总长度:4 + 8 + 6 = 18 字符(现有字段足够)。
| 页面 | 弹窗 |
|---|---|
| 采集协议单录入 | web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue |
| 采集协议单管理 | 同上 |
| 捐赠协议单录入 | web-admin/src/views/research-donation/list/DonationFormDialog.vue |
| 捐赠协议单管理 | 同上 |
| 模块 | 改动 |
|---|---|
backend/app/crud/collection_protocol_no.py 或新建 biz_no_generator.py |
原子发号函数 |
backend/app/crud/collection_protocol.py |
create_collection_protocol 内发号;update 禁止改 protocol_no |
backend/app/crud/research_donation.py |
create_research_donation 内发号;update 禁止改 donation_no |
backend/app/api/collection_protocol.py |
CreateBody 中 protocolNo / protocolNoId 改为可选或忽略 |
backend/app/api/research_donation.py |
CreateBody 中 donationNo 改为可选或忽略 |
sequenceDiagram
participant U as 用户
participant FE as 前端弹窗
participant API as 后端 create
participant DB as MySQL
U->>FE: 打开新增弹窗
FE->>U: 协议号展示「保存后自动生成」(只读占位)
U->>FE: 填写表单并提交
FE->>API: POST create(不传 protocolNo / donationNo)
API->>DB: 事务:查当日最大序号 + 1
DB-->>API: 新号 XBCC/JZCC...
API->>DB: INSERT 协议单/捐赠单
API-->>FE: 返回含 protocolNo / donationNo
FE->>U: 成功提示,列表刷新
在事务内查询以下三处 protocol_no / donation_no 的最大后缀,取 MAX + 1:
biz_collection_protocol.protocol_no — 匹配 XBCC{date}%(仅采集发号时)biz_research_donation.donation_no — 匹配 JZCC{date}%(仅捐赠发号时)biz_collection_protocol_no.protocol_no — 同前缀同日期(兼容历史号池数据,避免撞号)过滤条件:tenant_no = 当前租户,del_flag = 0。
SELECT ... FOR UPDATE 锁定序列表行,或biz_biz_no_sequence 表:(tenant_no, prefix, biz_date, last_seq) 唯一键,update 时 last_seq = last_seq + 1推荐序列表(清晰、可扩展):
CREATE TABLE biz_biz_no_sequence (
id VARCHAR(64) NOT NULL PRIMARY KEY,
tenant_no VARCHAR(64) NOT NULL,
prefix VARCHAR(16) NOT NULL COMMENT 'XBCC / JZCC',
biz_date CHAR(8) NOT NULL COMMENT 'YYYYMMDD',
last_seq INT NOT NULL DEFAULT 0 COMMENT '当日已分配最大序号',
-- 公共字段 tenant_no 已在业务字段,其余 public fields 按 database-conventions
...
UNIQUE KEY uk_tenant_prefix_date (tenant_no, prefix, biz_date)
);
发号:INSERT ... ON DUPLICATE KEY UPDATE last_seq = last_seq + 1,再读回 last_seq 拼完整编号。
生成后仍调用现有 assert_biz_no_available(采集与捐赠跨表校验),防止极端竞态。
el-input,disabled,placeholder / 固定文案:「保存后自动生成」el-select 远程检索、protocolNoOptions、remoteSearchProtocolNos、占用/下发校验protocolNo / donationNo 新增时不必填(由后端生成)protocolNo / protocolNoId / donationNo(或传空,后端忽略)el-input 只读展示已有值protocolNo / donationNo 刷新列表(现有逻辑一般已满足)protocolNo 必填,protocolNoId 可选protocolNo、protocolNoId 均可选;若客户端传入非空 protocolNo,忽略并服务端生成(或 400 拒绝客户端传号,推荐忽略以降低前端耦合)protocolNodonationNo 必填donationNo 可选,服务端生成donationNoprotocolNo / donationNo 且与库中不一致 → 400「协议号不可修改」| 场景 | 行为 |
|---|---|
| 协议转科研 / 转存储 | 保留原号,不走新发号(现有 protocol_donation_convert 逻辑不变) |
| 历史数据 | 旧号格式不受影响;新规则仅作用于新 create |
| 号池 Tab(采集协议号管理) | 短期保留,与新自动发号并行;新单不再依赖下拉选号 |
protocol_no_id |
新单可置 NULL;若需审计可发号时同步写入号池并标记已使用 |
| 单日超 999999 条 | 返回业务错误「当日序号已用尽」 |
...000001、...000002后端
docs/sql/biz_biz_no_sequence.sql(序列表 DDL)backend/app/crud/biz_no_generator.py(新建)backend/app/crud/collection_protocol.pybackend/app/crud/research_donation.pybackend/app/api/collection_protocol.pybackend/app/api/research_donation.pybackend/app/models/models.py(序列表 Model,可选)前端
web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vueweb-admin/src/views/research-donation/list/DonationFormDialog.vueweb-admin/src/types/api.d.ts(CreateBody 字段可选化)