feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL 校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计, 优化 agent 运行和编排执行链路,清理旧开发文档,前端新增 系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈 对话框和工作台日期选择器,优化报销创建和审批详情交互, 补充单元测试覆盖。
This commit is contained in:
@@ -57,6 +57,8 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
currentInsight,
|
||||
currentUser,
|
||||
draftClaimId,
|
||||
emitOperationCompleted,
|
||||
emitRequestUpdated,
|
||||
extractReviewAttachmentNames,
|
||||
failCurrentFlowStep,
|
||||
fetchExpenseClaims,
|
||||
@@ -101,6 +103,36 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
|
||||
const pendingAttachmentAssociations = new Map()
|
||||
|
||||
function isSubmittedApplicationDraftPayload(draftPayload) {
|
||||
return (
|
||||
String(draftPayload?.draft_type || '').trim() === 'expense_application'
|
||||
&& String(draftPayload?.status || '').trim() === 'submitted'
|
||||
)
|
||||
}
|
||||
|
||||
function buildOperationFeedbackState(context) {
|
||||
if (!context) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
context,
|
||||
submitting: false,
|
||||
submitted: false,
|
||||
dismissed: false,
|
||||
rating: 0,
|
||||
reason: '',
|
||||
error: ''
|
||||
}
|
||||
}
|
||||
|
||||
function resolveAssistantResultText(payload, fallbackAnswer) {
|
||||
const result = payload?.result && typeof payload.result === 'object' ? payload.result : {}
|
||||
if (isSubmittedApplicationDraftPayload(result.draft_payload)) {
|
||||
return ''
|
||||
}
|
||||
return result.answer || result.message || fallbackAnswer
|
||||
}
|
||||
|
||||
function createPendingAttachmentAssociationId() {
|
||||
return `attachment-association-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
||||
}
|
||||
@@ -411,6 +443,7 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
? initialExtraContext
|
||||
: mergeBusinessTimeIntoExtraContext(initialExtraContext, selectedBusinessTimeContext)
|
||||
const reviewAction = String(extraContext.review_action || '').trim()
|
||||
const feedbackOperationType = String(options.feedbackOperationType || '').trim()
|
||||
const attachmentAssociationConfirmed = Boolean(
|
||||
options.associationConfirmed ||
|
||||
extraContext.attachment_association_confirmed ||
|
||||
@@ -966,7 +999,12 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
const fallbackAnswer = reviewActionResult === 'link_to_existing_draft'
|
||||
? (resultClaimNo ? `已将本次上传的票据关联到草稿 ${resultClaimNo}。` : '已将本次上传的票据关联到现有草稿。')
|
||||
: '智能体已完成处理。'
|
||||
const assistantMessage = createMessage('assistant', payload?.result?.answer || payload?.result?.message || fallbackAnswer, [], {
|
||||
const operationFeedbackContext = String(payload?.status || '').trim() === 'succeeded'
|
||||
? emitOperationCompleted?.(payload, {
|
||||
operationType: feedbackOperationType || reviewActionResult || (files.length ? 'attachment_review' : 'assistant_round')
|
||||
})
|
||||
: null
|
||||
const assistantMessage = createMessage('assistant', resolveAssistantResultText(payload, fallbackAnswer), [], {
|
||||
meta: buildMessageMeta(payload, effectiveFileNames),
|
||||
citations: Array.isArray(payload?.result?.citations) ? payload.result.citations : [],
|
||||
suggestedActions: Array.isArray(payload?.result?.suggested_actions)
|
||||
@@ -981,7 +1019,8 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
fileCount: files.length,
|
||||
rawText
|
||||
}),
|
||||
riskFlags: Array.isArray(payload?.result?.risk_flags) ? payload.result.risk_flags : []
|
||||
riskFlags: Array.isArray(payload?.result?.risk_flags) ? payload.result.risk_flags : [],
|
||||
operationFeedback: buildOperationFeedbackState(operationFeedbackContext)
|
||||
})
|
||||
replaceMessage(pendingMessage.id, assistantMessage)
|
||||
const nextInsight = buildAgentInsight(
|
||||
@@ -996,12 +1035,17 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
completeFlowResult(payload, flowRunDetail)
|
||||
persistSessionState()
|
||||
nextTick(scrollToBottom)
|
||||
|
||||
const resolvedDraftClaimId = String(payload?.result?.draft_payload?.claim_id || draftClaimId.value || '').trim()
|
||||
if (!isKnowledgeSession.value && resolvedDraftClaimId && files.length) {
|
||||
void syncComposerFilesToDraft(resolvedDraftClaimId, files)
|
||||
.then(() => {
|
||||
.then((syncResult) => {
|
||||
persistSessionState()
|
||||
if (detailScopedUpload && Number(syncResult?.uploadedCount || 0) > 0) {
|
||||
emitRequestUpdated?.({
|
||||
claimId: resolvedDraftClaimId,
|
||||
source: 'detail-smart-entry-attachment-sync'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('Failed to persist composer attachments to draft claim:', error)
|
||||
|
||||
Reference in New Issue
Block a user