room.service.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
  2. import { Socket } from 'socket.io';
  3. import { SocketGateway } from 'src/socket/socket.gateway';
  4. import { ActionsService } from './actions/actions.service';
  5. import { DelayService } from './delay/delay.service';
  6. import { TempService } from './temp/temp.service';
  7. import { UsersService } from './users/users.service';
  8. @Injectable()
  9. export class RoomService {
  10. constructor(
  11. @Inject(forwardRef(() => SocketGateway))
  12. public readonly socketGateway: SocketGateway,
  13. // @InjectRedis() private readonly redis: Redis,
  14. private readonly userService: UsersService,
  15. private readonly actionsService: ActionsService,
  16. private readonly delayService: DelayService,
  17. private readonly tempService: TempService,
  18. ) { }
  19. public readonly logger = new Logger('user');
  20. public _sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
  21. public _userInfo = {} as UserInfoType;
  22. /**
  23. * 初始化当前用户数据
  24. * @param userInfo
  25. */
  26. initUserProfile(userInfo: UserInfoParams): UserInfoType {
  27. const data = {
  28. id: userInfo.id,
  29. RoomId: userInfo.roomId,
  30. Role: userInfo.role,
  31. UserId: userInfo.userId,
  32. Avatar: userInfo.avatar,
  33. Nickname:
  34. decodeURIComponent(userInfo.nickname) || `用户[${userInfo.userId}]`,
  35. IsClient: userInfo.isClient,
  36. IsMuted: true,
  37. IsWords: true,
  38. Order: userInfo.role === 'leader' ? 0 : 1,
  39. JoinTime: Date.now(),
  40. InTime: Date.now(),
  41. IsOnline: true,
  42. };
  43. return data;
  44. }
  45. async handleUserOnline(socket: Socket) {
  46. if (socket.data.user) {
  47. socket.data.user.IsOnline = true;
  48. await this.userService.updateUserOnlineState(
  49. socket,
  50. socket.data.user.RoomId,
  51. socket.data.user.UserId,
  52. true,
  53. );
  54. }
  55. }
  56. async handleUserOffline(socket: Socket) {
  57. await this._sleep(500);
  58. if (socket.data?.user) {
  59. this.delayService.handleOffline(socket);
  60. const { RoomId, UserId, Role } = socket.data.user;
  61. await this.userService.setRoomEntranceLog(RoomId, UserId, 1);
  62. if (RoomId && UserId) {
  63. //标记主动退出房间
  64. if (socket.data.isRequestExit !== 1 && socket.data.isKick !== 1) {
  65. const roomUsers = await this.userService.getRoomUsers(RoomId);
  66. this.socketGateway.server.to(RoomId).emit('action', {
  67. type: 'user-leave',
  68. user: socket.data.user,
  69. members: roomUsers,
  70. });
  71. }
  72. socket.data.user.IsOnline = false;
  73. await this.userService.updateUserOnlineState(
  74. socket,
  75. socket.data.user.RoomId,
  76. socket.data.user.UserId,
  77. false,
  78. );
  79. await this.handleRoomStatusAction(socket);
  80. }
  81. // 压测试直接去除信息
  82. if (!!socket.data.isBenmark) {
  83. if (Role === 'leader') {
  84. this.handleRoomDismiss(RoomId);
  85. }
  86. this.userService.deleteRoomUser(RoomId, UserId);
  87. }
  88. }
  89. }
  90. /**
  91. * 加入房间
  92. * @param userInfo
  93. */
  94. async handleUserJoin(
  95. socket: Socket,
  96. userInfo: UserInfoParams,
  97. ): Promise<void> {
  98. const user = this.initUserProfile(userInfo);
  99. this._userInfo = user;
  100. socket.data.user = user;
  101. socket.data.user.roomConfig = userInfo.roomConfig;
  102. await this.handleUserOnline(socket);
  103. await this.delayService.handleOnine(socket);
  104. let blockJoin = false;
  105. const { RoomId, UserId, Role, roomConfig } = socket.data.user;
  106. await this.userService.setRoomEntranceLog(RoomId, UserId, 0);
  107. const _isLeader = Role === 'leader';
  108. if (RoomId?.length && UserId?.length) {
  109. //房主设置房间配置
  110. if (_isLeader) {
  111. const isValid = await this.userService.isRoomMaster(RoomId, UserId);
  112. //检查房主非法性
  113. if (isValid) {
  114. roomConfig.masterId = UserId;
  115. this.logger.log(JSON.stringify(userInfo), 'room-config');
  116. userInfo.roomConfig &&
  117. (await this.userService.setRoomConfig(RoomId, roomConfig));
  118. } else {
  119. blockJoin = true;
  120. socket.emit('manager-error', {
  121. type: 'invalid-master',
  122. code: 303,
  123. });
  124. this.logger.warn(`303:invalid-master`, 'join-error');
  125. socket.disconnect(true);
  126. }
  127. }
  128. const isExist = await this.userService.isUserInRooms(RoomId, UserId);
  129. const isMax = await this.userService.isMaxRoom(RoomId);
  130. // console.log('isMaxRoom', isMax);
  131. if (isMax && Role !== 'leader' && !isExist) {
  132. this.logger.warn(`308:room-maxing`, 'join-error');
  133. socket.emit('manager-error', {
  134. type: 'room-maxing',
  135. code: 308,
  136. });
  137. socket.disconnect(true);
  138. return;
  139. }
  140. if (!blockJoin) {
  141. if (!isExist) {
  142. await this.userService.insertUser(socket.data.user);
  143. } else {
  144. const updated = await this.userService.updateUsers(socket.data.user);
  145. if (!updated) {
  146. socket.emit('manager-error', {
  147. type: 'invalid-match-role',
  148. code: 405,
  149. });
  150. this.logger.warn(
  151. `same userId in room not matchRole `,
  152. 'join-error',
  153. );
  154. socket.disconnect(true);
  155. return;
  156. }
  157. }
  158. this.tempService.init(socket);
  159. const roomUsers = await this.userService.getRoomUsers(RoomId);
  160. socket.emit('join', {
  161. user: socket.data.user,
  162. members: roomUsers,
  163. });
  164. if (!socket.data.user.IsClient) {
  165. socket.broadcast.to(socket.data.user.RoomId).emit('action', {
  166. type: 'user-join',
  167. user: this._userInfo,
  168. members: roomUsers,
  169. });
  170. }
  171. // this.isJoin = true;
  172. this.logger.log(
  173. JSON.stringify(socket.data.user),
  174. `join-user-${socket.data.user.Role}`,
  175. );
  176. }
  177. } else {
  178. socket.emit('manager-error', {
  179. type: 'invalid-room-params',
  180. code: 403,
  181. });
  182. this.logger.warn(`403:not roomId and userId`, 'join-error');
  183. socket.disconnect(true);
  184. return;
  185. }
  186. }
  187. /**
  188. * 房间action大通道
  189. * @param message
  190. */
  191. async handleUserAction(socket: Socket, message: any) {
  192. this.actionsService.handleAllAction(socket, message);
  193. }
  194. /**
  195. * 房间sync大通道
  196. * @param message
  197. */
  198. async handleSyncAction(socket: Socket, message: any) {
  199. if (socket.data?.user) {
  200. const { RoomId } = socket.data?.user;
  201. socket.broadcast.to(RoomId).emit('sync', message);
  202. }
  203. }
  204. /**
  205. * 房间paint大通道
  206. * @param message
  207. */
  208. async handlePaintAction(socket: Socket, message: any) {
  209. if (socket.data?.user) {
  210. const { RoomId } = socket.data?.user;
  211. socket.broadcast.to(RoomId).emit('paint', message);
  212. }
  213. }
  214. /**
  215. * 房间T人
  216. * @param message
  217. */
  218. async handleKickAction(RoomId: string, userId: string) {
  219. const delUser = await this.userService.getUsersBy(RoomId, userId);
  220. const roomUsers = await this.userService.getRoomUsers(RoomId);
  221. const filterRoomUser = roomUsers.filter((i) => i.UserId !== userId);
  222. this.logger.warn(
  223. `RoomId: ${RoomId},userId:${userId} info:${JSON.stringify(delUser)}`,
  224. 'kick-user',
  225. );
  226. if (delUser) {
  227. const sockets = await this.socketGateway.server.fetchSockets();
  228. this.logger.warn(`当前有效连接数:${sockets.length}`, 'kick-user');
  229. sockets.forEach((soc) => {
  230. const kickAction = () => {
  231. soc.data.isKick = 1;
  232. soc.emit('action', {
  233. type: 'user-be-kicked',
  234. user: delUser,
  235. });
  236. soc.disconnect(true);
  237. };
  238. if (soc.data.user) {
  239. const userId = soc.data.user.UserId;
  240. if (userId === delUser.UserId) {
  241. this.logger.warn(`kick by UserId`, 'kick-user');
  242. kickAction();
  243. }
  244. } else {
  245. const userId = soc.id;
  246. if (userId === delUser.UserId) {
  247. this.logger.warn(`kick by SocketID`, 'kick-user');
  248. kickAction();
  249. }
  250. }
  251. });
  252. }
  253. const res = await this.userService.deleteRoomUser(RoomId, userId);
  254. if (res) {
  255. this.socketGateway.server.to(RoomId).emit('action', {
  256. type: 'user-exit',
  257. user: delUser,
  258. members: filterRoomUser,
  259. });
  260. }
  261. }
  262. /**
  263. * 房间全员状态更新
  264. * @param message
  265. */
  266. async handleRoomStatusAction(socket: Socket) {
  267. const roomId = socket.data.user.RoomId;
  268. const roomUsers = await this.userService.getRoomUsers(roomId);
  269. this.socketGateway.server.to(roomId).emit('action', {
  270. type: 'rooms-status',
  271. members: roomUsers,
  272. });
  273. }
  274. /**
  275. * 房间主动退出逻辑
  276. * @param message
  277. */
  278. async handleExitAction(socket: Socket, message: any) {
  279. const roomId = message?.roomId || socket.data.user?.RoomId;
  280. const userId = message?.userId || socket.data.user?.UserId;
  281. if (roomId && userId) {
  282. const delUser = await this.userService.getUsersBy(roomId, userId);
  283. this.logger.warn(
  284. `RoomId: ${roomId},userId:${userId} socketId:${delUser.id}`,
  285. 'room-exit',
  286. );
  287. socket.data.isRequestExit = 1;
  288. const roomUsers = await this.userService.getRoomUsers(roomId);
  289. const filterRoomUser = roomUsers.filter((i) => i.UserId !== userId);
  290. const res = await this.userService.deleteRoomUser(roomId, userId);
  291. console.log('user-exit', delUser.id, roomId, userId);
  292. if (res) {
  293. if (socket.data.user?.Role === 'leader') {
  294. this.handleRoomDismiss(roomId);
  295. } else {
  296. this.socketGateway.server.to(roomId).emit('action', {
  297. type: 'user-exit',
  298. user: delUser,
  299. members: filterRoomUser,
  300. });
  301. }
  302. socket.disconnect(true);
  303. }
  304. }
  305. }
  306. /**
  307. * 解散房间
  308. */
  309. async handleRoomDismiss(RoomId: string): Promise<boolean> {
  310. await this.userService.delRoom(RoomId);
  311. await this.userService.delRoomConfig(RoomId);
  312. // await this.userService.delRoomMsgs(RoomId);
  313. this.socketGateway.server
  314. .to(RoomId)
  315. .emit('action', { type: 'leader-dismiss' });
  316. return Promise.resolve(true);
  317. }
  318. }