```
refactor(api): 使用常量替代硬编码字符串 - 在health_check接口中使用ApiResponseKey.STATUS和ApiStatus.OK常量 - 替换硬编码的状态返回值为枚举常量 refactor(core): 配置模块错误信息统一使用常量 - 从constants模块导入ConfigErrorDetail并替换CORS_ORIGINS和LEGACY_ALLOWED_QUERIES的验证错误信息 - 配置类中的默认值使用constants中定义的常量 feat(constants): 添加API响应、安全错误和配置错误常量类 - 新增ApiResponseKey用于API状态键名 - 新增ApiStatus用于API状态值 - 新增SecurityErrorDetail用于安全认证错误详情 - 新增ConfigErrorDetail用于配置验证错误详情 - 添加DEFAULT_MODEL_PROVIDER和DEFAULT_OPENCLAW_ACTION_JSON常量 refactor(security): 安全认证模块使用错误常量 - 将硬编码的安全错误信息替换为SecurityErrorDetail常量 - 包括API密钥、审批密钥和审计密钥的相关错误信息 refactor(ai-agent): AI代理适配器改进错误处理 - 将HTTP状态码替换为FastAPI状态常量 - 添加OpenClaw工具和操作的错误常量 - 修复健康检查和工具调用中的状态码比较逻辑 - 添加AIToolAuditKey用于工具审计键名 feat(ai-agent): 扩展AI代理常量定义 - 新增AIToolAuditKey用于工具审计字段 - 添加OpenClaw相关的错误常量如OPENCLAW_CHAT_PROVIDER_REQUIRED等 - 添加UNSUPPORTED_AI_SKILL_TEMPLATE模板字符串 refactor(approvals): 审批模块常量化重构 - 新增ApprovalPayloadKey用于审批载荷字段 - 添加approval_action函数和APPROVAL_ACTION_SEPARATOR分隔符 - 使用常量替换字面量值 feat(audit): 审计模块新增飞书事件动作类型 - 添加FEISHU_WEBHOOK_EVENT和FEISHU_LONG_CONNECTION_EVENT审计动作 refactor(business): 业务模块全面常量化 - 新增BusinessDomain枚举包含所有业务域 - 添加BusinessResponseKey、BusinessPayloadKey等常量类 - 重构DOMAIN_MODELS为frozenset以提高性能 - 添加normalize_domain等辅助函数用于域标准化 - 使用常量替换路由和业务服务中的硬编码字符串 - 添加业务错误常量和字段验证模板 refactor(feishu): 飞书客户端错误处理优化 - 将HTTP状态码替换为FastAPI标准状态常量 - 改进错误处理的一致性 refactor(approvals): 审批服务使用新常量结构 - 使用ApprovalPayloadKey常量重构载荷字段 - 使用approval_action函数统一动作命名格式 - 优化高风险域判断逻辑 ```
This commit is contained in:
66
app/modules/risk/constants.py
Normal file
66
app/modules/risk/constants.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from enum import StrEnum
|
||||
|
||||
from app.modules.business.constants import RiskLevel
|
||||
|
||||
|
||||
class RiskSummaryKey(StrEnum):
|
||||
RISK_LEVEL = "risk_level"
|
||||
RISK_SCORE = "risk_score"
|
||||
OVERDUE_TASKS = "overdue_tasks"
|
||||
DELAYED_PROJECTS = "delayed_projects"
|
||||
OVER_BUDGET_PROJECTS = "over_budget_projects"
|
||||
FUND_RISKS = "fund_risks"
|
||||
SUPPLIER_RISKS = "supplier_risks"
|
||||
OPEN_EVENTS = "open_events"
|
||||
|
||||
|
||||
class RiskGenerationResultKey(StrEnum):
|
||||
CREATED = "created"
|
||||
UPDATED = "updated"
|
||||
SKIPPED = "skipped"
|
||||
ITEMS = "items"
|
||||
ACTION = "action"
|
||||
RISK_EVENT = "risk_event"
|
||||
|
||||
|
||||
class RiskGenerationAction(StrEnum):
|
||||
CREATED = "created"
|
||||
UPDATED = "updated"
|
||||
SKIPPED = "skipped"
|
||||
|
||||
|
||||
class RiskEventPayloadKey(StrEnum):
|
||||
CODE = "code"
|
||||
TITLE = "title"
|
||||
RISK_TYPE = "risk_type"
|
||||
RISK_LEVEL = "risk_level"
|
||||
STATUS = "status"
|
||||
SOURCE_DOMAIN = "source_domain"
|
||||
SOURCE_RECORD_ID = "source_record_id"
|
||||
PROJECT_CODE = "project_code"
|
||||
OWNER = "owner"
|
||||
DUE_DATE = "due_date"
|
||||
DETECTED_AT = "detected_at"
|
||||
DESCRIPTION = "description"
|
||||
MITIGATION = "mitigation"
|
||||
EVIDENCE = "evidence"
|
||||
|
||||
|
||||
RISK_SCORE_WEIGHTS = {
|
||||
RiskSummaryKey.OVERDUE_TASKS: 1,
|
||||
RiskSummaryKey.DELAYED_PROJECTS: 3,
|
||||
RiskSummaryKey.OVER_BUDGET_PROJECTS: 4,
|
||||
RiskSummaryKey.FUND_RISKS: 5,
|
||||
RiskSummaryKey.SUPPLIER_RISKS: 3,
|
||||
RiskSummaryKey.OPEN_EVENTS: 2,
|
||||
}
|
||||
RISK_LEVEL_HIGH_THRESHOLD = 15
|
||||
RISK_LEVEL_MEDIUM_THRESHOLD = 5
|
||||
|
||||
|
||||
def risk_level_for_score(score: int) -> RiskLevel:
|
||||
if score >= RISK_LEVEL_HIGH_THRESHOLD:
|
||||
return RiskLevel.HIGH
|
||||
if score >= RISK_LEVEL_MEDIUM_THRESHOLD:
|
||||
return RiskLevel.MEDIUM
|
||||
return RiskLevel.LOW
|
||||
@@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.security import ApiPrincipal, require_api_key
|
||||
from app.modules.risk.constants import RiskGenerationResultKey
|
||||
from app.modules.risk.service import RiskService
|
||||
|
||||
router = APIRouter(dependencies=[Depends(require_api_key)])
|
||||
@@ -15,27 +16,27 @@ def risk_summary(db: Session = Depends(get_db)) -> dict:
|
||||
|
||||
@router.get("/overdue-tasks")
|
||||
def overdue_tasks(db: Session = Depends(get_db)) -> dict:
|
||||
return {"items": RiskService(db).overdue_tasks()}
|
||||
return {RiskGenerationResultKey.ITEMS: RiskService(db).overdue_tasks()}
|
||||
|
||||
|
||||
@router.get("/delayed-projects")
|
||||
def delayed_projects(db: Session = Depends(get_db)) -> dict:
|
||||
return {"items": RiskService(db).delayed_projects()}
|
||||
return {RiskGenerationResultKey.ITEMS: RiskService(db).delayed_projects()}
|
||||
|
||||
|
||||
@router.get("/over-budget-projects")
|
||||
def over_budget_projects(db: Session = Depends(get_db)) -> dict:
|
||||
return {"items": RiskService(db).over_budget_projects()}
|
||||
return {RiskGenerationResultKey.ITEMS: RiskService(db).over_budget_projects()}
|
||||
|
||||
|
||||
@router.get("/funds")
|
||||
def fund_risks(db: Session = Depends(get_db)) -> dict:
|
||||
return {"items": RiskService(db).fund_risks()}
|
||||
return {RiskGenerationResultKey.ITEMS: RiskService(db).fund_risks()}
|
||||
|
||||
|
||||
@router.get("/suppliers")
|
||||
def supplier_risks(db: Session = Depends(get_db)) -> dict:
|
||||
return {"items": RiskService(db).supplier_risks()}
|
||||
return {RiskGenerationResultKey.ITEMS: RiskService(db).supplier_risks()}
|
||||
|
||||
|
||||
@router.get("/events")
|
||||
@@ -44,7 +45,12 @@ def risk_events(
|
||||
status: str | None = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
return {"items": RiskService(db).list_events(limit=limit, status_filter=status)}
|
||||
return {
|
||||
RiskGenerationResultKey.ITEMS: RiskService(db).list_events(
|
||||
limit=limit,
|
||||
status_filter=status,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@router.post("/events/generate")
|
||||
|
||||
@@ -29,6 +29,14 @@ from app.modules.business.constants import (
|
||||
)
|
||||
from app.modules.business.models import FundAccount, Project, RiskEvent, Supplier, WorkTask
|
||||
from app.modules.business.service import serialize_model
|
||||
from app.modules.risk.constants import (
|
||||
RISK_SCORE_WEIGHTS,
|
||||
RiskGenerationAction,
|
||||
RiskGenerationResultKey,
|
||||
RiskEventPayloadKey,
|
||||
RiskSummaryKey,
|
||||
risk_level_for_score,
|
||||
)
|
||||
|
||||
|
||||
class RiskService:
|
||||
@@ -97,31 +105,26 @@ class RiskService:
|
||||
external_open_events = [
|
||||
item
|
||||
for item in open_events
|
||||
if item.get("risk_type") not in GENERATED_RISK_EVENT_TYPES
|
||||
if item.get(RiskEventPayloadKey.RISK_TYPE) not in GENERATED_RISK_EVENT_TYPES
|
||||
]
|
||||
risk_score = (
|
||||
len(overdue_tasks) * 1
|
||||
+ len(delayed_projects) * 3
|
||||
+ len(over_budget_projects) * 4
|
||||
+ len(fund_risks) * 5
|
||||
+ len(supplier_risks) * 3
|
||||
+ len(external_open_events) * 2
|
||||
len(overdue_tasks) * RISK_SCORE_WEIGHTS[RiskSummaryKey.OVERDUE_TASKS]
|
||||
+ len(delayed_projects) * RISK_SCORE_WEIGHTS[RiskSummaryKey.DELAYED_PROJECTS]
|
||||
+ len(over_budget_projects)
|
||||
* RISK_SCORE_WEIGHTS[RiskSummaryKey.OVER_BUDGET_PROJECTS]
|
||||
+ len(fund_risks) * RISK_SCORE_WEIGHTS[RiskSummaryKey.FUND_RISKS]
|
||||
+ len(supplier_risks) * RISK_SCORE_WEIGHTS[RiskSummaryKey.SUPPLIER_RISKS]
|
||||
+ len(external_open_events) * RISK_SCORE_WEIGHTS[RiskSummaryKey.OPEN_EVENTS]
|
||||
)
|
||||
if risk_score >= 15:
|
||||
level = RiskLevel.HIGH
|
||||
elif risk_score >= 5:
|
||||
level = RiskLevel.MEDIUM
|
||||
else:
|
||||
level = RiskLevel.LOW
|
||||
return {
|
||||
"risk_level": level,
|
||||
"risk_score": Decimal(risk_score),
|
||||
"overdue_tasks": overdue_tasks,
|
||||
"delayed_projects": delayed_projects,
|
||||
"over_budget_projects": over_budget_projects,
|
||||
"fund_risks": fund_risks,
|
||||
"supplier_risks": supplier_risks,
|
||||
"open_events": open_events,
|
||||
RiskSummaryKey.RISK_LEVEL: risk_level_for_score(risk_score),
|
||||
RiskSummaryKey.RISK_SCORE: Decimal(risk_score),
|
||||
RiskSummaryKey.OVERDUE_TASKS: overdue_tasks,
|
||||
RiskSummaryKey.DELAYED_PROJECTS: delayed_projects,
|
||||
RiskSummaryKey.OVER_BUDGET_PROJECTS: over_budget_projects,
|
||||
RiskSummaryKey.FUND_RISKS: fund_risks,
|
||||
RiskSummaryKey.SUPPLIER_RISKS: supplier_risks,
|
||||
RiskSummaryKey.OPEN_EVENTS: open_events,
|
||||
}
|
||||
|
||||
def generate_events(self, actor: str = ActorValue.API) -> dict[str, Any]:
|
||||
@@ -135,25 +138,35 @@ class RiskService:
|
||||
|
||||
for payload in payloads:
|
||||
record = self.db.execute(
|
||||
select(RiskEvent).where(RiskEvent.code == payload["code"])
|
||||
select(RiskEvent).where(RiskEvent.code == payload[RiskEventPayloadKey.CODE])
|
||||
).scalar_one_or_none()
|
||||
if record is None:
|
||||
record = RiskEvent(**payload)
|
||||
self.db.add(record)
|
||||
self.db.flush()
|
||||
created += 1
|
||||
action = "created"
|
||||
action = RiskGenerationAction.CREATED
|
||||
elif record.status in CLOSED_RISK_STATUSES:
|
||||
skipped += 1
|
||||
items.append({"action": "skipped", "risk_event": serialize_model(record)})
|
||||
items.append(
|
||||
{
|
||||
RiskGenerationResultKey.ACTION: RiskGenerationAction.SKIPPED,
|
||||
RiskGenerationResultKey.RISK_EVENT: serialize_model(record),
|
||||
}
|
||||
)
|
||||
continue
|
||||
else:
|
||||
for key, value in payload.items():
|
||||
if key != "code":
|
||||
if key != RiskEventPayloadKey.CODE:
|
||||
setattr(record, key, value)
|
||||
updated += 1
|
||||
action = "updated"
|
||||
items.append({"action": action, "risk_event": serialize_model(record)})
|
||||
action = RiskGenerationAction.UPDATED
|
||||
items.append(
|
||||
{
|
||||
RiskGenerationResultKey.ACTION: action,
|
||||
RiskGenerationResultKey.RISK_EVENT: serialize_model(record),
|
||||
}
|
||||
)
|
||||
|
||||
self.db.commit()
|
||||
AuditService(self.db).log(
|
||||
@@ -164,13 +177,18 @@ class RiskService:
|
||||
target_type=AuditTargetType.RISK_EVENTS,
|
||||
risk_level=AuditRiskLevel.MEDIUM,
|
||||
response_payload={
|
||||
"created": created,
|
||||
"updated": updated,
|
||||
"skipped": skipped,
|
||||
RiskGenerationResultKey.CREATED: created,
|
||||
RiskGenerationResultKey.UPDATED: updated,
|
||||
RiskGenerationResultKey.SKIPPED: skipped,
|
||||
},
|
||||
)
|
||||
)
|
||||
return {"created": created, "updated": updated, "skipped": skipped, "items": items}
|
||||
return {
|
||||
RiskGenerationResultKey.CREATED: created,
|
||||
RiskGenerationResultKey.UPDATED: updated,
|
||||
RiskGenerationResultKey.SKIPPED: skipped,
|
||||
RiskGenerationResultKey.ITEMS: items,
|
||||
}
|
||||
|
||||
def _build_event_payloads(self) -> list[dict[str, Any]]:
|
||||
payloads: list[dict[str, Any]] = []
|
||||
@@ -191,22 +209,22 @@ class RiskService:
|
||||
for task in self.db.execute(stmt).scalars():
|
||||
payloads.append(
|
||||
{
|
||||
"code": f"RISK-TASK-OVERDUE-{task.id}",
|
||||
"title": f"任务逾期:{task.title}",
|
||||
"risk_type": RiskEventType.OVERDUE_TASK,
|
||||
"risk_level": RiskLevel.MEDIUM,
|
||||
"status": StatusValue.OPEN,
|
||||
"source_domain": BusinessDomain.TASKS,
|
||||
"source_record_id": str(task.id),
|
||||
"project_code": task.project_code,
|
||||
"owner": task.owner,
|
||||
"due_date": task.due_date,
|
||||
"detected_at": utc_now(),
|
||||
"description": "任务已超过截止日期且未完成。",
|
||||
"mitigation": (
|
||||
RiskEventPayloadKey.CODE: f"RISK-TASK-OVERDUE-{task.id}",
|
||||
RiskEventPayloadKey.TITLE: f"任务逾期:{task.title}",
|
||||
RiskEventPayloadKey.RISK_TYPE: RiskEventType.OVERDUE_TASK,
|
||||
RiskEventPayloadKey.RISK_LEVEL: RiskLevel.MEDIUM,
|
||||
RiskEventPayloadKey.STATUS: StatusValue.OPEN,
|
||||
RiskEventPayloadKey.SOURCE_DOMAIN: BusinessDomain.TASKS,
|
||||
RiskEventPayloadKey.SOURCE_RECORD_ID: str(task.id),
|
||||
RiskEventPayloadKey.PROJECT_CODE: task.project_code,
|
||||
RiskEventPayloadKey.OWNER: task.owner,
|
||||
RiskEventPayloadKey.DUE_DATE: task.due_date,
|
||||
RiskEventPayloadKey.DETECTED_AT: utc_now(),
|
||||
RiskEventPayloadKey.DESCRIPTION: "任务已超过截止日期且未完成。",
|
||||
RiskEventPayloadKey.MITIGATION: (
|
||||
"请负责人更新进度、明确阻塞项并给出新的完成时间。"
|
||||
),
|
||||
"evidence": serialize_model(task),
|
||||
RiskEventPayloadKey.EVIDENCE: serialize_model(task),
|
||||
}
|
||||
)
|
||||
return payloads
|
||||
@@ -222,22 +240,22 @@ class RiskService:
|
||||
level = RiskLevel.HIGH if project.progress_percent < 80 else RiskLevel.MEDIUM
|
||||
payloads.append(
|
||||
{
|
||||
"code": f"RISK-PROJECT-DELAY-{project.id}",
|
||||
"title": f"项目延期:{project.name}",
|
||||
"risk_type": RiskEventType.DELAYED_PROJECT,
|
||||
"risk_level": level,
|
||||
"status": StatusValue.OPEN,
|
||||
"source_domain": BusinessDomain.PROJECTS,
|
||||
"source_record_id": str(project.id),
|
||||
"project_code": project.code,
|
||||
"owner": project.owner,
|
||||
"due_date": project.due_date,
|
||||
"detected_at": utc_now(),
|
||||
"description": "项目已超过计划截止日期且未进入完成状态。",
|
||||
"mitigation": (
|
||||
RiskEventPayloadKey.CODE: f"RISK-PROJECT-DELAY-{project.id}",
|
||||
RiskEventPayloadKey.TITLE: f"项目延期:{project.name}",
|
||||
RiskEventPayloadKey.RISK_TYPE: RiskEventType.DELAYED_PROJECT,
|
||||
RiskEventPayloadKey.RISK_LEVEL: level,
|
||||
RiskEventPayloadKey.STATUS: StatusValue.OPEN,
|
||||
RiskEventPayloadKey.SOURCE_DOMAIN: BusinessDomain.PROJECTS,
|
||||
RiskEventPayloadKey.SOURCE_RECORD_ID: str(project.id),
|
||||
RiskEventPayloadKey.PROJECT_CODE: project.code,
|
||||
RiskEventPayloadKey.OWNER: project.owner,
|
||||
RiskEventPayloadKey.DUE_DATE: project.due_date,
|
||||
RiskEventPayloadKey.DETECTED_AT: utc_now(),
|
||||
RiskEventPayloadKey.DESCRIPTION: "项目已超过计划截止日期且未进入完成状态。",
|
||||
RiskEventPayloadKey.MITIGATION: (
|
||||
"请项目负责人提交延期原因、资源需求和纠偏计划。"
|
||||
),
|
||||
"evidence": serialize_model(project),
|
||||
RiskEventPayloadKey.EVIDENCE: serialize_model(project),
|
||||
}
|
||||
)
|
||||
return payloads
|
||||
@@ -251,22 +269,22 @@ class RiskService:
|
||||
for project in self.db.execute(stmt).scalars():
|
||||
payloads.append(
|
||||
{
|
||||
"code": f"RISK-PROJECT-BUDGET-{project.id}",
|
||||
"title": f"项目超预算:{project.name}",
|
||||
"risk_type": RiskEventType.OVER_BUDGET_PROJECT,
|
||||
"risk_level": RiskLevel.HIGH,
|
||||
"status": StatusValue.OPEN,
|
||||
"source_domain": BusinessDomain.PROJECTS,
|
||||
"source_record_id": str(project.id),
|
||||
"project_code": project.code,
|
||||
"owner": project.owner,
|
||||
"due_date": project.due_date,
|
||||
"detected_at": utc_now(),
|
||||
"description": "项目实际成本已超过预算。",
|
||||
"mitigation": (
|
||||
RiskEventPayloadKey.CODE: f"RISK-PROJECT-BUDGET-{project.id}",
|
||||
RiskEventPayloadKey.TITLE: f"项目超预算:{project.name}",
|
||||
RiskEventPayloadKey.RISK_TYPE: RiskEventType.OVER_BUDGET_PROJECT,
|
||||
RiskEventPayloadKey.RISK_LEVEL: RiskLevel.HIGH,
|
||||
RiskEventPayloadKey.STATUS: StatusValue.OPEN,
|
||||
RiskEventPayloadKey.SOURCE_DOMAIN: BusinessDomain.PROJECTS,
|
||||
RiskEventPayloadKey.SOURCE_RECORD_ID: str(project.id),
|
||||
RiskEventPayloadKey.PROJECT_CODE: project.code,
|
||||
RiskEventPayloadKey.OWNER: project.owner,
|
||||
RiskEventPayloadKey.DUE_DATE: project.due_date,
|
||||
RiskEventPayloadKey.DETECTED_AT: utc_now(),
|
||||
RiskEventPayloadKey.DESCRIPTION: "项目实际成本已超过预算。",
|
||||
RiskEventPayloadKey.MITIGATION: (
|
||||
"请复核预算科目、冻结非必要采购并补充审批依据。"
|
||||
),
|
||||
"evidence": serialize_model(project),
|
||||
RiskEventPayloadKey.EVIDENCE: serialize_model(project),
|
||||
}
|
||||
)
|
||||
return payloads
|
||||
@@ -277,21 +295,21 @@ class RiskService:
|
||||
for account in self.db.execute(stmt).scalars():
|
||||
payloads.append(
|
||||
{
|
||||
"code": f"RISK-FUND-{account.id}",
|
||||
"title": f"资金低于安全线:{account.name}",
|
||||
"risk_type": RiskEventType.FUND_SAFETY_LINE,
|
||||
"risk_level": RiskLevel.HIGH,
|
||||
"status": StatusValue.OPEN,
|
||||
"source_domain": BusinessDomain.FUND_ACCOUNTS,
|
||||
"source_record_id": str(account.id),
|
||||
"owner": None,
|
||||
"detected_at": utc_now(),
|
||||
"description": "账户当前余额低于设置的安全线。",
|
||||
"mitigation": (
|
||||
RiskEventPayloadKey.CODE: f"RISK-FUND-{account.id}",
|
||||
RiskEventPayloadKey.TITLE: f"资金低于安全线:{account.name}",
|
||||
RiskEventPayloadKey.RISK_TYPE: RiskEventType.FUND_SAFETY_LINE,
|
||||
RiskEventPayloadKey.RISK_LEVEL: RiskLevel.HIGH,
|
||||
RiskEventPayloadKey.STATUS: StatusValue.OPEN,
|
||||
RiskEventPayloadKey.SOURCE_DOMAIN: BusinessDomain.FUND_ACCOUNTS,
|
||||
RiskEventPayloadKey.SOURCE_RECORD_ID: str(account.id),
|
||||
RiskEventPayloadKey.OWNER: None,
|
||||
RiskEventPayloadKey.DETECTED_AT: utc_now(),
|
||||
RiskEventPayloadKey.DESCRIPTION: "账户当前余额低于设置的安全线。",
|
||||
RiskEventPayloadKey.MITIGATION: (
|
||||
"请财务确认收付款计划,"
|
||||
"并优先处理关键项目资金安排。"
|
||||
),
|
||||
"evidence": serialize_model(account),
|
||||
RiskEventPayloadKey.EVIDENCE: serialize_model(account),
|
||||
}
|
||||
)
|
||||
return payloads
|
||||
@@ -310,20 +328,20 @@ class RiskService:
|
||||
)
|
||||
payloads.append(
|
||||
{
|
||||
"code": f"RISK-SUPPLIER-{supplier.id}",
|
||||
"title": f"供应商风险:{supplier.name}",
|
||||
"risk_type": RiskEventType.SUPPLIER_RISK,
|
||||
"risk_level": level,
|
||||
"status": StatusValue.OPEN,
|
||||
"source_domain": BusinessDomain.SUPPLIERS,
|
||||
"source_record_id": str(supplier.id),
|
||||
"owner": supplier.contact,
|
||||
"detected_at": utc_now(),
|
||||
"description": "供应商风险等级或黑名单状态需要关注。",
|
||||
"mitigation": (
|
||||
RiskEventPayloadKey.CODE: f"RISK-SUPPLIER-{supplier.id}",
|
||||
RiskEventPayloadKey.TITLE: f"供应商风险:{supplier.name}",
|
||||
RiskEventPayloadKey.RISK_TYPE: RiskEventType.SUPPLIER_RISK,
|
||||
RiskEventPayloadKey.RISK_LEVEL: level,
|
||||
RiskEventPayloadKey.STATUS: StatusValue.OPEN,
|
||||
RiskEventPayloadKey.SOURCE_DOMAIN: BusinessDomain.SUPPLIERS,
|
||||
RiskEventPayloadKey.SOURCE_RECORD_ID: str(supplier.id),
|
||||
RiskEventPayloadKey.OWNER: supplier.contact,
|
||||
RiskEventPayloadKey.DETECTED_AT: utc_now(),
|
||||
RiskEventPayloadKey.DESCRIPTION: "供应商风险等级或黑名单状态需要关注。",
|
||||
RiskEventPayloadKey.MITIGATION: (
|
||||
"请采购负责人复核供应商准入、履约和替代方案。"
|
||||
),
|
||||
"evidence": serialize_model(supplier),
|
||||
RiskEventPayloadKey.EVIDENCE: serialize_model(supplier),
|
||||
}
|
||||
)
|
||||
return payloads
|
||||
|
||||
Reference in New Issue
Block a user