Files
InternalAuditInterprise/backend/app/scenarios/base.py
T
selfrelease 74e35fed96 feat: 添加可配置场景注册表与规则引擎
- 新增场景基类、配置加载、注册表与原语模块
- 添加 r10_refund_split 规则及场景 JSON Schema
- 扩展 scan 引擎与 scenarios API
- 新增场景注册表/配置/集成测试
- 更新前端 App、api、labels 支持新场景
2026-06-17 09:57:02 +08:00

77 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""场景插件框架:统一的场景基类与产出物(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:
ScanOutcomescanned_count 体现全量覆盖,drafts 为候选线索
"""
...