Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
sdream-ai-fe
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
侯明涛
sdream-ai-fe
Commits
4682e40f
Commit
4682e40f
authored
Apr 15, 2026
by
Liu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix:权限参数
parent
daeee270
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
77 additions
and
89 deletions
+77
-89
src/api/conversation.ts
+19
-20
src/components/ChatEditor/index.tsx
+7
-6
src/lib/utils.ts
+26
-0
src/pages/Chat/Chat.tsx
+8
-8
src/pages/Home/HomeNew.tsx
+12
-51
src/store/conversationSlice.ts
+5
-4
No files found.
src/api/conversation.ts
View file @
4682e40f
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
:
sessionToolI
d
,
toolId
:
sessionToolI
d
,
busiType
:
payload
.
busiType
??
'01'
,
busiId
:
resolve
d
,
toolId
:
resolve
d
,
}
return
http
.
post
(
'/conversation/api/conversation/mobile/v1/get_user_conversation_session'
,
requestData
)
}
src/components/ChatEditor/index.tsx
View file @
4682e40f
...
...
@@ -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
(
current
ToolId
))
const
currentTool
=
toolList
.
find
(
tool
=>
String
(
tool
.
toolId
)
===
String
(
normalizedRedux
ToolId
))
if
(
currentTool
?.
toolName
)
{
safeSessionStorageSetItem
(
'currentToolName'
,
currentTool
.
toolName
)
}
}
setSessionToolId
(
String
(
currentToolId
)
)
setSessionToolId
(
normalizedReduxToolId
)
return
}
...
...
src/lib/utils.ts
View file @
4682e40f
...
...
@@ -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
)
}
...
...
src/pages/Chat/Chat.tsx
View file @
4682e40f
...
...
@@ -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
,
safeSessionStorageG
etItem
,
waitForToken
}
from
'@/lib/utils'
import
{
getUserRolesForApi
,
normalizeToolIdForApi
,
safeSessionStorageGetItem
,
safeSessionStorageS
etItem
,
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
s
essionStorage
.
s
etItem
(
'currentToolId'
,
toolIdFromState
)
s
afeSessionStorageS
etItem
(
'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
)
{
s
essionStorage
.
s
etItem
(
'currentToolId'
,
finalToolId
)
s
afeSessionStorageS
etItem
(
'currentToolId'
,
finalToolId
)
console
.
log
(
'[chat-toolid-log] Chat setItem:'
,
finalToolId
,
'| line:702'
)
}
}
else
{
// 从收藏返回时,确保 sessionStorage 中有值
if
(
fromCollect
&&
!
sessionStorage
.
getItem
(
'currentToolId'
))
{
s
essionStorage
.
s
etItem
(
'currentToolId'
,
finalToolId
)
s
afeSessionStorageS
etItem
(
'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'
))
{
s
essionStorage
.
s
etItem
(
'currentToolId'
,
currentToolId
)
s
afeSessionStorageS
etItem
(
'currentToolId'
,
currentToolId
)
console
.
log
(
'[chat-toolid-log] Chat setItem:'
,
currentToolId
,
'| line:722'
)
}
}
...
...
src/pages/Home/HomeNew.tsx
View file @
4682e40f
...
...
@@ -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
{
getUserRolesF
orApi
,
getUserRolesFromRoute
,
safeLocalStorageGetItem
,
safeLocalStorageSetItem
,
safeSessionStorageGetItem
,
safeSessionStorageRemoveItem
,
safeSessionStorageSetItem
,
waitForToken
}
from
'@/lib/utils'
import
{
getUserRolesF
romRoute
,
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
))
...
...
src/store/conversationSlice.ts
View file @
4682e40f
...
...
@@ -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
=
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment