fix(approval): 版本号唯一键兜底 + 取号改当前读防并发重复

- V12 迁移:先重排存量重复 (order_type, ref_id) 分组的 version_no
  (V11 把存量一律置 1,而 V11 前重提是新建行),再加
  uk_approval_orders_order_type_ref_version;只重排真有重复的分组,
  避免改动前端已展示过的历史版本号
- _next_version 改锁读(select version_no ... FOR UPDATE,最大值在
  Python 侧算):请求级鉴权的普通 SELECT 已把 REPEATABLE READ 快照
  锚定在请求开始时刻,原 MAX 快照读在并发下会算出同一版本号;
  聚合 + FOR UPDATE 为 MySQL 明确禁止,故不锁聚合结果
- 编辑与月结提交路径把 1062 收敛为 400「提交冲突(版本号生成重复),
  请重试」,其余完整性错误原样上抛;submit_order 内不 catch/rollback
- 驳回清空方案的过时注释改为 V11 起的 append-only 语义(置历史不删行)
- init-dev.sql 同步唯一键与 alembic_version 7f4f7b918bc4,导出 V12 SQL
- 测试:集成 +5(锁读 SQL 形态、两会话快照回归、唯一键 1062、冲突 400、
  正常递增)+ 单元 +1;工作台假审批单改用不同 ref_id 适配唯一键
- 全量回归 897 passed;ruff 与前端 type-check 绿

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-22 13:31:01 +08:00
1 parent 694d21d1fd
commit 9964281c14
12 files changed
+398 -42

No files matched your search

+8 -2
View File
@@ -250,7 +250,13 @@ def leader(make_user, auth_headers) -> dict:
def _insert_approval(status: int) -> None:
"""直插在途审批单(避免依赖两次流转,专注工作台过滤口径)。"""
"""直插在途审批单(避免依赖两次流转,专注工作台过滤口径)。
``ref_id`` 仅作对象分组锚点、本用例不校验:**每张单必须给不同的
ref_id**——V12 的 ``uk_approval_orders_order_type_ref_version`` 要求
(order_type, ref_id, version_no) 唯一,同一假对象连插两张 version_no=1
会撞 1062。
"""
from app.core.db import get_session_factory
from app.models import ApprovalOrder
@@ -260,7 +266,7 @@ def _insert_approval(status: int) -> None:
ApprovalOrder(
order_no=f"AP{uuid.uuid4().hex[:12]}",
order_type=2,
ref_id=0,
ref_id=status,
refer_no="t_工作台",
status=status,
submitter_name="t_admin",