feat(ai): add expense application feedback ledger
This commit is contained in:
@@ -8,6 +8,7 @@ from sqlalchemy import or_, select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from app.api.deps import CurrentUserContext
|
||||
from app.models.expense_case import BusinessEvent
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
from app.schemas.reimbursement import TravelReimbursementCalculatorRequest
|
||||
from app.schemas.user_agent import (
|
||||
@@ -28,6 +29,7 @@ from app.services.document_numbering import (
|
||||
from app.services.expense_application_draft_events import (
|
||||
ExpenseApplicationDraftEventService,
|
||||
)
|
||||
from app.services.expense_application_learning import ExpenseApplicationLearningService
|
||||
from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy
|
||||
from app.services.expense_claim_risk_stage import with_risk_business_stage
|
||||
from app.services.travel_reimbursement_calculator import TravelReimbursementCalculatorService
|
||||
@@ -635,6 +637,27 @@ class UserAgentApplicationSlotMixin:
|
||||
|
||||
|
||||
class UserAgentApplicationPersistenceMixin:
|
||||
def _record_expense_application_learning(
|
||||
self,
|
||||
payload: UserAgentRequest,
|
||||
facts: dict[str, str],
|
||||
claim: ExpenseClaim,
|
||||
trusted_current_user: CurrentUserContext | None,
|
||||
*,
|
||||
action_type: str,
|
||||
business_event: BusinessEvent,
|
||||
) -> None:
|
||||
if trusted_current_user is None:
|
||||
return
|
||||
ExpenseApplicationLearningService(self.db).record_action(
|
||||
payload,
|
||||
facts,
|
||||
claim,
|
||||
trusted_current_user,
|
||||
action_type=action_type,
|
||||
business_event=business_event,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_application_edit_claim_id(context_json: dict[str, object]) -> str:
|
||||
if not isinstance(context_json, dict):
|
||||
@@ -703,6 +726,7 @@ class UserAgentApplicationPersistenceMixin:
|
||||
claim: ExpenseClaim,
|
||||
*,
|
||||
submit: bool,
|
||||
learning_current_user: CurrentUserContext | None = None,
|
||||
) -> ExpenseClaim:
|
||||
current_user = self._build_application_current_user(payload)
|
||||
previous_status = str(claim.status or "").strip()
|
||||
@@ -732,7 +756,7 @@ class UserAgentApplicationPersistenceMixin:
|
||||
claim.approval_stage = "待提交"
|
||||
claim.submitted_at = None
|
||||
try:
|
||||
ExpenseApplicationDraftEventService(self.db).record(
|
||||
_, draft_event = ExpenseApplicationDraftEventService(self.db).record(
|
||||
payload,
|
||||
claim,
|
||||
current_user,
|
||||
@@ -740,6 +764,14 @@ class UserAgentApplicationPersistenceMixin:
|
||||
previous_status=previous_status,
|
||||
previous_approval_stage=previous_approval_stage,
|
||||
)
|
||||
self._record_expense_application_learning(
|
||||
payload,
|
||||
facts,
|
||||
claim,
|
||||
learning_current_user,
|
||||
action_type="save_draft",
|
||||
business_event=draft_event,
|
||||
)
|
||||
self.db.commit()
|
||||
self.db.refresh(claim)
|
||||
return claim
|
||||
@@ -749,10 +781,28 @@ class UserAgentApplicationPersistenceMixin:
|
||||
|
||||
from app.services.expense_claims import ExpenseClaimService
|
||||
|
||||
submitted = ExpenseClaimService(self.db).submit_claim(claim.id, current_user)
|
||||
if submitted is None:
|
||||
raise ValueError("未找到可修改的申请单。")
|
||||
return submitted
|
||||
try:
|
||||
submitted = ExpenseClaimService(self.db).submit_claim(
|
||||
claim.id,
|
||||
current_user,
|
||||
correlation_id=payload.run_id,
|
||||
before_commit=(
|
||||
lambda event: self._record_expense_application_learning(
|
||||
payload,
|
||||
facts,
|
||||
claim,
|
||||
learning_current_user,
|
||||
action_type="submit",
|
||||
business_event=event,
|
||||
)
|
||||
),
|
||||
)
|
||||
if submitted is None:
|
||||
raise ValueError("未找到可修改的申请单。")
|
||||
return submitted
|
||||
except Exception:
|
||||
self.db.rollback()
|
||||
raise
|
||||
|
||||
def _create_expense_application_record(
|
||||
self,
|
||||
@@ -760,6 +810,7 @@ class UserAgentApplicationPersistenceMixin:
|
||||
facts: dict[str, str],
|
||||
*,
|
||||
submit: bool,
|
||||
learning_current_user: CurrentUserContext | None = None,
|
||||
) -> ExpenseClaim:
|
||||
current_user = self._build_application_current_user(payload)
|
||||
access_policy = ExpenseClaimAccessPolicy(self.db)
|
||||
@@ -821,19 +872,41 @@ class UserAgentApplicationPersistenceMixin:
|
||||
return existing
|
||||
raise
|
||||
if not submit:
|
||||
draft_event_service.record(
|
||||
_, draft_event = draft_event_service.record(
|
||||
payload,
|
||||
claim,
|
||||
current_user,
|
||||
event_type="claim_draft_created",
|
||||
)
|
||||
self._record_expense_application_learning(
|
||||
payload,
|
||||
facts,
|
||||
claim,
|
||||
learning_current_user,
|
||||
action_type="save_draft",
|
||||
business_event=draft_event,
|
||||
)
|
||||
self.db.commit()
|
||||
self.db.refresh(claim)
|
||||
return claim
|
||||
|
||||
from app.services.expense_claims import ExpenseClaimService
|
||||
|
||||
submitted = ExpenseClaimService(self.db).submit_claim(claim.id, current_user)
|
||||
submitted = ExpenseClaimService(self.db).submit_claim(
|
||||
claim.id,
|
||||
current_user,
|
||||
correlation_id=payload.run_id,
|
||||
before_commit=(
|
||||
lambda event: self._record_expense_application_learning(
|
||||
payload,
|
||||
facts,
|
||||
claim,
|
||||
learning_current_user,
|
||||
action_type="submit",
|
||||
business_event=event,
|
||||
)
|
||||
),
|
||||
)
|
||||
if submitted is None:
|
||||
raise ValueError("未找到可提交的申请单。")
|
||||
return submitted
|
||||
@@ -1212,6 +1285,7 @@ class UserAgentApplicationMixin(UserAgentApplicationSlotMixin, UserAgentApplicat
|
||||
payload: UserAgentRequest,
|
||||
*,
|
||||
risk_flags: list[str],
|
||||
learning_current_user: CurrentUserContext | None = None,
|
||||
) -> UserAgentResponse:
|
||||
facts = self._resolve_expense_application_facts(payload)
|
||||
step = self._resolve_expense_application_step(payload, facts)
|
||||
@@ -1224,6 +1298,7 @@ class UserAgentApplicationMixin(UserAgentApplicationSlotMixin, UserAgentApplicat
|
||||
facts,
|
||||
editable_claim,
|
||||
submit=step == "submitted",
|
||||
learning_current_user=learning_current_user,
|
||||
)
|
||||
facts["application_edit_mode"] = "true"
|
||||
elif step == "submitted":
|
||||
@@ -1236,12 +1311,14 @@ class UserAgentApplicationMixin(UserAgentApplicationSlotMixin, UserAgentApplicat
|
||||
payload,
|
||||
facts,
|
||||
submit=True,
|
||||
learning_current_user=learning_current_user,
|
||||
)
|
||||
else:
|
||||
application_claim = self._create_expense_application_record(
|
||||
payload,
|
||||
facts,
|
||||
submit=False,
|
||||
learning_current_user=learning_current_user,
|
||||
)
|
||||
if application_claim is not None:
|
||||
facts["application_no"] = application_claim.claim_no
|
||||
|
||||
Reference in New Issue
Block a user