feat: 添加可配置场景注册表与规则引擎

- 新增场景基类、配置加载、注册表与原语模块
- 添加 r10_refund_split 规则及场景 JSON Schema
- 扩展 scan 引擎与 scenarios API
- 新增场景注册表/配置/集成测试
- 更新前端 App、api、labels 支持新场景
This commit is contained in:
selfrelease
2026-06-17 09:57:02 +08:00
parent cbaa2156fc
commit 74e35fed96
20 changed files with 998 additions and 2 deletions
+50
View File
@@ -8,6 +8,9 @@ from __future__ import annotations
from dataclasses import dataclass
from app.scenarios.base import BaseScenario, ClueDraft, ScanOutcome
from app.scenarios.registry import register_scenario
@dataclass
class CohortPoint:
@@ -83,3 +86,50 @@ def build_rationale(finding: ChurnFinding, mismatch: float) -> str:
return (
f"未见明显断崖退订,但佣金与业务质量不匹配度为 {mismatch:.0%},建议关注。"
)
@register_scenario
class ChurnFraudScenario(BaseScenario):
"""场景二养卡骗补检测的插件封装:时序断崖 + 佣金质量不匹配 → 线索草稿。"""
code = "R9"
title = "疑似养卡骗补(脉冲增长+规律退订)"
risk_domain = "成本"
label = "养卡骗补"
score_threshold = 0.5 # 与原 run_churn_scan 的 score>=0.5 一致
def __init__(
self,
retention_curve: list[CohortPoint],
commission_paid: float,
active_ratio: float,
zero_usage_ratio: float,
channel_key: str,
) -> None:
self.retention_curve = retention_curve
self.commission_paid = commission_paid
self.active_ratio = active_ratio
self.zero_usage_ratio = zero_usage_ratio
self.channel_key = channel_key
def scan(self, session, *, data_version_id=None) -> ScanOutcome:
finding = detect_pulse_decay(self.retention_curve)
mismatch = commission_quality_mismatch(
self.commission_paid, self.active_ratio, self.zero_usage_ratio
)
score = churn_risk_score(finding, mismatch)
draft = ClueDraft(
score=score,
rationale=build_rationale(finding, mismatch),
evidence={
"cliff_month": finding.cliff_month,
"max_drop": finding.max_drop,
"commission_paid": self.commission_paid,
"active_ratio": self.active_ratio,
"zero_usage_ratio": self.zero_usage_ratio,
"mismatch": mismatch,
},
subjects={"channel": self.channel_key},
amount_involved=self.commission_paid,
)
return ScanOutcome(scanned_count=len(self.retention_curve), drafts=[draft])