Refine travel reimbursement steward flow
Align planner, runtime rules, and policy assets so travel guidance matches the updated reimbursement workflow.
This commit is contained in:
@@ -98,8 +98,15 @@ const FIELD_VALUE_DISPLAY_CONFIG = {
|
||||
}
|
||||
}
|
||||
|
||||
export function buildStewardPlanRequest({ rawText = '', files = [], currentUser = {} } = {}) {
|
||||
export function buildStewardPlanRequest({
|
||||
rawText = '',
|
||||
files = [],
|
||||
currentUser = {},
|
||||
conversationId = '',
|
||||
stewardState = null
|
||||
} = {}) {
|
||||
const safeFiles = Array.isArray(files) ? files : []
|
||||
const normalizedConversationId = String(conversationId || '').trim()
|
||||
return {
|
||||
message: String(rawText || '').trim(),
|
||||
user_id: String(currentUser.username || currentUser.name || 'anonymous').trim() || 'anonymous',
|
||||
@@ -111,6 +118,8 @@ export function buildStewardPlanRequest({ rawText = '', files = [], currentUser
|
||||
context_json: {
|
||||
entry_source: 'workbench',
|
||||
session_type: 'steward',
|
||||
conversation_id: normalizedConversationId,
|
||||
steward_state: stewardState && typeof stewardState === 'object' ? stewardState : null,
|
||||
role_codes: Array.isArray(currentUser.roleCodes) ? currentUser.roleCodes : [],
|
||||
username: currentUser.username || '',
|
||||
name: currentUser.name || currentUser.username || '',
|
||||
@@ -124,9 +133,13 @@ export function normalizeStewardPlan(rawPlan = {}, options = {}) {
|
||||
const visibleThinkingEventCount = Number.isFinite(options.visibleThinkingEventCount)
|
||||
? Number(options.visibleThinkingEventCount)
|
||||
: Number(rawPlan.visibleThinkingEventCount || rawPlan.visible_thinking_event_count || 0)
|
||||
const pendingFlowConfirmation = normalizePendingFlowConfirmation(rawPlan)
|
||||
return {
|
||||
planId: String(rawPlan.plan_id || rawPlan.planId || ''),
|
||||
planStatus: String(rawPlan.plan_status || rawPlan.planStatus || ''),
|
||||
nextAction: String(rawPlan.next_action || rawPlan.nextAction || ''),
|
||||
conversationId: String(rawPlan.conversation_id || rawPlan.conversationId || ''),
|
||||
stewardState: rawPlan.steward_state || rawPlan.stewardState || null,
|
||||
summary: String(rawPlan.summary || ''),
|
||||
visibleThinkingEventCount,
|
||||
initialSummaryOnly: Boolean(rawPlan.initial_summary_only || rawPlan.initialSummaryOnly || options.initialSummaryOnly),
|
||||
@@ -185,12 +198,17 @@ export function normalizeStewardPlan(rawPlan = {}, options = {}) {
|
||||
: [],
|
||||
confirmationGroups: Array.isArray(rawPlan.confirmation_groups)
|
||||
? rawPlan.confirmation_groups
|
||||
: []
|
||||
: [],
|
||||
pendingFlowConfirmation,
|
||||
candidateFlows: pendingFlowConfirmation.candidateFlows
|
||||
}
|
||||
}
|
||||
|
||||
export function buildStewardPlanMessageText(plan) {
|
||||
const normalized = normalizeStewardPlan(plan)
|
||||
if (isPendingFlowConfirmationPlan(normalized)) {
|
||||
return buildPendingFlowConfirmationMessageText(normalized)
|
||||
}
|
||||
const nextContext = resolveNextActionContext(normalized)
|
||||
const orderedTasks = buildOrderedStewardTasks(normalized, nextContext?.task)
|
||||
const taskLines = orderedTasks.map((task, index) =>
|
||||
@@ -266,6 +284,28 @@ export function formatStewardOntologyFields(fields = {}, taskType = '') {
|
||||
|
||||
export function buildStewardSuggestedActions(plan) {
|
||||
const normalized = normalizeStewardPlan(plan)
|
||||
if (isPendingFlowConfirmationPlan(normalized)) {
|
||||
return normalized.candidateFlows.map((flow) => ({
|
||||
label: flow.label,
|
||||
description: flow.reason || '选择后小财管家会继续整理对应流程材料。',
|
||||
icon: flow.flowId === 'travel_application'
|
||||
? 'mdi mdi-file-plus-outline'
|
||||
: 'mdi mdi-receipt-text-plus-outline',
|
||||
action_type: ASSISTANT_SCOPE_ACTION_SWITCH,
|
||||
payload: {
|
||||
steward_confirm_flow: true,
|
||||
steward_plan_id: normalized.planId,
|
||||
flow_id: flow.flowId,
|
||||
session_type: flow.flowId === 'travel_application'
|
||||
? SESSION_TYPE_APPLICATION
|
||||
: SESSION_TYPE_EXPENSE,
|
||||
selected_flow_label: flow.label,
|
||||
carry_text: flow.label,
|
||||
auto_submit: true,
|
||||
steward_state: normalized.stewardState || null
|
||||
}
|
||||
}))
|
||||
}
|
||||
const nextContext = resolveNextActionContext(normalized)
|
||||
if (!nextContext) {
|
||||
return []
|
||||
@@ -300,6 +340,70 @@ export function buildStewardSuggestedActions(plan) {
|
||||
]
|
||||
}
|
||||
|
||||
function normalizePendingFlowConfirmation(rawPlan = {}) {
|
||||
const rawPending = rawPlan.pending_flow_confirmation || rawPlan.pendingFlowConfirmation || {}
|
||||
const rawCandidates = Array.isArray(rawPlan.candidate_flows || rawPlan.candidateFlows)
|
||||
? rawPlan.candidate_flows || rawPlan.candidateFlows
|
||||
: rawPending?.candidate_flows || rawPending?.candidateFlows || []
|
||||
const candidateFlows = Array.isArray(rawCandidates)
|
||||
? rawCandidates
|
||||
.map((item) => normalizeCandidateFlow(item))
|
||||
.filter((item) => item.flowId)
|
||||
: []
|
||||
return {
|
||||
status: String(rawPending?.status || '').trim(),
|
||||
sourceMessage: String(rawPending?.source_message || rawPending?.sourceMessage || '').trim(),
|
||||
reason: String(rawPending?.reason || '').trim(),
|
||||
candidateFlows
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCandidateFlow(item = {}) {
|
||||
const flowId = String(item.flow_id || item.flowId || '').trim()
|
||||
if (!['travel_application', 'travel_reimbursement'].includes(flowId)) {
|
||||
return { flowId: '' }
|
||||
}
|
||||
return {
|
||||
flowId,
|
||||
label: String(item.label || (flowId === 'travel_application' ? '补办出差申请' : '发起费用报销')).trim(),
|
||||
confidence: Number(item.confidence || 0),
|
||||
reason: String(item.reason || '').trim(),
|
||||
ontologyFields: item.ontology_fields || item.ontologyFields || {},
|
||||
missingFields: Array.isArray(item.missing_fields || item.missingFields)
|
||||
? item.missing_fields || item.missingFields
|
||||
: []
|
||||
}
|
||||
}
|
||||
|
||||
function isPendingFlowConfirmationPlan(normalized) {
|
||||
return (
|
||||
String(normalized?.nextAction || '').trim() === 'confirm_flow' ||
|
||||
String(normalized?.planStatus || '').trim() === 'needs_flow_confirmation' ||
|
||||
String(normalized?.pendingFlowConfirmation?.status || '').trim() === 'pending'
|
||||
) && Array.isArray(normalized?.candidateFlows) && normalized.candidateFlows.length > 0
|
||||
}
|
||||
|
||||
function buildPendingFlowConfirmationMessageText(normalized) {
|
||||
const fields = normalized.candidateFlows[0]?.ontologyFields || {}
|
||||
const knownParts = formatStewardOntologyFields(fields, 'expense_application')
|
||||
const candidateLines = normalized.candidateFlows.map((flow, index) =>
|
||||
`${index + 1}. **${flow.label}**${flow.reason ? `\n - ${flow.reason}` : ''}`
|
||||
)
|
||||
return [
|
||||
'### 需要先确认流程方向',
|
||||
'',
|
||||
knownParts
|
||||
? `我识别到这是一项财务事项,已提取到:**${knownParts}**。`
|
||||
: '我识别到这是一项财务事项,但还需要确认你要进入哪个流程。',
|
||||
'',
|
||||
normalized.pendingFlowConfirmation.reason || normalized.summary || '当前还不能确定你要补办申请还是发起报销。',
|
||||
'',
|
||||
...candidateLines,
|
||||
'',
|
||||
'请先选择一个方向,我会继续整理对应材料。'
|
||||
].filter((line, index, lines) => line || lines[index - 1]).join('\n')
|
||||
}
|
||||
|
||||
function resolveNextActionContext(normalized) {
|
||||
const applicationTask = normalized.tasks.find((task) => task.taskType === 'expense_application')
|
||||
const applicationAction = applicationTask
|
||||
@@ -508,6 +612,13 @@ function buildStewardCarryText(actionType, task, group, normalized = null) {
|
||||
group?.attachmentNames?.length ? `相关附件:${group.attachmentNames.join('、')}` : '',
|
||||
group?.excludedAttachmentNames?.length ? `暂不归集附件:${group.excludedAttachmentNames.join('、')}` : '',
|
||||
missingFields ? `还需要补充:${missingFields}` : '',
|
||||
actionType === 'confirm_create_application'
|
||||
? missingFields
|
||||
? '请先追问上述缺失信息,不要直接生成申请单核对表,也不要替用户默认填写。'
|
||||
: '请直接生成申请单核对结果;信息足够时生成申请单,但在入库或提交审批前仍需让我确认。'
|
||||
: missingFields
|
||||
? '请先追问上述缺失信息,不要直接生成报销核对结果,也不要替用户默认填写。'
|
||||
: '请直接生成报销核对结果;需要创建草稿、绑定附件或提交审批前仍需让我确认。'
|
||||
]
|
||||
const remainingTaskText = normalized ? buildRemainingTaskText(normalized, task.taskId) : ''
|
||||
if (remainingTaskText) {
|
||||
|
||||
Reference in New Issue
Block a user