feat: 重构报销单服务并完善前端提交与审核交互
重构 expense_claims 服务模块结构并优化差旅票据审核逻辑, 增强用户代理服务的票据类型识别,前端报销创建页面拆分为 附件模型和会话模型模块,重构提交编排器和草稿关联确认流 程,更新知识库索引,补充单元测试。
This commit is contained in:
@@ -224,14 +224,22 @@ export function resolveReviewMissingSlotCards(reviewPayload) {
|
||||
|
||||
export function resolveReviewExtraMissingLabels(reviewPayload) {
|
||||
const labels = Array.isArray(reviewPayload?.missing_slots)
|
||||
? reviewPayload.missing_slots.map((item) => String(item || '').trim()).filter(Boolean)
|
||||
? reviewPayload.missing_slots
|
||||
.map((item) => {
|
||||
if (item && typeof item === 'object') {
|
||||
return String(item.label || item.title || item.key || '').trim()
|
||||
}
|
||||
return String(item || '').trim()
|
||||
})
|
||||
.filter(Boolean)
|
||||
: []
|
||||
if (!labels.length) return []
|
||||
|
||||
const slotLabels = new Set(
|
||||
(Array.isArray(reviewPayload?.slot_cards) ? reviewPayload.slot_cards : [])
|
||||
.map((item) => String(item?.label || item?.key || '').trim())
|
||||
.filter(Boolean)
|
||||
(Array.isArray(reviewPayload?.slot_cards) ? reviewPayload.slot_cards : []).flatMap((item) => [
|
||||
String(item?.label || '').trim(),
|
||||
String(item?.key || '').trim()
|
||||
]).filter(Boolean)
|
||||
)
|
||||
return labels.filter((label) => !slotLabels.has(label))
|
||||
}
|
||||
@@ -1239,23 +1247,66 @@ function buildReviewPlainFollowupItem(item, pendingMode) {
|
||||
}
|
||||
}
|
||||
|
||||
const REVIEW_PENDING_SUMMARY_TEMPLATES = [
|
||||
({ issueSummary }) => `当前还有 ${issueSummary}。请核查对话中的文字说明;如果想先暂存,也可以点击对话文字中的“草稿”。`,
|
||||
({ issueSummary }) => `我这边看到还有 ${issueSummary},建议先把下方内容核对一下;暂时不处理也没关系,可以点击“草稿”先保存。`,
|
||||
({ issueSummary }) => `下方还有 ${issueSummary},需要你确认。信息没补齐前可以先核查说明,后续需要暂存时点“草稿”。`,
|
||||
({ issueSummary }) => `这笔报销还有 ${issueSummary},尚未完全确认。请先看一下下面的补充项;需要中途保存时,可以点“草稿”。`,
|
||||
({ issueSummary }) => `目前还有 ${issueSummary}。你可以先按下面的提示补充,也可以稍后再处理,点击“草稿”即可暂存当前信息。`,
|
||||
({ issueSummary }) => `还有 ${issueSummary},建议先核对下面说明;如果票据或金额暂时不全,可以通过“草稿”保留当前进度。`,
|
||||
({ issueSummary }) => `这次识别结果里还有 ${issueSummary}。请重点看下面几项,暂不提交时可以点“草稿”保存。`,
|
||||
({ issueSummary }) => `我还需要你确认 ${issueSummary}。下面列出了具体内容;如果现在不方便补齐,可以先点“草稿”。`,
|
||||
({ issueSummary }) => `当前还有 ${issueSummary},需要进一步处理。请根据下面提示核查,待补充完再继续;临时保存可点击“草稿”。`,
|
||||
({ issueSummary }) => `本次报销还有 ${issueSummary},请先检查下面的补充项;想先留存当前识别结果时可以点“草稿”。`
|
||||
]
|
||||
|
||||
function buildStableTemplateIndex(signature, total) {
|
||||
const source = String(signature || '')
|
||||
let hash = 0
|
||||
for (let index = 0; index < source.length; index += 1) {
|
||||
hash = ((hash << 5) - hash + source.charCodeAt(index)) >>> 0
|
||||
}
|
||||
return total ? hash % total : 0
|
||||
}
|
||||
|
||||
function buildReviewPendingSummary(pendingCount, riskCount, signature = '') {
|
||||
const issueParts = []
|
||||
if (pendingCount) {
|
||||
issueParts.push(`${pendingCount} 项信息待补充`)
|
||||
}
|
||||
if (riskCount) {
|
||||
issueParts.push(`${riskCount} 条风险提醒`)
|
||||
}
|
||||
const issueSummary = issueParts.length ? issueParts.join('、') : '一些细节还需要进一步确认'
|
||||
const templateIndex = buildStableTemplateIndex(signature || issueSummary, REVIEW_PENDING_SUMMARY_TEMPLATES.length)
|
||||
return REVIEW_PENDING_SUMMARY_TEMPLATES[templateIndex]({ issueSummary })
|
||||
}
|
||||
|
||||
export function buildReviewPlainFollowupCopy(reviewPayload) {
|
||||
const todoItems = buildReviewTodoItems(reviewPayload)
|
||||
const pendingCount = countReviewPendingItems(reviewPayload)
|
||||
const riskBriefs = resolvePresentationRiskBriefs(reviewPayload)
|
||||
const extraMissingCount = resolveReviewExtraMissingLabels(reviewPayload).length
|
||||
|
||||
if (pendingCount || resolveReviewExtraMissingLabels(reviewPayload).length) {
|
||||
if (pendingCount || extraMissingCount) {
|
||||
const summarySignature = [
|
||||
pendingCount || extraMissingCount,
|
||||
riskBriefs.length,
|
||||
...todoItems.map((item) => `${item.key}:${item.title}:${item.status}`)
|
||||
].join('|')
|
||||
return {
|
||||
lead: '我还需要你核查或补充下面这些信息:',
|
||||
lead: '补充信息:',
|
||||
tone: 'danger',
|
||||
summary: buildReviewPendingSummary(pendingCount || extraMissingCount, riskBriefs.length, summarySignature),
|
||||
items: todoItems.map((item) => buildReviewPlainFollowupItem(item, true)),
|
||||
notes: riskBriefs.length
|
||||
? [`另外还有 ${riskBriefs.length} 条风险提醒,提交前建议一起确认。`]
|
||||
: []
|
||||
notes: []
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
lead: todoItems.length ? '我已整理出当前识别到的关键信息:' : '当前关键信息已基本整理完成。',
|
||||
tone: 'neutral',
|
||||
summary: '',
|
||||
items: todoItems.map((item) => buildReviewPlainFollowupItem(item, false)),
|
||||
notes: [
|
||||
reviewPayload?.can_proceed ? '确认无误后,可以继续下一步。' : '',
|
||||
|
||||
Reference in New Issue
Block a user