feat(web): 工作台 AI 模式报销预审与文档查询模型拆分
- 新增 aiApplicationPrecheckModel/aiDocumentQueryModel/aiApplicationPreviewActions/aiConversationHtmlRenderer 四个独立模型与服务,按职责从主组件拆出 - PersonalWorkbenchAiMode 接入拆分后的预审、文档查询与 HTML 渲染逻辑,配合 markdown 工具增强结构化展示 - 文档中心与归档筛选、风险可见性、申请预览等工具同步适配,补充对应单元测试 - 新增 AI 文档卡片背景资源
This commit is contained in:
136
web/src/services/aiApplicationPreviewActions.js
Normal file
136
web/src/services/aiApplicationPreviewActions.js
Normal file
@@ -0,0 +1,136 @@
|
||||
import { runOrchestrator } from './orchestrator.js'
|
||||
import {
|
||||
buildApplicationPreviewRows,
|
||||
buildApplicationPreviewSubmitText,
|
||||
normalizeApplicationPreview
|
||||
} from '../utils/expenseApplicationPreview.js'
|
||||
|
||||
export const AI_APPLICATION_ACTION_SAVE_DRAFT = 'ai_application_save_draft'
|
||||
export const AI_APPLICATION_ACTION_SUBMIT = 'ai_application_submit'
|
||||
|
||||
function normalizeText(value) {
|
||||
return String(value || '').trim()
|
||||
}
|
||||
|
||||
function resolveUserValue(user = {}, ...keys) {
|
||||
for (const key of keys) {
|
||||
const value = normalizeText(user?.[key])
|
||||
if (value) return value
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function buildClientTimeContext() {
|
||||
const now = new Date()
|
||||
const locale =
|
||||
typeof navigator !== 'undefined' && typeof navigator.language === 'string'
|
||||
? navigator.language
|
||||
: 'zh-CN'
|
||||
|
||||
return {
|
||||
client_now_iso: now.toISOString(),
|
||||
client_timezone_offset_minutes: now.getTimezoneOffset(),
|
||||
client_locale: locale
|
||||
}
|
||||
}
|
||||
|
||||
function buildApplicationPreviewSaveText(preview = {}) {
|
||||
const rows = buildApplicationPreviewRows(preview)
|
||||
return [
|
||||
'费用申请保存草稿',
|
||||
...rows.map((row) => `${row.label}:${row.value}`),
|
||||
'',
|
||||
'保存草稿'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
export function buildAiApplicationPreviewActionText(actionType, preview = {}) {
|
||||
const normalized = normalizeApplicationPreview(preview)
|
||||
return actionType === AI_APPLICATION_ACTION_SUBMIT
|
||||
? buildApplicationPreviewSubmitText(normalized)
|
||||
: buildApplicationPreviewSaveText(normalized)
|
||||
}
|
||||
|
||||
export function buildAiApplicationPreviewActionPayload({
|
||||
actionType,
|
||||
applicationPreview,
|
||||
currentUser = {},
|
||||
conversationId = '',
|
||||
draftPayload = null
|
||||
} = {}) {
|
||||
const normalizedPreview = normalizeApplicationPreview(applicationPreview || {})
|
||||
const message = buildAiApplicationPreviewActionText(actionType, normalizedPreview)
|
||||
const username = resolveUserValue(currentUser, 'username', 'account', 'email', 'name') || 'anonymous'
|
||||
const name = resolveUserValue(currentUser, 'name', 'username')
|
||||
const employeeNo = resolveUserValue(currentUser, 'employeeNo', 'employee_no')
|
||||
const managerName = resolveUserValue(currentUser, 'managerName', 'manager_name', 'directManagerName', 'direct_manager_name')
|
||||
const departmentName = resolveUserValue(currentUser, 'departmentName', 'department_name', 'department')
|
||||
const position = resolveUserValue(currentUser, 'position', 'employeePosition', 'employee_position')
|
||||
const grade = resolveUserValue(currentUser, 'grade', 'employeeGrade', 'employee_grade')
|
||||
const roleCodes = Array.isArray(currentUser.roleCodes)
|
||||
? currentUser.roleCodes.map((item) => normalizeText(item)).filter(Boolean)
|
||||
: []
|
||||
const draftClaimId = normalizeText(draftPayload?.claim_id || draftPayload?.claimId)
|
||||
const isSubmit = actionType === AI_APPLICATION_ACTION_SUBMIT
|
||||
|
||||
return {
|
||||
source: 'user_message',
|
||||
user_id: username,
|
||||
conversation_id: normalizeText(conversationId) || null,
|
||||
message,
|
||||
context_json: {
|
||||
role_codes: roleCodes,
|
||||
is_admin: Boolean(currentUser.isAdmin),
|
||||
name,
|
||||
role: resolveUserValue(currentUser, 'role'),
|
||||
department: departmentName,
|
||||
department_name: departmentName,
|
||||
position,
|
||||
employee_position: position,
|
||||
employeePosition: position,
|
||||
grade,
|
||||
employee_grade: grade,
|
||||
employeeGrade: grade,
|
||||
employee_no: employeeNo,
|
||||
employeeNo,
|
||||
manager_name: managerName,
|
||||
managerName,
|
||||
direct_manager_name: managerName,
|
||||
directManagerName: managerName,
|
||||
cost_center: resolveUserValue(currentUser, 'costCenter', 'cost_center'),
|
||||
finance_owner_name: resolveUserValue(currentUser, 'financeOwnerName', 'finance_owner_name'),
|
||||
...buildClientTimeContext(),
|
||||
session_type: 'application',
|
||||
entry_source: 'workbench_ai_inline',
|
||||
source: 'workbench',
|
||||
document_type: 'expense_application',
|
||||
application_stage: 'expense_application',
|
||||
user_input_text: message,
|
||||
application_preview: normalizedPreview,
|
||||
...(isSubmit
|
||||
? {}
|
||||
: {
|
||||
application_action: 'save_draft',
|
||||
application_save_mode: true
|
||||
}),
|
||||
...(draftClaimId
|
||||
? {
|
||||
application_edit_claim_id: draftClaimId,
|
||||
draft_claim_id: draftClaimId,
|
||||
selected_claim_id: draftClaimId,
|
||||
application_edit_mode: true
|
||||
}
|
||||
: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function runAiApplicationPreviewAction(params = {}, options = {}) {
|
||||
return runOrchestrator(buildAiApplicationPreviewActionPayload(params), {
|
||||
timeoutMs: params.actionType === AI_APPLICATION_ACTION_SUBMIT ? 120000 : 75000,
|
||||
timeoutMessage: params.actionType === AI_APPLICATION_ACTION_SUBMIT
|
||||
? '申请提交处理超时,请稍后重试。'
|
||||
: '申请草稿保存超时,请稍后重试。',
|
||||
...options
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user