Commit e9c54c3a by Liu

fix:answer自动提问逻辑

parent c877b1a4
...@@ -17,7 +17,7 @@ import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat' ...@@ -17,7 +17,7 @@ import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { clearCurrentToolId, clearShouldSendQuestion, exitBusinessInsightSubWorkflow, fetchConversations, setBusinessInsightMainWorkflow, setBusinessInsightPendingQuestion, setBusinessInsightSubWorkflow, setCurrentToolId, updateBusinessInsightParamMap } from '@/store/conversationSlice' import { clearCurrentToolId, clearShouldSendQuestion, exitBusinessInsightSubWorkflow, fetchConversations, setBusinessInsightMainWorkflow, setBusinessInsightPendingQuestion, setBusinessInsightSubWorkflow, setCurrentToolId, updateBusinessInsightParamMap } from '@/store/conversationSlice'
import { getUserRolesForApi, safeSessionStorageGetItem, waitForToken } from '@/lib/utils' import { getUserRolesForApi, safeSessionStorageGetItem, waitForToken } from '@/lib/utils'
import { findToolRowBySessionToolId, getFirstToolListRow, readToolListFromSessionStorage, resolveSubmitQuestionToolPair } from '@/lib/toolRoleRecordTypes' import { findToolRowBySessionToolId, getFirstToolListRow, readToolListFromSessionStorage, resolveSubmitQuestionToolPair } from '@/lib/toolRoleRecordTypes'
import { setWorkflowLastToolPair } from '@/lib/workflowToolParams' import { getWorkflowLastToolPair, setWorkflowLastToolPair } from '@/lib/workflowToolParams'
import type { RootState } from '@/store' import type { RootState } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook' import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react' import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
...@@ -111,6 +111,8 @@ export const Chat: React.FC = () => { ...@@ -111,6 +111,8 @@ export const Chat: React.FC = () => {
const expectingSubWorkflowOpenRef = useRef(false) const expectingSubWorkflowOpenRef = useRef(false)
const pendingSubOpenRecordTypeRef = useRef<string | null>(null) const pendingSubOpenRecordTypeRef = useRef<string | null>(null)
const pendingSubQuestionRef = useRef<string | null>(null) const pendingSubQuestionRef = useRef<string | null>(null)
/** 最近一次向主工作流提交的用户问题(开子后 cardList 为空时优先用于问子) */
const lastMainUserQuestionRef = useRef<string | null>(null)
/** 当前流式请求过程中最后一条 AI 回答的快照(避免流结束回调读到过期的 allItems) */ /** 当前流式请求过程中最后一条 AI 回答的快照(避免流结束回调读到过期的 allItems) */
const lastStreamedAiAnswerRef = useRef<WorkflowStreamAnswer | null>(null) const lastStreamedAiAnswerRef = useRef<WorkflowStreamAnswer | null>(null)
...@@ -249,6 +251,25 @@ export const Chat: React.FC = () => { ...@@ -249,6 +251,25 @@ export const Chat: React.FC = () => {
) => Promise<void> ) => Promise<void>
const handleSubmitQuestionRef = useRef<SubmitQuestionFn | null>(null) const handleSubmitQuestionRef = useRef<SubmitQuestionFn | null>(null)
interface WorkflowContinueExtra {
workFlowSessionId: string
recordType?: string
busiType?: string
}
const buildWorkflowContinueExtraFromRef = (): WorkflowContinueExtra | undefined => {
const wf = lastLoadedQaWorkflowContextRef.current
const sessionId = wf?.workFlowSessionId?.trim()
if (!sessionId)
return undefined
const recordType = wf?.recordType?.trim()
return {
workFlowSessionId: sessionId,
...(recordType ? { recordType } : {}),
busiType: wf?.busiType ?? '01',
}
}
const resolveWorkflowToolId = useCallback(() => { const resolveWorkflowToolId = useCallback(() => {
try { try {
return (sessionStorage.getItem('currentToolId') ?? '').trim() return (sessionStorage.getItem('currentToolId') ?? '').trim()
...@@ -376,7 +397,9 @@ export const Chat: React.FC = () => { ...@@ -376,7 +397,9 @@ export const Chat: React.FC = () => {
if (parsedAnswer?.recordType != null && parsedAnswer.question != null) { if (parsedAnswer?.recordType != null && parsedAnswer.question != null) {
const subRecordType = String(parsedAnswer.recordType).trim() const subRecordType = String(parsedAnswer.recordType).trim()
const pendingQ = String(parsedAnswer.question) const controlQ = parsedAnswer.question != null ? String(parsedAnswer.question).trim() : ''
const mainUserQ = lastMainUserQuestionRef.current?.trim() || ''
const pendingQ = mainUserQ || controlQ
if (subRecordType) { if (subRecordType) {
const mainSessionId = ( const mainSessionId = (
lastAnswerSessionId || businessInsightWorkflowRef.current.mainWorkflowSessionId?.trim() || '' lastAnswerSessionId || businessInsightWorkflowRef.current.mainWorkflowSessionId?.trim() || ''
...@@ -389,8 +412,8 @@ export const Chat: React.FC = () => { ...@@ -389,8 +412,8 @@ export const Chat: React.FC = () => {
isInSubWorkflow: false, isInSubWorkflow: false,
} }
} }
dispatch(setBusinessInsightPendingQuestion(pendingQ)) dispatch(setBusinessInsightPendingQuestion(pendingQ || null))
pendingSubQuestionRef.current = pendingQ pendingSubQuestionRef.current = pendingQ || null
pendingSubOpenRecordTypeRef.current = subRecordType pendingSubOpenRecordTypeRef.current = subRecordType
expectingSubWorkflowOpenRef.current = true expectingSubWorkflowOpenRef.current = true
queueMicrotask(() => { queueMicrotask(() => {
...@@ -428,6 +451,7 @@ export const Chat: React.FC = () => { ...@@ -428,6 +451,7 @@ export const Chat: React.FC = () => {
window.setTimeout(() => { window.setTimeout(() => {
void handleSubmitQuestionRef.current?.(followQ, undefined, resolveWorkflowToolId() || undefined, { void handleSubmitQuestionRef.current?.(followQ, undefined, resolveWorkflowToolId() || undefined, {
busiType: '01', busiType: '01',
...(reopenRt ? { recordType: reopenRt } : {}),
...(mainSessionId ? { workFlowSessionId: mainSessionId } : {}), ...(mainSessionId ? { workFlowSessionId: mainSessionId } : {}),
}) })
}, 400) }, 400)
...@@ -621,6 +645,14 @@ export const Chat: React.FC = () => { ...@@ -621,6 +645,14 @@ export const Chat: React.FC = () => {
const sessionToolId = sessionStorage.getItem('currentToolId') ?? undefined const sessionToolId = sessionStorage.getItem('currentToolId') ?? undefined
const resolvedToolId = toolId ?? sessionToolId ?? currentToolId ?? undefined const resolvedToolId = toolId ?? sessionToolId ?? currentToolId ?? undefined
const omitWorkFlowSessionId = extraParams?.omitWorkFlowSessionId === true const omitWorkFlowSessionId = extraParams?.omitWorkFlowSessionId === true
const isEmptyQuestion = !question.trim()
if (
!isEmptyQuestion
&& !extraParams?.submitViaParamMap
&& !businessInsightWorkflowRef.current.isInSubWorkflow
) {
lastMainUserQuestionRef.current = question.trim()
}
// 业务洞察等工作流参数:有主/子会话态时合并进 extra(新建会话首包静默提问不传 session) // 业务洞察等工作流参数:有主/子会话态时合并进 extra(新建会话首包静默提问不传 session)
let workflowExtraParams = extraParams let workflowExtraParams = extraParams
if (!omitWorkFlowSessionId && businessInsightWorkflowRef.current) { if (!omitWorkFlowSessionId && businessInsightWorkflowRef.current) {
...@@ -644,16 +676,23 @@ export const Chat: React.FC = () => { ...@@ -644,16 +676,23 @@ export const Chat: React.FC = () => {
} }
const submitPairFromList = resolveSubmitQuestionToolPair(resolvedToolId) const submitPairFromList = resolveSubmitQuestionToolPair(resolvedToolId)
if (!workflowExtraParams?.recordType && submitPairFromList.recordType) { if (!workflowExtraParams?.recordType) {
const resolvedRecordType = (
lastLoadedQaWorkflowContextRef.current?.recordType?.trim()
|| getWorkflowLastToolPair()?.recordType?.trim()
|| submitPairFromList.recordType?.trim()
|| ''
)
if (resolvedRecordType) {
workflowExtraParams = { workflowExtraParams = {
...workflowExtraParams, ...workflowExtraParams,
recordType: submitPairFromList.recordType, recordType: resolvedRecordType,
}
} }
} }
// 空问题 + 显式传入 recordType/busiType(如工具入口 autoSubmit)时走自动欢迎语流式逻辑; // 空问题 + 显式传入 recordType/busiType(如工具入口 autoSubmit)时走自动欢迎语流式逻辑;
// 开子工作流、已带 session、或已有主会话的空问题不应走欢迎语分支 // 开子工作流、已带 session、或已有主会话的空问题不应走欢迎语分支
const isEmptyQuestion = !question.trim()
const bootstrapBusiType = workflowExtraParams?.busiType ?? extraParams?.busiType ?? '01' const bootstrapBusiType = workflowExtraParams?.busiType ?? extraParams?.busiType ?? '01'
const isNewSessionSilentBootstrap = isEmptyQuestion const isNewSessionSilentBootstrap = isEmptyQuestion
&& omitWorkFlowSessionId && omitWorkFlowSessionId
...@@ -1288,10 +1327,7 @@ export const Chat: React.FC = () => { ...@@ -1288,10 +1327,7 @@ export const Chat: React.FC = () => {
// 延迟发送,确保状态已更新 // 延迟发送,确保状态已更新
setTimeout(() => { setTimeout(() => {
// 左侧常见问题自动发送:若 QA 列表最后一条带 recordType + workFlowSessionId,一并透传 // 左侧常见问题自动发送:若 QA 列表最后一条带 recordType + workFlowSessionId,一并透传
const wfExtra = lastLoadedQaWorkflowContextRef.current const autoExtra = buildWorkflowContinueExtraFromRef()
const autoExtra = wfExtra?.workFlowSessionId
? { workFlowSessionId: wfExtra.workFlowSessionId, busiType: wfExtra.busiType ?? '01' }
: undefined
handleSubmitQuestion(questionToSend, undefined, currentToolId, autoExtra) handleSubmitQuestion(questionToSend, undefined, currentToolId, autoExtra)
}, 100) }, 100)
} }
...@@ -1364,6 +1400,7 @@ export const Chat: React.FC = () => { ...@@ -1364,6 +1400,7 @@ export const Chat: React.FC = () => {
// 重置历史记录加载标记,确保新会话时能正确触发自动发送 // 重置历史记录加载标记,确保新会话时能正确触发自动发送
historyLoadedRef.current.conversationId = null historyLoadedRef.current.conversationId = null
lastLoadedQaWorkflowContextRef.current = null lastLoadedQaWorkflowContextRef.current = null
lastMainUserQuestionRef.current = null
// 保存 fromCollect 标记到 ref,避免 location.state 被清除后丢失 // 保存 fromCollect 标记到 ref,避免 location.state 被清除后丢失
// 优先使用 location.state,其次使用 sessionStorage(用于 navigate(-1) 的情况) // 优先使用 location.state,其次使用 sessionStorage(用于 navigate(-1) 的情况)
fromCollectRef.current = Boolean(location.state?.fromCollect) || Boolean(sessionStorage.getItem('fromCollect')) fromCollectRef.current = Boolean(location.state?.fromCollect) || Boolean(sessionStorage.getItem('fromCollect'))
...@@ -1421,10 +1458,7 @@ export const Chat: React.FC = () => { ...@@ -1421,10 +1458,7 @@ export const Chat: React.FC = () => {
// 确保历史记录加载完成后再发送问题 // 确保历史记录加载完成后再发送问题
setTimeout(() => { setTimeout(() => {
console.log('[Chat] 执行自动发送问题:', questionToSend) console.log('[Chat] 执行自动发送问题:', questionToSend)
const wfExtra = lastLoadedQaWorkflowContextRef.current const autoExtra = buildWorkflowContinueExtraFromRef()
const autoExtra = wfExtra?.workFlowSessionId
? { workFlowSessionId: wfExtra.workFlowSessionId, busiType: wfExtra.busiType ?? '01' }
: undefined
handleSubmitQuestion(questionToSend, undefined, currentToolId, autoExtra) handleSubmitQuestion(questionToSend, undefined, currentToolId, autoExtra)
}, 100) }, 100)
} }
...@@ -1684,10 +1718,7 @@ export const Chat: React.FC = () => { ...@@ -1684,10 +1718,7 @@ export const Chat: React.FC = () => {
<ChatEditor <ChatEditor
inputDisabled={disableChatEditorForWorkflowForm} inputDisabled={disableChatEditorForWorkflowForm}
onSubmit={(question, toolId) => { onSubmit={(question, toolId) => {
const wf = lastLoadedQaWorkflowContextRef.current const extra = buildWorkflowContinueExtraFromRef()
const extra = wf?.workFlowSessionId
? { workFlowSessionId: wf.workFlowSessionId, busiType: wf.busiType ?? '01' }
: undefined
handleSubmitQuestion(question, undefined, toolId, extra) handleSubmitQuestion(question, undefined, toolId, extra)
}} }}
onToolClick={(isToolBtn, toolId, toolName, shouldChangeStyle, conversationId, isLoading, toolMeta) => { onToolClick={(isToolBtn, toolId, toolName, shouldChangeStyle, conversationId, isLoading, toolMeta) => {
......
...@@ -70,19 +70,29 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, ...@@ -70,19 +70,29 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
const handleSubmitQuestionWithWorkflow = (question: string, productCode?: string) => { const handleSubmitQuestionWithWorkflow = (question: string, productCode?: string) => {
// 从会话历史中找到最后一个有 workFlowSessionId 的答案(从后往前查找) // 从会话历史中找到最后一个有 workFlowSessionId 的答案(从后往前查找)
let lastWorkFlowSessionId: string | undefined let lastWorkFlowSessionId: string | undefined
let lastRecordType: string | undefined
let lastRecordId: string | undefined let lastRecordId: string | undefined
for (let i = items.length - 1; i >= 0; i--) { for (let i = items.length - 1; i >= 0; i--) {
const historyItem = items[i] const historyItem = items[i]
if (historyItem.role === 'ai' && historyItem.answerList?.[0]?.workFlowSessionId) { if (historyItem.role === 'ai' && historyItem.answerList?.[0]?.workFlowSessionId) {
lastWorkFlowSessionId = historyItem.answerList[0].workFlowSessionId const ans = historyItem.answerList[0]
lastRecordId = historyItem.answerList[0].recordId lastWorkFlowSessionId = ans.workFlowSessionId
lastRecordId = ans.recordId
if (ans.recordType != null && String(ans.recordType).trim())
lastRecordType = String(ans.recordType).trim()
break break
} }
} }
// 调用父组件传递的 onSubmitQuestion,传递 workFlowSessionId 和 recordId const workflowExtra = lastWorkFlowSessionId
// 跳过 toolId 和 extraParams 参数,正确传递 workFlowSessionId 和 recordId ? {
onSubmitQuestion(question, productCode, undefined, undefined, lastWorkFlowSessionId, lastRecordId) workFlowSessionId: lastWorkFlowSessionId,
...(lastRecordType ? { recordType: lastRecordType } : {}),
busiType: '01' as const,
}
: undefined
onSubmitQuestion(question, productCode, undefined, workflowExtra, lastWorkFlowSessionId, lastRecordId)
} }
return ( return (
......
...@@ -202,6 +202,23 @@ function parseWorkflowControlFromText(text: string): WorkflowControlPayload | nu ...@@ -202,6 +202,23 @@ function parseWorkflowControlFromText(text: string): WorkflowControlPayload | nu
return null return null
} }
/** 兼容 API 返回 workFlowSessionId / workflowSessionId 等字段名 */
export function resolveWorkFlowSessionId(
data: Record<string, unknown> | Answer | null | undefined,
fallback?: string | null,
): string | undefined {
if (data && typeof data === 'object') {
const record = data as Record<string, unknown>
for (const key of ['workFlowSessionId', 'workflowSessionId', 'work_flow_session_id']) {
const val = record[key]
if (val != null && String(val).trim())
return String(val).trim()
}
}
const fb = fallback != null ? String(fallback).trim() : ''
return fb || undefined
}
/** 流式分包元数据是否携带工作流结束标识 */ /** 流式分包元数据是否携带工作流结束标识 */
export function isWorkflowPacketEnd(data: Record<string, unknown> | null | undefined): boolean { export function isWorkflowPacketEnd(data: Record<string, unknown> | null | undefined): boolean {
if (!data) if (!data)
...@@ -314,7 +331,7 @@ export function getLastQaRecordWorkflowContext(qaRecords: OriginalRecord[] | nul ...@@ -314,7 +331,7 @@ export function getLastQaRecordWorkflowContext(qaRecords: OriginalRecord[] | nul
for (let i = list.length - 1; i >= 0; i--) { for (let i = list.length - 1; i >= 0; i--) {
const a = list[i] as Answer const a = list[i] as Answer
const recordType = a?.recordType != null ? String(a.recordType).trim() : '' const recordType = a?.recordType != null ? String(a.recordType).trim() : ''
const workFlowSessionId = a?.workFlowSessionId != null ? String(a.workFlowSessionId).trim() : '' const workFlowSessionId = resolveWorkFlowSessionId(a) ?? ''
if (recordType && workFlowSessionId) { if (recordType && workFlowSessionId) {
const busiType = a?.busiType != null ? String(a.busiType).trim() : '' const busiType = a?.busiType != null ? String(a.busiType).trim() : ''
return { return {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment