feat(migration): V6 系统设置/二次分配/审批表与角色参数种子

- 新增 system_settings(公司信息单行)/ business_secondary_allocations(二次
  分配明细,比例不强制合计满额,分摊计算待澄清另批)/ approval_orders+
  approval_steps(三级审批状态机,开关关闭自动通过留痕)
- roles 种子新增营销后勤/财务/领导三职能角色;内置保护改 BUILTIN_ROLE_CODES
  白名单(roles 表无 is_builtin 列,不加列)
- PARAM_REGISTRY 首例 BOOL:两个审批开关(16→18 项,种子双写)
- 双轨同步:V6 导出 SQL + init-dev.sql(表/索引/种子/版本号 79bcc5aa98d4)
- 测试同步:EXPECTED_TABLES 四新表、角色种子断言 5 角色、目录断言 18 项
  八分组、BOOL 校验边界;迁移 downgrade 修 1553(只保留 drop_table)并补
  行级种子反向清理

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-15 17:25:23 +08:00
1 parent 94c49138bf
commit acf87fdfdc
14 files changed
+918 -25

No files matched your search

+22 -3
View File
@@ -18,7 +18,7 @@ from app.services.param_registry import (
validate_and_normalize,
)
# 与 db/migration/V3 迁移种子保持一致的 16 项编码
# 与 db/migration 迁移种子保持一致的 18 项编码(V3 十六项 + V6 审批开关两项)
EXPECTED_CODES = {
"salesperson.rate.start",
"salesperson.rate.slope",
@@ -36,14 +36,16 @@ EXPECTED_CODES = {
"salary.basic.mode",
"probation.annual.threshold",
"probation.salary.basic",
"approval.secondary_allocation.enabled",
"approval.monthly_settlement.enabled",
}
class TestRegistry:
"""注册表元数据完整性(与迁移种子同源)。"""
def test_注册表共16项(self) -> None:
assert len(PARAM_REGISTRY) == 16
def test_注册表共18项(self) -> None:
assert len(PARAM_REGISTRY) == 18
def test_编码集合与迁移种子一致(self) -> None:
assert set(PARAM_REGISTRY) == EXPECTED_CODES
@@ -61,10 +63,27 @@ class TestRegistry:
continue
if spec.value_type in {"DECIMAL", "INT"}:
parse_fraction(spec.default_value) # 不可解析会抛 BadRequestError
elif spec.value_type == "BOOL":
assert spec.default_value in {"0", "1"}, f"{spec.code} 默认值非 0/1"
elif spec.value_type == "ENUM":
assert spec.options, f"{spec.code} 为 ENUM 但无选项"
assert spec.default_value in spec.options
def test_BOOL开关_仅接受0与1(self) -> None:
"""V6 首例 BOOL 注册的两个审批开关:合法值与非法值边界。"""
for code in (
"approval.secondary_allocation.enabled",
"approval.monthly_settlement.enabled",
):
spec = PARAM_REGISTRY[code]
assert spec.value_type == "BOOL"
assert validate_and_normalize(spec, "0") == "0"
assert validate_and_normalize(spec, "1") == "1"
with pytest.raises(BadRequestError):
validate_and_normalize(spec, "true")
with pytest.raises(BadRequestError):
validate_and_normalize(spec, "2")
def test_取值域上下限可解析(self) -> None:
for spec in PARAM_REGISTRY.values():
if spec.value_min is not None: