74e35fed96
- 新增场景基类、配置加载、注册表与原语模块 - 添加 r10_refund_split 规则及场景 JSON Schema - 扩展 scan 引擎与 scenarios API - 新增场景注册表/配置/集成测试 - 更新前端 App、api、labels 支持新场景
77 lines
2.5 KiB
Python
77 lines
2.5 KiB
Python
"""场景插件框架:统一的场景基类与产出物(A 打底)。
|
||
|
||
每个审计场景实现一个 BaseScenario 子类,自包含"检测→评分→产出线索草稿",
|
||
并通过 @register_scenario 注册。扫描调度器(engines/scan.run_scenarios)统一遍历
|
||
执行并落库,新增场景无需改动调度器、API 与前端。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import uuid
|
||
from abc import ABC, abstractmethod
|
||
from dataclasses import dataclass, field
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
|
||
@dataclass
|
||
class ClueDraft:
|
||
"""场景产出的线索草稿:仅承载"内容",落库由调度器统一完成。
|
||
|
||
风险域、场景码等元数据来自场景类属性;模型/数据版本由调度器注入。
|
||
"""
|
||
|
||
score: float
|
||
rationale: str
|
||
evidence: dict
|
||
subjects: dict = field(default_factory=dict)
|
||
amount_involved: float | None = None
|
||
title: str | None = None # 覆盖场景默认标题;为 None 时用 BaseScenario.title
|
||
|
||
|
||
@dataclass
|
||
class ScanOutcome:
|
||
"""单个场景一次扫描的结果:扫描覆盖数(证明全量性)+ 线索草稿列表。"""
|
||
|
||
scanned_count: int
|
||
drafts: list[ClueDraft] = field(default_factory=list)
|
||
|
||
|
||
class BaseScenario(ABC):
|
||
"""审计场景抽象基类。
|
||
|
||
子类通过类属性声明元数据(场景码、标题、风险域、前端展示名、阈值),
|
||
并实现 scan() 完成检测与评分。命中阈值的草稿由调度器统一生成线索。
|
||
"""
|
||
|
||
# 场景码(如 "R8"),全局唯一,注册表按此索引
|
||
code: str = ""
|
||
# 默认线索标题(草稿可覆盖)
|
||
title: str = ""
|
||
# 风险域(如 "收入"/"成本")
|
||
risk_domain: str = ""
|
||
# 前端展示名(替代 labels.ts 中写死的 scenarioLabel)
|
||
label: str = ""
|
||
# 评分阈值:草稿 score >= 阈值才生成线索
|
||
score_threshold: float = 0.5
|
||
# 规则版本(配置驱动场景可用于追溯;代码场景一般为 None)
|
||
rule_version: str | None = None
|
||
|
||
@abstractmethod
|
||
def scan(
|
||
self,
|
||
session: Session,
|
||
*,
|
||
data_version_id: uuid.UUID | None = None,
|
||
) -> ScanOutcome:
|
||
"""执行检测与评分,返回扫描覆盖数与命中草稿。
|
||
|
||
Args:
|
||
session: 数据库会话(如需自取数)
|
||
data_version_id: 当前数据版本 ID(用于线索可追溯)
|
||
|
||
Returns:
|
||
ScanOutcome:scanned_count 体现全量覆盖,drafts 为候选线索
|
||
"""
|
||
...
|