feat(群推任务): 添加获取群推任务详情的API接口

feat(客服聊天): 实现搜索功能并优化联系人筛选逻辑

refactor(WebSocket): 重构连接逻辑,增加状态检查和调试信息

fix(WebSocket): 修复页面刷新后重连逻辑,避免重复连接
This commit is contained in:
超级老白兔
2025-09-01 17:28:33 +08:00
parent 832be374b5
commit 3198959cb7
8 changed files with 219 additions and 55 deletions

View File

@@ -20,6 +20,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
countLables: [], //标签列表
newContractList: [], //联系人分组
kfSelected: 0, //选中的客服
searchKeyword: "", //搜索关键词
//客服列表
asyncKfUserList: async data => {
set({ kfUserList: data });
@@ -42,6 +43,14 @@ export const useCkChatStore = createPersistStore<CkChatState>(
await state.getNewContractList();
}
},
// 设置搜索关键词
setSearchKeyword: (keyword: string) => {
set({ searchKeyword: keyword });
},
// 清除搜索关键词
clearSearchKeyword: () => {
set({ searchKeyword: "" });
},
asyncKfSelected: async (data: number) => {
set({ kfSelected: data });
// 清除getChatSessions、getContractList和getNewContractList缓存
@@ -74,6 +83,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
let cachedResult: any = null;
let lastKfSelected: number | null = null;
let lastCountLablesLength: number = 0;
let lastSearchKeyword: string = "";
return async () => {
const state = useCkChatStore.getState();
@@ -82,16 +92,38 @@ export const useCkChatStore = createPersistStore<CkChatState>(
const shouldRecalculate =
cachedResult === null ||
lastKfSelected !== state.kfSelected ||
lastCountLablesLength !== (state.countLables?.length || 0);
lastCountLablesLength !== (state.countLables?.length || 0) ||
lastSearchKeyword !== state.searchKeyword;
if (shouldRecalculate) {
// 使用createContractList构建联系人分组数据
cachedResult = await createContractList(
let contractList = await createContractList(
state.kfSelected,
state.countLables,
);
// 根据搜索关键词筛选联系人分组
if (state.searchKeyword.trim()) {
const keyword = state.searchKeyword.toLowerCase();
contractList = contractList
.map(group => ({
...group,
contracts:
group.contracts?.filter(item => {
const nickname = (item.nickname || "").toLowerCase();
const conRemark = (item.conRemark || "").toLowerCase();
return (
nickname.includes(keyword) || conRemark.includes(keyword)
);
}) || [],
}))
.filter(group => group.contracts.length > 0);
}
cachedResult = contractList;
lastKfSelected = state.kfSelected;
lastCountLablesLength = state.countLables?.length || 0;
lastSearchKeyword = state.searchKeyword;
}
return cachedResult;
@@ -142,6 +174,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
let cachedResult: any = null;
let lastKfSelected: number | null = null;
let lastContractListLength: number = 0;
let lastSearchKeyword: string = "";
return () => {
const state = useCkChatStore.getState();
@@ -150,17 +183,33 @@ export const useCkChatStore = createPersistStore<CkChatState>(
const shouldRecalculate =
cachedResult === null ||
lastKfSelected !== state.kfSelected ||
lastContractListLength !== state.contractList.length;
lastContractListLength !== state.contractList.length ||
lastSearchKeyword !== state.searchKeyword;
if (shouldRecalculate) {
const filteredContracts = state.contractList.filter(
item => item.wechatAccountId === state.kfSelected,
);
let filteredContracts = state.contractList;
cachedResult =
state.kfSelected !== 0 ? filteredContracts : state.contractList;
// 根据客服筛选
if (state.kfSelected !== 0) {
filteredContracts = filteredContracts.filter(
item => item.wechatAccountId === state.kfSelected,
);
}
// 根据搜索关键词筛选
if (state.searchKeyword.trim()) {
const keyword = state.searchKeyword.toLowerCase();
filteredContracts = filteredContracts.filter(item => {
const nickname = (item.nickname || "").toLowerCase();
const conRemark = (item.conRemark || "").toLowerCase();
return nickname.includes(keyword) || conRemark.includes(keyword);
});
}
cachedResult = filteredContracts;
lastKfSelected = state.kfSelected;
lastContractListLength = state.contractList.length;
lastSearchKeyword = state.searchKeyword;
}
return cachedResult;
@@ -203,6 +252,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
let cachedResult: any = null;
let lastKfSelected: number | null = null;
let lastChatSessionsLength: number = 0;
let lastSearchKeyword: string = "";
return () => {
const state = useCkChatStore.getState();
@@ -211,17 +261,33 @@ export const useCkChatStore = createPersistStore<CkChatState>(
const shouldRecalculate =
cachedResult === null ||
lastKfSelected !== state.kfSelected ||
lastChatSessionsLength !== state.chatSessions.length;
lastChatSessionsLength !== state.chatSessions.length ||
lastSearchKeyword !== state.searchKeyword;
if (shouldRecalculate) {
const filteredSessions = state.chatSessions.filter(
item => item.wechatAccountId === state.kfSelected,
);
let filteredSessions = state.chatSessions;
cachedResult =
state.kfSelected !== 0 ? filteredSessions : state.chatSessions;
// 根据客服筛选
if (state.kfSelected !== 0) {
filteredSessions = filteredSessions.filter(
item => item.wechatAccountId === state.kfSelected,
);
}
// 根据搜索关键词筛选
if (state.searchKeyword.trim()) {
const keyword = state.searchKeyword.toLowerCase();
filteredSessions = filteredSessions.filter(item => {
const nickname = (item.nickname || "").toLowerCase();
const conRemark = (item.conRemark || "").toLowerCase();
return nickname.includes(keyword) || conRemark.includes(keyword);
});
}
cachedResult = filteredSessions;
lastKfSelected = state.kfSelected;
lastChatSessionsLength = state.chatSessions.length;
lastSearchKeyword = state.searchKeyword;
}
return cachedResult;
@@ -348,6 +414,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
partialize: state => ({
userInfo: state.userInfo,
isLoggedIn: state.isLoggedIn,
kfUserList: state.kfUserList,
}),
onRehydrateStorage: () => state => {
// console.log("CkChat store hydrated:", state);
@@ -399,4 +466,8 @@ export const asyncCountLables = (data: ContactGroupByLabel[]) =>
export const asyncNewContractList = (data: any[]) =>
useCkChatStore.getState().asyncNewContractList(data);
export const getCountLables = () => useCkChatStore.getState().countLables;
export const setSearchKeyword = (keyword: string) =>
useCkChatStore.getState().setSearchKeyword(keyword);
export const clearSearchKeyword = () =>
useCkChatStore.getState().clearSearchKeyword();
useCkChatStore.getState().getKfSelectedUser();

View File

@@ -72,7 +72,7 @@ interface WebSocketState {
// 默认配置
const DEFAULT_CONFIG: WebSocketConfig = {
url: (import.meta as any).env?.VITE_API_WS_URL,
url: "wss://kf.quwanzhi.com:9993",
client: "kefu-client",
accountId: 0,
accessToken: "",
@@ -97,8 +97,21 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
connect: (config: Partial<WebSocketConfig>) => {
const currentState = get();
// 如果已经连接,先断开
// 检查当前连接状态,避免重复连接
if (
currentState.status === WebSocketStatus.CONNECTED ||
currentState.status === WebSocketStatus.CONNECTING
) {
console.log("WebSocket已连接或正在连接跳过重复连接", {
currentStatus: currentState.status,
hasWebSocket: !!currentState.ws,
});
return;
}
// 如果已经有WebSocket实例先断开
if (currentState.ws) {
console.log("断开现有WebSocket连接");
currentState.disconnect();
}
@@ -124,8 +137,18 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
t: Date.now().toString(),
});
// 检查URL是否为localhost如果是则不连接
// 调试信息:输出配置和环境变量
console.log("WebSocket配置信息:", {
configUrl: fullConfig.url,
envUrl: (import.meta as any).env?.VITE_API_WS_URL,
fullConfig,
params: params.toString(),
});
const wsUrl = fullConfig.url + "?" + params;
console.log("最终WebSocket URL:", wsUrl);
// 检查URL是否为localhost如果是则不连接
if (wsUrl.includes("localhost") || wsUrl.includes("127.0.0.1")) {
console.error("WebSocket连接被拦截不允许连接到本地地址", wsUrl);
Toast.show({
@@ -273,11 +296,6 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
client: currentState.config?.client || "kefu-client",
seq: +new Date(),
});
//获取UserId
currentState.sendCommand("CmdRequestWechatAccountsAliveStatus", {
wechatAccountIds: ["300745", "4880930", "32686452"],
seq: +new Date(),
});
}
Toast.show({ content: "WebSocket连接成功", position: "top" });
@@ -416,9 +434,56 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
onRehydrateStorage: () => state => {
// 页面刷新后,如果之前是连接状态,尝试重新连接
if (state && state.status === WebSocketStatus.CONNECTED && state.config) {
console.log("页面刷新后恢复WebSocket连接", {
persistedConfig: state.config,
currentDefaultConfig: DEFAULT_CONFIG,
});
// 使用最新的默认配置,而不是持久化的配置
const freshConfig = {
...DEFAULT_CONFIG,
client: state.config.client,
accountId: state.config.accountId,
accessToken: state.config.accessToken,
autoReconnect: state.config.autoReconnect,
};
console.log("使用刷新后的配置重连:", freshConfig);
// 延迟一下再重连,确保页面完全加载
// 同时检查当前状态,避免重复连接
setTimeout(() => {
state.connect(state.config);
// 重新获取最新的状态而不是使用闭包中的state
const currentState = useWebSocketStore.getState();
console.log("页面刷新后检查状态", {
status: currentState.status,
hasWs: !!currentState.ws,
});
// 强制重置状态为disconnected因为页面刷新后WebSocket实例已失效
if (
currentState.status === WebSocketStatus.CONNECTED &&
!currentState.ws
) {
console.log("检测到状态不一致重置为disconnected");
useWebSocketStore.setState({
status: WebSocketStatus.DISCONNECTED,
});
}
// 重新获取状态进行连接
const latestState = useWebSocketStore.getState();
if (
latestState.status === WebSocketStatus.DISCONNECTED ||
latestState.status === WebSocketStatus.ERROR
) {
console.log("页面刷新后开始重连");
latestState.connect(freshConfig);
} else {
console.log("WebSocket已连接或正在连接跳过页面刷新重连", {
status: latestState.status,
});
}
}, 1000);
}
},