import { DOCUMENT_TYPE_LABELS, EXPENSE_TYPE_LABELS } from './travelReimbursementReviewConstants.js' export function cloneReviewDocumentDrafts(items) { return (Array.isArray(items) ? items : []).map((item) => ({ ...item, warnings: Array.isArray(item?.warnings) ? [...item.warnings] : [], fields: Array.isArray(item?.fields) ? item.fields.map((field) => ({ label: String(field?.label || '').trim(), value: String(field?.value || ''), source: String(field?.source || 'ocr').trim() || 'ocr' })) : [] })) } export function buildReviewDocumentDrafts(reviewPayload) { return buildReviewDocumentSummaries(reviewPayload).map((item) => ({ index: Number(item.index || 0), filename: String(item.filename || '').trim(), document_type: String(item.document_type || 'other').trim() || 'other', suggested_expense_type: String(item.suggested_expense_type || 'other').trim() || 'other', scene_label: String(item.scene_label || '').trim(), summary: String(item.summary || '').trim(), confidenceLabel: String(item.confidenceLabel || '').trim(), documentTypeLabel: String(item.documentTypeLabel || '').trim(), expenseTypeLabel: String(item.expenseTypeLabel || '').trim(), preview_kind: String(item.preview_kind || '').trim(), preview_data_url: String(item.preview_data_url || '').trim(), preview_url: String(item.preview_url || '').trim(), warnings: Array.isArray(item.warnings) ? [...item.warnings] : [], fields: Array.isArray(item.fields) ? item.fields.map((field) => ({ label: String(field?.label || '').trim(), value: String(field?.value || ''), source: String(field?.source || 'ocr').trim() || 'ocr' })) : [] })) } export function normalizeReviewDocumentComparableValue(item) { return { index: Number(item?.index || 0), filename: String(item?.filename || '').trim(), scene_label: String(item?.scene_label || '').trim(), summary: String(item?.summary || '').trim(), fields: (Array.isArray(item?.fields) ? item.fields : []).map((field) => ({ label: String(field?.label || '').trim(), value: String(field?.value || '').trim() })) } } export function buildReviewDocumentCorrectionLines(baseDrafts, nextDrafts) { const baseMap = new Map( cloneReviewDocumentDrafts(baseDrafts).map((item) => [`${item.index}:${item.filename}`, item]) ) return cloneReviewDocumentDrafts(nextDrafts).reduce((lines, item) => { const key = `${item.index}:${item.filename}` const base = baseMap.get(key) const changes = [] const nextSceneLabel = String(item.scene_label || '').trim() const baseSceneLabel = String(base?.scene_label || '').trim() const nextSummary = String(item.summary || '').trim() const baseSummary = String(base?.summary || '').trim() if (nextSceneLabel !== baseSceneLabel) { changes.push(`票据场景:${nextSceneLabel || '待补充'}`) } if (nextSummary !== baseSummary) { changes.push(`识别摘要:${nextSummary || '待补充'}`) } const baseFieldMap = new Map( (Array.isArray(base?.fields) ? base.fields : []).map((field) => [ String(field?.label || '').trim(), String(field?.value || '').trim() ]) ) for (const field of Array.isArray(item.fields) ? item.fields : []) { const label = String(field?.label || '').trim() if (!label) continue const nextValue = String(field?.value || '').trim() const baseValue = baseFieldMap.get(label) || '' if (nextValue !== baseValue) { changes.push(`${label}:${nextValue || '待补充'}`) } } if (changes.length) { lines.push(`第${item.index}张票据(${item.filename}):${changes.join(';')}`) } return lines }, []) } export function buildReviewDocumentCorrectionMessage(baseDrafts, nextDrafts) { const lines = buildReviewDocumentCorrectionLines(baseDrafts, nextDrafts) if (!lines.length) { return '' } return `请同步修正逐票据识别结果:\n${lines.join('\n')}` } export function buildReviewDocumentCorrectionContext(drafts) { return cloneReviewDocumentDrafts(drafts).map((item) => ({ index: item.index, filename: item.filename, scene_label: String(item.scene_label || '').trim(), summary: String(item.summary || '').trim(), fields: item.fields.map((field) => ({ label: String(field.label || '').trim(), value: String(field.value || '').trim() })) })) } export function formatConfidenceLabel(value) { const score = Number(value || 0) if (!score) return '待补充' return `${Math.round(score * 100)}%` } export function resolveDocumentTypeLabel(type) { return DOCUMENT_TYPE_LABELS[String(type || '').trim()] || DOCUMENT_TYPE_LABELS.other } export function resolveExpenseTypeLabel(type, fallbackLabel = '') { const normalized = String(type || '').trim() return EXPENSE_TYPE_LABELS[normalized] || String(fallbackLabel || '').trim() || EXPENSE_TYPE_LABELS.other } export function buildReviewDocumentSummaries(reviewPayload) { const docs = Array.isArray(reviewPayload?.document_cards) ? reviewPayload.document_cards : [] return docs.map((item) => { const fields = Array.isArray(item.fields) ? item.fields : [] return { ...item, documentTypeLabel: resolveDocumentTypeLabel(item.document_type), expenseTypeLabel: resolveExpenseTypeLabel(item.suggested_expense_type, item.scene_label), confidenceLabel: formatConfidenceLabel(item.avg_score), lines: fields .filter((field) => String(field?.value || '').trim()) .map((field) => `${field.label}:${field.value}`) } }) }