Commit a72a2ec1 authored by Data Governance Dev's avatar Data Governance Dev

feat(standards): IND-002/003/004 合并 + IND-005 改名(去 -a)

需求:把 IND-002(USCC 3 子)/ IND-003(手机号 2 子)/ IND-004(行政区划 2 子)
也和 IND-001 一样合并成单 step;IND-005(固定电话,1 子)UI 不再显示 -a 后缀。

改动:
- step7_standards.py: 把 IND-001 专属的合并逻辑通用化为配置驱动
  - _IND_COMBINED: {parent_id: {sub_ids, title, format}} 配置表(覆盖 ind_001/-002/-003/-004)
  - _wrap_combined_ind_001 → _wrap_combined_indicator(parent_id, ...)
  - run_step7_for_ind_001 → run_step7_combined(parent_id, ...)
  - 旧名保留为薄包装(backward compat)
- orchestrator.py:
  - _MERGED_SUBS 扩展到 12 个子指标(4+3+2+2+1)
  - _run_standards_combined(parent_id) 工厂 + _COMBINED_STEP_META 注册表
  - 循环注册 std_ind_001/-002/-003/-004 四个 step
  - _run_standards_ind_005 端到端改 4 处:section_key / step_id / tab.key / tab.title
- analysis_tree.json: 标准组 7 行 → 5 行(去掉 -a/-b/-c 后缀)
- routes.py: _COMBINED_GROUPS 4 组聚合 + IND-005 单独处理(YAML key 仍叫 IND-005-a)
- WORKLOG 追加整段记录 + 踩坑说明(IND-005 改名必须 4 处一致)
parent ba75ed1b
...@@ -2,6 +2,86 @@ ...@@ -2,6 +2,86 @@
> 任务做完一次记一次。最近的在最上面。 > 任务做完一次记一次。最近的在最上面。
## 2026-08-13 · IND-002 / -003 / -004 合并 + IND-005 改名(去 -a)
### 需求
> 用户反馈:「同样的把 IND-002/IND-003/IND-004 也合并,IND-005 不显示 -a」
2026-08-12 只合并了 IND-001(4 子检查),其余 4 个 IND 各自的 a/b/c 子检查仍各跑各的,结果散落多个 tab。IND-005 只有 1 个子检查(IND-005-a 固定电话),但 UI 上显示了 -a 后缀视觉不一致。
### 设计决策(用户已确认)
1. **IND-002(USCC 统一社会信用代码)**:3 子检查 → 合并到 `std_ind_002`
2. **IND-003(手机号)**:2 子检查 → 合并到 `std_ind_003`
3. **IND-004(行政区划代码)**:2 子检查 → 合并到 `std_ind_004`
4. **IND-005(固定电话)**:仅 1 子检查,**改 step_id + 标题去掉 -a 后缀**(`std_ind_005_a` → `std_ind_005`)
### 改动 / 新增的 3 个文件
**[web/core/step_impl/step7_standards.py](web/core/step_impl/step7_standards.py)** — 把 IND-001 专属的合并逻辑**通用化**为配置驱动
```python
_IND_COMBINED: dict[str, dict] = {
"ind_001": {"sub_ids": ("IND-001-a", "-b", "-c", "-d"),
"title": "IND-001 · 身份证号校验",
"format": "GB 11643-1999(合并 a/b/c/d)/ GB 11643-1989(d 子项兼容)"},
"ind_002": {"sub_ids": ("IND-002-a", "-b", "-c"),
"title": "IND-002 · 统一社会信用代码校验",
"format": "GB 32100-2015(含 /-c 老代码兼容转换)"},
"ind_003": {"sub_ids": ("IND-003-a", "-b"),
"title": "IND-003 · 手机号校验",
"format": "工信部(《电信网编号计划》)"},
"ind_004": {"sub_ids": ("IND-004-a", "-b"),
"title": "IND-004 · 行政区划代码校验",
"format": "GB/T 2260"},
}
# 通用化 _wrap_combined_ind_001 → _wrap_combined_indicator(parent_id, ...)
# 通用化 run_step7_for_ind_001 → run_step7_combined(parent_id, ...)
# 旧名保留为薄包装,留作 backward compat
```
**[web/core/orchestrator.py](web/core/orchestrator.py)** — 3 大块:
1. `_MERGED_SUBS` 集合扩展到 12 个子指标(4+3+2+2+1)
2. `_run_standards_combined(parent_id)` 工厂 + `_COMBINED_STEP_META` 注册表,循环注册 4 个 step
3. `_run_standards_ind_005` 单独实现:跑 IND-005-a 单 indicator,**端到端重命名** section_key / step_id / tab.key / tab.title
**[web/configs/analysis_tree.json](web/configs/analysis_tree.json)** — 国标字段规范组 5 行:
```json
{ "step_id": "std_ind_001" },
{ "step_id": "std_ind_002" },
{ "step_id": "std_ind_003" },
{ "step_id": "std_ind_004" },
{ "step_id": "std_ind_005" }
```
**[web/api/routes.py](web/api/routes.py)** — `/api/match-config` 聚合块泛化:
```python
_COMBINED_GROUPS = (
("std_ind_001", ("IND-001-a", "IND-001-b", "IND-001-c", "IND-001-d")),
("std_ind_002", ("IND-002-a", "IND-002-b", "IND-002-c")),
("std_ind_003", ("IND-003-a", "IND-003-b")),
("std_ind_004", ("IND-004-a", "IND-004-b")),
)
# + IND-005 单独处理:YAML key 仍叫 IND-005-a,UI step_id 改为 std_ind_005
```
### 踩坑
- **IND-005 改名必须 4 处一致**:`section_key` / `_protocol.step_id` / `tab.key` / `tab.title`。
- 最初只改了 section_key 和 step_id,结果 tab.key 还是 "standards_ind_005_a"、tab.title 还是 "IND-005-a · …"
- 后来加 2 行 for tab 改 key + title
- **tab.title 不能直接 `parent_id.upper().replace('_', '-')`**:合并模板里有 `s.standard_name` 显示用,生成时会拼成 `IND-001 · 身份证号校验` 之类,所以必须是配置表的 `title` 字段
### 烟测
```
ind_001: section_key=standards_ind_001 step_id=std_ind_001 tab.key=standards_ind_001
ind_002: section_key=standards_ind_002 step_id=std_ind_002 tab.key=standards_ind_002
ind_003: section_key=standards_ind_003 step_id=std_ind_003 tab.key=standards_ind_003
ind_004: section_key=standards_ind_004 step_id=std_ind_004 tab.key=standards_ind_004
ind_005: section_key=standards_ind_005 step_id=std_ind_005 tab.key=standards_ind_005
```
`/api/analysis-tree` 标准组 5 个 checkbox、`/api/match-config` 5 个输入框聚合正确,零孤儿。
---
## 2026-08-12 · IND-001 合并后「结果又不显示」修复(double-wrap bug) ## 2026-08-12 · IND-001 合并后「结果又不显示」修复(double-wrap bug)
### 问题 ### 问题
......
...@@ -264,27 +264,43 @@ async def get_match_config(): ...@@ -264,27 +264,43 @@ async def get_match_config():
"skip": False, "skip": False,
} }
# ── 合并 step:std_ind_001(IND-001 a/b/c/d 聚合)─────────── # ── 合并 step:std_ind_001 / -002 / -003 / -004 聚合子检查 ──
# 4 个子 indicator 的 YAML 配置聚合到 1 个 step 输入框。 # N 个子 indicator 的 YAML 配置聚合到 1 个 step 输入框。
# 用户编辑的 override 在后端 run_step7_for_ind_001 里会下推到各子检查。 # 用户编辑的 override 在后端 run_step7_combined 里会下推到各子检查。
ind001_sub_ids = ("IND-001-a", "IND-001-b", "IND-001-c", "IND-001-d") _COMBINED_GROUPS = (
ind001_names: list[str] = [] ("std_ind_001", ("IND-001-a", "IND-001-b", "IND-001-c", "IND-001-d")),
ind001_comments: list[str] = [] ("std_ind_002", ("IND-002-a", "IND-002-b", "IND-002-c")),
ind001_seen_n: set = set() ("std_ind_003", ("IND-003-a", "IND-003-b")),
ind001_seen_c: set = set() ("std_ind_004", ("IND-004-a", "IND-004-b")),
for sub_id in ind001_sub_ids: )
for combined_step_id, sub_ids in _COMBINED_GROUPS:
agg_names: list[str] = []
agg_comments: list[str] = []
seen_n: set = set()
seen_c: set = set()
for sub_id in sub_ids:
rec = step7.get(sub_id) or {} rec = step7.get(sub_id) or {}
for n in (rec.get("applies_to_fields") or []): for n in (rec.get("applies_to_fields") or []):
if n not in ind001_seen_n: if n not in seen_n:
ind001_names.append(n) agg_names.append(n)
ind001_seen_n.add(n) seen_n.add(n)
for c in (rec.get("comment_keywords") or []): for c in (rec.get("comment_keywords") or []):
if c not in ind001_seen_c: if c not in seen_c:
ind001_comments.append(c) agg_comments.append(c)
ind001_seen_c.add(c) seen_c.add(c)
by_step["std_ind_001"] = { by_step[combined_step_id] = {
"names": ", ".join(ind001_names), "names": ", ".join(agg_names),
"comments": ", ".join(ind001_comments), "comments": ", ".join(agg_comments),
"skip": False,
}
# ── IND-005 不再带 -a 后缀:YAML key 仍叫 IND-005-a,UI step_id 改为 std_ind_005 ──
ind005_rec = step7.get("IND-005-a") or {}
ind005_atf = ind005_rec.get("applies_to_fields") or []
ind005_cmk = ind005_rec.get("comment_keywords") or []
by_step["std_ind_005"] = {
"names": ", ".join(ind005_atf),
"comments": ", ".join(ind005_cmk),
"skip": False, "skip": False,
} }
......
...@@ -15,18 +15,14 @@ ...@@ -15,18 +15,14 @@
{ {
"key": "standards", "key": "standards",
"title": "国标字段规范", "title": "国标字段规范",
"description": "IND-001 ~ IND-005 系列:按 GB / GA 标准做字段值级别合规校验(格式 / 校验位 / 出生日期 / 15 位兼容 / 号段 / 编码存在性 / 固定电话 / 老代码兼容)", "description": "IND-001 ~ IND-005 系列:按 GB / GA 标准做字段值级别合规校验。IND-001(身份证)/ IND-002(USCC)/ IND-003(手机号)/ IND-004(行政区划)多子检查已合并为单 step;IND-005(固定电话)单 sub。包含格式 / 校验位 / 出生日期 / 15 位兼容 / 号段 / 编码存在性 / 老代码兼容",
"default_expand": true, "default_expand": true,
"children": [ "children": [
{ "step_id": "std_ind_001" }, { "step_id": "std_ind_001" },
{ "step_id": "std_ind_002_a" }, { "step_id": "std_ind_002" },
{ "step_id": "std_ind_002_b" }, { "step_id": "std_ind_003" },
{ "step_id": "std_ind_002_c" }, { "step_id": "std_ind_004" },
{ "step_id": "std_ind_003_a" }, { "step_id": "std_ind_005" }
{ "step_id": "std_ind_003_b" },
{ "step_id": "std_ind_004_a" },
{ "step_id": "std_ind_004_b" },
{ "step_id": "std_ind_005_a" }
] ]
}, },
{ {
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment