- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制 - 引入费用审批动态路由、平台风险分级、预审与风险阶段管理 - 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板 - 新增 Hermes 风险线索收集器、Agent 链路追踪中心 - 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估 - 完善报销申请快速预览、权限控制与前端测试覆盖
148 lines
4.6 KiB
Python
148 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
RiskObservationStatus = Literal[
|
|
"pending_review",
|
|
"confirmed",
|
|
"false_positive",
|
|
"ignored",
|
|
"resolved",
|
|
]
|
|
|
|
RiskObservationFeedbackType = Literal[
|
|
"confirm",
|
|
"false_positive",
|
|
"ignore",
|
|
"resolve",
|
|
"comment",
|
|
]
|
|
|
|
|
|
class RiskObservationFeedbackRead(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
observation_id: str
|
|
feedback_type: str
|
|
action: str
|
|
actor: str
|
|
comment: str | None
|
|
payload_json: dict[str, Any]
|
|
decision: str = ""
|
|
candidate_rule_source: str = ""
|
|
confidence_score: float = 0.0
|
|
escalation_target: str = ""
|
|
supplement_required: bool = False
|
|
created_at: datetime
|
|
|
|
|
|
class RiskObservationRead(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
observation_key: str
|
|
subject_type: str
|
|
subject_key: str
|
|
subject_label: str
|
|
claim_id: str | None
|
|
claim_no: str
|
|
run_id: str | None
|
|
execution_log_id: str | None
|
|
risk_type: str
|
|
risk_signal: str
|
|
title: str
|
|
description: str
|
|
risk_score: int
|
|
risk_level: str
|
|
confidence_score: float
|
|
control_stage: str
|
|
control_mode: str
|
|
automation_mode: str
|
|
source: str
|
|
algorithm_version: str
|
|
status: str
|
|
feedback_status: str
|
|
contribution_scores_json: dict[str, Any]
|
|
baseline_json: dict[str, Any]
|
|
evidence_json: list[Any]
|
|
graph_node_keys_json: list[Any]
|
|
graph_edge_keys_json: list[Any]
|
|
policy_refs_json: list[Any]
|
|
similar_case_claim_ids_json: list[Any]
|
|
ontology_json: dict[str, Any]
|
|
decision_trace_json: dict[str, Any]
|
|
sampling_strategy: dict[str, Any] = Field(default_factory=dict)
|
|
evaluation_case_id: str = ""
|
|
ontology_parse_id: str = ""
|
|
ontology_version: str = ""
|
|
domain: str = ""
|
|
scenario: str = ""
|
|
intent: str = ""
|
|
ontology_entities_json: list[Any] = Field(default_factory=list)
|
|
risk_signals_json: list[Any] = Field(default_factory=list)
|
|
canonical_subject_key: str = ""
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
feedback_items: list[RiskObservationFeedbackRead] = Field(default_factory=list)
|
|
|
|
|
|
class RiskObservationListRead(BaseModel):
|
|
items: list[RiskObservationRead]
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class RiskObservationFeedbackCreate(BaseModel):
|
|
feedback_type: RiskObservationFeedbackType
|
|
action: str | None = Field(default=None, max_length=50)
|
|
actor: str | None = Field(default=None, max_length=100)
|
|
comment: str | None = Field(default=None, max_length=1000)
|
|
payload_json: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
@field_validator("action", "actor", "comment", mode="before")
|
|
@classmethod
|
|
def normalize_text(cls, value: Any) -> Any:
|
|
if value is None:
|
|
return None
|
|
normalized = str(value).strip()
|
|
return normalized or None
|
|
|
|
|
|
class RiskObservationDashboardRead(BaseModel):
|
|
window_days: int
|
|
total_observations: int
|
|
pending_count: int
|
|
risk_clue_count: int = 0
|
|
high_or_above_count: int
|
|
confirmed_count: int
|
|
false_positive_count: int
|
|
feedback_sample_count: int = 0
|
|
total_amount: float = 0.0
|
|
average_score: float
|
|
level_distribution: dict[str, int] = Field(default_factory=dict)
|
|
status_distribution: dict[str, int] = Field(default_factory=dict)
|
|
signal_distribution: dict[str, int] = Field(default_factory=dict)
|
|
source_distribution: dict[str, int] = Field(default_factory=dict)
|
|
automation_distribution: dict[str, int] = Field(default_factory=dict)
|
|
department_distribution: dict[str, int] = Field(default_factory=dict)
|
|
expense_type_distribution: dict[str, int] = Field(default_factory=dict)
|
|
risk_type_distribution: dict[str, int] = Field(default_factory=dict)
|
|
supplier_distribution: dict[str, int] = Field(default_factory=dict)
|
|
employee_grade_distribution: dict[str, int] = Field(default_factory=dict)
|
|
daily_trend: list[dict[str, Any]] = Field(default_factory=list)
|
|
top_risk_signals: list[dict[str, Any]] = Field(default_factory=list)
|
|
top_departments: list[dict[str, Any]] = Field(default_factory=list)
|
|
top_employees: list[dict[str, Any]] = Field(default_factory=list)
|
|
top_suppliers: list[dict[str, Any]] = Field(default_factory=list)
|
|
top_expense_types: list[dict[str, Any]] = Field(default_factory=list)
|
|
top_rules: list[dict[str, Any]] = Field(default_factory=list)
|
|
candidate_rule_count: int = 0
|
|
confirmation_rate: float
|
|
false_positive_rate: float
|
|
recent_high_observations: list[RiskObservationRead] = Field(default_factory=list)
|