From 3d5342ec9206f5b4936309b64aed9ff6ff7b8c48 Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Sat, 14 Sep 2024 17:59:11 +0900 Subject: [PATCH] =?UTF-8?q?fix(mongodb):=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EB=8C=80=EB=A5=BC=20KST=EB=A1=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mongo/mongo-model.factory.util.ts | 14 +++- .../controller/project.admin.controller.ts | 6 +- .../project/repository/project.repository.ts | 17 ---- .../project/service/project.admin.service.ts | 16 +++- .../controller/social.admin.controller.ts | 6 +- .../social/repository/social.repository.ts | 11 +-- .../social/service/social.admin.service.ts | 12 ++- .../controller/study.admin.controller.ts | 6 +- .../study/repository/category.repository.ts | 5 +- .../study/service/study.admin.service.ts | 10 ++- .../member/repository/member.repository.ts | 39 +--------- .../member/service/member.admin.service.ts | 14 +++- src/domain/member/service/member.service.ts | 35 +++++---- .../mock/repository/member-repository.mock.ts | 78 ++----------------- 14 files changed, 98 insertions(+), 171 deletions(-) diff --git a/src/common/database/mongo/mongo-model.factory.util.ts b/src/common/database/mongo/mongo-model.factory.util.ts index a57e66c..130546a 100644 --- a/src/common/database/mongo/mongo-model.factory.util.ts +++ b/src/common/database/mongo/mongo-model.factory.util.ts @@ -3,6 +3,8 @@ import { ModelDefinition } from '@nestjs/mongoose'; import { Schema } from 'mongoose'; import AutoPopulate from 'mongoose-autopopulate'; +const convertToKST = (date: Date): Date => new Date(date.getTime() + 9 * 60 * 60 * 1000); + export class MongoModelFactory { static generate(name: string, schema: Schema): ModelDefinition { schema = MongoModelFactory.#setSchemaOptions(schema); @@ -32,8 +34,18 @@ export class MongoModelFactory { } static #setSchemaOptions(schema: Schema): Schema { - schema.set('timestamps', true); schema.set('versionKey', false); + schema.set('timestamps', true); + + schema.pre('save', function (next) { + if ('createdAt' in this) { + this['createdAt'] = convertToKST(new Date()); + } + + this['updatedAt'] = convertToKST(new Date()); + + next(); + }); schema.plugin(AutoPopulate); diff --git a/src/domain/activity/project/controller/project.admin.controller.ts b/src/domain/activity/project/controller/project.admin.controller.ts index ac85f29..fbc7d33 100644 --- a/src/domain/activity/project/controller/project.admin.controller.ts +++ b/src/domain/activity/project/controller/project.admin.controller.ts @@ -38,7 +38,11 @@ export class ProjectAdminController { @AuthAdminAccount() @ApiOperation({ summary: '프로젝트 수정' }) @ApiProperty({ type: UpdateProjectRequestDto }) - @ApiCustomErrorResponse([...AuthAdminAccountException, ProjectNotFoundException]) + @ApiCustomErrorResponse([ + ...AuthAdminAccountException, + ProjectNotFoundException, + AlreadyExistsProjectException, + ]) async updateProject(@Body() request: UpdateProjectRequestDto): Promise { return this.projectAdminService.updateProject(request); } diff --git a/src/domain/activity/project/repository/project.repository.ts b/src/domain/activity/project/repository/project.repository.ts index e566ea6..f74d792 100644 --- a/src/domain/activity/project/repository/project.repository.ts +++ b/src/domain/activity/project/repository/project.repository.ts @@ -32,23 +32,6 @@ export class ProjectRepository { return this.projectModel.findById(id).exec(); } - // Update - async updateTitleById(id: string, title: string): Promise { - await this.projectModel.updateOne({ _id: id }, { title }).exec(); - } - - async updateContentById(id: string, content: string): Promise { - await this.projectModel.updateOne({ _id: id }, { content }).exec(); - } - - async updateTagsById(id: string, tags: string[]): Promise { - await this.projectModel.updateOne({ _id: id }, { tags }).exec(); - } - - async updateImageById(id: string, image: string): Promise { - await this.projectModel.updateOne({ _id: id }, { image }).exec(); - } - // Delete async deleteById(id: string): Promise { await this.projectModel.deleteOne({ _id: id }).exec(); diff --git a/src/domain/activity/project/service/project.admin.service.ts b/src/domain/activity/project/service/project.admin.service.ts index 6ecd765..d755850 100644 --- a/src/domain/activity/project/service/project.admin.service.ts +++ b/src/domain/activity/project/service/project.admin.service.ts @@ -48,10 +48,18 @@ export class ProjectAdminService { throw new ProjectNotFoundException(); } - await this.projectRepository.updateTitleById(projectId, title); - await this.projectRepository.updateContentById(projectId, content); - await this.projectRepository.updateTagsById(projectId, tags); - await this.projectRepository.updateImageById(projectId, image); + const project = (await this.projectRepository.findById(projectId))!; + + if (title !== project.title && (await this.projectRepository.existsByTitle(title))) { + throw new AlreadyExistsProjectException(); + } + + project.title = title; + project.content = content; + project.tags = tags; + project.image = image; + + await this.projectRepository.save(project); } async deleteProject({ projectId }: DeleteProjectRequestDto): Promise { diff --git a/src/domain/activity/social/controller/social.admin.controller.ts b/src/domain/activity/social/controller/social.admin.controller.ts index 72ad058..fed842a 100644 --- a/src/domain/activity/social/controller/social.admin.controller.ts +++ b/src/domain/activity/social/controller/social.admin.controller.ts @@ -31,7 +31,11 @@ export class SocialAdminController { @AuthAdminAccount() @ApiOperation({ summary: '친목 활동 수정' }) @ApiProperty({ type: UpdateSocialRequestDto }) - @ApiCustomErrorResponse([...AuthAdminAccountException, SocialNotFoundException]) + @ApiCustomErrorResponse([ + ...AuthAdminAccountException, + SocialNotFoundException, + AlreadyExistsSocialException, + ]) async updateProject(@Body() request: UpdateSocialRequestDto): Promise { return this.socialAdminService.updateSocial(request); } diff --git a/src/domain/activity/social/repository/social.repository.ts b/src/domain/activity/social/repository/social.repository.ts index 1fe26e1..fda948c 100644 --- a/src/domain/activity/social/repository/social.repository.ts +++ b/src/domain/activity/social/repository/social.repository.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { Content, Social } from '@wink/activity/schema'; +import { Social } from '@wink/activity/schema'; import { Model } from 'mongoose'; @@ -23,15 +23,6 @@ export class SocialRepository { return this.socialModel.findById(id).exec(); } - // Update - async updateTitleById(id: string, title: string): Promise { - await this.socialModel.updateOne({ _id: id }, { title }).exec(); - } - - async updateContentsById(id: string, contents: Content[]): Promise { - await this.socialModel.updateOne({ _id: id }, { contents }).exec(); - } - // Delete async deleteById(id: string): Promise { await this.socialModel.deleteOne({ _id: id }).exec(); diff --git a/src/domain/activity/social/service/social.admin.service.ts b/src/domain/activity/social/service/social.admin.service.ts index d182307..5195c14 100644 --- a/src/domain/activity/social/service/social.admin.service.ts +++ b/src/domain/activity/social/service/social.admin.service.ts @@ -37,8 +37,16 @@ export class SocialAdminService { throw new SocialNotFoundException(); } - await this.socialRepository.updateTitleById(socialId, title); - await this.socialRepository.updateContentsById(socialId, contents); + const social = (await this.socialRepository.findById(socialId))!; + + if (title !== social.title && (await this.socialRepository.existsByTitle(title))) { + throw new AlreadyExistsSocialException(); + } + + social.title = title; + social.contents = contents; + + await this.socialRepository.save(social); } async deleteSocial({ socialId }: DeleteSocialRequestDto): Promise { diff --git a/src/domain/activity/study/controller/study.admin.controller.ts b/src/domain/activity/study/controller/study.admin.controller.ts index bdb7218..40b02b8 100644 --- a/src/domain/activity/study/controller/study.admin.controller.ts +++ b/src/domain/activity/study/controller/study.admin.controller.ts @@ -44,7 +44,11 @@ export class StudyAdminController { @ApiOperation({ summary: '카테고리 수정' }) @ApiProperty({ type: CreateStudyRequestDto }) @ApiCustomResponse(CreateStudyResponseDto) - @ApiCustomErrorResponse([...AuthAdminAccountException, CategoryNotFoundException]) + @ApiCustomErrorResponse([ + ...AuthAdminAccountException, + CategoryNotFoundException, + AlreadyExistsCategoryException, + ]) async updateCategory(@Body() request: UpdateCategoryRequestDto): Promise { return this.studyAdminService.updateCategory(request); } diff --git a/src/domain/activity/study/repository/category.repository.ts b/src/domain/activity/study/repository/category.repository.ts index 0591803..7114ba5 100644 --- a/src/domain/activity/study/repository/category.repository.ts +++ b/src/domain/activity/study/repository/category.repository.ts @@ -26,9 +26,8 @@ export class CategoryRepository { return this.categoryModel.findOne({ name }).exec(); } - // Update - async updateNameById(id: string, name: string): Promise { - await this.categoryModel.updateOne({ _id: id }, { name }).exec(); + async findById(id: string): Promise { + return this.categoryModel.findById(id).exec(); } // Delete diff --git a/src/domain/activity/study/service/study.admin.service.ts b/src/domain/activity/study/service/study.admin.service.ts index 4668ff5..1a2ceed 100644 --- a/src/domain/activity/study/service/study.admin.service.ts +++ b/src/domain/activity/study/service/study.admin.service.ts @@ -47,7 +47,15 @@ export class StudyAdminService { throw new CategoryNotFoundException(); } - await this.categoryRepository.updateNameById(categoryId, name); + const category = (await this.categoryRepository.findById(categoryId))!; + + if (name !== category.name && (await this.categoryRepository.existsByName(name))) { + throw new AlreadyExistsCategoryException(); + } + + category.name = name; + + await this.categoryRepository.save(category); } async deleteCategory({ categoryId }: DeleteCategoryRequestDto): Promise { diff --git a/src/domain/member/repository/member.repository.ts b/src/domain/member/repository/member.repository.ts index 92ed673..b628aa3 100644 --- a/src/domain/member/repository/member.repository.ts +++ b/src/domain/member/repository/member.repository.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { Member, Role } from '@wink/member/schema'; +import { Member } from '@wink/member/schema'; import { Model } from 'mongoose'; @@ -35,43 +35,6 @@ export class MemberRepository { return this.memberModel.findOne({ email }).select('+password').exec(); } - // Update - async updatePasswordById(id: string, password: string): Promise { - await this.memberModel.updateOne({ _id: id }, { password }).exec(); - } - - async updateDescriptionById(id: string, description: string | null): Promise { - await this.memberModel.updateOne({ _id: id }, { description }).exec(); - } - - async updateGithubUrlById(id: string, githubUrl: string | null): Promise { - await this.memberModel.updateOne({ _id: id }, { 'link.github': githubUrl }).exec(); - } - - async updateInstagramUrlById(id: string, instagramUrl: string | null): Promise { - await this.memberModel.updateOne({ _id: id }, { 'link.instagram': instagramUrl }).exec(); - } - - async updateBlogById(id: string, blog: string | null): Promise { - await this.memberModel.updateOne({ _id: id }, { 'link.blog': blog }).exec(); - } - - async updateAvatarById(id: string, avatar: string | null): Promise { - await this.memberModel.updateOne({ _id: id }, { avatar }).exec(); - } - - async updateRoleById(id: string, role: Role): Promise { - await this.memberModel.updateOne({ _id: id }, { role }).exec(); - } - - async updateFeeById(id: string, fee: boolean): Promise { - await this.memberModel.updateOne({ _id: id }, { fee }).exec(); - } - - async updateApprovedById(id: string, approved: boolean): Promise { - await this.memberModel.updateOne({ _id: id }, { approved }).exec(); - } - // Delete async deleteById(id: string): Promise { await this.memberModel.deleteOne({ _id: id }).exec(); diff --git a/src/domain/member/service/member.admin.service.ts b/src/domain/member/service/member.admin.service.ts index d391e4d..a6b43c4 100644 --- a/src/domain/member/service/member.admin.service.ts +++ b/src/domain/member/service/member.admin.service.ts @@ -56,8 +56,10 @@ export class MemberAdminService { throw new NotWaitingMemberException(); } - await this.memberRepository.updateApprovedById(toId, true); - await this.memberRepository.updateRoleById(toId, Role.MEMBER); + to.approved = true; + to.role = Role.MEMBER; + + await this.memberRepository.save(to); this.mailService.sendTemplate(to.email, new ApproveAccountTemplate(to.name)).then((_) => _); @@ -117,7 +119,9 @@ export class MemberAdminService { throw new SuperRoleException(); } - await this.memberRepository.updateRoleById(to._id, role); + to.role = role; + + await this.memberRepository.save(to); this.eventEmitter.emit(UpdateRoleEvent.EVENT_NAME, new UpdateRoleEvent(from, to, role)); } @@ -137,7 +141,9 @@ export class MemberAdminService { throw new SuperRoleException(); } - await this.memberRepository.updateFeeById(toId, fee); + to.fee = fee; + + await this.memberRepository.save(to); this.eventEmitter.emit(UpdateFeeEvent.EVENT_NAME, new UpdateFeeEvent(from, to, fee)); } diff --git a/src/domain/member/service/member.service.ts b/src/domain/member/service/member.service.ts index c27de16..7f99717 100644 --- a/src/domain/member/service/member.service.ts +++ b/src/domain/member/service/member.service.ts @@ -46,12 +46,12 @@ export class MemberService { member: Member, { description, github, instagram, blog }: UpdateMyInfoRequestDto, ): Promise { - const { _id: id } = member; + member.description = description; + member.link.github = github; + member.link.instagram = instagram; + member.link.blog = blog; - await this.memberRepository.updateDescriptionById(id, description); - await this.memberRepository.updateGithubUrlById(id, github); - await this.memberRepository.updateInstagramUrlById(id, instagram); - await this.memberRepository.updateBlogById(id, blog); + await this.memberRepository.save(member); this.eventEmitter.emit( UpdateMyInfoEvent.EVENT_NAME, @@ -63,17 +63,16 @@ export class MemberService { member: Member, { password, newPassword }: UpdateMyPasswordRequestDto, ): Promise { - const { _id: id } = member; - const fullMember = await this.memberRepository.findByIdWithPassword(id); + const fullMember = (await this.memberRepository.findByIdWithPassword(member._id))!; - if (!(await bcrypt.compare(password, fullMember!.password))) { + if (!(await bcrypt.compare(password, fullMember.password))) { throw new WrongPasswordException(); } const salt = await bcrypt.genSalt(10); - const hash = await bcrypt.hash(newPassword, salt); + fullMember.password = await bcrypt.hash(newPassword, salt); - await this.memberRepository.updatePasswordById(id, hash); + await this.memberRepository.save(fullMember); this.eventEmitter.emit(UpdateMyPasswordEvent.EVENT_NAME, new UpdateMyPasswordEvent(member)); } @@ -82,10 +81,12 @@ export class MemberService { member: Member, file: Express.Multer.File, ): Promise { - const { _id: id, avatar: original } = member; + const { avatar: original } = member; const avatar = await this.avatarService.upload(file); - await this.memberRepository.updateAvatarById(id, avatar); + + member.avatar = avatar; + await this.memberRepository.save(member); if (original) { const key = this.avatarService.extractKeyFromUrl(original); @@ -99,13 +100,13 @@ export class MemberService { } async deleteMyAvatar(member: Member): Promise { - const { _id: id, avatar } = member; - - if (avatar) { - const key = this.avatarService.extractKeyFromUrl(avatar); + if (member.avatar) { + const key = this.avatarService.extractKeyFromUrl(member.avatar); await this.avatarService.delete(key); - await this.memberRepository.updateAvatarById(id, null); + + member.avatar = null; + await this.memberRepository.save(member); this.eventEmitter.emit(DeleteMyAvatarEvent.EVENT_NAME, new DeleteMyAvatarEvent(member)); } diff --git a/test/mock/repository/member-repository.mock.ts b/test/mock/repository/member-repository.mock.ts index a506b68..9e4f349 100644 --- a/test/mock/repository/member-repository.mock.ts +++ b/test/mock/repository/member-repository.mock.ts @@ -1,14 +1,14 @@ -import { Member, Role } from '@wink/member/schema'; +import { Member } from '@wink/member/schema'; -// noinspection t export const mockMemberRepository = (memory: Member[]) => ({ // Create save: jest.fn(async (member: Partial) => { - Object.assign(member, { - _id: member['_id'], - ...member, - }); - memory.push(member as Member); + const index = memory.findIndex((m) => m._id === member._id); + if (index !== -1) { + memory[index] = { ...memory[index], ...member }; + } else { + memory.push(member as Member); + } return member as Member; }), @@ -33,70 +33,6 @@ export const mockMemberRepository = (memory: Member[]) => ({ return memory.find((member) => member.email === email); }), - // Update - updatePasswordById: jest.fn(async (id: string, password: string) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.password = password; - } - }), - - updateDescriptionById: jest.fn(async (id: string, description: string) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.description = description; - } - }), - - updateGithubUrlById: jest.fn(async (id: string, githubUrl: string) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.link.github = githubUrl; - } - }), - - updateInstagramUrlById: jest.fn(async (id: string, instagramUrl: string) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.link.instagram = instagramUrl; - } - }), - - updateBlogById: jest.fn(async (id: string, blog: string) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.link.blog = blog; - } - }), - - updateAvatarById: jest.fn(async (id: string, avatar: string) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.avatar = avatar; - } - }), - - updateRoleById: jest.fn(async (id: string, role: Role) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.role = role; - } - }), - - updateFeeById: jest.fn(async (id: string, fee: boolean) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.fee = fee; - } - }), - - updateApprovedById: jest.fn(async (id: string, approved: boolean) => { - const member = memory.find((member) => member._id === id); - if (member) { - member.approved = approved; - } - }), - // Delete deleteById: jest.fn(async (id: string) => { const index = memory.findIndex((member) => member._id === id);