Commit 4682e40f by Liu

fix:权限参数

parent daeee270
import http from '@/utils/request'
import { safeSessionStorageGetItem } from '@/lib/utils'
import { normalizeToolIdForApi, safeSessionStorageGetItem } from '@/lib/utils'
/**
* 查询用户历史会话列表
......@@ -37,32 +37,31 @@ export function fetchDeleteUserConversation(conversationIdList: string[]) {
/**
* 获取历史会话ID
* @params
* 制度活化:不传 toolId/busiId(空字符串);工具模式:以 session 或入参为准。
* 若入参显式包含 toolId 或 busiId(含空串),优先于 session,避免轮询把「默认第 0 个工具」误写入后再带上。
*/
export async function fetchSessionConversationId<T>(data: T) {
const payload = (data || {}) as Record<string, any>
const fromSession = normalizeToolIdForApi(safeSessionStorageGetItem('currentToolId'))
// 首次进入时 `currentToolId` 可能还没被 `ChatEditor` 写入 sessionStorage。
// 这里做一个极短轮询,避免第一次请求 `toolId/busiId` 读到空字符串。
let sessionToolId = safeSessionStorageGetItem('currentToolId') || ''
if (!sessionToolId) {
const maxWaitMs = 600
const intervalMs = 100
const start = Date.now()
while (Date.now() - start < maxWaitMs) {
await new Promise(resolve => setTimeout(resolve, intervalMs))
sessionToolId = safeSessionStorageGetItem('currentToolId') || ''
if (sessionToolId) {
break
}
}
}
const hasExplicitTool
= Object.prototype.hasOwnProperty.call(payload, 'toolId')
|| Object.prototype.hasOwnProperty.call(payload, 'busiId')
const explicitRaw = Object.prototype.hasOwnProperty.call(payload, 'toolId')
? payload.toolId
: Object.prototype.hasOwnProperty.call(payload, 'busiId')
? payload.busiId
: undefined
const resolved = hasExplicitTool
? normalizeToolIdForApi(explicitRaw)
: fromSession
const requestData = {
...payload,
busiType: '01',
busiId: sessionToolId,
toolId: sessionToolId,
busiType: payload.busiType ?? '01',
busiId: resolved,
toolId: resolved,
}
return http.post('/conversation/api/conversation/mobile/v1/get_user_conversation_session', requestData)
}
......@@ -11,7 +11,7 @@ import { useAppDispatch, useAppSelector } from '@/store/hook'
import { clearCurrentToolId, setCurrentToolId } from '@/store/conversationSlice'
import { setIsAsking } from '@/store/chatSlice'
import { fetchToolList } from '@/api/home'
import { getUserRolesForApi, safeSessionStorageGetItem, safeSessionStorageRemoveItem, safeSessionStorageSetItem } from '@/lib/utils'
import { getUserRolesForApi, normalizeToolIdForApi, safeSessionStorageGetItem, safeSessionStorageRemoveItem, safeSessionStorageSetItem } from '@/lib/utils'
import { fetchSessionConversationId, fetchUserQaRecordPage } from '@/api/conversation'
interface ChatEditorProps {
......@@ -182,18 +182,19 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
console.log('[chat-toolid-log] ChatEditor currentToolId:', currentToolId, '| sessionToolId:', sessionToolId, '| fromCollect:', fromCollect)
// 关键修复:首次进入/切换路由时,Redux 里的 toolId 可能先到,但 sessionToolId 还没同步。
// 此时不应该“清空 Redux / 写空缓存”,而是用 Redux 回填 sessionStorage,避免出现“高亮有值但缓存为空”。
if (currentToolId && !sessionToolId && !fromCollect) {
setSelectedToolId(currentToolId)
const normalizedReduxToolId = normalizeToolIdForApi(currentToolId)
if (normalizedReduxToolId && !sessionToolId && !fromCollect) {
setSelectedToolId(normalizedReduxToolId)
setIsToolBtnActive(false)
// 用 Redux 回填缓存,确保后续接口/其他组件读取到一致的 toolId
safeSessionStorageSetItem('currentToolId', String(currentToolId))
safeSessionStorageSetItem('currentToolId', normalizedReduxToolId)
if (toolList.length > 0) {
const currentTool = toolList.find(tool => String(tool.toolId) === String(currentToolId))
const currentTool = toolList.find(tool => String(tool.toolId) === String(normalizedReduxToolId))
if (currentTool?.toolName) {
safeSessionStorageSetItem('currentToolName', currentTool.toolName)
}
}
setSessionToolId(String(currentToolId))
setSessionToolId(normalizedReduxToolId)
return
}
......
......@@ -8,6 +8,21 @@ export function cn(...inputs: ClassValue[]) {
}
/**
* 将 toolId 规范为合法字符串。
* 避免 `sessionStorage.setItem(k, undefined)`、Redux 误存、`?toolId=undefined` 等产生字面量 "undefined"。
*/
export function normalizeToolIdForApi(value: string | number | undefined | null): string {
if (value == null) {
return ''
}
const t = String(value).trim()
if (t === '' || t === 'undefined' || t === 'null') {
return ''
}
return t
}
/**
* 安全地访问 sessionStorage
* 在无痕模式或存储被禁用时返回 null,避免抛出错误
*/
......@@ -16,6 +31,10 @@ export function safeSessionStorageGetItem(key: string): string | null {
const result = sessionStorage.getItem(key)
if (key === 'currentToolId') {
console.log('[chat-toolid-log] safeGetItem:', result, '| caller line')
if (result != null && normalizeToolIdForApi(result) === '') {
sessionStorage.removeItem(key)
return null
}
}
return result
}
......@@ -33,6 +52,13 @@ export function safeSessionStorageSetItem(key: string, value: string): void {
try {
if (key === 'currentToolId') {
console.log('[chat-toolid-log] safeSetItem:', value, '| caller line')
const normalized = normalizeToolIdForApi(value)
if (!normalized) {
sessionStorage.removeItem(key)
return
}
sessionStorage.setItem(key, normalized)
return
}
sessionStorage.setItem(key, value)
}
......
......@@ -15,7 +15,7 @@ import { fetchSessionConversationId, fetchUserQaRecordPage } from '@/api/convers
import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { fetchToolList } from '@/api/home'
import { clearCurrentToolId, clearShouldSendQuestion, fetchConversations, setCurrentToolId } from '@/store/conversationSlice'
import { getUserRolesForApi, safeSessionStorageGetItem, waitForToken } from '@/lib/utils'
import { getUserRolesForApi, normalizeToolIdForApi, safeSessionStorageGetItem, safeSessionStorageSetItem, waitForToken } from '@/lib/utils'
import type { RootState } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
......@@ -303,9 +303,9 @@ export const Chat: React.FC = () => {
workFlowSessionId?: string,
recordId?: string,
) => {
// 优先读取缓存中的 toolId,再回退到传参或 Redux
const sessionToolId = sessionStorage.getItem('currentToolId') ?? undefined
const resolvedToolId = toolId ?? sessionToolId ?? currentToolId ?? undefined
// 优先读取缓存中的 toolId,再回退到传参或 Redux(字面量 "undefined" 等与空等同)
const sessionToolId = normalizeToolIdForApi(sessionStorage.getItem('currentToolId')) || undefined
const resolvedToolId = normalizeToolIdForApi(toolId ?? sessionToolId ?? currentToolId ?? undefined) || undefined
// 判断是否为提质增效模式的自动调用(空问题)
const isEmptyQuestion = !question.trim()
const isQualityImprovement = resolvedToolId === '6712395743241'
......@@ -716,7 +716,7 @@ export const Chat: React.FC = () => {
dispatch(setCurrentToolId(toolIdFromState))
}
// 无条件同步到 sessionStorage,确保 ChatEditor 和 handleSubmitQuestion 能获取到正确的 toolId
sessionStorage.setItem('currentToolId', toolIdFromState)
safeSessionStorageSetItem('currentToolId', toolIdFromState)
console.log('[chat-toolid-log] Chat setItem:', toolIdFromState, '| line:676')
}
else {
......@@ -737,14 +737,14 @@ export const Chat: React.FC = () => {
dispatch(setCurrentToolId(finalToolId))
// 从收藏返回时,同步到 sessionStorage,避免 ChatEditor 清除 toolId
if (fromCollect) {
sessionStorage.setItem('currentToolId', finalToolId)
safeSessionStorageSetItem('currentToolId', finalToolId)
console.log('[chat-toolid-log] Chat setItem:', finalToolId, '| line:702')
}
}
else {
// 从收藏返回时,确保 sessionStorage 中有值
if (fromCollect && !sessionStorage.getItem('currentToolId')) {
sessionStorage.setItem('currentToolId', finalToolId)
safeSessionStorageSetItem('currentToolId', finalToolId)
console.log('[chat-toolid-log] Chat setItem:', finalToolId, '| line:708')
}
}
......@@ -760,7 +760,7 @@ export const Chat: React.FC = () => {
// 因为可能是刚点击历史记录但 API 还没返回,或者 location.state 传递失败但 Redux 中已有正确的值
else if (!hasQaRecords && currentToolId) {
if (fromCollect && !sessionStorage.getItem('currentToolId')) {
sessionStorage.setItem('currentToolId', currentToolId)
safeSessionStorageSetItem('currentToolId', currentToolId)
console.log('[chat-toolid-log] Chat setItem:', currentToolId, '| line:722')
}
}
......
......@@ -8,13 +8,14 @@ import { QuestionList } from './components/QuestionList'
import HomeIcon2 from '@/assets/homeIcon2.png'
import SmartIce from '@/assets/smart-ice.png'
import { clearCurrentToolId, fetchConversations, setCurrentConversation, setCurrentToolId, setNavigationFlag } from '@/store/conversationSlice'
import { store } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import { fetchEfficiencyQuestionList, fetchToolList } from '@/api/home'
import { fetchEfficiencyQuestionList } from '@/api/home'
import SdreamLoading from '@/components/SdreamLoading'
import { fetchLoginByToken, fetchLoginByUid } from '@/api/common'
import { fetchSessionConversationId } from '@/api/conversation'
import { fetchCheckTokenApi } from '@/api/chat'
import { getUserRolesForApi, getUserRolesFromRoute, safeLocalStorageGetItem, safeLocalStorageSetItem, safeSessionStorageGetItem, safeSessionStorageRemoveItem, safeSessionStorageSetItem, waitForToken } from '@/lib/utils'
import { getUserRolesFromRoute, normalizeToolIdForApi, safeLocalStorageGetItem, safeLocalStorageSetItem, safeSessionStorageGetItem, safeSessionStorageRemoveItem, safeSessionStorageSetItem, waitForToken } from '@/lib/utils'
// 从 localStorage 获取 token 的辅助函数
function getTokenFromStorage(): string | null {
......@@ -86,49 +87,6 @@ export const Home: React.FC = () => {
defaultValue: '',
})
const ensureToolIdReady = useCallback(async (): Promise<string> => {
const storedToolId = safeSessionStorageGetItem('currentToolId') || ''
if (storedToolId) {
return storedToolId
}
const userRoles = getUserRolesForApi()
const cacheKey = `toolList_${JSON.stringify(userRoles)}`
let toolList: any[] = []
const cached = safeSessionStorageGetItem(cacheKey)
if (cached) {
try {
const parsed = JSON.parse(cached)
if (Date.now() - Number(parsed?.timestamp || 0) < 5 * 60 * 1000 && Array.isArray(parsed?.toolList)) {
toolList = parsed.toolList
}
}
catch {
toolList = []
}
}
if (toolList.length === 0) {
const res = await fetchToolList({ userRoles })
if (Array.isArray(res?.data)) {
toolList = res.data
safeSessionStorageSetItem(cacheKey, JSON.stringify({ toolList, timestamp: Date.now() }))
}
}
const defaultTool = toolList[0]
const defaultToolId = String(defaultTool?.toolId || '')
if (defaultToolId) {
safeSessionStorageSetItem('currentToolId', defaultToolId)
if (defaultTool?.toolName) {
safeSessionStorageSetItem('currentToolName', defaultTool.toolName)
}
dispatch(setCurrentToolId(defaultToolId))
}
return defaultToolId
}, [dispatch])
// 根据 toolId 获取对应的 recordType
const getRecordTypeByToolId = (toolId: string | null | undefined): string => {
if (!toolId) {
......@@ -152,12 +110,15 @@ export const Home: React.FC = () => {
try {
// 等待 token 就绪后再调用接口
await waitForToken()
// 首次进入时先确保有默认 toolId,再调用依赖 toolId 的业务接口
const toolId = await ensureToolIdReady()
const res = await fetchSessionConversationId({
...(data || {}),
...(toolId ? { toolId, busiId: toolId } : {}),
})
// 调用瞬间同步读取 session 与 Redux(避免 login 闭包陈旧导致漏读)
const fromSession = normalizeToolIdForApi(safeSessionStorageGetItem('currentToolId'))
const fromRedux = normalizeToolIdForApi(store.getState().conversation.currentToolId)
const effectiveToolId = fromSession || fromRedux
const res = await fetchSessionConversationId(
effectiveToolId
? { ...(data || {}), toolId: effectiveToolId, busiId: effectiveToolId }
: (data || {}),
)
if (res?.data?.conversationId) {
const conversationId = res.data.conversationId
dispatch(setCurrentConversation(conversationId))
......
......@@ -3,7 +3,7 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { processConversationData } from './conversationSlice.helper'
import type { Conversation, ConversationState } from '@/types/conversation'
import { fetchCreateConversation, fetchDeleteUserConversation, fetchQueryUserConversationPage } from '@/api/conversation'
import { waitForToken } from '@/lib/utils'
import { normalizeToolIdForApi, waitForToken } from '@/lib/utils'
const initialState: ConversationState = {
conversations: [],
......@@ -68,9 +68,10 @@ export const createConversation = createAsyncThunk<
async ({ conversationData, shouldNavigate, shouldSendQuestion }, { dispatch }) => {
await waitForToken()
// 从缓存中获取 toolId 作为 busiId
const cachedToolId = typeof window !== 'undefined' ? sessionStorage.getItem('currentToolId') : null
const busiId = cachedToolId || ''
const toolId = cachedToolId || ''
const rawCached = typeof window !== 'undefined' ? sessionStorage.getItem('currentToolId') : null
const cachedToolId = normalizeToolIdForApi(rawCached)
const busiId = cachedToolId
const toolId = cachedToolId
// 排除 toolId 字段,只传递 busiId 和 busiType
const { toolId: _, ...restConversationData } = conversationData
const requestPayload = {
......
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