159 lines
6.0 KiB
JavaScript
159 lines
6.0 KiB
JavaScript
|
|
const EXPENSE_TYPE_LABELS = {
|
|||
|
|
travel: '差旅费',
|
|||
|
|
hotel: '住宿费',
|
|||
|
|
transport: '交通费',
|
|||
|
|
meal: '业务招待费',
|
|||
|
|
entertainment: '业务招待费',
|
|||
|
|
meeting: '会务费',
|
|||
|
|
office: '办公用品费',
|
|||
|
|
training: '培训费',
|
|||
|
|
communication: '通讯费',
|
|||
|
|
welfare: '福利费',
|
|||
|
|
other: '其他费用'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const SLOT_LABELS = {
|
|||
|
|
expense_type: '费用场景',
|
|||
|
|
amount: '申请金额',
|
|||
|
|
time_range: '业务时间',
|
|||
|
|
reason: '申请事由',
|
|||
|
|
attachments: '附件说明',
|
|||
|
|
customer_name: '客户名称',
|
|||
|
|
participants: '参与人员'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const PRE_APPROVAL_TYPES = new Set(['travel', 'meeting', 'office', 'training'])
|
|||
|
|
const ATTACHMENT_REQUIRED_TYPES = new Set(['meeting', 'training'])
|
|||
|
|
|
|||
|
|
export const APPLICATION_EXAMPLES = [
|
|||
|
|
'申请下周去北京做客户现场验收,差旅预算18000元',
|
|||
|
|
'申请上海产品发布会会务费32000元,需要场地和物料',
|
|||
|
|
'申请部门集中采购办公用品4800元,用于新员工入职'
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
export function buildExpenseApplicationOntologyContext(currentUser = {}) {
|
|||
|
|
return {
|
|||
|
|
document_type: 'expense_application',
|
|||
|
|
application_stage: 'pre_approval',
|
|||
|
|
conversation_scenario: 'expense',
|
|||
|
|
entry_source: 'documents_application',
|
|||
|
|
role_codes: Array.isArray(currentUser.roleCodes) ? currentUser.roleCodes : [],
|
|||
|
|
is_admin: Boolean(currentUser.isAdmin),
|
|||
|
|
name: currentUser.name || '',
|
|||
|
|
role: currentUser.role || '',
|
|||
|
|
department: currentUser.department || currentUser.departmentName || '',
|
|||
|
|
department_name: currentUser.department || currentUser.departmentName || '',
|
|||
|
|
position: currentUser.position || '',
|
|||
|
|
grade: currentUser.grade || '',
|
|||
|
|
employee_no: currentUser.employeeNo || currentUser.employee_no || ''
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveEntity(ontology, type) {
|
|||
|
|
const entities = Array.isArray(ontology?.entities) ? ontology.entities : []
|
|||
|
|
return entities.find((item) => item?.type === type) || null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveConstraint(ontology, field) {
|
|||
|
|
const constraints = Array.isArray(ontology?.constraints) ? ontology.constraints : []
|
|||
|
|
return constraints.find((item) => item?.field === field) || null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveExpenseTypeCode(ontology) {
|
|||
|
|
const entity = resolveEntity(ontology, 'expense_type')
|
|||
|
|
return String(entity?.normalized_value || entity?.value || 'other').trim() || 'other'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveExpenseTypeLabel(code) {
|
|||
|
|
return EXPENSE_TYPE_LABELS[String(code || '').trim()] || EXPENSE_TYPE_LABELS.other
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveApplicationAmount(ontology) {
|
|||
|
|
const amountEntity = resolveEntity(ontology, 'amount')
|
|||
|
|
const amountConstraint = resolveConstraint(ontology, 'amount')
|
|||
|
|
const rawValue = amountEntity?.normalized_value || amountEntity?.value || amountConstraint?.value || ''
|
|||
|
|
const numericValue = Number(String(rawValue).replace(/[^\d.]/g, ''))
|
|||
|
|
return {
|
|||
|
|
raw: String(rawValue || '').trim(),
|
|||
|
|
value: Number.isFinite(numericValue) ? numericValue : 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveTimeRangeText(ontology) {
|
|||
|
|
const range = ontology?.time_range || {}
|
|||
|
|
if (range.start_date && range.end_date) {
|
|||
|
|
return range.start_date === range.end_date
|
|||
|
|
? range.start_date
|
|||
|
|
: `${range.start_date} 至 ${range.end_date}`
|
|||
|
|
}
|
|||
|
|
return String(range.raw || '').trim()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function resolveAttachmentPolicy(expenseTypeCode, amount = 0) {
|
|||
|
|
const code = String(expenseTypeCode || '').trim()
|
|||
|
|
if (ATTACHMENT_REQUIRED_TYPES.has(code)) {
|
|||
|
|
return {
|
|||
|
|
level: 'required',
|
|||
|
|
label: '必须提交',
|
|||
|
|
description: code === 'meeting'
|
|||
|
|
? '需补充会议通知、议程、参会范围或预算说明。'
|
|||
|
|
: '需补充培训通知、课程说明、报价或审批依据。'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (code === 'office' && amount >= 5000) {
|
|||
|
|
return {
|
|||
|
|
level: 'required',
|
|||
|
|
label: '必须提交',
|
|||
|
|
description: '办公采购金额较高,需补充采购清单、报价或预算说明。'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (code === 'travel') {
|
|||
|
|
return {
|
|||
|
|
level: 'optional',
|
|||
|
|
label: '说明可选',
|
|||
|
|
description: '可先提交出差目的、时间和预算;行程或邀请材料可作为补充说明。'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
level: 'none',
|
|||
|
|
label: '无需附件',
|
|||
|
|
description: '当前申请事项可先不提交附件,后续报销阶段再按票据要求补充。'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function buildApplicationFieldsFromOntology(ontology, prompt, currentUser = {}) {
|
|||
|
|
const expenseTypeCode = resolveExpenseTypeCode(ontology)
|
|||
|
|
const amount = resolveApplicationAmount(ontology)
|
|||
|
|
const locationEntity = resolveEntity(ontology, 'location')
|
|||
|
|
const documentTypeEntity = resolveEntity(ontology, 'document_type')
|
|||
|
|
const workflowStageEntity = resolveEntity(ontology, 'workflow_stage')
|
|||
|
|
const attachmentPolicy = resolveAttachmentPolicy(expenseTypeCode, amount.value)
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
documentType: documentTypeEntity?.normalized_value || 'expense_application',
|
|||
|
|
documentTypeLabel: documentTypeEntity?.value || '费用申请',
|
|||
|
|
workflowStage: workflowStageEntity?.normalized_value || 'pre_approval',
|
|||
|
|
workflowStageLabel: workflowStageEntity?.value || '前置申请',
|
|||
|
|
expenseTypeCode,
|
|||
|
|
expenseTypeLabel: resolveExpenseTypeLabel(expenseTypeCode),
|
|||
|
|
amount: amount.value,
|
|||
|
|
amountDisplay: amount.value ? `¥${amount.value.toLocaleString('zh-CN')}` : '待补充',
|
|||
|
|
timeRange: resolveTimeRangeText(ontology) || '待补充',
|
|||
|
|
location: locationEntity?.normalized_value || locationEntity?.value || '待补充',
|
|||
|
|
reason: String(prompt || '').trim() || '待补充',
|
|||
|
|
applicant: currentUser.name || currentUser.username || '当前用户',
|
|||
|
|
department: currentUser.department || currentUser.departmentName || '待补充',
|
|||
|
|
preApprovalRequired: PRE_APPROVAL_TYPES.has(expenseTypeCode),
|
|||
|
|
attachmentPolicy,
|
|||
|
|
missingSlots: normalizeMissingSlots(ontology?.missing_slots || [])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function normalizeMissingSlots(slots = []) {
|
|||
|
|
const normalized = Array.isArray(slots) ? slots : []
|
|||
|
|
return normalized.map((item) => ({
|
|||
|
|
key: String(item || '').trim(),
|
|||
|
|
label: SLOT_LABELS[String(item || '').trim()] || String(item || '').trim()
|
|||
|
|
})).filter((item) => item.key)
|
|||
|
|
}
|