function normalizeText(value) { return String(value || '').trim() } function uniqueTexts(values) { return [...new Set(values.map((item) => normalizeText(item)).filter(Boolean))] } function isPlaceholderValue(value) { const text = normalizeText(value) if (!text) { return true } return ['待补充', '暂无', '无', '未知', '处理中'].includes(text.replace(/\s+/g, '')) } function isApplicationDocumentRequest(requestModel) { const documentType = normalizeText( requestModel?.documentTypeCode || requestModel?.document_type_code || requestModel?.documentType || requestModel?.document_type ).toLowerCase() const claimNo = normalizeText(requestModel?.claimNo || requestModel?.claim_no || requestModel?.documentNo).toUpperCase() return documentType === 'application' || claimNo.startsWith('AP-') || claimNo.startsWith('APP-') } function isHotelExpenseItem(item) { const text = [ item?.itemType, item?.typeCode, item?.name, item?.category, item?.desc, item?.itemReason ].map((value) => normalizeText(value)).join(' ') return /hotel_ticket|hotel|住宿|酒店|水单/.test(text) } export function buildTravelReceiptMaterialPrompts(requestModel, items) { if (isApplicationDocumentRequest(requestModel)) { return [] } const normalizedItems = Array.isArray(items) ? items : [] const missingHotelItems = normalizedItems.filter( (item) => !item?.isSystemGenerated && isHotelExpenseItem(item) && isPlaceholderValue(item.invoiceId) ) if (!missingHotelItems.length) { return [] } return [ `当前包含 ${missingHotelItems.length} 条住宿费用明细,但暂未关联住宿发票或酒店水单。请补充住宿材料,避免后续被退回补件。` ] } function profileMetric(profile, key) { const profiles = Array.isArray(profile?.profiles) ? profile.profiles : [] for (const item of profiles) { const metrics = item?.metrics && typeof item.metrics === 'object' ? item.metrics : {} const value = Number(metrics[key]) if (Number.isFinite(value) && value > 0) { return value } } return 0 } function profileReviewSuggestionTexts(profile) { const suggestions = Array.isArray(profile?.review_suggestions) ? profile.review_suggestions : Array.isArray(profile?.reviewSuggestions) ? profile.reviewSuggestions : [] return suggestions .map((item) => normalizeText(item?.message || item?.title || item?.label)) .filter(Boolean) } function profileRiskTagTexts(profile) { const tags = Array.isArray(profile?.profile_tags) ? profile.profile_tags : Array.isArray(profile?.profileTags) ? profile.profileTags : [] return tags .filter((tag) => normalizeText(tag?.polarity) === 'risk') .map((tag) => normalizeText(tag?.reason || tag?.display_label || tag?.label)) .filter(Boolean) } export function buildEmployeeProfileAdviceItems(profile) { if (!profile || typeof profile !== 'object') { return [] } const returnCount = profileMetric(profile, 'return_count') const missingAttachmentCount = profileMetric(profile, 'missing_attachment_count') const invoiceMismatchCount = profileMetric(profile, 'invoice_mismatch_count') const missingContextCount = profileMetric(profile, 'missing_business_context_count') const items = [] if (returnCount > 0) { items.push(`历史退单建议:近 90 天存在 ${returnCount} 次退单或退回记录,提交前重点复核退回原因对应的票据、事由和说明,避免重复被退。`) } if (missingAttachmentCount > 0 || missingContextCount > 0) { items.push(`材料完整性建议:历史材料或业务上下文缺失累计 ${missingAttachmentCount + missingContextCount} 项,本次提交前请重点核对附件、事由、地点和补充说明。`) } if (invoiceMismatchCount > 0) { items.push(`票据一致性建议:历史存在 ${invoiceMismatchCount} 次票据不一致记录,本次请重点核对票据日期、城市、金额和费用明细。`) } return uniqueTexts([ ...items, ...profileReviewSuggestionTexts(profile), ...profileRiskTagTexts(profile) ]).slice(0, 4) }