feat: 本体字段治理与风险规则模板执行器重构
- 新增本体字段注册表与字段治理审计脚本 - 重构风险规则模板执行器、DSL 验证与清单分类器 - 完善票据夹服务与差旅请求详情页交互 - 优化趋势图表与总览页数据展示 - 增强报销平台风险分级与模拟公司筛选 - 补充本体字段、风险规则生成与票据夹服务测试覆盖
This commit is contained in:
@@ -38,6 +38,7 @@ from app.services.expense_rule_runtime import ExpenseRuleRuntimeService, Runtime
|
||||
from app.services.risk_ontology_bridge import resolve_rule_codes_for_risk_check
|
||||
from app.services.travel_reimbursement_calculator import TravelReimbursementCalculatorService
|
||||
from app.services.expense_type_keywords import resolve_expense_type_label_from_text
|
||||
from app.services.ontology_field_registry import normalize_ontology_form_values
|
||||
from app.services.user_agent_constants import *
|
||||
|
||||
|
||||
@@ -151,10 +152,9 @@ class UserAgentReviewSlotMixin:
|
||||
|
||||
def _resolve_location_value(self, payload: UserAgentRequest) -> str:
|
||||
review_form_values = self._resolve_review_form_values(payload)
|
||||
for key in ("business_location", "location"):
|
||||
value = str(review_form_values.get(key) or "").strip()
|
||||
if value:
|
||||
return value
|
||||
value = str(review_form_values.get("location") or "").strip()
|
||||
if value:
|
||||
return value
|
||||
|
||||
if str(payload.context_json.get("entry_source") or "").strip() == "detail":
|
||||
request_context = payload.context_json.get("request_context")
|
||||
@@ -181,21 +181,7 @@ class UserAgentReviewSlotMixin:
|
||||
|
||||
@staticmethod
|
||||
def _resolve_review_form_values(payload: UserAgentRequest) -> dict[str, str]:
|
||||
values = payload.context_json.get("review_form_values")
|
||||
if not isinstance(values, dict):
|
||||
return {}
|
||||
normalized: dict[str, str] = {}
|
||||
for key, value in values.items():
|
||||
cleaned_key = str(key or "").strip()
|
||||
if not cleaned_key:
|
||||
continue
|
||||
normalized[cleaned_key] = str(value or "").strip()
|
||||
if not normalized.get("transport_mode"):
|
||||
for alias in ("transportMode", "application_transport_mode", "applicationTransportMode"):
|
||||
if normalized.get(alias):
|
||||
normalized["transport_mode"] = normalized[alias]
|
||||
break
|
||||
return normalized
|
||||
return normalize_ontology_form_values(payload.context_json.get("review_form_values"))
|
||||
|
||||
|
||||
@staticmethod
|
||||
@@ -220,12 +206,7 @@ class UserAgentReviewSlotMixin:
|
||||
|
||||
def _build_time_slot(self, payload: UserAgentRequest) -> dict[str, str | float]:
|
||||
review_form_values = self._resolve_review_form_values(payload)
|
||||
edited_value = str(
|
||||
review_form_values.get("time_range")
|
||||
or review_form_values.get("business_time")
|
||||
or review_form_values.get("occurred_date")
|
||||
or ""
|
||||
).strip()
|
||||
edited_value = str(review_form_values.get("time_range") or "").strip()
|
||||
if edited_value:
|
||||
raw_value = str(review_form_values.get("time_range_raw") or edited_value).strip()
|
||||
return self._build_slot_value(
|
||||
@@ -237,17 +218,6 @@ class UserAgentReviewSlotMixin:
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
application_time = str(review_form_values.get("application_business_time") or "").strip()
|
||||
if application_time:
|
||||
return self._build_slot_value(
|
||||
value=application_time,
|
||||
raw_value=application_time,
|
||||
normalized_value=application_time,
|
||||
source="detail_context",
|
||||
confidence=0.86,
|
||||
evidence="来源于已关联申请单,作为本次报销草稿的发生时间依据。",
|
||||
)
|
||||
|
||||
time_range = payload.ontology.time_range
|
||||
if time_range.start_date and time_range.end_date:
|
||||
normalized_value = (
|
||||
@@ -270,25 +240,14 @@ class UserAgentReviewSlotMixin:
|
||||
|
||||
def _build_location_slot(self, payload: UserAgentRequest) -> dict[str, str | float]:
|
||||
review_form_values = self._resolve_review_form_values(payload)
|
||||
for key in ("business_location", "location"):
|
||||
value = str(review_form_values.get(key) or "").strip()
|
||||
if value:
|
||||
return self._build_slot_value(
|
||||
value=value,
|
||||
normalized_value=value,
|
||||
source="user_form",
|
||||
confidence=1.0,
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
application_location = str(review_form_values.get("application_location") or "").strip()
|
||||
if application_location:
|
||||
value = str(review_form_values.get("location") or "").strip()
|
||||
if value:
|
||||
return self._build_slot_value(
|
||||
value=application_location,
|
||||
normalized_value=application_location,
|
||||
source="detail_context",
|
||||
confidence=0.86,
|
||||
evidence="来源于已关联申请单,作为本次报销草稿的地点依据。",
|
||||
value=value,
|
||||
normalized_value=value,
|
||||
source="user_form",
|
||||
confidence=1.0,
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
if str(payload.context_json.get("entry_source") or "").strip() == "detail":
|
||||
@@ -396,17 +355,6 @@ class UserAgentReviewSlotMixin:
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
application_reason = str(review_form_values.get("application_reason") or "").strip()
|
||||
if application_reason:
|
||||
return self._build_slot_value(
|
||||
value=application_reason,
|
||||
raw_value=application_reason,
|
||||
normalized_value=application_reason,
|
||||
source="detail_context",
|
||||
confidence=0.9,
|
||||
evidence="来源于已关联申请单,作为本次报销草稿的事由依据。",
|
||||
)
|
||||
|
||||
inferred_reason = self._infer_reason_from_claim_groups(
|
||||
claim_groups=claim_groups,
|
||||
)
|
||||
@@ -457,22 +405,6 @@ class UserAgentReviewSlotMixin:
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
application_amount = str(
|
||||
review_form_values.get("application_amount")
|
||||
or review_form_values.get("application_amount_label")
|
||||
or ""
|
||||
).strip()
|
||||
if application_amount:
|
||||
normalized = self._normalize_amount_text(application_amount)
|
||||
return self._build_slot_value(
|
||||
value=normalized,
|
||||
raw_value=application_amount,
|
||||
normalized_value=normalized,
|
||||
source="detail_context",
|
||||
confidence=0.86,
|
||||
evidence="来源于已关联申请单,作为本次报销草稿的金额依据。",
|
||||
)
|
||||
|
||||
amount_value = entity_map.get("amount", "")
|
||||
if amount_value:
|
||||
normalized = self._normalize_amount_text(amount_value)
|
||||
@@ -506,7 +438,7 @@ class UserAgentReviewSlotMixin:
|
||||
ocr_documents: list[dict[str, object]],
|
||||
) -> dict[str, str | float]:
|
||||
review_form_values = self._resolve_review_form_values(payload)
|
||||
edited_value = str(review_form_values.get("expense_type") or review_form_values.get("reimbursement_type") or "").strip()
|
||||
edited_value = str(review_form_values.get("expense_type") or "").strip()
|
||||
if edited_value:
|
||||
normalized_code, normalized_label = self._normalize_expense_type_input(edited_value)
|
||||
return self._build_slot_value(
|
||||
@@ -581,7 +513,7 @@ class UserAgentReviewSlotMixin:
|
||||
|
||||
def _build_attachment_slot(self, payload: UserAgentRequest) -> dict[str, str | float]:
|
||||
review_form_values = self._resolve_review_form_values(payload)
|
||||
attachment_names = str(review_form_values.get("attachment_names") or "").strip()
|
||||
attachment_names = str(review_form_values.get("attachments") or "").strip()
|
||||
if attachment_names:
|
||||
return self._build_slot_value(
|
||||
value=attachment_names,
|
||||
|
||||
Reference in New Issue
Block a user