diff --git a/yunke-server/app/management/metric_definitions.py b/yunke-server/app/management/metric_definitions.py index f0bb4a6..f13a55f 100644 --- a/yunke-server/app/management/metric_definitions.py +++ b/yunke-server/app/management/metric_definitions.py @@ -7,6 +7,10 @@ DEFAULT_SCORE_CONFIG 为 v1.0 试算起点——所有阈值标「试算」,调 """ from __future__ import annotations +import copy + +import sqlalchemy as sa + # direction: +1 越大越好 / -1 越小越好;caliber 注明与看板差异 METRIC_DEFINITIONS: dict[str, dict] = { "msg_sent": {"name": "发出消息(1:1)", "unit": "条", "direction": 1, @@ -103,3 +107,37 @@ def validate_config(config: dict) -> list[str]: if src not in METRIC_DEFINITIONS: problems.append(f"{key}.{code} 引用未定义指标 {src}") return problems + + +async def _seed_score_config(session) -> str | None: + """种子默认评分配置;已存在同版本返回 None(幂等)。纯会话函数可测。 + + 同 ensure_starter_words 先例:域包提供纯会话种子函数,CLI 只做接线。 + config_json 用 deepcopy 冻结快照——JSON 列须整列重赋值,深拷贝防止 + 长驻 API 进程未来的原地修改波及模块常量。 + """ + from app.db.models import EmployeeScoreConfig + + problems = validate_config(DEFAULT_SCORE_CONFIG) + if problems: + raise ValueError(f"score config invalid: {problems}") + exists = ( + await session.execute( + sa.select(EmployeeScoreConfig.id).where( + EmployeeScoreConfig.version == DEFAULT_SCORE_CONFIG["version"] + ) + ) + ).scalar_one_or_none() + if exists is not None: + return None + session.add( + EmployeeScoreConfig( + version=DEFAULT_SCORE_CONFIG["version"], + name=DEFAULT_SCORE_CONFIG["name"], + status="active", + config_json=copy.deepcopy(DEFAULT_SCORE_CONFIG), + created_by="seed-cli", + ) + ) + await session.flush() + return DEFAULT_SCORE_CONFIG["version"] diff --git a/yunke-server/app/sync/cli.py b/yunke-server/app/sync/cli.py index d7a23e9..a142125 100644 --- a/yunke-server/app/sync/cli.py +++ b/yunke-server/app/sync/cli.py @@ -17,6 +17,7 @@ Three invocations (plus analytics backfills, see below):: python -m app.sync.cli sensitive-scan --full python -m app.sync.cli first-reply-facts python -m app.sync.cli employee-metrics + python -m app.sync.cli score-config Auth + DB --------- @@ -471,43 +472,12 @@ async def _cmd_employee_metrics(args: argparse.Namespace) -> int: await engine.dispose() -async def _seed_score_config(session) -> str | None: - """种子默认评分配置;已存在同版本返回 None(幂等)。纯会话函数可测。""" - from app.db.models import EmployeeScoreConfig - from app.management.metric_definitions import ( - DEFAULT_SCORE_CONFIG, - validate_config, - ) - - problems = validate_config(DEFAULT_SCORE_CONFIG) - if problems: - raise ValueError(f"score config invalid: {problems}") - exists = ( - await session.execute( - sa.select(EmployeeScoreConfig.id).where( - EmployeeScoreConfig.version == DEFAULT_SCORE_CONFIG["version"] - ) - ) - ).scalar_one_or_none() - if exists is not None: - return None - session.add( - EmployeeScoreConfig( - version=DEFAULT_SCORE_CONFIG["version"], - name=DEFAULT_SCORE_CONFIG["name"], - status="active", - config_json=DEFAULT_SCORE_CONFIG, - created_by="seed-cli", - ) - ) - await session.flush() - return DEFAULT_SCORE_CONFIG["version"] - - async def _cmd_score_config(args: argparse.Namespace) -> int: settings = get_settings() engine, session_maker = await _init_db(settings.db_url) try: + from app.management.metric_definitions import _seed_score_config + async with session_maker() as session: version = await _seed_score_config(session) await session.commit() diff --git a/yunke-server/tests/test_management_scoring.py b/yunke-server/tests/test_management_scoring.py index dfd4f8f..a148bf3 100644 --- a/yunke-server/tests/test_management_scoring.py +++ b/yunke-server/tests/test_management_scoring.py @@ -36,7 +36,6 @@ def test_metric_definitions_cover_scored_metrics(): # 每个参与评分的 metric_code 都有元数据(名称/单位/方向) for dim in DEFAULT_SCORE_CONFIG["dimensions"].values(): for code in dim.get("metrics", {}): - md = METRIC_DEFINITIONS[code if code in METRIC_DEFINITIONS else "active_share"] # band 型指标的 key 带 _band 后缀,spec 里有 metric 字段: spec = dim["metrics"][code] src = spec.get("metric", code) @@ -46,7 +45,7 @@ def test_metric_definitions_cover_scored_metrics(): async def test_seed_and_idempotent(make_session): - from app.sync.cli import _seed_score_config # 纯会话函数 + from app.management.metric_definitions import _seed_score_config # 纯会话函数 s = make_session v1 = await _seed_score_config(s) await s.commit()