feat(ai): add tenant-safe hierarchical expense learning

This commit is contained in:
caoxiaozhu
2026-07-16 14:30:41 +08:00
parent 6bdf65bc24
commit ee88a36baf
65 changed files with 6909 additions and 232 deletions

View File

@@ -31,7 +31,11 @@ from app.services.expense_application_draft_events import (
ExpenseApplicationDraftEventService,
)
from app.services.expense_application_learning import ExpenseApplicationLearningService
from app.services.expense_cases import ExpenseCaseService
from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy
from app.services.expense_claim_historical_evidence import (
build_user_agent_historical_evidence_notice,
)
from app.services.expense_claim_risk_stage import with_risk_business_stage
from app.services.travel_reimbursement_calculator import TravelReimbursementCalculatorService
from app.services.user_agent_application_dates import (
@@ -884,6 +888,12 @@ class UserAgentApplicationPersistenceMixin:
if existing is not None:
return existing
raise
# 非默认租户的费用单必须先建立 tenant-scoped Case Link后续
# submit_claim() 的访问策略才能在同一事务内重新查询到刚创建的记录。
ExpenseCaseService(self.db).ensure_case_for_claim(
claim,
tenant_id=current_user.tenant_id,
)
if not submit:
_, draft_event = draft_event_service.record(
payload,
@@ -1349,6 +1359,9 @@ class UserAgentApplicationMixin(UserAgentApplicationSlotMixin, UserAgentApplicat
facts["application_no"] = application_claim.claim_no
facts["application_claim_id"] = application_claim.id
facts["manager_name"] = self._resolve_application_manager_name(payload, application_claim)
facts["historical_case_evidence_notice"] = (
build_user_agent_historical_evidence_notice(application_claim)
)
return UserAgentResponse(
answer=self._build_expense_application_answer(payload, facts=facts, step=step),
citations=[],
@@ -1417,6 +1430,9 @@ class UserAgentApplicationMixin(UserAgentApplicationSlotMixin, UserAgentApplicat
if step == "submitted":
application_no = str(facts.get("application_no") or "").strip() or self._build_application_claim_no(payload, facts)
manager_name = str(facts.get("manager_name") or "").strip() or "直属领导"
historical_notice = str(
facts.get("historical_case_evidence_notice") or ""
).strip()
submitted_title = (
"申请单据已修改并重新提交,已进入审批流程。"
if str(facts.get("application_edit_mode") or "").strip().lower() == "true"
@@ -1427,6 +1443,7 @@ class UserAgentApplicationMixin(UserAgentApplicationSlotMixin, UserAgentApplicat
submitted_title,
f"系统已推送给 {manager_name} 审核,当前节点:{manager_name}审核中。",
f"申请单号:{application_no}",
*([historical_notice] if historical_notice else []),
"下方是简要单据信息。需要查看完整详情时,请点击快捷方式进入单据详情。",
]
)