feat: 重构报销单服务并完善前端提交与审核交互

重构 expense_claims 服务模块结构并优化差旅票据审核逻辑,
增强用户代理服务的票据类型识别,前端报销创建页面拆分为
附件模型和会话模型模块,重构提交编排器和草稿关联确认流
程,更新知识库索引,补充单元测试。
This commit is contained in:
caoxiaozhu
2026-05-22 08:58:59 +08:00
parent f6f787ff38
commit 5fe3b201d9
42 changed files with 13697 additions and 9496 deletions

View File

@@ -0,0 +1,34 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
ATTACHMENT_ASSOCIATION_CONFIRM_HREF,
buildAttachmentAssociationConfirmationMessage
} from '../src/views/scripts/travelReimbursementAttachmentModel.js'
test('attachment association prompt prints recognized receipt details before confirmation link', () => {
const message = buildAttachmentAssociationConfirmationMessage({
claimNo: 'EXP-202605-001',
fileNames: ['train-ticket.pdf'],
ocrDocuments: [
{
filename: 'train-ticket.pdf',
document_type: 'train_ticket',
scene_label: '差旅票据',
summary: '铁路电子客票 武汉-上海 票价 354 元',
document_fields: [
{ key: 'route', label: '行程', value: '武汉-上海' },
{ key: 'amount', label: '票价', value: '354.00' },
{ key: 'date', label: '乘车日期', value: '2026-02-20' }
]
}
]
})
assert.match(message, /已识别附件信息:/)
assert.match(message, /附件类型:差旅票据/)
assert.match(message, /行程:武汉-上海/)
assert.match(message, /票价354.00/)
assert.match(message, /草稿单号EXP-202605-001/)
assert.match(message, new RegExp(`\\[确认\\]\\(${ATTACHMENT_ASSOCIATION_CONFIRM_HREF}\\)`))
})

View File

@@ -156,19 +156,19 @@ test('review drawer save action is disabled while receipt recognition is submitt
)
})
test('draft creation waits for composer attachments to be persisted before leaving submit state', () => {
test('draft creation starts composer attachment persistence after response rendering', () => {
assert.match(
submitComposerScript,
/try \{\s*await syncComposerFilesToDraft\(resolvedDraftClaimId, files\)\s*\} catch \(error\) \{/s
/void syncComposerFilesToDraft\(resolvedDraftClaimId, files\)\s*\.then\(\(\) => \{\s*persistSessionState\(\)\s*\}\)\s*\.catch\(\(error\) => \{/s
)
assert.doesNotMatch(
submitComposerScript,
/syncComposerFilesToDraft\(resolvedDraftClaimId, files\)\.catch/
/await syncComposerFilesToDraft\(resolvedDraftClaimId, files\)/
)
assert.ok(
submitComposerScript.indexOf('await syncComposerFilesToDraft(resolvedDraftClaimId, files)') <
submitComposerScript.indexOf('submitting.value = false'),
'attachment persistence should finish before submit state is cleared'
submitComposerScript.indexOf('replaceMessage(pendingMessage.id, assistantMessage)') <
submitComposerScript.indexOf('void syncComposerFilesToDraft(resolvedDraftClaimId, files)'),
'assistant response should render before background attachment persistence starts'
)
assert.match(attachmentsScript, /function normalizeAttachmentMatchName\(value\)/)
assert.match(attachmentsScript, /const normalizedMatchBuckets = new Map\(\)/)