diff --git a/src/apis/questions/questions.service.ts b/src/apis/questions/questions.service.ts index abc7177..31a2df4 100644 --- a/src/apis/questions/questions.service.ts +++ b/src/apis/questions/questions.service.ts @@ -71,6 +71,13 @@ export class QuestionsService { blockUserListDto, ); + break; + case QUESTION_FIND_FILTER_TYPE.RECENT: + result = await this.questionRepository.getRecent2Questions( + myRoomIdDto, + blockUserListDto, + ); + break; } diff --git a/src/apis/rooms/rooms.service.ts b/src/apis/rooms/rooms.service.ts index 5168b3d..8e0e233 100644 --- a/src/apis/rooms/rooms.service.ts +++ b/src/apis/rooms/rooms.service.ts @@ -118,6 +118,7 @@ export class RoomsService { // 유저가현재 들어가있는 방이있으면 // safe 어프로치 populate 안때려도 가상으로 데려감 몽고디비 Document 형식이면 // console.log(typeof roomIdDto.roomId, roomIdDto.roomId, user.myRoom._id); + const room = await this.roomRepository.findOneByRoomId(roomIdDto); if (user.myRoom) { // 유저가 들어간 채팅방이 있을경우 @@ -129,7 +130,6 @@ export class RoomsService { ? true : false; // const isFavoritRoom = user.favoriteRoomList.includes(user.myRoom._id); - const room = await this.roomRepository.findOneByRoomId(roomIdDto); // 차단 유저아웃 room.userList = this.filterRemoveBlockedUserFromUserList( @@ -143,16 +143,28 @@ export class RoomsService { }); } else { // 다른 룸일 경우 다른룸에서 해당 유저를 빼줌 + // 300명인지 체크하는 로직추가 + if (room.userCount >= 300) { + throw new BadRequestException('유저수가 300명이 넘었습니다.'); + } + await this.roomRepository.pullUserFromRoom( new RoomIdDto(user.myRoom._id), userIdDto, ); } } + // 300명인지 체크하는 로직추가 + if (room.userCount >= 300) { + throw new BadRequestException('유저수가 300명이 넘었습니다.'); + } // 룸에 새로 들어갈때,,,? await this.userRepository.setMyRoom(userIdDto, roomIdDto); await this.userRepository.turnOnChatAlarm(userIdDto); - const room = await this.roomRepository.addUserToRoom(roomIdDto, userIdDto); + const newRoom = await this.roomRepository.addUserToRoom( + roomIdDto, + userIdDto, + ); //check const iFavorite = user.favoriteRoomList.find((room) => room._id.equals(roomIdDto.roomId), @@ -161,12 +173,12 @@ export class RoomsService { : false; // 차단 유저아웃 - room.userList = this.filterRemoveBlockedUserFromUserList( - room.userList, + newRoom.userList = this.filterRemoveBlockedUserFromUserList( + newRoom.userList, blockUserListDto, ); - const result = { ...room, iFavorite, iAlarm: true }; + const result = { ...newRoom, iFavorite, iAlarm: true }; // console.log(result); return plainToInstance(ResFindOneRoomDto, result, { excludeExtraneousValues: true, diff --git a/src/apis/users/dto/sendLigningSuccessDto.res.dto.ts b/src/apis/users/dto/sendLigningSuccessDto.res.dto.ts new file mode 100644 index 0000000..92829bb --- /dev/null +++ b/src/apis/users/dto/sendLigningSuccessDto.res.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Expose } from 'class-transformer'; + +export class SendLightningSuccessDtoResDto { + constructor(sendLightningSuccess: boolean) { + this.sendLightningSuccess = sendLightningSuccess; + } + @ApiProperty({ + type: Boolean, + default: false, + description: '번개보낸 성공여부', + }) + @Expose() + sendLightningSuccess: boolean; +} diff --git a/src/apis/users/user.controller.ts b/src/apis/users/user.controller.ts index cad596f..35ef01c 100644 --- a/src/apis/users/user.controller.ts +++ b/src/apis/users/user.controller.ts @@ -30,6 +30,7 @@ import { UserProfileDto } from 'src/common/dtos/UserProfile.dto'; import { ReportResultDtoResDto } from './dto/reportResultDto.res.dto'; import { CanChangeNicknameResDto } from './dto/canChangeNickname.res.dto'; import { NewAlarmStateResDto } from './dto/newAlarmState.res.dto'; +import { SendLightningSuccessDtoResDto } from './dto/sendLigningSuccessDto.res.dto'; @ApiTags('user') @Controller('user') @@ -67,6 +68,17 @@ export class UserController { updateProfileReqDto, ); } + + @ApiOperation({ summary: '내 차단유저 목록을 불러온다' }) + @Get('/block') + @ApiResponse({ + status: 201, + description: '요청 성공시', + type: [UserProfileDto], + }) + getMyBlockUser(@ReqUser() user: User) { + return this.userService.getMyBlockUser(user.userIdDto); + } // @ApiOperation({ summary: '상대방 유저정보를 가져온다.' }) @ApiResponse({ @@ -92,7 +104,7 @@ export class UserController { @ApiResponse({ status: 201, description: '요청 성공시', - type: User, + type: [UserProfileDto], }) blockUser(@Param() otherUSerIdDto: UserIdDto, @ReqUser() user: User) { return this.userService.blockUser(user.userIdDto, otherUSerIdDto); @@ -102,7 +114,7 @@ export class UserController { @ApiResponse({ status: 200, description: '요청 성공시', - type: User, + type: [UserProfileDto], }) @Delete(':userId/block') unblockUser(@Param() otherUSerIdDto: UserIdDto, @ReqUser() user: User) { @@ -153,4 +165,22 @@ export class UserController { toggleAppAlarm(@ReqUser() user: User) { return this.userService.toggleAlarmAlarm(user.userIdDto); } + + @ApiOperation({ + summary: '상대방에게 번개를 보낸다', + }) + @Post(':userId/lightning') + @ApiResponse({ + status: 201, + description: '요청 성공시', + type: SendLightningSuccessDtoResDto, + }) + @ApiResponse({ + status: 201, + description: '이미 요청되었으면하루에 한번 ', + type: SendLightningSuccessDtoResDto, + }) + sendLightningToUser(@ReqUser() user: User, @Param() userIdDto: UserIdDto) { + return this.userService.sendLightningToUser(user.userIdDto, userIdDto); + } } diff --git a/src/apis/users/user.module.ts b/src/apis/users/user.module.ts index 4f0a47d..285513e 100644 --- a/src/apis/users/user.module.ts +++ b/src/apis/users/user.module.ts @@ -1,8 +1,10 @@ import { forwardRef, Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { AuthModule } from 'src/auth/auth.module'; +import { Lightning, LightningSchema } from 'src/models/lightning.model'; import { Report, ReportSchema } from 'src/models/report.model'; import { User, UserSchema } from 'src/models/user.model'; +import { LightningRepository } from 'src/repositories/lightning.repository'; import { ReportRepository } from 'src/repositories/report.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { UserController } from './user.controller'; @@ -13,11 +15,17 @@ import { UserService } from './user.service'; MongooseModule.forFeature([ { name: User.name, schema: UserSchema }, { name: Report.name, schema: ReportSchema }, + { name: Lightning.name, schema: LightningSchema }, ]), forwardRef(() => AuthModule), ], controllers: [UserController], - providers: [UserRepository, UserService, ReportRepository], + providers: [ + UserRepository, + UserService, + ReportRepository, + LightningRepository, + ], exports: [UserService, ReportRepository, UserRepository], }) export class UserModule {} diff --git a/src/apis/users/user.service.ts b/src/apis/users/user.service.ts index 64fc880..60e6f02 100644 --- a/src/apis/users/user.service.ts +++ b/src/apis/users/user.service.ts @@ -18,6 +18,12 @@ import { NewAlarmStateResDto } from './dto/newAlarmState.res.dto'; import { UserProfileDto } from 'src/common/dtos/UserProfile.dto'; import { BlockedUserDto } from 'src/common/dtos/BlockedUserList.dto'; import { AuthService } from 'src/auth/auth.service'; +import { LightningRepository } from 'src/repositories/lightning.repository'; +import { SendLightningSuccessDtoResDto } from './dto/sendLigningSuccessDto.res.dto'; +import { + USER_LEVELUP_COUNT_TYPE, + USER_LEVEL_TYPE, +} from 'src/common/consts/enum'; @Injectable() export class UserService { @@ -26,6 +32,7 @@ export class UserService { private reportRepository: ReportRepository, @Inject(forwardRef(() => AuthService)) private authService: AuthService, + private lightnignRepository: LightningRepository, ) {} private checkBlocked(userIdDto: UserIdDto, blockedUserDto: BlockedUserDto) { @@ -57,17 +64,19 @@ export class UserService { updateProfileReqDto: UpdateProfileReqDto, ): Promise { // auto 시리얼 라이징 + + //TODO : 닉네임 변경시에 한번더 밸리데이션? 내프로필 정보랑 닉네임 일치하면 변경하고.. return await this.userRepository.updateProfile( userIdDto, updateProfileReqDto, ); } - @returnValueToDto(User) + @returnValueToDto(UserProfileDto) async blockUser( myUserIdDto: UserIdDto, otherUserIdDto: UserIdDto, - ): Promise { + ): Promise { if (myUserIdDto.userId.equals(otherUserIdDto.userId)) { throw new BadRequestException('내 자신 차단'); } @@ -88,15 +97,20 @@ export class UserService { ); console.log('asdfasdfasdfasdfasdf', returnUser); // auto 시리얼 라이징 - return returnUser; + return returnUser.iBlockUsers; } - @returnValueToDto(User) + @returnValueToDto(UserProfileDto) async upBlockUser( myUserIdDto: UserIdDto, otherUserIdDto: UserIdDto, - ): Promise { + ): Promise { // auto 시리얼 라이징 - return await this.userRepository.unBlockUser(myUserIdDto, otherUserIdDto); + const user = await this.userRepository.unBlockUser( + myUserIdDto, + otherUserIdDto, + ); + + return user.iBlockUsers; } async reportUser( @@ -172,4 +186,51 @@ export class UserService { const appAlarm = await this.userRepository.toggleApptAlarm(myUserIdDto); return { appAlarm: appAlarm }; } + + @returnValueToDto(SendLightningSuccessDtoResDto) + async sendLightningToUser( + sender: UserIdDto, + receive: UserIdDto, + ): Promise { + const checkReportExist = this.lightnignRepository.findOneLightningByUserId( + sender, + receive, + ); + if (checkReportExist) { + return { sendLightningSuccess: false }; + } + + const expireAt = new Date(); + // utc 한국시간 기준으로 자정으로 설정 + expireAt.setUTCHours(15, 0, 0, 0); + // expireAt.setHours() + await this.lightnignRepository.saveLighting(sender, receive, expireAt); + const addUserScore = await this.userRepository.addUserLigthningScore( + receive, + ); + + switch (addUserScore.lightningScore) { + case USER_LEVELUP_COUNT_TYPE.LEVEL1: + await this.userRepository.levelUpUser(receive, USER_LEVEL_TYPE.LEVEL1); + break; + case USER_LEVELUP_COUNT_TYPE.LEVEL2: + await this.userRepository.levelUpUser(receive, USER_LEVEL_TYPE.LEVEL2); + break; + case USER_LEVELUP_COUNT_TYPE.LEVEL3: + await this.userRepository.levelUpUser(receive, USER_LEVEL_TYPE.LEVEL3); + break; + default: + break; + } + return { sendLightningSuccess: true }; + } + + @returnValueToDto(UserProfileDto) + async getMyBlockUser(myUserIdDto: UserIdDto) { + console.log('check'); + const user = await this.userRepository.findOneByUserId(myUserIdDto); + console.log(user); + + return user.iBlockUsers; + } } diff --git a/src/common/consts/enum.ts b/src/common/consts/enum.ts index c0fafb2..0f7680b 100644 --- a/src/common/consts/enum.ts +++ b/src/common/consts/enum.ts @@ -113,12 +113,29 @@ enum NOTIFICATION_POST_TYPE { //카테고리 타입 한글로 enum QUESTION_FIND_FILTER_TYPE { - NOTANSWERED = 'NOTANSWERED', //대학교 - OLDORDER = 'OLDORDER', //공연장 - NEWORDER = 'NEWORDER', //한강공원 + NOTANSWERED = 'NOTANSWERED', // 답변못받은거 + OLDORDER = 'OLDORDER', //오래된순 + NEWORDER = 'NEWORDER', //최신순 + RECENT = 'RECENT', //최신순 +} + +enum USER_LEVEL_TYPE { + LEVEL0 = 0, //대학교 + LEVEL1 = 1, //공연장 + LEVEL2 = 2, //한강공원 + LEVEL3 = 2, //한강공원 +} + +enum USER_LEVELUP_COUNT_TYPE { + LEVEL0 = 0, //대학교 + LEVEL1 = 5, //공연장 + LEVEL2 = 25, //한강공원 + LEVEL3 = 100, //한강공원 } export { + USER_LEVELUP_COUNT_TYPE, + USER_LEVEL_TYPE, QUESTION_FIND_FILTER_TYPE, getEnumToArray, getEnumTypeValues, diff --git a/src/common/dtos/UserProfile.dto.ts b/src/common/dtos/UserProfile.dto.ts index 988c8f8..31d586b 100644 --- a/src/common/dtos/UserProfile.dto.ts +++ b/src/common/dtos/UserProfile.dto.ts @@ -1,15 +1,16 @@ import { ApiProperty } from '@nestjs/swagger'; import { Expose, Transform, Type } from 'class-transformer'; -import { Profile } from 'src/models/user.model'; +import { Profile, User } from 'src/models/user.model'; import { Types } from 'mongoose'; import { TransformObjectIdToString } from '../decorators/Expose.decorator'; export class UserProfileDto { - constructor(user) { + constructor(user: User) { if (user) { this._id = user._id; this.nickname = user.nickname; this.profile = user.profile; + this.level = user.level; } } @ApiProperty({ type: String, example: '626cf238b51596721c21289b' }) @@ -26,6 +27,13 @@ export class UserProfileDto { @Type(() => Profile) @Expose() profile: Profile; + + @ApiProperty({ + type: Number, + description: '유저의 확성기 레벨', + }) + @Expose() + level: number; } -export const UserProfileSelect = { _id: 1, nickname: 1, profile: 1 }; +export const UserProfileSelect = { _id: 1, nickname: 1, profile: 1, level: 1 }; diff --git a/src/models/lightning.model.ts b/src/models/lightning.model.ts new file mode 100644 index 0000000..c0ccbc7 --- /dev/null +++ b/src/models/lightning.model.ts @@ -0,0 +1,27 @@ +import { Prop, Schema, SchemaFactory, SchemaOptions } from '@nestjs/mongoose'; + +import { Exclude, Expose, Transform, Type } from 'class-transformer'; +import { Types } from 'mongoose'; + +const options: SchemaOptions = { + collection: 'lightning', + timestamps: true, +}; + +@Schema(options) +export class Lightning { + @Prop({ type: Types.ObjectId }) + sendUser: Types.ObjectId; + + @Prop({ type: Types.ObjectId }) + receiveUser: Types.ObjectId; + + @Prop({ + type: Date, + expires: 0, + }) + @Expose() + expireAt: Date; +} + +export const LightningSchema = SchemaFactory.createForClass(Lightning); diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 2b5f476..9b4bc4a 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -174,6 +174,28 @@ export class User { @Transform(({ value }) => toKRTimeZone(value), { toClassOnly: true }) @Expose() createdAt: Date; + + @ApiProperty({ + type: Number, + description: '유저의 번개 점수', + }) + @Prop({ + type: Number, + default: 0, + }) + @Expose() + lightningScore: number; + + @ApiProperty({ + type: Number, + description: '유저의 확성기 레벨', + }) + @Prop({ + type: Number, + default: 0, + }) + @Expose() + level: number; } export const _UserSchema = SchemaFactory.createForClass(User); diff --git a/src/repositories/lightning.repository.ts b/src/repositories/lightning.repository.ts new file mode 100644 index 0000000..0a8485a --- /dev/null +++ b/src/repositories/lightning.repository.ts @@ -0,0 +1,36 @@ +import { InjectModel } from '@nestjs/mongoose'; +import { Injectable } from '@nestjs/common'; +import { Model } from 'mongoose'; +import { UserIdDto } from 'src/common/dtos/UserId.dto'; +import { Lightning } from 'src/models/lightning.model'; + +@Injectable() +export class LightningRepository { + constructor( + @InjectModel(Lightning.name) + private readonly LightningModel: Model, + ) {} + + async saveLighting( + senderIdDto: UserIdDto, + receiveIdDto: UserIdDto, + expireAtDate: Date, + ): Promise { + const lightning = new this.LightningModel({ + sendUser: senderIdDto.userId, + receiveUser: receiveIdDto.userId, + expireAt: expireAtDate, + }); + return await lightning.save(); + } + + async findOneLightningByUserId( + senderIdDto: UserIdDto, + receiveIdDto: UserIdDto, + ): Promise { + return await this.LightningModel.findOne({ + sendUser: senderIdDto.userId, + receiveUser: receiveIdDto.userId, + }); + } +} diff --git a/src/repositories/question.repository.ts b/src/repositories/question.repository.ts index b150390..da51f4b 100644 --- a/src/repositories/question.repository.ts +++ b/src/repositories/question.repository.ts @@ -50,6 +50,23 @@ export class QuestionRepository { .lean({ defaults: true }); } + async getRecent2Questions( + roomIdDto: RoomIdDto, + blockUserListDto: BlockedUserDto, + ): Promise { + return await this.questionModel + .find({ + room: roomIdDto.roomId, + user: { $nin: blockUserListDto.blockedUsers }, + }) + .sort({ createdAt: -1 }) + .limit(2) + .populate({ + path: 'user', + select: UserProfileSelect, + }) + .lean({ defaults: true }); + } async getQuestionsByRoomIdNotAnswerd( roomIdDto: RoomIdDto, blockUserListDto: BlockedUserDto, diff --git a/src/repositories/user.repository.ts b/src/repositories/user.repository.ts index 5b6c2d1..e7b4cb9 100644 --- a/src/repositories/user.repository.ts +++ b/src/repositories/user.repository.ts @@ -15,7 +15,7 @@ import { Room } from 'src/models/room.model'; import { UpdateProfileReqDto } from 'src/apis/users/dto/updateUserDto.req.dto'; import { UserProfileSelect } from 'src/common/dtos/UserProfile.dto'; import { ResShortCutRoomDto } from 'src/common/dtos/shortCutRoomInfo.res.dto'; -import { STATUS_TYPE } from 'src/common/consts/enum'; +import { STATUS_TYPE, USER_LEVEL_TYPE } from 'src/common/consts/enum'; @Injectable() export class UserRepository { @@ -383,4 +383,20 @@ export class UserRepository { return roomInfo.length ? roomInfo[0] : null; } + + async addUserLigthningScore(userIdDto: UserIdDto): Promise { + return await this.userModel.findOneAndUpdate( + { _id: userIdDto.userId }, + { $inc: { lightningScore: 1 } }, + { new: true }, + ); + } + + async levelUpUser(userIdDto: UserIdDto, userlevel: number): Promise { + return await this.userModel.findOneAndUpdate( + { _id: userIdDto.userId }, + { level: userlevel }, + { new: true }, + ); + } }