156 lines
5.6 KiB
JavaScript
156 lines
5.6 KiB
JavaScript
|
|
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(),
|
|||
|
|
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}`)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|