Skip to content

Commit 14613cc

Browse files
committed
refactor: refactor ack logic to not fetch all acks but fetch only the ones that are needed
1 parent 5c338b5 commit 14613cc

File tree

8 files changed

+224
-43
lines changed

8 files changed

+224
-43
lines changed

client/shared/cache/cache.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
import { ChatConverseInfo, fetchConverseInfo } from '../model/converse';
1+
import {
2+
ChatConverseInfo,
3+
fetchConverseInfo,
4+
getConverseAckInfo,
5+
} from '../model/converse';
26
import {
37
findGroupInviteByCode,
48
getGroupBasicInfo,
59
GroupBasicInfo,
610
GroupInvite,
711
} from '../model/group';
12+
import { getConverseLastMessageInfo } from '../model/message';
813
import {
914
fetchLocalStaticRegistryPlugins,
1015
fetchRegistryPlugins,
@@ -18,6 +23,7 @@ import { queryClient } from './index';
1823
export enum CacheKey {
1924
user = 'user',
2025
converse = 'converse',
26+
converseAck = 'converseAck',
2127
baseGroupInfo = 'baseGroupInfo',
2228
groupInvite = 'groupInvite',
2329
pluginRegistry = 'pluginRegistry',
@@ -84,6 +90,32 @@ export async function getCachedGroupInviteInfo(
8490
return data;
8591
}
8692

93+
/**
94+
* 获取缓存的用户信息
95+
*/
96+
export async function getCachedAckInfo(converseId: string, refetch = false) {
97+
const data = await queryClient.fetchQuery(
98+
[CacheKey.converseAck, converseId],
99+
() => {
100+
return Promise.all([
101+
getConverseAckInfo([converseId]).then((d) => d[0]),
102+
getConverseLastMessageInfo([converseId]).then((d) => d[0]),
103+
]).then(([ack, lastMessage]) => {
104+
return {
105+
converseId,
106+
ack,
107+
lastMessage,
108+
};
109+
});
110+
},
111+
{
112+
staleTime: 2 * 1000, // 缓存2s, 减少一秒内的重复请求(无意义)
113+
}
114+
);
115+
116+
return data;
117+
}
118+
87119
/**
88120
* 获取缓存的插件列表
89121
*/

client/shared/model/converse.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import { request } from '../api/request';
2+
import {
3+
createAutoMergedRequest,
4+
createAutoSplitRequest,
5+
} from '../utils/request';
6+
import _uniq from 'lodash/uniq';
7+
import _flatten from 'lodash/flatten';
8+
import _zipObject from 'lodash/zipObject';
29

310
export enum ChatConverseType {
411
DM = 'DM', // 单人会话
@@ -88,3 +95,50 @@ export async function fetchUserAck(): Promise<AckInfo[]> {
8895

8996
return data;
9097
}
98+
99+
/**
100+
* 获取用户存储在远程的会话信息
101+
*/
102+
export async function fetchUserAckList(
103+
converseIds: string[]
104+
): Promise<(AckInfo | null)[]> {
105+
const { data } = await request.post('/api/chat/ack/list', {
106+
converseIds,
107+
});
108+
109+
if (!Array.isArray(data)) {
110+
return [];
111+
}
112+
113+
return data;
114+
}
115+
116+
const _fetchConverseAckInfo = createAutoMergedRequest<
117+
string[],
118+
(AckInfo | null)[]
119+
>(
120+
createAutoSplitRequest(
121+
async (converseIdsList) => {
122+
const uniqList = _uniq(_flatten(converseIdsList));
123+
const infoList = await fetchUserAckList(uniqList);
124+
125+
const map = _zipObject<AckInfo | null>(uniqList, infoList);
126+
127+
// 将请求结果根据传输来源重新分组
128+
return converseIdsList.map((converseIds) =>
129+
converseIds.map((converseId) => map[converseId] ?? null)
130+
);
131+
},
132+
'serial',
133+
100
134+
)
135+
);
136+
137+
/**
138+
* 获取会话信息
139+
*/
140+
export async function getConverseAckInfo(
141+
converseIds: string[]
142+
): Promise<(AckInfo | null)[]> {
143+
return _fetchConverseAckInfo(converseIds);
144+
}

client/shared/model/message.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { request } from '../api/request';
22
import type { ChatMessageReaction, ChatMessage } from 'tailchat-types';
3+
import {
4+
createAutoMergedRequest,
5+
createAutoSplitRequest,
6+
} from '../utils/request';
7+
import _uniq from 'lodash/uniq';
8+
import _flatten from 'lodash/flatten';
9+
import _zipObject from 'lodash/zipObject';
310

411
export { ChatMessageReaction, ChatMessage };
512

@@ -103,10 +110,15 @@ export async function searchMessage(
103110
return data;
104111
}
105112

113+
interface LastMessageInfo {
114+
converseId: string;
115+
lastMessageId: string;
116+
}
117+
106118
/**
107119
* 基于会话id获取会话最后一条消息的id
108120
*/
109-
export async function fetchConverseLastMessages(
121+
async function fetchConverseLastMessages(
110122
converseIds: string[]
111123
): Promise<{ converseId: string; lastMessageId: string }[]> {
112124
const { data } = await request.post(
@@ -119,6 +131,30 @@ export async function fetchConverseLastMessages(
119131
return data;
120132
}
121133

134+
export const _fetchConverseLastMessageInfo = createAutoMergedRequest<
135+
string[],
136+
(LastMessageInfo | null)[]
137+
>(
138+
createAutoSplitRequest(
139+
async (converseIdsList) => {
140+
const uniqList = _uniq(_flatten(converseIdsList));
141+
const infoList = await fetchConverseLastMessages(uniqList);
142+
143+
const map = _zipObject<LastMessageInfo | null>(uniqList, infoList);
144+
145+
// 将请求结果根据传输来源重新分组
146+
return converseIdsList.map((converseIds) =>
147+
converseIds.map((converseId) => map[converseId] ?? null)
148+
);
149+
},
150+
'serial',
151+
100
152+
)
153+
);
154+
export function getConverseLastMessageInfo(converseIds: string[]) {
155+
return _fetchConverseLastMessageInfo(converseIds);
156+
}
157+
122158
/**
123159
* @param converseId 会话ID
124160
* @param messageId 消息ID
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useAppDispatch, useAppSelector } from './useAppSelector';
2+
import { chatActions } from '../slices';
3+
import { useEvent } from '../../hooks/useEvent';
4+
import { getCachedAckInfo } from '../../cache/cache';
5+
6+
export function useAckInfoChecker() {
7+
const ack = useAppSelector((state) => state.chat.ack);
8+
const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap);
9+
const dispatch = useAppDispatch();
10+
11+
const ensureAckInfo = useEvent((converseId: string) => {
12+
if (
13+
ack[converseId] === undefined ||
14+
lastMessageMap[converseId] === undefined
15+
) {
16+
getCachedAckInfo(converseId).then((info) => {
17+
if (info.ack?.lastMessageId) {
18+
dispatch(
19+
chatActions.setConverseAck({
20+
converseId,
21+
lastMessageId: info.ack.lastMessageId,
22+
})
23+
);
24+
}
25+
26+
if (info.lastMessage?.lastMessageId) {
27+
dispatch(
28+
chatActions.setLastMessageMap([
29+
{
30+
converseId,
31+
lastMessageId: info.lastMessage.lastMessageId,
32+
},
33+
])
34+
);
35+
}
36+
});
37+
}
38+
});
39+
40+
return { ensureAckInfo };
41+
}
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
import { useAppSelector } from './useAppSelector';
2+
import { useAckInfoChecker } from './useAckInfo';
3+
import { useEffect } from 'react';
24

35
/**
46
* 返回某些会话是否有未读信息
57
*/
68
export function useUnread(converseIds: string[]) {
79
const ack = useAppSelector((state) => state.chat.ack);
810
const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap);
11+
const { ensureAckInfo } = useAckInfoChecker();
912

10-
return converseIds.map((converseId) => {
13+
useEffect(() => {
14+
converseIds.forEach((converseId) => ensureAckInfo(converseId));
15+
}, [converseIds]);
16+
17+
const unreadList = converseIds.map((converseId) => {
1118
if (
1219
ack[converseId] === undefined &&
1320
lastMessageMap[converseId] !== undefined
1421
) {
15-
// 没有已读记录且远程有数据
22+
// 远程没有已读记录且获取到了最后一条消息
1623
return true;
1724
}
1825

1926
// 当远端最后一条消息的id > 本地已读状态的最后一条消息id,
2027
// 则返回true(有未读消息)
2128
return lastMessageMap[converseId] > ack[converseId];
2229
});
30+
31+
return unreadList;
2332
}

client/shared/redux/setup.ts

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,11 @@ import {
99
import type { FriendRequest } from '../model/friend';
1010
import { getCachedConverseInfo } from '../cache/cache';
1111
import type { GroupInfo } from '../model/group';
12-
import {
13-
ChatMessage,
14-
ChatMessageReaction,
15-
fetchConverseLastMessages,
16-
} from '../model/message';
12+
import { ChatMessage, ChatMessageReaction } from '../model/message';
1713
import { socketEventListeners } from '../manager/socket';
1814
import { showToasts } from '../manager/ui';
1915
import { t } from '../i18n';
20-
import {
21-
ChatConverseInfo,
22-
ChatConverseType,
23-
fetchUserAck,
24-
} from '../model/converse';
16+
import { ChatConverseInfo, ChatConverseType } from '../model/converse';
2517
import { appendUserDMConverse } from '../model/user';
2618
import { sharedEvent } from '../event';
2719
import type { InboxItem } from '../model/inbox';
@@ -61,7 +53,7 @@ function initial(socket: AppSocket, store: AppStore) {
6153
console.log('初始化Redux上下文...');
6254

6355
// 立即请求加入房间
64-
const conversesP = socket
56+
socket
6557
.request<{
6658
dmConverseIds: string[];
6759
groupIds: string[];
@@ -77,30 +69,6 @@ function initial(socket: AppSocket, store: AppStore) {
7769
throw new Error('findAndJoinRoom failed');
7870
});
7971

80-
Promise.all([conversesP, fetchUserAck()]).then(
81-
([{ dmConverseIds, textPanelIds }, acks]) => {
82-
/**
83-
* TODO: 这里的逻辑还需要优化
84-
* 可能ack和lastMessageMap可以无关?
85-
*/
86-
87-
// 设置已读消息
88-
acks.forEach((ackInfo) => {
89-
store.dispatch(
90-
chatActions.setConverseAck({
91-
converseId: ackInfo.converseId,
92-
lastMessageId: ackInfo.lastMessageId,
93-
})
94-
);
95-
});
96-
97-
const converseIds = [...dmConverseIds, ...textPanelIds];
98-
fetchConverseLastMessages(converseIds).then((list) => {
99-
store.dispatch(chatActions.setLastMessageMap(list));
100-
});
101-
}
102-
);
103-
10472
// 获取好友列表
10573
socket
10674
.request<{ id: string; nickname?: string }[]>('friend.getAllFriends')

server/services/core/chat/ack.service.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ class AckService extends TcService {
2323
lastMessageId: 'string',
2424
},
2525
});
26+
this.registerAction('list', this.listAck, {
27+
params: {
28+
converseIds: {
29+
type: 'array',
30+
items: 'string',
31+
},
32+
},
33+
});
2634
this.registerAction('all', this.allAck);
2735
}
2836

@@ -54,6 +62,35 @@ class AckService extends TcService {
5462
// TODO: 如果要实现消息已读可以在此处基于会话id进行通知
5563
}
5664

65+
/**
66+
* 所有的ack信息
67+
*/
68+
async listAck(ctx: TcContext<{ converseIds: string[] }>) {
69+
const userId = ctx.meta.userId;
70+
const { converseIds } = ctx.params;
71+
72+
const list = await this.adapter.model.find({
73+
userId,
74+
converseId: {
75+
$in: [...converseIds],
76+
},
77+
});
78+
79+
return converseIds.map((converseId) => {
80+
const lastMessageId =
81+
list
82+
.find((item) => String(item.converseId) === converseId)
83+
?.lastMessageId?.toString() ?? null;
84+
85+
return lastMessageId
86+
? {
87+
converseId,
88+
lastMessageId,
89+
}
90+
: null;
91+
});
92+
}
93+
5794
/**
5895
* 所有的ack信息
5996
*/

server/services/core/chat/message.service.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,10 +479,14 @@ class MessageService extends TcService {
479479
})
480480
);
481481

482-
return list.filter(Boolean).map((item) => ({
483-
converseId: String(item.converseId),
484-
lastMessageId: String(item._id),
485-
}));
482+
return list.map((item) =>
483+
item
484+
? {
485+
converseId: String(item.converseId),
486+
lastMessageId: String(item._id),
487+
}
488+
: null
489+
);
486490
}
487491

488492
async addReaction(

0 commit comments

Comments
 (0)