2
0

routes_io_messages.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import { SocketWithUser , Variables} from './router_io.ts';
  2. import { MessageType, MessageStatus } from '../client/share/types.ts'
  3. import { APIClient } from "@d8d-appcontainer/api";
  4. interface MessageSendData {
  5. title: string;
  6. content: string;
  7. type: MessageType;
  8. receiver_ids: number[];
  9. }
  10. interface MessageListData {
  11. page?: number;
  12. pageSize?: number;
  13. type?: MessageType;
  14. status?: MessageStatus;
  15. }
  16. export function setupMessageEvents({ socket , apiClient }:Variables) {
  17. // 订阅频道
  18. socket.on('message:subscribe', (channel: string) => {
  19. try {
  20. socket.join(channel);
  21. socket.emit('message:subscribed', {
  22. message: `成功订阅频道: ${channel}`,
  23. channel
  24. });
  25. } catch (error) {
  26. console.error('订阅频道失败:', error);
  27. socket.emit('error', '订阅频道失败');
  28. }
  29. });
  30. // 取消订阅
  31. socket.on('message:unsubscribe', (channel: string) => {
  32. try {
  33. socket.leave(channel);
  34. socket.emit('message:unsubscribed', {
  35. message: `已取消订阅频道: ${channel}`,
  36. channel
  37. });
  38. } catch (error) {
  39. console.error('取消订阅失败:', error);
  40. socket.emit('error', '取消订阅失败');
  41. }
  42. });
  43. // 广播消息
  44. socket.on('message:broadcast', async (data: {
  45. channel?: string;
  46. title: string;
  47. content: string;
  48. type: MessageType;
  49. }) => {
  50. try {
  51. const { channel, title, content, type } = data;
  52. const user = socket.user;
  53. if (!user) {
  54. socket.emit('error', '未授权访问');
  55. return;
  56. }
  57. // 创建广播消息
  58. const [messageId] = await apiClient.database.table('messages').insert({
  59. title,
  60. content,
  61. type,
  62. sender_id: user.id,
  63. sender_name: user.username,
  64. is_broadcast: 1,
  65. created_at: apiClient.database.fn.now(),
  66. updated_at: apiClient.database.fn.now()
  67. });
  68. // 广播到所有客户端或特定频道
  69. const broadcastTarget = channel ? socket.to(channel) : socket.broadcast;
  70. broadcastTarget.emit('message:broadcasted', {
  71. id: messageId,
  72. title,
  73. content,
  74. type,
  75. sender_id: user.id,
  76. sender_name: user.username,
  77. created_at: new Date().toISOString()
  78. });
  79. socket.emit('message:broadcasted', {
  80. message: '广播消息发送成功',
  81. data: { id: messageId }
  82. });
  83. } catch (error) {
  84. console.error('广播消息失败:', error);
  85. socket.emit('error', '广播消息失败');
  86. }
  87. });
  88. // 发送消息
  89. socket.on('message:send', async (data: MessageSendData) => {
  90. try {
  91. const { title, content, type, receiver_ids } = data;
  92. if (!title || !content || !type || !receiver_ids?.length) {
  93. socket.emit('error', '缺少必要参数');
  94. return;
  95. }
  96. const user = socket.user;
  97. if (!user) {
  98. socket.emit('error', '未授权访问');
  99. return;
  100. }
  101. // 创建消息
  102. const [messageId] = await apiClient.database.table('messages').insert({
  103. title,
  104. content,
  105. type,
  106. sender_id: user.id,
  107. sender_name: user.username,
  108. created_at: apiClient.database.fn.now(),
  109. updated_at: apiClient.database.fn.now()
  110. });
  111. // 关联用户消息
  112. const userMessages = receiver_ids.map((userId: number) => ({
  113. user_id: userId,
  114. message_id: messageId,
  115. status: MessageStatus.UNREAD,
  116. created_at: apiClient.database.fn.now(),
  117. updated_at: apiClient.database.fn.now()
  118. }));
  119. await apiClient.database.table('user_messages').insert(userMessages);
  120. // 根据消息类型推送到不同频道
  121. const messageData = {
  122. id: messageId,
  123. title,
  124. content,
  125. type,
  126. sender_id: user.id,
  127. sender_name: user.username,
  128. created_at: new Date().toISOString()
  129. };
  130. if (type === MessageType.SYSTEM) {
  131. socket.to('system').emit('message:received', messageData);
  132. } else if (type === MessageType.ANNOUNCE) {
  133. socket.to('announce').emit('message:received', messageData);
  134. } else if (type === MessageType.PRIVATE) {
  135. receiver_ids.forEach(userId => {
  136. socket.to(`user_${userId}`).emit('message:received', messageData);
  137. });
  138. }
  139. socket.emit('message:sent', {
  140. message: '消息发送成功',
  141. data: { id: messageId }
  142. });
  143. } catch (error) {
  144. console.error('发送消息失败:', error);
  145. socket.emit('error', '发送消息失败');
  146. }
  147. });
  148. // 获取消息列表
  149. socket.on('message:list', async (data: MessageListData) => {
  150. try {
  151. const { page = 1, pageSize = 20, type, status } = data;
  152. const user = socket.user;
  153. if (!user) {
  154. socket.emit('error', '未授权访问');
  155. return;
  156. }
  157. const query = apiClient.database.table('user_messages as um')
  158. .select('m.*', 'um.status as user_status', 'um.read_at', 'um.id as user_message_id')
  159. .leftJoin('messages as m', 'um.message_id', 'm.id')
  160. .where('um.user_id', user.id)
  161. .where('um.is_deleted', 0)
  162. .orderBy('m.created_at', 'desc')
  163. .limit(pageSize)
  164. .offset((page - 1) * pageSize);
  165. if (type) query.where('m.type', type);
  166. if (status) query.where('um.status', status);
  167. const countQuery = query.clone();
  168. const messages = await query;
  169. // 获取总数用于分页
  170. const total = await countQuery.count();
  171. const totalCount = Number(total);
  172. const totalPages = Math.ceil(totalCount / pageSize);
  173. socket.emit('message:list', {
  174. data: messages,
  175. pagination: {
  176. total: totalCount,
  177. current: page,
  178. pageSize,
  179. totalPages
  180. }
  181. });
  182. } catch (error) {
  183. console.error('获取消息列表失败:', error);
  184. socket.emit('error', '获取消息列表失败');
  185. }
  186. });
  187. // 获取消息详情
  188. socket.on('message:detail', async (messageId: number) => {
  189. try {
  190. const user = socket.user;
  191. if (!user) {
  192. socket.emit('error', '未授权访问');
  193. return;
  194. }
  195. const message = await apiClient.database.table('user_messages as um')
  196. .select('m.*', 'um.status as user_status', 'um.read_at')
  197. .leftJoin('messages as m', 'um.message_id', 'm.id')
  198. .where('um.user_id', user.id)
  199. .where('um.message_id', messageId)
  200. .first();
  201. if (!message) {
  202. socket.emit('error', '消息不存在或无权访问');
  203. return;
  204. }
  205. // 标记为已读
  206. if (message.user_status === MessageStatus.UNREAD) {
  207. await apiClient.database.table('user_messages')
  208. .where('user_id', user.id)
  209. .where('message_id', messageId)
  210. .update({
  211. status: MessageStatus.READ,
  212. read_at: apiClient.database.fn.now(),
  213. updated_at: apiClient.database.fn.now()
  214. });
  215. }
  216. socket.emit('message:detail', {
  217. message: '获取消息成功',
  218. data: message
  219. });
  220. } catch (error) {
  221. console.error('获取消息详情失败:', error);
  222. socket.emit('error', '获取消息详情失败');
  223. }
  224. });
  225. // 删除消息
  226. socket.on('message:delete', async (messageId: number) => {
  227. try {
  228. const user = socket.user;
  229. if (!user) {
  230. socket.emit('error', '未授权访问');
  231. return;
  232. }
  233. await apiClient.database.table('user_messages')
  234. .where('user_id', user.id)
  235. .where('message_id', messageId)
  236. .update({
  237. is_deleted: 1,
  238. updated_at: apiClient.database.fn.now()
  239. });
  240. socket.emit('message:deleted', { message: '消息已删除' });
  241. } catch (error) {
  242. console.error('删除消息失败:', error);
  243. socket.emit('error', '删除消息失败');
  244. }
  245. });
  246. // 获取未读消息数
  247. socket.on('message:count', async () => {
  248. try {
  249. const user = socket.user;
  250. if (!user) {
  251. socket.emit('error', '未授权访问');
  252. return;
  253. }
  254. const count = await apiClient.database.table('user_messages')
  255. .where('user_id', user.id)
  256. .where('status', MessageStatus.UNREAD)
  257. .where('is_deleted', 0)
  258. .count();
  259. socket.emit('message:count', { count: Number(count) });
  260. } catch (error) {
  261. console.error('获取未读消息数失败:', error);
  262. socket.emit('error', '获取未读消息数失败');
  263. }
  264. });
  265. // 标记消息为已读
  266. socket.on('message:read', async (messageId: number) => {
  267. try {
  268. const user = socket.user;
  269. if (!user) {
  270. socket.emit('error', '未授权访问');
  271. return;
  272. }
  273. await apiClient.database.table('user_messages')
  274. .where('user_id', user.id)
  275. .where('message_id', messageId)
  276. .update({
  277. status: MessageStatus.READ,
  278. read_at: apiClient.database.fn.now(),
  279. updated_at: apiClient.database.fn.now()
  280. });
  281. socket.emit('message:read', { message: '消息已标记为已读' });
  282. } catch (error) {
  283. console.error('标记消息为已读失败:', error);
  284. socket.emit('error', '标记消息为已读失败');
  285. }
  286. });
  287. }