From 07c9d091a1ab1f145579c69fa0e223028c4255f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Wed, 9 Sep 2026 17:56:55 +0800 Subject: [PATCH] =?UTF-8?q?fix(management):=20validate=5Fconfig=E8=A1=A5gr?= =?UTF-8?q?ade=5Fbounds=E5=BD=A2=E7=8A=B6=E4=B8=8E=E8=B7=A8=E7=BB=B4?= =?UTF-8?q?=E5=BA=A6code=E5=94=AF=E4=B8=80=E6=80=A7=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/management/metric_definitions.py | 21 ++++++++++++++- yunke-server/tests/test_management_scoring.py | 26 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/yunke-server/app/management/metric_definitions.py b/yunke-server/app/management/metric_definitions.py index f2934b1..8fe9891 100644 --- a/yunke-server/app/management/metric_definitions.py +++ b/yunke-server/app/management/metric_definitions.py @@ -96,23 +96,42 @@ DEFAULT_SCORE_CONFIG: dict = { def validate_config(config: dict) -> list[str]: - """配置校验:权重总和/结构/引用的 metric_code 有元数据/阈值形状。 + """配置校验:权重总和/结构/引用的 metric_code 有元数据/阈值形状/ + grade_bounds 形状/指标 code 跨维度唯一。 阈值形状(随评分引擎落地的 B0 评审预埋要求): - type 须为 target/reverse/band 三者之一 - target : floor < target - reverse: target_ms < floor_ms(100 分线在 0 分线之前) - band : low < high + B4 预埋(配置将自 DB 加载,写入侧不再重跑种子校验): + - grade_bounds 须含 A/B/C/D 且严格降序 + - 指标 code 跨维度不得重复:metrics_detail 为扁平 dict, + 同名 code 会相互覆盖(后写胜出) """ problems: list[str] = [] dims = config.get("dimensions", {}) total = sum(d.get("weight", 0) for d in dims.values()) if total != 100: problems.append(f"维度权重总和须=100,当前 {total}") + gb = config.get("grade_bounds") + if not isinstance(gb, dict) or not all( + isinstance(gb.get(k), int) for k in ("A", "B", "C", "D") + ): + problems.append("grade_bounds 须含 A/B/C/D 且严格降序") + elif not (gb["A"] > gb["B"] > gb["C"] > gb["D"]): + problems.append( + f"grade_bounds 须严格降序 A>B>C>D" + f"(当前 A={gb['A']} B={gb['B']} C={gb['C']} D={gb['D']})" + ) + seen_codes: dict[str, str] = {} for key, dim in dims.items(): if dim.get("status") == "pending_data": continue for code, spec in dim.get("metrics", {}).items(): + if code in seen_codes: + problems.append(f"指标 code 重复定义于多维度: {code}") + seen_codes[code] = key src = spec.get("metric", code) if src not in METRIC_DEFINITIONS: problems.append(f"{key}.{code} 引用未定义指标 {src}") diff --git a/yunke-server/tests/test_management_scoring.py b/yunke-server/tests/test_management_scoring.py index f0aafa1..7170ab0 100644 --- a/yunke-server/tests/test_management_scoring.py +++ b/yunke-server/tests/test_management_scoring.py @@ -277,3 +277,29 @@ def test_validate_config_threshold_shapes(): .update(low=300)) # low>high 200 assert any("call_band" in p and "low