Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. import { ChatSearchParams } from '@/constants/chat';
  2. import {
  3. IConversation,
  4. IDialog,
  5. IStats,
  6. IToken,
  7. } from '@/interfaces/database/chat';
  8. import {
  9. IAskRequestBody,
  10. IFeedbackRequestBody,
  11. } from '@/interfaces/request/chat';
  12. import i18n from '@/locales/config';
  13. import { IClientConversation } from '@/pages/chat/interface';
  14. import chatService from '@/services/chat-service';
  15. import {
  16. buildMessageListWithUuid,
  17. getConversationId,
  18. isConversationIdExist,
  19. } from '@/utils/chat';
  20. import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
  21. import { message } from 'antd';
  22. import dayjs, { Dayjs } from 'dayjs';
  23. import { has, set } from 'lodash';
  24. import { useCallback, useMemo, useState } from 'react';
  25. import { history, useSearchParams } from 'umi';
  26. //#region logic
  27. export const useClickDialogCard = () => {
  28. const [_, setSearchParams] = useSearchParams();
  29. const newQueryParameters: URLSearchParams = useMemo(() => {
  30. return new URLSearchParams();
  31. }, []);
  32. const handleClickDialog = useCallback(
  33. (dialogId: string) => {
  34. newQueryParameters.set(ChatSearchParams.DialogId, dialogId);
  35. // newQueryParameters.set(
  36. // ChatSearchParams.ConversationId,
  37. // EmptyConversationId,
  38. // );
  39. setSearchParams(newQueryParameters);
  40. },
  41. [newQueryParameters, setSearchParams],
  42. );
  43. return { handleClickDialog };
  44. };
  45. export const useClickConversationCard = () => {
  46. const [currentQueryParameters, setSearchParams] = useSearchParams();
  47. const newQueryParameters: URLSearchParams = useMemo(
  48. () => new URLSearchParams(currentQueryParameters.toString()),
  49. [currentQueryParameters],
  50. );
  51. const handleClickConversation = useCallback(
  52. (conversationId: string, isNew: string) => {
  53. newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
  54. newQueryParameters.set(ChatSearchParams.isNew, isNew);
  55. setSearchParams(newQueryParameters);
  56. },
  57. [setSearchParams, newQueryParameters],
  58. );
  59. return { handleClickConversation };
  60. };
  61. export const useGetChatSearchParams = () => {
  62. const [currentQueryParameters] = useSearchParams();
  63. return {
  64. dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
  65. conversationId:
  66. currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
  67. isNew: currentQueryParameters.get(ChatSearchParams.isNew) || '',
  68. };
  69. };
  70. //#endregion
  71. //#region dialog
  72. export const useFetchNextDialogList = () => {
  73. const { handleClickDialog } = useClickDialogCard();
  74. const { dialogId } = useGetChatSearchParams();
  75. const {
  76. data,
  77. isFetching: loading,
  78. refetch,
  79. } = useQuery<IDialog[]>({
  80. queryKey: ['fetchDialogList'],
  81. initialData: [],
  82. gcTime: 0,
  83. refetchOnWindowFocus: false,
  84. queryFn: async (...params) => {
  85. console.log('🚀 ~ queryFn: ~ params:', params);
  86. const { data } = await chatService.listDialog();
  87. if (data.code === 0) {
  88. const list: IDialog[] = data.data;
  89. if (list.length > 0) {
  90. if (list.every((x) => x.id !== dialogId)) {
  91. handleClickDialog(data.data[0].id);
  92. }
  93. } else {
  94. history.push('/chat');
  95. }
  96. }
  97. return data?.data ?? [];
  98. },
  99. });
  100. return { data, loading, refetch };
  101. };
  102. export const useSetNextDialog = () => {
  103. const queryClient = useQueryClient();
  104. const {
  105. data,
  106. isPending: loading,
  107. mutateAsync,
  108. } = useMutation({
  109. mutationKey: ['setDialog'],
  110. mutationFn: async (params: IDialog) => {
  111. const { data } = await chatService.setDialog(params);
  112. if (data.code === 0) {
  113. queryClient.invalidateQueries({
  114. exact: false,
  115. queryKey: ['fetchDialogList'],
  116. });
  117. queryClient.invalidateQueries({
  118. queryKey: ['fetchDialog'],
  119. });
  120. message.success(
  121. i18n.t(`message.${params.dialog_id ? 'modified' : 'created'}`),
  122. );
  123. }
  124. return data?.code;
  125. },
  126. });
  127. return { data, loading, setDialog: mutateAsync };
  128. };
  129. export const useFetchNextDialog = () => {
  130. const { dialogId } = useGetChatSearchParams();
  131. const {
  132. data,
  133. isFetching: loading,
  134. refetch,
  135. } = useQuery<IDialog>({
  136. queryKey: ['fetchDialog', dialogId],
  137. gcTime: 0,
  138. initialData: {} as IDialog,
  139. enabled: !!dialogId,
  140. refetchOnWindowFocus: false,
  141. queryFn: async () => {
  142. const { data } = await chatService.getDialog({ dialogId });
  143. return data?.data ?? ({} as IDialog);
  144. },
  145. });
  146. return { data, loading, refetch };
  147. };
  148. export const useFetchManualDialog = () => {
  149. const {
  150. data,
  151. isPending: loading,
  152. mutateAsync,
  153. } = useMutation({
  154. mutationKey: ['fetchManualDialog'],
  155. gcTime: 0,
  156. mutationFn: async (dialogId: string) => {
  157. const { data } = await chatService.getDialog({ dialogId });
  158. return data;
  159. },
  160. });
  161. return { data, loading, fetchDialog: mutateAsync };
  162. };
  163. export const useRemoveNextDialog = () => {
  164. const queryClient = useQueryClient();
  165. const {
  166. data,
  167. isPending: loading,
  168. mutateAsync,
  169. } = useMutation({
  170. mutationKey: ['removeDialog'],
  171. mutationFn: async (dialogIds: string[]) => {
  172. const { data } = await chatService.removeDialog({ dialogIds });
  173. if (data.code === 0) {
  174. queryClient.invalidateQueries({ queryKey: ['fetchDialogList'] });
  175. message.success(i18n.t('message.deleted'));
  176. }
  177. return data.code;
  178. },
  179. });
  180. return { data, loading, removeDialog: mutateAsync };
  181. };
  182. //#endregion
  183. //#region conversation
  184. export const useFetchNextConversationList = () => {
  185. const { dialogId } = useGetChatSearchParams();
  186. const { handleClickConversation } = useClickConversationCard();
  187. const {
  188. data,
  189. isFetching: loading,
  190. refetch,
  191. } = useQuery<IConversation[]>({
  192. queryKey: ['fetchConversationList', dialogId],
  193. initialData: [],
  194. gcTime: 0,
  195. refetchOnWindowFocus: false,
  196. enabled: !!dialogId,
  197. queryFn: async () => {
  198. const { data } = await chatService.listConversation({ dialogId });
  199. if (data.code === 0 && data.data.length > 0) {
  200. handleClickConversation(data.data[0].id, '');
  201. }
  202. return data?.data;
  203. },
  204. });
  205. return { data, loading, refetch };
  206. };
  207. export const useFetchNextConversation = () => {
  208. const { isNew, conversationId } = useGetChatSearchParams();
  209. const {
  210. data,
  211. isFetching: loading,
  212. refetch,
  213. } = useQuery<IClientConversation>({
  214. queryKey: ['fetchConversation', conversationId],
  215. initialData: {} as IClientConversation,
  216. // enabled: isConversationIdExist(conversationId),
  217. gcTime: 0,
  218. refetchOnWindowFocus: false,
  219. queryFn: async () => {
  220. if (isNew !== 'true' && isConversationIdExist(conversationId)) {
  221. const { data } = await chatService.getConversation({ conversationId });
  222. const conversation = data?.data ?? {};
  223. const messageList = buildMessageListWithUuid(conversation?.message);
  224. return { ...conversation, message: messageList };
  225. }
  226. return { message: [] };
  227. },
  228. });
  229. return { data, loading, refetch };
  230. };
  231. export const useFetchManualConversation = () => {
  232. const {
  233. data,
  234. isPending: loading,
  235. mutateAsync,
  236. } = useMutation({
  237. mutationKey: ['fetchManualConversation'],
  238. gcTime: 0,
  239. mutationFn: async (conversationId: string) => {
  240. const { data } = await chatService.getConversation({ conversationId });
  241. return data;
  242. },
  243. });
  244. return { data, loading, fetchConversation: mutateAsync };
  245. };
  246. export const useUpdateNextConversation = () => {
  247. const queryClient = useQueryClient();
  248. const {
  249. data,
  250. isPending: loading,
  251. mutateAsync,
  252. } = useMutation({
  253. mutationKey: ['updateConversation'],
  254. mutationFn: async (params: Record<string, any>) => {
  255. const { data } = await chatService.setConversation({
  256. ...params,
  257. conversation_id: params.conversation_id
  258. ? params.conversation_id
  259. : getConversationId(),
  260. });
  261. if (data.code === 0) {
  262. queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
  263. }
  264. return data;
  265. },
  266. });
  267. return { data, loading, updateConversation: mutateAsync };
  268. };
  269. export const useRemoveNextConversation = () => {
  270. const queryClient = useQueryClient();
  271. const { dialogId } = useGetChatSearchParams();
  272. const {
  273. data,
  274. isPending: loading,
  275. mutateAsync,
  276. } = useMutation({
  277. mutationKey: ['removeConversation'],
  278. mutationFn: async (conversationIds: string[]) => {
  279. const { data } = await chatService.removeConversation({
  280. conversationIds,
  281. dialogId,
  282. });
  283. if (data.code === 0) {
  284. queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
  285. }
  286. return data.code;
  287. },
  288. });
  289. return { data, loading, removeConversation: mutateAsync };
  290. };
  291. export const useDeleteMessage = () => {
  292. const { conversationId } = useGetChatSearchParams();
  293. const {
  294. data,
  295. isPending: loading,
  296. mutateAsync,
  297. } = useMutation({
  298. mutationKey: ['deleteMessage'],
  299. mutationFn: async (messageId: string) => {
  300. const { data } = await chatService.deleteMessage({
  301. messageId,
  302. conversationId,
  303. });
  304. if (data.code === 0) {
  305. message.success(i18n.t(`message.deleted`));
  306. }
  307. return data.code;
  308. },
  309. });
  310. return { data, loading, deleteMessage: mutateAsync };
  311. };
  312. export const useFeedback = () => {
  313. const { conversationId } = useGetChatSearchParams();
  314. const {
  315. data,
  316. isPending: loading,
  317. mutateAsync,
  318. } = useMutation({
  319. mutationKey: ['feedback'],
  320. mutationFn: async (params: IFeedbackRequestBody) => {
  321. const { data } = await chatService.thumbup({
  322. ...params,
  323. conversationId,
  324. });
  325. if (data.code === 0) {
  326. message.success(i18n.t(`message.operated`));
  327. }
  328. return data.code;
  329. },
  330. });
  331. return { data, loading, feedback: mutateAsync };
  332. };
  333. //#endregion
  334. // #region API provided for external calls
  335. export const useCreateNextToken = () => {
  336. const queryClient = useQueryClient();
  337. const {
  338. data,
  339. isPending: loading,
  340. mutateAsync,
  341. } = useMutation({
  342. mutationKey: ['createToken'],
  343. mutationFn: async (params: Record<string, any>) => {
  344. const { data } = await chatService.createToken(params);
  345. if (data.code === 0) {
  346. queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
  347. }
  348. return data?.data ?? [];
  349. },
  350. });
  351. return { data, loading, createToken: mutateAsync };
  352. };
  353. export const useFetchTokenList = (params: Record<string, any>) => {
  354. const {
  355. data,
  356. isFetching: loading,
  357. refetch,
  358. } = useQuery<IToken[]>({
  359. queryKey: ['fetchTokenList', params],
  360. initialData: [],
  361. gcTime: 0,
  362. queryFn: async () => {
  363. const { data } = await chatService.listToken(params);
  364. return data?.data ?? [];
  365. },
  366. });
  367. return { data, loading, refetch };
  368. };
  369. export const useRemoveNextToken = () => {
  370. const queryClient = useQueryClient();
  371. const {
  372. data,
  373. isPending: loading,
  374. mutateAsync,
  375. } = useMutation({
  376. mutationKey: ['removeToken'],
  377. mutationFn: async (params: {
  378. tenantId: string;
  379. dialogId?: string;
  380. tokens: string[];
  381. }) => {
  382. const { data } = await chatService.removeToken(params);
  383. if (data.code === 0) {
  384. queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
  385. }
  386. return data?.data ?? [];
  387. },
  388. });
  389. return { data, loading, removeToken: mutateAsync };
  390. };
  391. type RangeValue = [Dayjs | null, Dayjs | null] | null;
  392. const getDay = (date?: Dayjs) => date?.format('YYYY-MM-DD');
  393. export const useFetchNextStats = () => {
  394. const [pickerValue, setPickerValue] = useState<RangeValue>([
  395. dayjs().subtract(7, 'day'),
  396. dayjs(),
  397. ]);
  398. const { data, isFetching: loading } = useQuery<IStats>({
  399. queryKey: ['fetchStats', pickerValue],
  400. initialData: {} as IStats,
  401. gcTime: 0,
  402. queryFn: async () => {
  403. if (Array.isArray(pickerValue) && pickerValue[0]) {
  404. const { data } = await chatService.getStats({
  405. fromDate: getDay(pickerValue[0]),
  406. toDate: getDay(pickerValue[1] ?? dayjs()),
  407. });
  408. return data?.data ?? {};
  409. }
  410. return {};
  411. },
  412. });
  413. return { data, loading, pickerValue, setPickerValue };
  414. };
  415. //#endregion
  416. //#region shared chat
  417. export const useCreateNextSharedConversation = () => {
  418. const {
  419. data,
  420. isPending: loading,
  421. mutateAsync,
  422. } = useMutation({
  423. mutationKey: ['createSharedConversation'],
  424. mutationFn: async (userId?: string) => {
  425. const { data } = await chatService.createExternalConversation({ userId });
  426. return data;
  427. },
  428. });
  429. return { data, loading, createSharedConversation: mutateAsync };
  430. };
  431. export const useFetchNextSharedConversation = (conversationId: string) => {
  432. const { data, isPending: loading } = useQuery({
  433. queryKey: ['fetchSharedConversation'],
  434. enabled: !!conversationId,
  435. queryFn: async () => {
  436. const { data } = await chatService.getExternalConversation(
  437. null,
  438. conversationId,
  439. );
  440. const messageList = buildMessageListWithUuid(data?.data?.message);
  441. set(data, 'data.message', messageList);
  442. return data;
  443. },
  444. });
  445. return { data, loading };
  446. };
  447. //#endregion
  448. //#region search page
  449. export const useFetchMindMap = () => {
  450. const {
  451. data,
  452. isPending: loading,
  453. mutateAsync,
  454. } = useMutation({
  455. mutationKey: ['fetchMindMap'],
  456. gcTime: 0,
  457. mutationFn: async (params: IAskRequestBody) => {
  458. try {
  459. const ret = await chatService.getMindMap(params);
  460. return ret?.data?.data ?? {};
  461. } catch (error) {
  462. if (has(error, 'message')) {
  463. message.error(error.message);
  464. }
  465. return [];
  466. }
  467. },
  468. });
  469. return { data, loading, fetchMindMap: mutateAsync };
  470. };
  471. export const useFetchRelatedQuestions = () => {
  472. const {
  473. data,
  474. isPending: loading,
  475. mutateAsync,
  476. } = useMutation({
  477. mutationKey: ['fetchRelatedQuestions'],
  478. gcTime: 0,
  479. mutationFn: async (question: string): Promise<string[]> => {
  480. const { data } = await chatService.getRelatedQuestions({ question });
  481. return data?.data ?? [];
  482. },
  483. });
  484. return { data, loading, fetchRelatedQuestions: mutateAsync };
  485. };
  486. //#endregion