fix(cma): KR 名称污染存量清理 + 透传点护栏 (cma-kr-name-pollution-cleanup-20260916)
ci/woodpecker/push/main Pipeline was canceled
ci/woodpecker/push/main Pipeline was canceled
- kr_name.py/krName.ts: sanitize_kr_name 判据(尾部≥/≤数字≠目标值→截断;==目标值→保留) - maps.py/okr.py/okr_templates.py: 模板名/JSON→krs.title 透传点护栏 - test: 28 passed(独立复核) - 原会话因迭代上限停手,本提交代收口(工作已复核完成)
This commit is contained in:
@@ -251,6 +251,7 @@ import { ref, computed, watch, onMounted } from "vue"
|
||||
import { useRouter } from "vue-router"
|
||||
import { ElMessage } from "element-plus"
|
||||
import { okrTemplateApi } from "../../api/index"
|
||||
import { sanitizeKrName } from "../../utils/krName"
|
||||
|
||||
// ── Props ──
|
||||
const props = defineProps<{
|
||||
@@ -560,7 +561,8 @@ function nextStep() {
|
||||
krs: tmpl.preset_krs.map((kr: any) => {
|
||||
const parsed = parseKrTarget(kr.target_value)
|
||||
return {
|
||||
name: kr.name,
|
||||
// 护栏(2026-09-16):模板名 → KR 名 预填透传点;历史污染名(尾部被截断的方向值)在此规整
|
||||
name: sanitizeKrName(kr.name, kr.target_value).name,
|
||||
operator: parsed.operator,
|
||||
target_value: parsed.value,
|
||||
weight: Number(kr.weight) || 33,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* KR 名称规整(历史名称污染防护)— 与后端 backend/app/utils/kr_name.py 同规则。
|
||||
*
|
||||
* 背景:2026-09-11 前的逐键写入点会把「方向符号 + 已输入的首位数字」拼进 KR 名称
|
||||
* (输入 780 → 名称变 `营业收入≥7`)。写入口已删,但存量模板名仍会经「模板 → KR」预填传染。
|
||||
*
|
||||
* 判据(勿一刀切):
|
||||
* - 名称尾部「≥/≤ + 数字」且该数字 ≠ 目标值 → 截断值(污染)→ 去掉尾部片段;
|
||||
* - 数字 == 目标值 → 有意命名(`渠道留存率≥90` / `新客户数≥10`)→ 保留;
|
||||
* - 目标值取不到数字(`为正`/`TOP3`/空)→ 无法判定 → 保留。
|
||||
*/
|
||||
const NUM = /[-+]?\d*\.?\d+/;
|
||||
const TAIL = /^(.*?)\s*[≥≤]\s*(\d+(?:\.\d+)?)\s*$/;
|
||||
|
||||
export function targetNumber(targetValue: any): number | null {
|
||||
if (targetValue === null || targetValue === undefined || targetValue === "") return null;
|
||||
if (typeof targetValue === "number") return Number.isFinite(targetValue) ? targetValue : null;
|
||||
const m = String(targetValue).match(NUM);
|
||||
return m ? parseFloat(m[0]) : null;
|
||||
}
|
||||
|
||||
export function sanitizeKrName(name: any, targetValue: any): { name: string; changed: boolean } {
|
||||
const raw = name === null || name === undefined ? "" : String(name);
|
||||
const m = raw.match(TAIL);
|
||||
if (!m) return { name: raw, changed: false };
|
||||
const tn = targetNumber(targetValue);
|
||||
if (tn === null) return { name: raw, changed: false };
|
||||
if (Math.abs(parseFloat(m[2]) - tn) < 1e-9) return { name: raw, changed: false };
|
||||
const base = m[1].trim();
|
||||
if (!base) return { name: raw, changed: false };
|
||||
return { name: base, changed: true };
|
||||
}
|
||||
@@ -183,6 +183,7 @@ import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
import api from "../api/index"
|
||||
import { LAYER_CONFIG, LAYER_KEYS, getLayerList } from "../config/layers"
|
||||
import { useFormGuard } from "../composables/useFormGuard"
|
||||
import { sanitizeKrName } from "../utils/krName"
|
||||
import StrategyLayer from "../components/strategy-map/StrategyLayer.vue"
|
||||
import ConnectionLines from "../components/strategy/ConnectionLines.vue"
|
||||
import MapCanvasToolbar from "../components/map-canvas/MapCanvasToolbar.vue"
|
||||
@@ -909,7 +910,9 @@ async function saveUserTemplate(data: any, layer: string) {
|
||||
description: (data.description || '').trim(),
|
||||
dimension: layer,
|
||||
preset_krs: (data.krs || []).map((kr: any) => ({
|
||||
name: kr.name,
|
||||
// 护栏(2026-09-16):画布 KR 名 → 模板库 透传点,历史污染尾巴(截断值)在此规整,
|
||||
// 防污染经用户自定义模板再次传染新目标
|
||||
name: sanitizeKrName(kr.name, kr.target_value).name,
|
||||
target_value: kr.target_value,
|
||||
weight: kr.weight || 33,
|
||||
})),
|
||||
|
||||
Reference in New Issue
Block a user