Files
X-Financial/web/src/services/aiApplicationPreviewActions.js
caoxiaozhu c4b5fcc067 feat(web): AI 工作台多 task 串行推进与会话适配
- useWorkbenchAiApplicationPreviewFlow/useWorkbenchAiActionRouter/useWorkbenchAiCommandIntents 支持 task1 完成后自动推进 task2,确认按钮直接拉起申请预览,草稿/提交成功后继续推进下一 task
- workbenchAiIntentPlannerModel/workbenchAiMessageModel/workbenchAiCommandIntentModel 适配多 task 意图规划与消息结构
- aiApplicationPreviewActions/aiApplicationPrecheckModel/aiExpenseDraftModel/aiWorkbenchConversationStore 草稿与会话存储适配
- PersonalWorkbenchAiMode 与样式适配,更新 preview-actions/expense-draft/conversation-store/fast-preview/action-router/command-intent/intent-planner 测试
2026-06-26 22:42:23 +08:00

145 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { apiRequest } from './api.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
const applicationEditableFields = Array.isArray(normalizedPreview.editableFields)
? normalizedPreview.editableFields.map((field) => normalizeText(field)).filter(Boolean)
: []
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,
...(applicationEditableFields.length
? { application_editable_fields: applicationEditableFields }
: {}),
...(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 = {}) {
const payload = buildAiApplicationPreviewActionPayload(params)
const isSubmit = params.actionType === AI_APPLICATION_ACTION_SUBMIT
return apiRequest('/reimbursements/application-preview-action', {
method: 'POST',
body: JSON.stringify(payload),
timeoutMs: isSubmit ? 45000 : 30000,
timeoutMessage: isSubmit ? '申请提交处理超时,请稍后重试。' : '申请草稿保存超时,请稍后重试。',
...options
})
}