gemercheung 2 年 前
コミット
7b42007b36

+ 4 - 0
src/room-manager/dto/danmaku.dto.ts

@@ -11,5 +11,9 @@ export class DanmakuDto {
   mode: string;
 }
 export class DanmakuAllDto {
+  @ApiProperty({
+    isArray: true,
+    type: DanmakuDto,
+  })
   data: DanmakuDto[];
 }

+ 51 - 0
src/room-manager/dto/user.dto.ts

@@ -0,0 +1,51 @@
+import { ApiProperty } from '@nestjs/swagger';
+
+// id?: string; //socketId
+// UserId: string;
+// RoomId: string;
+// Role: string;
+// Avatar: string;
+// Nickname: string;
+// IsClient?: boolean;
+// IsMuted?: boolean;
+// IsWords?: boolean;
+// JoinTime?: Timestamp;
+// InTime?: Timestamp;
+// Order?: number;
+// IsOnline?: boolean;
+class RoomCount {
+  @ApiProperty()
+  count: number;
+}
+
+export class UsersDto {
+  @ApiProperty()
+  id: string;
+  @ApiProperty()
+  UserId: string;
+  @ApiProperty()
+  RoomId: string;
+  @ApiProperty()
+  Role: string;
+  @ApiProperty()
+  Avatar: string;
+  @ApiProperty()
+  Nickname: string;
+  @ApiProperty()
+  IsClient: boolean;
+  @ApiProperty()
+  IsMuted: boolean;
+  @ApiProperty()
+  Order: boolean;
+  @ApiProperty()
+  IsOnline: boolean;
+}
+export class RoomStatusDto {
+  @ApiProperty({
+    isArray: true,
+    type: UsersDto,
+  })
+  data: UsersDto[];
+  @ApiProperty()
+  count: number;
+}

+ 51 - 16
src/room-manager/room-manager.controller.ts

@@ -9,11 +9,18 @@ import {
 } from '@nestjs/common';
 import { RoomManagerService } from './room-manager.service';
 import { DanmakuDto } from './dto/danmaku.dto';
+import { RoomStatusDto } from './dto/user.dto';
 import { ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger';
+import { RoomService } from 'src/room/room.service';
+import { UsersService } from 'src/room/users/users.service';
 
 @Controller('room-manager')
 export class RoomManagerController {
-  constructor(private readonly roomManagerService: RoomManagerService) {}
+  constructor(
+    private readonly roomManagerService: RoomManagerService,
+    private readonly roomService: RoomService,
+    private readonly usersService: UsersService,
+  ) { }
 
   @Get('/danmaku/:id')
   @ApiOperation({ summary: '获取房间弹幕列表' })
@@ -32,21 +39,49 @@ export class RoomManagerController {
     return this.roomManagerService.findDanmakuList(id);
   }
 
-  // @Get(':id')
-  // findOne(@Param('id') id: string) {
-  //   return this.roomManagerService.findOne(+id);
-  // }
+  @Get('/dismissRoom/:id')
+  @ApiOperation({ summary: '主动解散房间' })
+  @ApiParam({
+    name: 'id',
+    required: true,
+    description: '房间ID',
+    schema: { oneOf: [{ type: 'string' }] },
+    type: 'string',
+  })
+  @ApiResponse({
+    description: 'The record has been successfully created.',
+    type: Boolean,
+  })
+  async disMissRoom(@Param('id') id: string) {
+    this.roomService.logger.log(`roomId: ${id}`, 'api-dismiss');
+    const res = await this.roomService.handleRoomDismiss(id);
+    return res;
+  }
 
-  // @Patch(':id')
-  // update(
-  //   @Param('id') id: string,
-  //   @Body() updateRoomManagerDto: UpdateRoomManagerDto,
-  // ) {
-  //   return this.roomManagerService.update(+id, updateRoomManagerDto);
-  // }
+  @Get('/status/:id')
+  @ApiOperation({ summary: '获取当前房间状态' })
+  @ApiParam({
+    name: 'id',
+    required: true,
+    description: '房间ID',
+    schema: { oneOf: [{ type: 'string' }] },
+    type: 'string',
+  })
+  @ApiResponse({
+    description: 'The record has been successfully created.',
+    type: RoomStatusDto,
+  })
+  async getRoomStatus(@Param('id') id: string) {
+    this.roomService.logger.log(`roomId: ${id}`, 'api-room-status');
+    if (id) {
+      const roomUsers = await this.usersService.getRoomUsers(id);
+      return {
+        data: roomUsers,
+        count: roomUsers.length,
+      };
+    }
 
-  // @Delete(':id')
-  // remove(@Param('id') id: string) {
-  //   return this.roomManagerService.remove(+id);
-  // }
+    // const res = await this.roomService.handleRoomDismiss(id);
+    // return res;
+  }
 }

+ 6 - 2
src/room-manager/room-manager.module.ts

@@ -1,9 +1,13 @@
 import { Module } from '@nestjs/common';
 import { RoomManagerService } from './room-manager.service';
 import { RoomManagerController } from './room-manager.controller';
+// import { RoomService } from 'src/room/room.service';
+import { RoomModule } from 'src/room/room.module';
+import { UsersService } from 'src/room/users/users.service';
 
 @Module({
+  imports: [RoomModule],
   controllers: [RoomManagerController],
-  providers: [RoomManagerService],
+  providers: [RoomManagerService, UsersService],
 })
-export class RoomManagerModule {}
+export class RoomManagerModule { }

+ 0 - 17
src/room-manager/room-manager.service.ts

@@ -7,13 +7,6 @@ import { Redis } from 'ioredis';
 @Injectable()
 export class RoomManagerService {
   constructor(@InjectRedis() private readonly redis: Redis) {}
-  // create(createRoomManagerDto: CreateRoomManagerDto) {
-  //   return 'This action adds a new roomManager';
-  // }
-
-  findAll() {
-    return `This action returns all roomManager`;
-  }
 
   async findDanmakuList(id: string) {
     if (id) {
@@ -30,15 +23,5 @@ export class RoomManagerService {
     return [];
   }
 
-  findOne(id: number) {
-    return `This action returns a #${id} roomManager`;
-  }
-
-  // update(id: number, updateRoomManagerDto: UpdateRoomManagerDto) {
-  //   return `This action updates a #${id} roomManager`;
-  // }
 
-  // remove(id: number) {
-  //   return `This action removes a #${id} roomManager`;
-  // }
 }

+ 67 - 20
src/room/delay/delay.service.ts

@@ -1,10 +1,12 @@
 import { InjectRedis } from '@liaoliaots/nestjs-redis';
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, forwardRef } from '@nestjs/common';
 import { Redis } from 'ioredis';
 import { Socket } from 'socket.io';
 import * as dayjs from 'dayjs';
 import * as duration from 'dayjs/plugin/duration';
 import { ConfigService } from '@nestjs/config';
+import { UsersService } from '../users/users.service';
+import { RoomService } from '../room.service';
 dayjs.extend(duration);
 @Injectable()
 export class DelayService {
@@ -12,33 +14,78 @@ export class DelayService {
     @InjectRedis() private readonly redis: Redis,
     @InjectRedis('sub') private readonly subRedis: Redis,
     private configService: ConfigService,
+    private readonly userService: UsersService,
+    @Inject(forwardRef(() => RoomService))
+    public readonly roomService: RoomService,
   ) {}
 
   init(): void {
     const redisDB = this.configService.get<string>('REDIS_DB');
-    console.log('redisDB', redisDB);
-    this.redis.on('ready', () => {
-      console.log('ready');
-      this.redis.config('SET', 'notify-keyspace-events', 'Ex');
-      //   this.subRedis.config('SET', 'notify-keyspace-events', 'Ex');
-    });
-    // const subscribeEvent = `__keyevent@${redisDB}__:expired`;
-    console.log('subRedis', this.subRedis);
-    this.subRedis.on('ready', () => {
-      this.subRedis.subscribe(`_key*@*__:*`, function () {
-        console.log('Arguments', arguments);
-      });
+    // console.log('redisDB', redisDB);
+    this.redis.config('SET', 'notify-keyspace-events', 'Ex');
+    const subscribeEvent = `__keyevent@${redisDB}__:expired`;
+    // console.log('subRedis', this.subRedis);
+    this.subRedis.subscribe(subscribeEvent);
+    this.subRedis.on('message', (channel, key) => {
+      if (channel === subscribeEvent) {
+        this.handleExpiredSubscribe(key);
+      }
     });
   }
-  handleExpiredSubscribe(key) {
-    // eslint-disable-next-line prefer-rest-params
-    console.log('key', arguments);
+  async handleExpiredSubscribe(key: string) {
+    if (key.includes('kankan:socket:delay')) {
+      const tKey = key.replace('kankan:socket:delay:', '');
+      const params = tKey.split(':');
+      const RoomId = params[0];
+      const UserId = params[1];
+      const delayUser = await this.userService.getUsersBy(RoomId, UserId);
+      if (delayUser) {
+        const roomUsers = await this.userService.getRoomUsers(RoomId);
+        const filterRoomUser = roomUsers.filter((i) => i.UserId !== UserId);
+        console.log('delayUser', delayUser.UserId);
+        const res = await this.userService.deleteRoomUser(RoomId, UserId);
+        if (res) {
+          if (delayUser.Role === 'leader') {
+            this.roomService.handleRoomDismiss(RoomId);
+          } else {
+            this.roomService.socketGateway.server.to(RoomId).emit('action', {
+              type: 'user-exit',
+              user: delayUser,
+              members: filterRoomUser,
+            });
+          }
+          this.roomService.socketGateway.server.sockets.sockets.forEach(
+            (soc) => {
+              if (soc.id === delayUser.id) {
+                soc.disconnect(true);
+              }
+            },
+          );
+        }
+      }
+    }
+  }
+  async handleOnine(socket?: Socket): Promise<void> {
+    if (socket?.data) {
+      const { RoomId, UserId } = socket.data?.user;
+      if (RoomId && UserId) {
+        await this.redis.del(`kankan:socket:delay:${RoomId}:${UserId}`);
+      }
+    }
   }
 
   async handleOffline(socket?: Socket): Promise<void> {
-    this.redis.set('test-test', 'wwwsssss', 'EX', 20);
-    this.redis.publish('what', 'xxxx');
-    // console.log('socket', socket);
-    // this.redis.subscribe();
+    if (socket?.data) {
+      const delayTime = 60 * 5;
+      const { RoomId, UserId } = socket.data?.user;
+      if (RoomId && UserId) {
+        this.redis.set(
+          `kankan:socket:delay:${RoomId}:${UserId}`,
+          '',
+          'EX',
+          delayTime,
+        );
+      }
+    }
   }
 }

+ 45 - 23
src/room/room.service.ts

@@ -15,7 +15,7 @@ export class RoomService {
     private readonly userService: UsersService,
     private readonly actionsService: ActionsService,
     private readonly delayService: DelayService,
-  ) {}
+  ) { }
   public readonly logger = new Logger('user');
   public _sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
   public _userInfo = {} as UserInfoType;
@@ -67,15 +67,29 @@ export class RoomService {
 
   async handleUserOffline(socket: Socket) {
     await this._sleep(500);
-    if (socket.data.user) {
-      socket.data.user.IsOnline = false;
-      await this.userService.updateUserOnlineState(
-        socket,
-        socket.data.user.RoomId,
-        socket.data.user.UserId,
-        false,
-      );
-      await this.handleRoomStatusAction(socket);
+    if (socket.data?.user) {
+      this.delayService.handleOffline(socket);
+      const { RoomId, UserId } = socket.data.user;
+      if (RoomId && UserId) {
+        //标记主动退出房间
+        if (socket.data.isRequestExit !== 1) {
+          const roomUsers = await this.userService.getRoomUsers(RoomId);
+          this.socketGateway.server.to(RoomId).emit('action', {
+            type: 'user-leave',
+            user: socket.data.user,
+            members: roomUsers,
+          });
+        }
+
+        socket.data.user.IsOnline = false;
+        await this.userService.updateUserOnlineState(
+          socket,
+          socket.data.user.RoomId,
+          socket.data.user.UserId,
+          false,
+        );
+        await this.handleRoomStatusAction(socket);
+      }
     }
   }
 
@@ -91,9 +105,7 @@ export class RoomService {
     this.initUserProfile(userInfo);
     socket.data.user = this._userInfo;
     await this.handleUserOnline(socket);
-    //test
-    this.delayService.handleOffline();
-
+    await this.delayService.handleOnine(socket);
     let blockJoin = false;
     if (this._roomId?.length && this._userId?.length) {
       //房主设置房间配置
@@ -110,11 +122,12 @@ export class RoomService {
           );
         } else {
           blockJoin = true;
-          socket.emit('action', {
+          socket.emit('manager-error', {
             type: 'invalid-master',
             code: 303,
           });
           this.logger.warn(`303:invalid-master`, 'join-error');
+          socket.disconnect(true);
         }
       }
       const isExist = await this.userService.isUserInRooms(
@@ -128,14 +141,15 @@ export class RoomService {
         } else {
           const updated = await this.userService.updateUsers(this._userInfo);
           if (!updated) {
-            socket.emit('action', {
-              type: 'error',
+            socket.emit('manager-error', {
+              type: 'invalid-match-role',
               code: 405,
             });
             this.logger.warn(
               `same userId in room not matchRole `,
               'join-error',
             );
+            socket.disconnect(true);
             return;
           }
         }
@@ -158,8 +172,13 @@ export class RoomService {
         );
       }
     } else {
-      socket.emit('action', { type: 'error', code: 403 });
+      socket.emit('manager-error', {
+        type: 'invalid-room-params',
+        code: 403,
+      });
       this.logger.warn(`403:not roomId and userId`, 'join-error');
+      socket.disconnect(true);
+      return;
     }
   }
   /**
@@ -191,7 +210,7 @@ export class RoomService {
    * @param message
    */
   async handleKickAction(socket: Socket, RoomId: string, userId: string) {
-    const delUser = await this.userService.getUsersByUserId(RoomId, userId);
+    const delUser = await this.userService.getUsersBy(RoomId, userId);
     this.logger.warn(
       `RoomId: ${RoomId},userId:${userId} socketId:${delUser.id}`,
       'kick-user',
@@ -209,7 +228,7 @@ export class RoomService {
     const res = await this.userService.deleteRoomUser(RoomId, userId);
     if (res) {
       this.socketGateway.server.to(RoomId).emit('action', {
-        type: 'user-leave',
+        type: 'user-exit',
         user: delUser,
         members: filterRoomUser,
       });
@@ -233,15 +252,15 @@ export class RoomService {
    * @param message
    */
   async handleExitAction(socket: Socket, message: any) {
-    console.log('socket', socket.data.user);
     const roomId = message?.roomId || socket.data.user?.RoomId;
     const userId = message?.userId || socket.data.user?.UserId;
     if (roomId && userId) {
-      const delUser = await this.userService.getUsersByUserId(roomId, userId);
+      const delUser = await this.userService.getUsersBy(roomId, userId);
       this.logger.warn(
         `RoomId: ${roomId},userId:${userId} socketId:${delUser.id}`,
         'room-exit',
       );
+      socket.data.isRequestExit = 1;
       const roomUsers = await this.userService.getRoomUsers(roomId);
       const filterRoomUser = roomUsers.filter((i) => i.UserId !== userId);
       const res = await this.userService.deleteRoomUser(roomId, userId);
@@ -251,7 +270,7 @@ export class RoomService {
           this.handleRoomDismiss(roomId);
         } else {
           this.socketGateway.server.to(roomId).emit('action', {
-            type: 'user-leave',
+            type: 'user-exit',
             user: delUser,
             members: filterRoomUser,
           });
@@ -264,11 +283,14 @@ export class RoomService {
    * 解散房间
    */
 
-  async handleRoomDismiss(RoomId: string): Promise<void> {
+  async handleRoomDismiss(RoomId: string): Promise<boolean> {
     await this.userService.delRoom(RoomId);
     await this.userService.delRoomConfig(RoomId);
+    await this.userService.delRoomMsgs(RoomId);
     this.socketGateway.server
       .to(RoomId)
       .emit('action', { type: 'leader-dismiss' });
+    return Promise.resolve(true);
   }
+
 }

+ 9 - 7
src/room/users/users.service.ts

@@ -10,7 +10,7 @@ export class UsersService {
     @InjectRedis() private readonly redis: Redis,
     @Inject(forwardRef(() => RoomService))
     private roomService: RoomService,
-  ) {}
+  ) { }
 
   async isUserInRooms(RoomId: string, UserId: string): Promise<boolean> {
     const res = await this.redis.hexists(
@@ -28,10 +28,7 @@ export class UsersService {
     );
     return Promise.resolve(Boolean(insert));
   }
-  async getUsersByUserId(
-    RoomId: string,
-    UserId: string,
-  ): Promise<UserInfoType> {
+  async getUsersBy(RoomId: string, UserId: string): Promise<UserInfoType> {
     const user = await this.redis.hget(`kankan:socket:rooms:${RoomId}`, UserId);
     return JSON.parse(user) as unknown as UserInfoType;
   }
@@ -88,8 +85,13 @@ export class UsersService {
   delRoom(RoomId: string): Promise<any> {
     return this.redis.del(`kankan:socket:rooms:${RoomId}`);
   }
-  delRoomConfig(RoomId: string): Promise<any> {
-    return this.redis.hdel(`kankan:socket:roomConfig`, RoomId);
+  delRoomConfig(RoomId: string): Promise<boolean> {
+    const res = this.redis.hdel(`kankan:socket:roomConfig`, RoomId);
+    return Promise.resolve(Boolean(res));
+  }
+  delRoomMsgs(RoomId: string): Promise<boolean> {
+    const res = this.redis.del(`kankan:socket:roomMsg:${RoomId}`);
+    return Promise.resolve(Boolean(res));
   }
   async setRoomConfig(
     RoomId: string,

+ 4 - 1
src/socket/socket.gateway.ts

@@ -89,7 +89,10 @@ export class SocketGateway
       this._loginLimit.set(flag, socket.id);
       return false;
     } else {
-      socket.emit('repeat-login', { isRepeat: true });
+      socket.emit('manager-error', {
+        type: 'repeat-login',
+        code: 306,
+      });
       this.logger.warn(`306:${message.userId}`, 'repeat-login');
       return true;
     }