From 253591b262d6a6eae9dc81963f5130f864daa3f6 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 23 Jan 2024 16:10:22 +0900 Subject: [PATCH 001/316] =?UTF-8?q?feat=20:=20journey=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=97=94=ED=84=B0=ED=8B=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.entity.ts | 10 ++++-- src/journey/journey.controller.ts | 0 src/journey/journey.entity.ts | 26 +++++++++++++++ src/journey/journey.module.ts | 0 src/journey/journey.service.ts | 0 .../journey/journey-dto/create-journey.dto.ts | 0 src/journey/monthly-journey.entity.ts | 0 src/schedule/schedule.entity.ts | 20 +++++++++--- src/schedule/schedule.group.entity.ts | 32 +++++++++++-------- src/user/user.dto.ts | 2 +- src/user/user.entity.ts | 12 ++++--- 11 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 src/journey/journey.controller.ts create mode 100644 src/journey/journey.entity.ts create mode 100644 src/journey/journey.module.ts create mode 100644 src/journey/journey.service.ts create mode 100644 src/journey/journey/journey-dto/create-journey.dto.ts create mode 100644 src/journey/monthly-journey.entity.ts diff --git a/src/diary/diary.entity.ts b/src/diary/diary.entity.ts index 8e797e9..155e09a 100644 --- a/src/diary/diary.entity.ts +++ b/src/diary/diary.entity.ts @@ -5,7 +5,8 @@ import { DeleteDateColumn, Entity, JoinColumn, - ManyToOne, OneToMany, + ManyToOne, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -32,7 +33,10 @@ export class DiaryEntity extends BaseEntity { @Column({ type: 'enum', enum: ['SUNNY', 'RAINY', 'SNOWY', 'CLOUDY'] }) weather: 'SUNNY' | 'RAINY' | 'SNOWY' | 'CLOUDY'; - @Column({ type: 'enum', enum: ['HAPPY', 'SAD', 'ANGRY', 'SHOCKED', 'SLEEPY', 'WINK'] }) + @Column({ + type: 'enum', + enum: ['HAPPY', 'SAD', 'ANGRY', 'SHOCKED', 'SLEEPY', 'WINK'], + }) mood: 'HAPPY' | 'SAD' | 'ANGRY' | 'SHOCKED' | 'SLEEPY' | 'WINK'; @Column({ type: 'mediumtext' }) @@ -49,4 +53,4 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/journey/journey.entity.ts b/src/journey/journey.entity.ts new file mode 100644 index 0000000..8c03222 --- /dev/null +++ b/src/journey/journey.entity.ts @@ -0,0 +1,26 @@ +// journey.entity.ts + +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; +import { ScheduleGroupEntity } from './schedule/schedule.group.entity'; +import { MonthlyJourneyEntity } from './monthly-journey.entity'; + +@Entity({ name: 'Journey' }) +export class JourneyEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ length: 255, nullable: false }) + journey_title: string; + + @OneToMany( + () => ScheduleGroupEntity, + (scheduleGroup) => scheduleGroup.journey, + ) + scheduleGroups: ScheduleGroupEntity[]; + + @OneToMany( + () => MonthlyJourneyEntity, + (monthlyJourney) => monthlyJourney.journey, + ) + monthlyJourneys: MonthlyJourneyEntity[]; +} diff --git a/src/journey/journey.module.ts b/src/journey/journey.module.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/journey/journey/journey-dto/create-journey.dto.ts b/src/journey/journey/journey-dto/create-journey.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/journey/monthly-journey.entity.ts b/src/journey/monthly-journey.entity.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index ccae4be..ef29894 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -1,8 +1,12 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, OneToMany, + Entity, + JoinColumn, + ManyToOne, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -15,10 +19,16 @@ export class ScheduleEntity extends BaseEntity { id: number; @JoinColumn() - @ManyToOne(() => ScheduleGroupEntity, (scheduleGroup) => scheduleGroup.schedules) + @ManyToOne( + () => ScheduleGroupEntity, + (scheduleGroup) => scheduleGroup.schedules, + ) scheduleGroup: ScheduleGroupEntity; - @OneToMany(() => ScheduleDetailEntity, (scheduleDetail) => scheduleDetail.schedule) + @OneToMany( + () => ScheduleDetailEntity, + (scheduleDetail) => scheduleDetail.schedule, + ) scheduleDetails: ScheduleDetailEntity[]; @Column({ type: 'date' }) @@ -38,4 +48,4 @@ export class ScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/schedule/schedule.group.entity.ts b/src/schedule/schedule.group.entity.ts index 004a5e7..cf6c08f 100644 --- a/src/schedule/schedule.group.entity.ts +++ b/src/schedule/schedule.group.entity.ts @@ -1,25 +1,23 @@ import { - BaseEntity, - CreateDateColumn, - DeleteDateColumn, - Entity, JoinColumn, ManyToOne, OneToMany, + Entity, PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + CreateDateColumn, UpdateDateColumn, + DeleteDateColumn, } from 'typeorm'; +import { JourneyEntity } from '../journey/journey.entity'; import { ScheduleEntity } from './schedule.entity'; -import { UserEntity } from '../user/user.entity'; -@Entity() -export class ScheduleGroupEntity extends BaseEntity { +@Entity({ name: 'schedule_group' }) +export class ScheduleGroup { @PrimaryGeneratedColumn() id: number; - @OneToMany(() => ScheduleEntity, (schedule) => schedule.scheduleGroup) - schedules: ScheduleEntity[]; - - @JoinColumn() - @ManyToOne(() => UserEntity) - user: UserEntity; + @Column({ type: 'date', nullable: false }) + date: string; @CreateDateColumn() created: Date; @@ -29,4 +27,10 @@ export class ScheduleGroupEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file + + @ManyToOne(() => Journey, (journey) => journey.scheduleGroups) + journey: JourneyEntity; + + @OneToMany(() => Schedule, (schedule) => schedule.scheduleGroup) + schedules: ScheduleEntity[]; +} diff --git a/src/user/user.dto.ts b/src/user/user.dto.ts index 1bf2737..7064a5f 100644 --- a/src/user/user.dto.ts +++ b/src/user/user.dto.ts @@ -1,3 +1,3 @@ export interface IReqUser { id: number; -} \ No newline at end of file +} diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index a2150fd..4a4c947 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -3,7 +3,9 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, OneToMany, OneToOne, + Entity, + OneToMany, + OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -39,13 +41,13 @@ export class UserEntity extends BaseEntity { @Column() oauthToken: string; - @OneToOne(() => UserProfileImageEntity, profileImage => profileImage.user) + @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user) profileImage: UserProfileImageEntity; - @OneToMany(() => UserFollowingEntity, following => following.user) + @OneToMany(() => UserFollowingEntity, (following) => following.user) following: UserFollowingEntity[]; - @OneToMany(() => UserFollowingEntity, followed => followed.followUser) + @OneToMany(() => UserFollowingEntity, (followed) => followed.followUser) follower: UserFollowingEntity[]; @CreateDateColumn() @@ -56,4 +58,4 @@ export class UserEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} From f7e0b9056edc8ad1f27d9f29b1ce58216fccf269 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 23 Jan 2024 17:31:07 +0900 Subject: [PATCH 002/316] =?UTF-8?q?feat=20:=20CreateJourneyDto=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.entity.ts | 2 +- .../journey/journey-dto/create-journey.dto.ts | 4 ++++ src/journey/monthly-journey.entity.ts | 22 +++++++++++++++++++ src/schedule/schedule.group.entity.ts | 6 ++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/journey/journey.entity.ts b/src/journey/journey.entity.ts index 8c03222..b534bda 100644 --- a/src/journey/journey.entity.ts +++ b/src/journey/journey.entity.ts @@ -1,7 +1,7 @@ // journey.entity.ts import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; -import { ScheduleGroupEntity } from './schedule/schedule.group.entity'; +import { ScheduleGroupEntity } from '../schedule/schedule.group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; @Entity({ name: 'Journey' }) diff --git a/src/journey/journey/journey-dto/create-journey.dto.ts b/src/journey/journey/journey-dto/create-journey.dto.ts index e69de29..4eb0f49 100644 --- a/src/journey/journey/journey-dto/create-journey.dto.ts +++ b/src/journey/journey/journey-dto/create-journey.dto.ts @@ -0,0 +1,4 @@ +export class CreateJourneyDto { + journey_title: string; + dates: string[]; +} diff --git a/src/journey/monthly-journey.entity.ts b/src/journey/monthly-journey.entity.ts index e69de29..66c6e34 100644 --- a/src/journey/monthly-journey.entity.ts +++ b/src/journey/monthly-journey.entity.ts @@ -0,0 +1,22 @@ +// monthly-journey.entity.ts + +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; +import { JourneyEntity } from './journey.entity'; + +@Entity({ name: 'MonthlyJourney' }) +export class MonthlyJourneyEntity { + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(() => JourneyEntity, (journey) => journey.monthlyJourneys) + journey: JourneyEntity; + + @Column() + journey_id: number; + + @Column() + year: number; + + @Column() + month: number; +} diff --git a/src/schedule/schedule.group.entity.ts b/src/schedule/schedule.group.entity.ts index cf6c08f..16c2903 100644 --- a/src/schedule/schedule.group.entity.ts +++ b/src/schedule/schedule.group.entity.ts @@ -12,7 +12,7 @@ import { JourneyEntity } from '../journey/journey.entity'; import { ScheduleEntity } from './schedule.entity'; @Entity({ name: 'schedule_group' }) -export class ScheduleGroup { +export class ScheduleGroupEntity { @PrimaryGeneratedColumn() id: number; @@ -28,9 +28,9 @@ export class ScheduleGroup { @DeleteDateColumn() deleted: Date; - @ManyToOne(() => Journey, (journey) => journey.scheduleGroups) + @ManyToOne(() => JourneyEntity, (journey) => journey.scheduleGroups) journey: JourneyEntity; - @OneToMany(() => Schedule, (schedule) => schedule.scheduleGroup) + @OneToMany(() => ScheduleEntity, (schedule) => schedule.scheduleGroup) schedules: ScheduleEntity[]; } From 5ad6f28a04171234131760a8680b864775d87200 Mon Sep 17 00:00:00 2001 From: You Jeong Jang Date: Wed, 24 Jan 2024 00:33:14 +0900 Subject: [PATCH 003/316] =?UTF-8?q?[Bug=20Fix]=20eslint=20prettier=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #6 개발 중 발생한 버그 FIX --- .eslintrc.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 259de13..0d6419a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,4 +22,19 @@ module.exports = { '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'off', }, + 'prettier/prettier': [ + 'error', + { + arrowSpacing: ['error', { before: true, after: true }], + singleQuote: true, + semi: false, + useTabs: false, + tabWidth: 2, + trailingComma: 'none', + printWidth: 80, + bracketSpacing: true, + arrowParens: 'always', + endOfLine: 'auto', // 이 부분이 lf로 되어있다면 auto로 변경 + }, + ], }; From ed1fa99afd80081f4a606b5388539e7b5674209d Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 24 Jan 2024 16:26:18 +0900 Subject: [PATCH 004/316] =?UTF-8?q?=20fix=20:=20entity=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.entity.ts | 16 +++++++++++----- src/location/location.entity.ts | 5 +++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/diary/diary.entity.ts b/src/diary/diary.entity.ts index 155e09a..e91c65b 100644 --- a/src/diary/diary.entity.ts +++ b/src/diary/diary.entity.ts @@ -26,21 +26,27 @@ export class DiaryEntity extends BaseEntity { @Column() title: string; + @Column() + place: string; + @JoinColumn() @ManyToOne(() => LocationEntity) location: LocationEntity; - @Column({ type: 'enum', enum: ['SUNNY', 'RAINY', 'SNOWY', 'CLOUDY'] }) - weather: 'SUNNY' | 'RAINY' | 'SNOWY' | 'CLOUDY'; + @Column({ + type: 'enum', + enum: ['CLOUDY', 'RAINY', 'SNOWY', 'PARTLY_CLOUDY', 'SUNNY'], + }) + weather: 'CLOUDY' | 'RAINY' | 'SNOWY' | 'PARTLY_CLOUDY' | 'SUNNY'; @Column({ type: 'enum', - enum: ['HAPPY', 'SAD', 'ANGRY', 'SHOCKED', 'SLEEPY', 'WINK'], + enum: ['ANGRY', 'SAD', 'SMILE', 'HAPPY', 'SHOCKED'], }) - mood: 'HAPPY' | 'SAD' | 'ANGRY' | 'SHOCKED' | 'SLEEPY' | 'WINK'; + mood: 'ANGRY' | 'SAD' | 'SMILE' | 'HAPPY' | 'SHOCKED'; @Column({ type: 'mediumtext' }) - detail: string; + content: string; @OneToMany(() => DiaryImageEntity, (image) => image.diary) images: DiaryImageEntity[]; diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index c057809..57fdcc2 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -1,5 +1,6 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, Entity, @@ -32,4 +33,4 @@ export class LocationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} From 4263cdcf1ca2325b8e9730824e31ba507e5996bf Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 24 Jan 2024 17:37:36 +0900 Subject: [PATCH 005/316] =?UTF-8?q?feat:journeyRepository=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../journey-dto/create-journey.dto.ts | 2 +- src/journey/journey.repository.ts | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) rename src/journey/{journey => }/journey-dto/create-journey.dto.ts (67%) create mode 100644 src/journey/journey.repository.ts diff --git a/src/journey/journey/journey-dto/create-journey.dto.ts b/src/journey/journey-dto/create-journey.dto.ts similarity index 67% rename from src/journey/journey/journey-dto/create-journey.dto.ts rename to src/journey/journey-dto/create-journey.dto.ts index 4eb0f49..457d333 100644 --- a/src/journey/journey/journey-dto/create-journey.dto.ts +++ b/src/journey/journey-dto/create-journey.dto.ts @@ -1,4 +1,4 @@ export class CreateJourneyDto { - journey_title: string; + journeyTitle: string; dates: string[]; } diff --git a/src/journey/journey.repository.ts b/src/journey/journey.repository.ts new file mode 100644 index 0000000..f16f103 --- /dev/null +++ b/src/journey/journey.repository.ts @@ -0,0 +1,22 @@ +import { Repository } from 'typeorm'; +import { JourneyEntity } from './journey.entity'; +import { CreateJourneyDto } from './journey-dto/create-journey.dto'; + +export class JourneyRepository { + constructor(private readonly repository: Repository) {} + + async createJourney( + createJourneyDto: CreateJourneyDto, + ): Promise { + const { journeyTitle, dates } = createJourneyDto; + + const journey = this.repository.create({ + journey_title: journeyTitle, + // scheduleGroups: dates.map((date) => ({ scheduleGroups: date })), + scheduleGroups: dates.map((date) => ({ date })), + }); + + await this.repository.save(journey); + return journey; + } +} From 82a60a4aa818e60a90fb62006038114a05a31585 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 24 Jan 2024 20:49:43 +0900 Subject: [PATCH 006/316] =?UTF-8?q?feat=20:=20journey=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.controller.ts | 16 ++++++++++++++++ src/journey/journey.module.ts | 13 +++++++++++++ src/journey/journey.repository.ts | 13 ++++++------- src/journey/journey.service.ts | 17 +++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index e69de29..0a4b60a 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -0,0 +1,16 @@ +// journey.controller.ts + +import { Controller, Post, Body } from '@nestjs/common'; +import { JourneyService } from './journey.service'; +import { CreateJourneyDto } from './journey-dto/create-journey.dto'; + +@Controller('api/journey') +export class JourneyController { + constructor(private readonly journeyService: JourneyService) {} + + @Post('create') + async createJourney(@Body() journeyInfo: CreateJourneyDto): Promise { + const result = await this.journeyService.createJourney(journeyInfo); + return result; + } +} diff --git a/src/journey/journey.module.ts b/src/journey/journey.module.ts index e69de29..05428ee 100644 --- a/src/journey/journey.module.ts +++ b/src/journey/journey.module.ts @@ -0,0 +1,13 @@ +// journey.module.ts + +import { Module } from '@nestjs/common'; + +import { JourneyController } from './journey.controller'; +import { JourneyService } from './journey.service'; +import { JourneyRepository } from './journey.repository'; + +@Module({ + controllers: [JourneyController], + providers: [JourneyService, JourneyRepository], +}) +export class JourneyModule {} diff --git a/src/journey/journey.repository.ts b/src/journey/journey.repository.ts index f16f103..cd99bef 100644 --- a/src/journey/journey.repository.ts +++ b/src/journey/journey.repository.ts @@ -1,3 +1,5 @@ +// journey.repository.ts + import { Repository } from 'typeorm'; import { JourneyEntity } from './journey.entity'; import { CreateJourneyDto } from './journey-dto/create-journey.dto'; @@ -5,18 +7,15 @@ import { CreateJourneyDto } from './journey-dto/create-journey.dto'; export class JourneyRepository { constructor(private readonly repository: Repository) {} - async createJourney( - createJourneyDto: CreateJourneyDto, - ): Promise { - const { journeyTitle, dates } = createJourneyDto; - + async createJourney(journeyInfo: CreateJourneyDto): Promise { + const { journeyTitle, dates } = journeyInfo; const journey = this.repository.create({ journey_title: journeyTitle, - // scheduleGroups: dates.map((date) => ({ scheduleGroups: date })), scheduleGroups: dates.map((date) => ({ date })), }); await this.repository.save(journey); - return journey; + + return true; } } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index e69de29..d1b2c33 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { JourneyRepository } from './journey.repository'; +import { CreateJourneyDto } from './journey-dto/create-journey.dto'; + +@Injectable() +export class JourneyService { + constructor(private readonly journeyRepository: JourneyRepository) {} + + async createJourney(journeyInfo: CreateJourneyDto): Promise { + const journey = await this.journeyRepository.createJourney({ + journeyTitle: journeyInfo.journeyTitle, + dates: journeyInfo.dates, + }); + console.log(journey); + return true; + } +} From 1e58b9b17e0cbaf83b8069253ad484f203351f79 Mon Sep 17 00:00:00 2001 From: You Jeong Jang Date: Fri, 26 Jan 2024 01:36:10 +0900 Subject: [PATCH 007/316] feat: first commit --- src/signature/signature.controller.ts | 0 src/signature/signature.entity.ts | 55 ++++++++++++++++++++ src/signature/signature.module.ts | 9 ++++ src/signature/signature.page.entity.ts | 0 src/signature/signature.page.image.entity.ts | 0 src/signature/signature.service.ts | 0 6 files changed, 64 insertions(+) create mode 100644 src/signature/signature.controller.ts create mode 100644 src/signature/signature.entity.ts create mode 100644 src/signature/signature.module.ts create mode 100644 src/signature/signature.page.entity.ts create mode 100644 src/signature/signature.page.image.entity.ts create mode 100644 src/signature/signature.service.ts diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/signature/signature.entity.ts b/src/signature/signature.entity.ts new file mode 100644 index 0000000..d7a6c28 --- /dev/null +++ b/src/signature/signature.entity.ts @@ -0,0 +1,55 @@ +import { + BaseEntity, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, OneToMany, OneToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, + } from 'typeorm'; + import { SignaturePageEntity } from './signature.page.entity'; + + @Entity() + export class SignatureEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @Column() + email: string; + + @Column() + password: string; + + @Column({ type: 'text' }) + bio: string; + + @Column() + age: number; + + @Column({ type: 'enum', enum: ['MALE', 'FEMALE', 'UNKNOWN'] }) + gender: 'MALE' | 'FEMALE' | 'UNKNOWN'; + + @Column({ type: 'enum', enum: ['KAKAO', 'GOOGLE'] }) + oauthType: 'KAKAO' | 'GOOGLE'; + + @Column() + oauthToken: string; + + @OneToMany(() => SignaturePageEntity, following => following.user) + following: UserFollowingEntity[]; + + @OneToMany(() => UserFollowingEntity, followed => followed.followUser) + follower: UserFollowingEntity[]; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; + } \ No newline at end of file diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts new file mode 100644 index 0000000..42e6a81 --- /dev/null +++ b/src/signature/signature.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { SignatureService } from './signature.service'; +import { SignatureController } from './signature.controller'; + +@Module({ + controllers: [SignatureController], + providers: [SignatureService], +}) +export class SignatureModule {} diff --git a/src/signature/signature.page.entity.ts b/src/signature/signature.page.entity.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/signature/signature.page.image.entity.ts b/src/signature/signature.page.image.entity.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts new file mode 100644 index 0000000..e69de29 From 1ddf96dff96bc487da1656fa51dc4b263d33fdba Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 29 Jan 2024 03:04:23 +0900 Subject: [PATCH 008/316] =?UTF-8?q?[feat]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=ED=99=88=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?1=EC=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 레파지토리 의존성 에러 발생 --- .env.example | 13 --- package-lock.json | 53 +++++++++--- package.json | 5 +- src/app.module.ts | 2 + src/database/database.providers.ts | 5 +- src/signature/dto/create-signature.dto.ts | 8 ++ src/signature/dto/home-signature.dto.ts | 6 ++ src/signature/dto/page-signature.dto.ts | 8 ++ src/signature/signature.controller.ts | 27 ++++++ src/signature/signature.entity.ts | 91 ++++++++------------ src/signature/signature.like.entity.ts | 36 ++++++++ src/signature/signature.module.ts | 10 ++- src/signature/signature.page.entity.ts | 44 ++++++++++ src/signature/signature.page.image.entity.ts | 0 src/signature/signature.repository.ts | 44 ++++++++++ src/signature/signature.service.ts | 49 +++++++++++ 16 files changed, 320 insertions(+), 81 deletions(-) delete mode 100644 .env.example create mode 100644 src/signature/dto/create-signature.dto.ts create mode 100644 src/signature/dto/home-signature.dto.ts create mode 100644 src/signature/dto/page-signature.dto.ts create mode 100644 src/signature/signature.like.entity.ts delete mode 100644 src/signature/signature.page.image.entity.ts create mode 100644 src/signature/signature.repository.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index bd3c4f0..0000000 --- a/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# Database Connection Config -DB_HOST= -DB_PORT= -DB_USER= -DB_PASS= -DB_NAME= - -# If DB_SYNC is true, the database will be synced with the entities on every start -# Set to false in production -DB_SYNC= - -# JWT Secret -JWT_SECRET= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7024466..f1d481d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,12 +14,13 @@ "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", - "mysql2": "^3.7.0", + "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "typeorm": "^0.3.19" + "typeorm": "^0.3.20" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -1890,6 +1891,33 @@ } } }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", + "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", + "dependencies": { + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, + "node_modules/@nestjs/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6790,9 +6818,9 @@ "dev": true }, "node_modules/mysql2": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz", - "integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.0.tgz", + "integrity": "sha512-yS7FtbnO9sYqUZbjaiUwflh1bJAENJ3DQ9aHEYj9G+Hi15+FP7UKaTDNe6SeXx/LpkU6coAQ6vIYQaAmsFA+qQ==", "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -8856,9 +8884,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typeorm": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.19.tgz", - "integrity": "sha512-OGelrY5qEoAU80mR1iyvmUHiKCPUydL6xp6bebXzS7jyv/X70Gp/jBWRAfF4qGOfy2A7orMiGRfwsBUNbEL65g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", "dependencies": { "@sqltools/formatter": "^1.2.5", "app-root-path": "^3.1.0", @@ -8870,7 +8898,7 @@ "dotenv": "^16.0.3", "glob": "^10.3.10", "mkdirp": "^2.1.3", - "reflect-metadata": "^0.1.13", + "reflect-metadata": "^0.2.1", "sha.js": "^2.4.11", "tslib": "^2.5.0", "uuid": "^9.0.0", @@ -8882,7 +8910,7 @@ "typeorm-ts-node-esm": "cli-ts-node-esm.js" }, "engines": { - "node": ">= 12.9.0" + "node": ">=16.13.0" }, "funding": { "url": "https://opencollective.com/typeorm" @@ -8997,6 +9025,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/typeorm/node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", diff --git a/package.json b/package.json index 6276248..9a39206 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,13 @@ "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", - "mysql2": "^3.7.0", + "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "typeorm": "^0.3.19" + "typeorm": "^0.3.20" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index 9404dc9..78e1f62 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,6 +8,7 @@ import { DiaryModule } from './diary/diary.module'; import { LocationModule } from './location/location.module'; import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; +import { SignatureModule } from './signature/signature.module'; @Module({ imports: [ @@ -20,6 +21,7 @@ import { PlaceModule } from './place/place.module'; LocationModule, ScheduleModule, PlaceModule, + SignatureModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index f732aa6..cbcb6d8 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,10 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: process.env.DB_SYNC.toString() === 'true', + //synchronize: process.env.DB_SYNC.toString() === 'true', + synchronize: process.env.DB_SYNC + ? process.env.DB_SYNC.toString() === 'true' + : false, }); return dataSource.initialize(); diff --git a/src/signature/dto/create-signature.dto.ts b/src/signature/dto/create-signature.dto.ts new file mode 100644 index 0000000..70cdac4 --- /dev/null +++ b/src/signature/dto/create-signature.dto.ts @@ -0,0 +1,8 @@ +// create-signature.dto.ts + +import { pageSignatureDto } from './page-signature.dto'; + +export class CreateSignatureDto { + title: string; + pages: pageSignatureDto[]; +} diff --git a/src/signature/dto/home-signature.dto.ts b/src/signature/dto/home-signature.dto.ts new file mode 100644 index 0000000..ca9154c --- /dev/null +++ b/src/signature/dto/home-signature.dto.ts @@ -0,0 +1,6 @@ +// home-signature.dto.ts + +export class HomeSignatureDto { + title: string; + date: Date; +} diff --git a/src/signature/dto/page-signature.dto.ts b/src/signature/dto/page-signature.dto.ts new file mode 100644 index 0000000..0674ae4 --- /dev/null +++ b/src/signature/dto/page-signature.dto.ts @@ -0,0 +1,8 @@ +// page-signature.dto.ts + +export class pageSignatureDto { + pageNum: number; + content: string; + location: string; + image: string; +} diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index e69de29..d0ec977 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -0,0 +1,27 @@ +// signature.controller.ts + +import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; +import { SignatureService } from './signature.service'; +import { CreateSignatureDto } from './dto/create-signature.dto'; +import { HomeSignatureDto } from './dto/home-signature.dto'; + +@Controller('signature') +//@UseGuards(new AuthGuard()) +export class SignatureController { + constructor(private readonly signatureService: SignatureService) {} + + @Get('/') + getMySignature(@Body() userId: number) { + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 + console.log('signature/: 내 시그니처 요청'); + return this.signatureService.homeSignature(userId); + } + + @Post('/new') + async createNewSignature( + @Body() newSignature: CreateSignatureDto, + ): Promise { + const result = await this.signatureService.createSignature(newSignature); + return result; + } +} diff --git a/src/signature/signature.entity.ts b/src/signature/signature.entity.ts index d7a6c28..ac08b66 100644 --- a/src/signature/signature.entity.ts +++ b/src/signature/signature.entity.ts @@ -1,55 +1,38 @@ +// signature.entity.ts + import { - BaseEntity, - Column, - CreateDateColumn, - DeleteDateColumn, - Entity, OneToMany, OneToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, - } from 'typeorm'; - import { SignaturePageEntity } from './signature.page.entity'; - - @Entity() - export class SignatureEntity extends BaseEntity { - @PrimaryGeneratedColumn() - id: number; - - @Column() - name: string; - - @Column() - email: string; - - @Column() - password: string; - - @Column({ type: 'text' }) - bio: string; - - @Column() - age: number; - - @Column({ type: 'enum', enum: ['MALE', 'FEMALE', 'UNKNOWN'] }) - gender: 'MALE' | 'FEMALE' | 'UNKNOWN'; - - @Column({ type: 'enum', enum: ['KAKAO', 'GOOGLE'] }) - oauthType: 'KAKAO' | 'GOOGLE'; - - @Column() - oauthToken: string; - - @OneToMany(() => SignaturePageEntity, following => following.user) - following: UserFollowingEntity[]; - - @OneToMany(() => UserFollowingEntity, followed => followed.followUser) - follower: UserFollowingEntity[]; - - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - - @DeleteDateColumn() - deleted: Date; - } \ No newline at end of file + BaseEntity, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + OneToMany, + OneToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { UserEntity } from 'src/user/user.entity'; + +@Entity() +export default class SignatureEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column() + title: string; + + @Column() + liked_cnt: number; + + @OneToMany(() => UserEntity, (owner) => owner.id) + owner: UserEntity[]; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; +} diff --git a/src/signature/signature.like.entity.ts b/src/signature/signature.like.entity.ts new file mode 100644 index 0000000..61ff1b7 --- /dev/null +++ b/src/signature/signature.like.entity.ts @@ -0,0 +1,36 @@ +// signature.like.entity.ts + +import { + BaseEntity, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + OneToMany, + OneToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import SignatureEntity from './signature.entity'; +import { UserEntity } from 'src/user/user.entity'; + +@Entity() +export class SignatureLikeEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @OneToMany(() => SignatureEntity, (signature) => signature.id) + signature: SignatureEntity[]; + + @OneToMany(() => UserEntity, (user) => user.id) + user: UserEntity[]; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; +} diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 42e6a81..4e02839 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -1,9 +1,17 @@ +//signature.module.ts + import { Module } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { SignatureController } from './signature.controller'; +import { SignatureEntityRepository } from './signature.repository'; +import SignatureEntity from './signature.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { DataSource } from 'typeorm'; +import { EntityManager } from 'typeorm'; @Module({ + //imports: [TypeOrmModule.forFeature([SignatureEntity])], controllers: [SignatureController], - providers: [SignatureService], + providers: [SignatureService, SignatureEntityRepository, EntityManager], }) export class SignatureModule {} diff --git a/src/signature/signature.page.entity.ts b/src/signature/signature.page.entity.ts index e69de29..6847388 100644 --- a/src/signature/signature.page.entity.ts +++ b/src/signature/signature.page.entity.ts @@ -0,0 +1,44 @@ +// signature.entity.ts + +import { + BaseEntity, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + OneToMany, + OneToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import SignatureEntity from './signature.entity'; + +@Entity() +export class SignaturePageEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column() + pageNum: number; + + @Column({ type: 'mediumtext' }) + content: string; + + @Column() + location: string; + + @Column() + image: string; + + @OneToMany(() => SignatureEntity, (signature) => signature.id) + signature: SignatureEntity[]; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; +} diff --git a/src/signature/signature.page.image.entity.ts b/src/signature/signature.page.image.entity.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/signature/signature.repository.ts b/src/signature/signature.repository.ts new file mode 100644 index 0000000..731dfbb --- /dev/null +++ b/src/signature/signature.repository.ts @@ -0,0 +1,44 @@ +//signature.repository.ts + +import { Repository, DataSource, EntityManager } from 'typeorm'; +import SignatureEntity from './signature.entity'; +import { CreateSignatureDto } from './dto/create-signature.dto'; +import { HomeSignatureDto } from './dto/home-signature.dto'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class SignatureEntityRepository extends Repository { + constructor(private readonly entityManager: EntityManager) { + super(SignatureEntity, entityManager); + } + + async findMySignature(userId: number): Promise { + const signatures = await this.createQueryBuilder('signature') + .leftJoinAndSelect('signature.owner', 'user') + .where('user.id = :userId', { userId }) + .getMany(); + + //결과를 HomeSignatureDto로 매핑 + const result: HomeSignatureDto[] = signatures.map((signature) => ({ + title: signature.title, + date: signature.created, + // 페이지 1장 사진 추가해야 + })); + + return result; + } + + async createSignature(newSignature: CreateSignatureDto): Promise { + const { title } = newSignature; + /* + const signature = this.signatureRepository.create({ + title: title, + liked_cnt: 0, + }); + + await this.signatureRepository.save(signature); + */ + + return true; + } +} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index e69de29..50e26f5 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -0,0 +1,49 @@ +// signature.service.ts + +import { HttpException, Injectable } from '@nestjs/common'; +import bcrypt from 'bcrypt'; +import jsonwebtoken from 'jsonwebtoken'; +import { SignatureEntityRepository } from './signature.repository'; +import { CreateSignatureDto } from './dto/create-signature.dto'; +import SignatureEntity from './signature.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { HomeSignatureDto } from './dto/home-signature.dto'; +import { UserEntity } from 'src/user/user.entity'; + +@Injectable() +export class SignatureService { + constructor( + @InjectRepository(SignatureEntity) + private signatureRepository: SignatureEntityRepository, + ) {} + + async homeSignature(userId: number): Promise { + /* 사용자 토큰 가져오기 구현 + const getByUserId = getRepository(UserEntity) + .createQueryBuilder('user') + .where('user.userId = :userId', {userId }); + + const getUser = await getByUserId.getOne(); + if(!getUser){ + + } + */ + console.log('홈 서비스 진입'); + const signatures: HomeSignatureDto[] = + await this.signatureRepository.findMySignature(userId); + return signatures; + } + catch(error) { + // 예외 처리 + throw new HttpException('Internal Server Error', 500); + } + + async createSignature(newSignature: CreateSignatureDto): Promise { + const signature = await this.signatureRepository.createSignature( + newSignature, + ); + console.log(signature); + if (signature == true) return true; + else return false; + } +} From 85936df4e5e83054adbe2a53dfe7aca76cc0b00f Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 29 Jan 2024 22:21:01 +0900 Subject: [PATCH 009/316] =?UTF-8?q?fix=20:=20=EB=94=94=EB=A0=89=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 81 ++++++++++++++++--- package.json | 8 +- src/app.module.ts | 3 + src/database/database.module.ts | 2 +- src/journey/journey-dto/create-journey.dto.ts | 4 - src/journey/journey.controller.ts | 7 -- src/journey/journey.entity.ts | 10 ++- src/journey/journey.module.ts | 5 +- src/journey/journey.repository.ts | 22 ++--- src/journey/journey.service.ts | 10 +-- src/schedule/schedule.group.repository.ts | 21 +++++ 11 files changed, 118 insertions(+), 55 deletions(-) delete mode 100644 src/journey/journey-dto/create-journey.dto.ts create mode 100644 src/schedule/schedule.group.repository.ts diff --git a/package-lock.json b/package-lock.json index 7024466..3d8581f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,16 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@nestjs/common": "^10.0.0", + "@nestjs/common": "^10.3.1", "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.0.0", + "@nestjs/core": "^10.3.1", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", + "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", - "mysql2": "^3.7.0", + "mysql2": "^3.8.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.19" @@ -1729,9 +1731,9 @@ } }, "node_modules/@nestjs/common": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.0.tgz", - "integrity": "sha512-DGv34UHsZBxCM3H5QGE2XE/+oLJzz5+714JQjBhjD9VccFlQs3LRxo/epso4l7nJIiNlZkPyIUC8WzfU/5RTsQ==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.1.tgz", + "integrity": "sha512-YuxeIlVemVQCuXMkNbBpNlmwZgp/Cu6dwCOjki63mhyYHEFX48GNNA4zZn5MFRjF4h7VSceABsScROuzsxs9LA==", "dependencies": { "iterare": "1.2.1", "tslib": "2.6.2", @@ -1772,9 +1774,9 @@ } }, "node_modules/@nestjs/core": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.0.tgz", - "integrity": "sha512-N06P5ncknW/Pm8bj964WvLIZn2gNhHliCBoAO1LeBvNImYkecqKcrmLbY49Fa1rmMfEM3MuBHeDys3edeuYAOA==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.1.tgz", + "integrity": "sha512-mh6FwTKh2R3CmLRuB50BF5q/lzc+Mz+7qAlEvpgCiTSIfSXzbQ47vWpfgLirwkL3SlCvtFS8onxOeI69RpxvXA==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", @@ -1890,6 +1892,33 @@ } } }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", + "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", + "dependencies": { + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, + "node_modules/@nestjs/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2279,6 +2308,11 @@ "@types/superagent": "*" } }, + "node_modules/@types/validator": { + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3417,6 +3451,16 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6429,6 +6473,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.54", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz", + "integrity": "sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6790,9 +6839,9 @@ "dev": true }, "node_modules/mysql2": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz", - "integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.8.0.tgz", + "integrity": "sha512-rC9J/Wy9TCaoQWhk/p4J0Jd+WCDYghniuawi7pheDqhQOEJyDfiWGiWOR3iPgTFJaOK3GezC7dmCki7cP1HFkQ==", "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -9124,6 +9173,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 6276248..0b51719 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,16 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { - "@nestjs/common": "^10.0.0", + "@nestjs/common": "^10.3.1", "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.0.0", + "@nestjs/core": "^10.3.1", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", + "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", - "mysql2": "^3.7.0", + "mysql2": "^3.8.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.19" diff --git a/src/app.module.ts b/src/app.module.ts index 9404dc9..bbc5356 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,3 +1,4 @@ +// app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @@ -8,6 +9,7 @@ import { DiaryModule } from './diary/diary.module'; import { LocationModule } from './location/location.module'; import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; +import { JourneyModule } from './journey/journey.module'; @Module({ imports: [ @@ -20,6 +22,7 @@ import { PlaceModule } from './place/place.module'; LocationModule, ScheduleModule, PlaceModule, + JourneyModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/database/database.module.ts b/src/database/database.module.ts index ff423a1..857bc80 100644 --- a/src/database/database.module.ts +++ b/src/database/database.module.ts @@ -5,4 +5,4 @@ import { databaseProviders } from './database.providers'; providers: [...databaseProviders], exports: [...databaseProviders], }) -export class DatabaseModule {} \ No newline at end of file +export class DatabaseModule {} diff --git a/src/journey/journey-dto/create-journey.dto.ts b/src/journey/journey-dto/create-journey.dto.ts deleted file mode 100644 index 457d333..0000000 --- a/src/journey/journey-dto/create-journey.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class CreateJourneyDto { - journeyTitle: string; - dates: string[]; -} diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 0a4b60a..d6f86e2 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -2,15 +2,8 @@ import { Controller, Post, Body } from '@nestjs/common'; import { JourneyService } from './journey.service'; -import { CreateJourneyDto } from './journey-dto/create-journey.dto'; @Controller('api/journey') export class JourneyController { constructor(private readonly journeyService: JourneyService) {} - - @Post('create') - async createJourney(@Body() journeyInfo: CreateJourneyDto): Promise { - const result = await this.journeyService.createJourney(journeyInfo); - return result; - } } diff --git a/src/journey/journey.entity.ts b/src/journey/journey.entity.ts index b534bda..653c7bf 100644 --- a/src/journey/journey.entity.ts +++ b/src/journey/journey.entity.ts @@ -1,11 +1,17 @@ // journey.entity.ts -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; +import { + Entity, + PrimaryGeneratedColumn, + Column, + OneToMany, + BaseEntity, +} from 'typeorm'; import { ScheduleGroupEntity } from '../schedule/schedule.group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; @Entity({ name: 'Journey' }) -export class JourneyEntity { +export class JourneyEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; diff --git a/src/journey/journey.module.ts b/src/journey/journey.module.ts index 05428ee..ce0892a 100644 --- a/src/journey/journey.module.ts +++ b/src/journey/journey.module.ts @@ -1,12 +1,13 @@ // journey.module.ts - import { Module } from '@nestjs/common'; - +import { TypeOrmModule } from '@nestjs/typeorm'; import { JourneyController } from './journey.controller'; import { JourneyService } from './journey.service'; import { JourneyRepository } from './journey.repository'; +import { JourneyEntity } from './journey.entity'; @Module({ + imports: [TypeOrmModule.forFeature([JourneyEntity])], controllers: [JourneyController], providers: [JourneyService, JourneyRepository], }) diff --git a/src/journey/journey.repository.ts b/src/journey/journey.repository.ts index cd99bef..7b9132b 100644 --- a/src/journey/journey.repository.ts +++ b/src/journey/journey.repository.ts @@ -1,21 +1,13 @@ // journey.repository.ts - +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { JourneyEntity } from './journey.entity'; -import { CreateJourneyDto } from './journey-dto/create-journey.dto'; +@Injectable() export class JourneyRepository { - constructor(private readonly repository: Repository) {} - - async createJourney(journeyInfo: CreateJourneyDto): Promise { - const { journeyTitle, dates } = journeyInfo; - const journey = this.repository.create({ - journey_title: journeyTitle, - scheduleGroups: dates.map((date) => ({ date })), - }); - - await this.repository.save(journey); - - return true; - } + constructor( + @InjectRepository(JourneyEntity) + private readonly journey: Repository, + ) {} } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index d1b2c33..1ac33f0 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,3 +1,4 @@ +// journey.service.ts import { Injectable } from '@nestjs/common'; import { JourneyRepository } from './journey.repository'; import { CreateJourneyDto } from './journey-dto/create-journey.dto'; @@ -5,13 +6,4 @@ import { CreateJourneyDto } from './journey-dto/create-journey.dto'; @Injectable() export class JourneyService { constructor(private readonly journeyRepository: JourneyRepository) {} - - async createJourney(journeyInfo: CreateJourneyDto): Promise { - const journey = await this.journeyRepository.createJourney({ - journeyTitle: journeyInfo.journeyTitle, - dates: journeyInfo.dates, - }); - console.log(journey); - return true; - } } diff --git a/src/schedule/schedule.group.repository.ts b/src/schedule/schedule.group.repository.ts new file mode 100644 index 0000000..d99407e --- /dev/null +++ b/src/schedule/schedule.group.repository.ts @@ -0,0 +1,21 @@ +// journey.repository.ts +import { Injectable } from '@nestjs/common'; +import { Repository } from 'typeorm'; +import { ScheduleGroupEntity } from './schedule.group.entity'; +import { CreateScheduleGroupDto } from './schedule-dto/create-schedule-group.dto'; + +@Injectable() +export class ScheduleRepository extends Repository { + async createScheduleGroup( + scheduleGroupInfo: CreateScheduleGroupDto, + ): Promise { + const { date, journeyId } = scheduleGroupInfo; + const scheduleGroup = this.create({ + date: date, + journey: { id: journeyId }, + }); + + await this.save(scheduleGroup); + return true; + } +} From 82aed7ca8f81b632251622405df0a73332a6d4a7 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 29 Jan 2024 22:39:40 +0900 Subject: [PATCH 010/316] =?UTF-8?q?docs=20:=20dto=EC=99=80=20model=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/dtos/create-journey.dto.ts | 0 src/journey/journey.controller.ts | 2 +- src/journey/journey.module.ts | 2 +- src/journey/journey.repository.ts | 2 +- src/journey/journey.service.ts | 1 - src/journey/{ => model}/journey.entity.ts | 2 +- src/journey/{ => model}/monthly-journey.entity.ts | 0 src/schedule/schedule.group.entity.ts | 2 +- 8 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 src/journey/dtos/create-journey.dto.ts rename src/journey/{ => model}/journey.entity.ts (89%) rename src/journey/{ => model}/monthly-journey.entity.ts (100%) diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index d6f86e2..338a4a4 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,6 +1,6 @@ // journey.controller.ts -import { Controller, Post, Body } from '@nestjs/common'; +import { Controller } from '@nestjs/common'; import { JourneyService } from './journey.service'; @Controller('api/journey') diff --git a/src/journey/journey.module.ts b/src/journey/journey.module.ts index ce0892a..94e09c3 100644 --- a/src/journey/journey.module.ts +++ b/src/journey/journey.module.ts @@ -4,7 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { JourneyController } from './journey.controller'; import { JourneyService } from './journey.service'; import { JourneyRepository } from './journey.repository'; -import { JourneyEntity } from './journey.entity'; +import { JourneyEntity } from './model/journey.entity'; @Module({ imports: [TypeOrmModule.forFeature([JourneyEntity])], diff --git a/src/journey/journey.repository.ts b/src/journey/journey.repository.ts index 7b9132b..120b8cd 100644 --- a/src/journey/journey.repository.ts +++ b/src/journey/journey.repository.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { JourneyEntity } from './journey.entity'; +import { JourneyEntity } from './model/journey.entity'; @Injectable() export class JourneyRepository { diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 1ac33f0..3188e7e 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,7 +1,6 @@ // journey.service.ts import { Injectable } from '@nestjs/common'; import { JourneyRepository } from './journey.repository'; -import { CreateJourneyDto } from './journey-dto/create-journey.dto'; @Injectable() export class JourneyService { diff --git a/src/journey/journey.entity.ts b/src/journey/model/journey.entity.ts similarity index 89% rename from src/journey/journey.entity.ts rename to src/journey/model/journey.entity.ts index 653c7bf..5e95605 100644 --- a/src/journey/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -7,7 +7,7 @@ import { OneToMany, BaseEntity, } from 'typeorm'; -import { ScheduleGroupEntity } from '../schedule/schedule.group.entity'; +import { ScheduleGroupEntity } from '../../schedule/schedule.group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; @Entity({ name: 'Journey' }) diff --git a/src/journey/monthly-journey.entity.ts b/src/journey/model/monthly-journey.entity.ts similarity index 100% rename from src/journey/monthly-journey.entity.ts rename to src/journey/model/monthly-journey.entity.ts diff --git a/src/schedule/schedule.group.entity.ts b/src/schedule/schedule.group.entity.ts index 16c2903..91b05f8 100644 --- a/src/schedule/schedule.group.entity.ts +++ b/src/schedule/schedule.group.entity.ts @@ -8,7 +8,7 @@ import { UpdateDateColumn, DeleteDateColumn, } from 'typeorm'; -import { JourneyEntity } from '../journey/journey.entity'; +import { JourneyEntity } from '../journey/model/journey.entity'; import { ScheduleEntity } from './schedule.entity'; @Entity({ name: 'schedule_group' }) From 58c6d1f2a479479ddd6466dfe499966b2a3887a5 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 30 Jan 2024 03:05:13 +0900 Subject: [PATCH 011/316] =?UTF-8?q?chore=20:=20entity=20=EA=B5=AC=EC=84=B1?= =?UTF-8?q?=20=EB=B0=8F=20=EA=B4=80=EA=B3=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/date/date-group.repository.ts | 13 ++++ src/date/model/date-group.entity.ts | 18 +++++ src/diary/diary.image.entity.ts | 5 +- src/journey/journey.service.ts | 79 ++++++++++++++++++++- src/journey/model/journey.entity.ts | 24 +++---- src/journey/model/monthly-journey.entity.ts | 18 ++--- src/journey/monthly-journey.repository.ts | 13 ++++ src/schedule/schedule.entity.ts | 11 +-- src/schedule/schedule.group.entity.ts | 36 ---------- 9 files changed, 145 insertions(+), 72 deletions(-) create mode 100644 src/date/date-group.repository.ts create mode 100644 src/date/model/date-group.entity.ts create mode 100644 src/journey/monthly-journey.repository.ts delete mode 100644 src/schedule/schedule.group.entity.ts diff --git a/src/date/date-group.repository.ts b/src/date/date-group.repository.ts new file mode 100644 index 0000000..5bec2a1 --- /dev/null +++ b/src/date/date-group.repository.ts @@ -0,0 +1,13 @@ +// date-group.repository.ts +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { DateGroupEntity } from './model/date-group.entity'; + +@Injectable() +export class DateGroupRepository { + constructor( + @InjectRepository(DateGroupEntity) + private readonly dateGroup: Repository, + ) {} +} diff --git a/src/date/model/date-group.entity.ts b/src/date/model/date-group.entity.ts new file mode 100644 index 0000000..78d6ef0 --- /dev/null +++ b/src/date/model/date-group.entity.ts @@ -0,0 +1,18 @@ +// journey-date-group.entity.ts +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; +import { JourneyEntity } from '../../journey/model/journey.entity'; + +@Entity('dateGroup') +export class DateGroupEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column() + startDate: string; + + @Column() + endDate: string; + + @OneToMany(() => JourneyEntity, (journey) => journey.dateGroup) + journeys: JourneyEntity[]; +} diff --git a/src/diary/diary.image.entity.ts b/src/diary/diary.image.entity.ts index f57e444..a18772d 100644 --- a/src/diary/diary.image.entity.ts +++ b/src/diary/diary.image.entity.ts @@ -1,5 +1,6 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, Entity, @@ -30,4 +31,4 @@ export class DiaryImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 3188e7e..f779bab 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,8 +1,85 @@ // journey.service.ts import { Injectable } from '@nestjs/common'; import { JourneyRepository } from './journey.repository'; +import { DateGroupRepository } from 'src/date/date-group.repository'; +import { MonthlyJourneyRepository } from './monthly-journey.repository'; @Injectable() export class JourneyService { - constructor(private readonly journeyRepository: JourneyRepository) {} + constructor( + private readonly journeyRepository: JourneyRepository, + private readonly dateGroupRepository: DateGroupRepository, + private readonly monthlyJourneyRepository: MonthlyJourneyRepository, + ) {} + + async createJourney( + startDate: string, + endDate: string, + ): Promise<{ message: string; dateGroupId: number }> { + // DateGroup 생성 + const dateGroup = await this.dateGroupRepository.createDateGroup( + startDate, + endDate, + ); + + return { + message: 'Journey created successfully', + dateGroupId: dateGroup.id, + }; + } + + async saveJourney( + title: string, + dateGroupId: number, + ): Promise<{ message: string; journeyId: number }> { + // DateGroup에서 startDate와 endDate 조회 + const dateGroup = await this.dateGroupRepository.findOne(dateGroupId); + + if (!dateGroup) { + throw new Error('DateGroup not found'); + } + + // MonthlyJourney에서 해당 날짜의 연,월 조회 + const { year, month } = this.extractYearMonthFromDate(dateGroup.startDate); + const monthlyJourney = await this.monthlyJourneyRepository.findOne({ + year, + month, + }); + + if (!monthlyJourney) { + throw new Error('MonthlyJourney not found'); + } + + // Journey 저장 + const journey = await this.journeyRepository.saveJourney( + title, + dateGroupId, + monthlyJourney.id, + ); + + return { + message: 'Journey saved successfully', + journeyId: journey.id, + }; + } + + async getMonthlyJourney( + year: number, + month: number, + ): Promise< + Array<{ id: number; title: string; dateGroupId: number; monthlyId: number }> + > { + return await this.journeyRepository.findMonthlyJourney(year, month); + } + + private extractYearMonthFromDate(date: string): { + year: number; + month: number; + } { + const parsedDate = new Date(date); + return { + year: parsedDate.getFullYear(), + month: parsedDate.getMonth() + 1, // 월은 0부터 시작하므로 +1 + }; + } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 5e95605..94d5d75 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -1,16 +1,17 @@ // journey.entity.ts import { + BaseEntity, Entity, PrimaryGeneratedColumn, Column, - OneToMany, - BaseEntity, + ManyToOne, + JoinColumn, } from 'typeorm'; -import { ScheduleGroupEntity } from '../../schedule/schedule.group.entity'; +import { DateGroupEntity } from '../../date/model/date-group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; -@Entity({ name: 'Journey' }) +@Entity() export class JourneyEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; @@ -18,15 +19,14 @@ export class JourneyEntity extends BaseEntity { @Column({ length: 255, nullable: false }) journey_title: string; - @OneToMany( - () => ScheduleGroupEntity, - (scheduleGroup) => scheduleGroup.journey, - ) - scheduleGroups: ScheduleGroupEntity[]; + @ManyToOne(() => DateGroupEntity, (dateGroup) => dateGroup.journeys) + @JoinColumn({ name: 'date_group_id' }) + dateGroup: DateGroupEntity; - @OneToMany( + @ManyToOne( () => MonthlyJourneyEntity, - (monthlyJourney) => monthlyJourney.journey, + (monthlyJourney) => monthlyJourney.journeys, ) - monthlyJourneys: MonthlyJourneyEntity[]; + @JoinColumn({ name: 'monthly_id' }) + monthlyJourney: MonthlyJourneyEntity; } diff --git a/src/journey/model/monthly-journey.entity.ts b/src/journey/model/monthly-journey.entity.ts index 66c6e34..b583e32 100644 --- a/src/journey/model/monthly-journey.entity.ts +++ b/src/journey/model/monthly-journey.entity.ts @@ -1,22 +1,18 @@ // monthly-journey.entity.ts - -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; import { JourneyEntity } from './journey.entity'; -@Entity({ name: 'MonthlyJourney' }) +@Entity() export class MonthlyJourneyEntity { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => JourneyEntity, (journey) => journey.monthlyJourneys) - journey: JourneyEntity; - - @Column() - journey_id: number; - - @Column() + @Column({ name: 'year' }) year: number; - @Column() + @Column({ name: 'month' }) month: number; + + @OneToMany(() => JourneyEntity, (journey) => journey.monthlyJourney) + journeys: JourneyEntity[]; } diff --git a/src/journey/monthly-journey.repository.ts b/src/journey/monthly-journey.repository.ts new file mode 100644 index 0000000..6788082 --- /dev/null +++ b/src/journey/monthly-journey.repository.ts @@ -0,0 +1,13 @@ +// journey.repository.ts +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { MonthlyJourneyEntity } from './model/monthly-journey.entity'; + +@Injectable() +export class MonthlyJourneyRepository { + constructor( + @InjectRepository(MonthlyJourneyEntity) + private readonly monthlyJourney: Repository, + ) {} +} diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index ef29894..cf483b9 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -4,13 +4,11 @@ import { CreateDateColumn, DeleteDateColumn, Entity, - JoinColumn, - ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import { ScheduleGroupEntity } from './schedule.group.entity'; + import { ScheduleDetailEntity } from './schedule.detail.entity'; @Entity() @@ -18,13 +16,6 @@ export class ScheduleEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @JoinColumn() - @ManyToOne( - () => ScheduleGroupEntity, - (scheduleGroup) => scheduleGroup.schedules, - ) - scheduleGroup: ScheduleGroupEntity; - @OneToMany( () => ScheduleDetailEntity, (scheduleDetail) => scheduleDetail.schedule, diff --git a/src/schedule/schedule.group.entity.ts b/src/schedule/schedule.group.entity.ts deleted file mode 100644 index 91b05f8..0000000 --- a/src/schedule/schedule.group.entity.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - ManyToOne, - OneToMany, - CreateDateColumn, - UpdateDateColumn, - DeleteDateColumn, -} from 'typeorm'; -import { JourneyEntity } from '../journey/model/journey.entity'; -import { ScheduleEntity } from './schedule.entity'; - -@Entity({ name: 'schedule_group' }) -export class ScheduleGroupEntity { - @PrimaryGeneratedColumn() - id: number; - - @Column({ type: 'date', nullable: false }) - date: string; - - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - - @DeleteDateColumn() - deleted: Date; - - @ManyToOne(() => JourneyEntity, (journey) => journey.scheduleGroups) - journey: JourneyEntity; - - @OneToMany(() => ScheduleEntity, (schedule) => schedule.scheduleGroup) - schedules: ScheduleEntity[]; -} From 4168073e6199b1c67356d87ebec4988adbf11339 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 30 Jan 2024 03:37:46 +0900 Subject: [PATCH 012/316] =?UTF-8?q?feat=20:=20createJourney=20Dto=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/response.ts | 6 +++ src/journey/dtos/create-journey.dto.ts | 10 +++++ src/journey/journey.service.ts | 62 ++------------------------ 3 files changed, 20 insertions(+), 58 deletions(-) create mode 100644 src/constants/response.ts diff --git a/src/constants/response.ts b/src/constants/response.ts new file mode 100644 index 0000000..02a996f --- /dev/null +++ b/src/constants/response.ts @@ -0,0 +1,6 @@ +export const MESSAGE = { + WRONG_INPUT: { + code: 500, + message: '잘못된 입력 값입니다.', + }, +} as const; diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts index e69de29..2e5c4b1 100644 --- a/src/journey/dtos/create-journey.dto.ts +++ b/src/journey/dtos/create-journey.dto.ts @@ -0,0 +1,10 @@ +// create-journey.dto.ts +import { IsDateString } from 'class-validator'; + +export class CreateJourneyDto { + @IsDateString() + startDate: string; + + @IsDateString() + endDate: string; +} diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index f779bab..d6929c9 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,8 +1,10 @@ // journey.service.ts -import { Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; +import { MESSAGE } from '../constants/response'; import { JourneyRepository } from './journey.repository'; import { DateGroupRepository } from 'src/date/date-group.repository'; import { MonthlyJourneyRepository } from './monthly-journey.repository'; +import { CreateJourneyDto } from './dtos/create-journey.dto'; @Injectable() export class JourneyService { @@ -13,8 +15,7 @@ export class JourneyService { ) {} async createJourney( - startDate: string, - endDate: string, + createJourneyDto: CreateJourneyDto, ): Promise<{ message: string; dateGroupId: number }> { // DateGroup 생성 const dateGroup = await this.dateGroupRepository.createDateGroup( @@ -27,59 +28,4 @@ export class JourneyService { dateGroupId: dateGroup.id, }; } - - async saveJourney( - title: string, - dateGroupId: number, - ): Promise<{ message: string; journeyId: number }> { - // DateGroup에서 startDate와 endDate 조회 - const dateGroup = await this.dateGroupRepository.findOne(dateGroupId); - - if (!dateGroup) { - throw new Error('DateGroup not found'); - } - - // MonthlyJourney에서 해당 날짜의 연,월 조회 - const { year, month } = this.extractYearMonthFromDate(dateGroup.startDate); - const monthlyJourney = await this.monthlyJourneyRepository.findOne({ - year, - month, - }); - - if (!monthlyJourney) { - throw new Error('MonthlyJourney not found'); - } - - // Journey 저장 - const journey = await this.journeyRepository.saveJourney( - title, - dateGroupId, - monthlyJourney.id, - ); - - return { - message: 'Journey saved successfully', - journeyId: journey.id, - }; - } - - async getMonthlyJourney( - year: number, - month: number, - ): Promise< - Array<{ id: number; title: string; dateGroupId: number; monthlyId: number }> - > { - return await this.journeyRepository.findMonthlyJourney(year, month); - } - - private extractYearMonthFromDate(date: string): { - year: number; - month: number; - } { - const parsedDate = new Date(date); - return { - year: parsedDate.getFullYear(), - month: parsedDate.getMonth() + 1, // 월은 0부터 시작하므로 +1 - }; - } } From 3f4fd7e12b901c5f4138b42b7d318bf2894ce0dd Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 30 Jan 2024 04:44:21 +0900 Subject: [PATCH 013/316] =?UTF-8?q?=20feat=20:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/date-group/date-group.controller.ts | 15 +++++++++++++ src/date-group/date-group.module.js | 13 +++++++++++ .../date-group.repository.ts | 13 +++++++++++ src/date-group/date-group.service.ts | 22 +++++++++++++++++++ .../model/date-group.entity.ts | 2 +- src/journey/journey.service.ts | 21 ++---------------- src/journey/model/journey.entity.ts | 2 +- 7 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 src/date-group/date-group.controller.ts create mode 100644 src/date-group/date-group.module.js rename src/{date => date-group}/date-group.repository.ts (52%) create mode 100644 src/date-group/date-group.service.ts rename src/{date => date-group}/model/date-group.entity.ts (95%) diff --git a/src/date-group/date-group.controller.ts b/src/date-group/date-group.controller.ts new file mode 100644 index 0000000..887fc64 --- /dev/null +++ b/src/date-group/date-group.controller.ts @@ -0,0 +1,15 @@ +// DateGroup.controller.ts + +import { Controller, Post, Body } from '@nestjs/common'; +import { DateGroupService } from './date-group.service'; +import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; + +@Controller() +export class DateGroupController { + constructor(private readonly dateGroupService: DateGroupService) {} + + @Post('/api/post/create-date-group') + createDateGroup(@Body('createDateGroup') dates: CreateJourneyDto) { + return this.dateGroupService.createDateGroup(dates); + } +} diff --git a/src/date-group/date-group.module.js b/src/date-group/date-group.module.js new file mode 100644 index 0000000..5e96618 --- /dev/null +++ b/src/date-group/date-group.module.js @@ -0,0 +1,13 @@ +// dateGroup.module.ts +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { dateGroupController } from './dateGroup.controller'; +import { dateGroupService } from './dateGroup.service'; +import { dateGroupRepository } from './dateGroup.repository'; + +@Module({ + imports: [TypeOrmModule.forFeature([dateGroupEntity])], + controllers: [dateGroupController], + providers: [dateGroupService, dateGroupRepository], +}) +export class dateGroupModule {} diff --git a/src/date/date-group.repository.ts b/src/date-group/date-group.repository.ts similarity index 52% rename from src/date/date-group.repository.ts rename to src/date-group/date-group.repository.ts index 5bec2a1..7ebfb8d 100644 --- a/src/date/date-group.repository.ts +++ b/src/date-group/date-group.repository.ts @@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { DateGroupEntity } from './model/date-group.entity'; +import { CreateJourneyDto } from 'src/journey/dtos/create-journey.dto'; @Injectable() export class DateGroupRepository { @@ -10,4 +11,16 @@ export class DateGroupRepository { @InjectRepository(DateGroupEntity) private readonly dateGroup: Repository, ) {} + + //날짜 그룹 생성 + async createDateGroup( + createJourneyDto: CreateJourneyDto, + ): Promise { + try { + const dateGroup = await this.dateGroup.save(createJourneyDto); + return dateGroup; + } catch (error) { + throw new Error('fail'); + } + } } diff --git a/src/date-group/date-group.service.ts b/src/date-group/date-group.service.ts new file mode 100644 index 0000000..81e5f70 --- /dev/null +++ b/src/date-group/date-group.service.ts @@ -0,0 +1,22 @@ +// data-group.service.ts +import { BadRequestException, Injectable } from '@nestjs/common'; +import { MESSAGE } from '../constants/response'; +import { DateGroupRepository } from './date-group.repository'; +import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; + +@Injectable() +export class DateGroupService { + constructor(private readonly dateGroupRepository: DateGroupRepository) {} + + async createDateGroup(createJourneyDto: CreateJourneyDto): Promise { + // DateGroup 생성 + const dateGroup = await this.dateGroupRepository.createDateGroup( + createJourneyDto, + ); + if (!dateGroup) { + throw new BadRequestException(MESSAGE.WRONG_INPUT); + } + + return dateGroup.id; + } +} diff --git a/src/date/model/date-group.entity.ts b/src/date-group/model/date-group.entity.ts similarity index 95% rename from src/date/model/date-group.entity.ts rename to src/date-group/model/date-group.entity.ts index 78d6ef0..e88f56f 100644 --- a/src/date/model/date-group.entity.ts +++ b/src/date-group/model/date-group.entity.ts @@ -2,7 +2,7 @@ import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; import { JourneyEntity } from '../../journey/model/journey.entity'; -@Entity('dateGroup') +@Entity() export class DateGroupEntity { @PrimaryGeneratedColumn() id: number; diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index d6929c9..501ceca 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,10 +1,8 @@ // journey.service.ts -import { BadRequestException, Injectable } from '@nestjs/common'; -import { MESSAGE } from '../constants/response'; +import { Injectable } from '@nestjs/common'; import { JourneyRepository } from './journey.repository'; -import { DateGroupRepository } from 'src/date/date-group.repository'; +import { DateGroupRepository } from 'src/date-group/date-group.repository'; import { MonthlyJourneyRepository } from './monthly-journey.repository'; -import { CreateJourneyDto } from './dtos/create-journey.dto'; @Injectable() export class JourneyService { @@ -13,19 +11,4 @@ export class JourneyService { private readonly dateGroupRepository: DateGroupRepository, private readonly monthlyJourneyRepository: MonthlyJourneyRepository, ) {} - - async createJourney( - createJourneyDto: CreateJourneyDto, - ): Promise<{ message: string; dateGroupId: number }> { - // DateGroup 생성 - const dateGroup = await this.dateGroupRepository.createDateGroup( - startDate, - endDate, - ); - - return { - message: 'Journey created successfully', - dateGroupId: dateGroup.id, - }; - } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 94d5d75..27c99ab 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -8,7 +8,7 @@ import { ManyToOne, JoinColumn, } from 'typeorm'; -import { DateGroupEntity } from '../../date/model/date-group.entity'; +import { DateGroupEntity } from '../../date-group/model/date-group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; @Entity() From a45b4e5b79ae188781abb6b2f50bfd357ae37cc9 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 30 Jan 2024 15:06:33 +0900 Subject: [PATCH 014/316] =?UTF-8?q?bug=20:=20db=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 7 +++++ src/database/database.providers.ts | 2 +- src/date-group/date-group.controller.ts | 4 +-- .../{model => }/date-group.entity.ts | 2 +- src/date-group/date-group.module.js | 13 ---------- src/date-group/date-group.module.ts | 15 +++++++++++ src/date-group/date-group.repository.ts | 26 +++++++++---------- src/date-group/date-group.service.ts | 7 ++++- src/journey/journey.module.ts | 3 --- src/journey/journey.repository.ts | 11 +++----- src/journey/journey.service.ts | 11 +------- src/journey/model/journey.entity.ts | 2 +- src/journey/monthly-journey.module.ts | 8 ++++++ src/journey/monthly-journey.repository.ts | 10 +------ src/schedule/schedule.detail.entity.ts | 9 ++++--- src/schedule/schedule.group.repository.ts | 21 --------------- 16 files changed, 66 insertions(+), 85 deletions(-) rename src/date-group/{model => }/date-group.entity.ts (84%) delete mode 100644 src/date-group/date-group.module.js create mode 100644 src/date-group/date-group.module.ts create mode 100644 src/journey/monthly-journey.module.ts delete mode 100644 src/schedule/schedule.group.repository.ts diff --git a/src/app.module.ts b/src/app.module.ts index bbc5356..948838b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,6 +10,9 @@ import { LocationModule } from './location/location.module'; import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; import { JourneyModule } from './journey/journey.module'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { DateGroupModule } from './date-group/date-group.module'; +import { DateGroupEntity } from './date-group/date-group.entity'; @Module({ imports: [ @@ -23,6 +26,10 @@ import { JourneyModule } from './journey/journey.module'; ScheduleModule, PlaceModule, JourneyModule, + DateGroupModule, + TypeOrmModule.forRoot({ + entities: [DateGroupEntity], + }), ], controllers: [AppController], providers: [AppService], diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index f732aa6..08cb027 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: process.env.DB_SYNC.toString() === 'true', + synchronize: false, }); return dataSource.initialize(); diff --git a/src/date-group/date-group.controller.ts b/src/date-group/date-group.controller.ts index 887fc64..85013df 100644 --- a/src/date-group/date-group.controller.ts +++ b/src/date-group/date-group.controller.ts @@ -9,7 +9,7 @@ export class DateGroupController { constructor(private readonly dateGroupService: DateGroupService) {} @Post('/api/post/create-date-group') - createDateGroup(@Body('createDateGroup') dates: CreateJourneyDto) { - return this.dateGroupService.createDateGroup(dates); + createDateGroup(@Body() createJourneyDto: CreateJourneyDto) { + return this.dateGroupService.createDateGroup(createJourneyDto); } } diff --git a/src/date-group/model/date-group.entity.ts b/src/date-group/date-group.entity.ts similarity index 84% rename from src/date-group/model/date-group.entity.ts rename to src/date-group/date-group.entity.ts index e88f56f..ecc767b 100644 --- a/src/date-group/model/date-group.entity.ts +++ b/src/date-group/date-group.entity.ts @@ -1,6 +1,6 @@ // journey-date-group.entity.ts import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; -import { JourneyEntity } from '../../journey/model/journey.entity'; +import { JourneyEntity } from '../journey/model/journey.entity'; @Entity() export class DateGroupEntity { diff --git a/src/date-group/date-group.module.js b/src/date-group/date-group.module.js deleted file mode 100644 index 5e96618..0000000 --- a/src/date-group/date-group.module.js +++ /dev/null @@ -1,13 +0,0 @@ -// dateGroup.module.ts -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { dateGroupController } from './dateGroup.controller'; -import { dateGroupService } from './dateGroup.service'; -import { dateGroupRepository } from './dateGroup.repository'; - -@Module({ - imports: [TypeOrmModule.forFeature([dateGroupEntity])], - controllers: [dateGroupController], - providers: [dateGroupService, dateGroupRepository], -}) -export class dateGroupModule {} diff --git a/src/date-group/date-group.module.ts b/src/date-group/date-group.module.ts new file mode 100644 index 0000000..7a1ea3a --- /dev/null +++ b/src/date-group/date-group.module.ts @@ -0,0 +1,15 @@ +// dateGroup.module.ts +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { DateGroupController } from './date-group.controller'; +import { DateGroupService } from './date-group.service'; +import { DateGroupRepository } from './date-group.repository'; +import { EntityManager } from 'typeorm'; +import { DateGroupEntity } from './date-group.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([DateGroupEntity])], + controllers: [DateGroupController], + providers: [DateGroupService, DateGroupRepository, EntityManager], +}) +export class DateGroupModule {} diff --git a/src/date-group/date-group.repository.ts b/src/date-group/date-group.repository.ts index 7ebfb8d..9fa26d0 100644 --- a/src/date-group/date-group.repository.ts +++ b/src/date-group/date-group.repository.ts @@ -1,26 +1,26 @@ // date-group.repository.ts import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { DateGroupEntity } from './model/date-group.entity'; +import { Repository, EntityManager } from 'typeorm'; +import { DateGroupEntity } from './date-group.entity'; import { CreateJourneyDto } from 'src/journey/dtos/create-journey.dto'; @Injectable() -export class DateGroupRepository { - constructor( - @InjectRepository(DateGroupEntity) - private readonly dateGroup: Repository, - ) {} - - //날짜 그룹 생성 +export class DateGroupRepository extends Repository { + constructor(private readonly entityManager: EntityManager) { + super(DateGroupEntity, entityManager); + } + // 날짜 그룹 생성 async createDateGroup( createJourneyDto: CreateJourneyDto, ): Promise { try { - const dateGroup = await this.dateGroup.save(createJourneyDto); - return dateGroup; + const dateGroup = new DateGroupEntity(); + dateGroup.startDate = createJourneyDto.startDate; + dateGroup.endDate = createJourneyDto.endDate; + + return await this.entityManager.save(dateGroup); } catch (error) { - throw new Error('fail'); + throw new Error('Failed to create DateGroup'); } } } diff --git a/src/date-group/date-group.service.ts b/src/date-group/date-group.service.ts index 81e5f70..b184ce5 100644 --- a/src/date-group/date-group.service.ts +++ b/src/date-group/date-group.service.ts @@ -3,10 +3,15 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { MESSAGE } from '../constants/response'; import { DateGroupRepository } from './date-group.repository'; import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DateGroupEntity } from './date-group.entity'; @Injectable() export class DateGroupService { - constructor(private readonly dateGroupRepository: DateGroupRepository) {} + constructor( + @InjectRepository(DateGroupEntity) + private readonly dateGroupRepository: DateGroupRepository, + ) {} async createDateGroup(createJourneyDto: CreateJourneyDto): Promise { // DateGroup 생성 diff --git a/src/journey/journey.module.ts b/src/journey/journey.module.ts index 94e09c3..5614375 100644 --- a/src/journey/journey.module.ts +++ b/src/journey/journey.module.ts @@ -1,13 +1,10 @@ // journey.module.ts import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { JourneyController } from './journey.controller'; import { JourneyService } from './journey.service'; import { JourneyRepository } from './journey.repository'; -import { JourneyEntity } from './model/journey.entity'; @Module({ - imports: [TypeOrmModule.forFeature([JourneyEntity])], controllers: [JourneyController], providers: [JourneyService, JourneyRepository], }) diff --git a/src/journey/journey.repository.ts b/src/journey/journey.repository.ts index 120b8cd..5196099 100644 --- a/src/journey/journey.repository.ts +++ b/src/journey/journey.repository.ts @@ -1,13 +1,10 @@ // journey.repository.ts import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { JourneyEntity } from './model/journey.entity'; +// import { Repository } from 'typeorm'; + +// import { JourneyEntity } from './model/journey.entity'; @Injectable() export class JourneyRepository { - constructor( - @InjectRepository(JourneyEntity) - private readonly journey: Repository, - ) {} + // constructor(private readonly journey: Repository) {} } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 501ceca..06664b1 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,14 +1,5 @@ // journey.service.ts import { Injectable } from '@nestjs/common'; -import { JourneyRepository } from './journey.repository'; -import { DateGroupRepository } from 'src/date-group/date-group.repository'; -import { MonthlyJourneyRepository } from './monthly-journey.repository'; @Injectable() -export class JourneyService { - constructor( - private readonly journeyRepository: JourneyRepository, - private readonly dateGroupRepository: DateGroupRepository, - private readonly monthlyJourneyRepository: MonthlyJourneyRepository, - ) {} -} +export class JourneyService {} diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 27c99ab..6dc3b17 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -8,7 +8,7 @@ import { ManyToOne, JoinColumn, } from 'typeorm'; -import { DateGroupEntity } from '../../date-group/model/date-group.entity'; +import { DateGroupEntity } from '../../date-group/date-group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; @Entity() diff --git a/src/journey/monthly-journey.module.ts b/src/journey/monthly-journey.module.ts new file mode 100644 index 0000000..c89a474 --- /dev/null +++ b/src/journey/monthly-journey.module.ts @@ -0,0 +1,8 @@ +// journey.module.ts +import { Module } from '@nestjs/common'; +import { MonthlyJourneyRepository } from './monthly-journey.repository'; + +@Module({ + providers: [MonthlyJourneyRepository], +}) +export class JourneyModule {} diff --git a/src/journey/monthly-journey.repository.ts b/src/journey/monthly-journey.repository.ts index 6788082..5374a5a 100644 --- a/src/journey/monthly-journey.repository.ts +++ b/src/journey/monthly-journey.repository.ts @@ -1,13 +1,5 @@ // journey.repository.ts import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { MonthlyJourneyEntity } from './model/monthly-journey.entity'; @Injectable() -export class MonthlyJourneyRepository { - constructor( - @InjectRepository(MonthlyJourneyEntity) - private readonly monthlyJourney: Repository, - ) {} -} +export class MonthlyJourneyRepository {} diff --git a/src/schedule/schedule.detail.entity.ts b/src/schedule/schedule.detail.entity.ts index 6f48087..6874f11 100644 --- a/src/schedule/schedule.detail.entity.ts +++ b/src/schedule/schedule.detail.entity.ts @@ -1,8 +1,11 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -31,4 +34,4 @@ export class ScheduleDetailEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/schedule/schedule.group.repository.ts b/src/schedule/schedule.group.repository.ts deleted file mode 100644 index d99407e..0000000 --- a/src/schedule/schedule.group.repository.ts +++ /dev/null @@ -1,21 +0,0 @@ -// journey.repository.ts -import { Injectable } from '@nestjs/common'; -import { Repository } from 'typeorm'; -import { ScheduleGroupEntity } from './schedule.group.entity'; -import { CreateScheduleGroupDto } from './schedule-dto/create-schedule-group.dto'; - -@Injectable() -export class ScheduleRepository extends Repository { - async createScheduleGroup( - scheduleGroupInfo: CreateScheduleGroupDto, - ): Promise { - const { date, journeyId } = scheduleGroupInfo; - const scheduleGroup = this.create({ - date: date, - journey: { id: journeyId }, - }); - - await this.save(scheduleGroup); - return true; - } -} From d0926c200502faaaff3711fcd0adfd396631cbec Mon Sep 17 00:00:00 2001 From: You Jeong Jang Date: Tue, 30 Jan 2024 21:39:07 +0900 Subject: [PATCH 015/316] =?UTF-8?q?feat:=20repository=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.controller.ts | 4 ++- src/signature/signature.entity.ts | 37 +++++++++++++++++++++- src/signature/signature.like.entity.ts | 2 +- src/signature/signature.module.ts | 5 ++- src/signature/signature.page.entity.ts | 2 +- src/signature/signature.repository.ts | 44 -------------------------- src/signature/signature.service.ts | 39 +++++++++++------------ 7 files changed, 62 insertions(+), 71 deletions(-) delete mode 100644 src/signature/signature.repository.ts diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index d0ec977..4085a58 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -10,17 +10,19 @@ import { HomeSignatureDto } from './dto/home-signature.dto'; export class SignatureController { constructor(private readonly signatureService: SignatureService) {} + /* @Get('/') getMySignature(@Body() userId: number) { // 임시로 토큰이 아닌 유저 아이디 받도록 구현 console.log('signature/: 내 시그니처 요청'); return this.signatureService.homeSignature(userId); } + */ @Post('/new') async createNewSignature( @Body() newSignature: CreateSignatureDto, - ): Promise { + ): Promise { const result = await this.signatureService.createSignature(newSignature); return result; } diff --git a/src/signature/signature.entity.ts b/src/signature/signature.entity.ts index ac08b66..5f56858 100644 --- a/src/signature/signature.entity.ts +++ b/src/signature/signature.entity.ts @@ -12,9 +12,11 @@ import { UpdateDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; +import { HomeSignatureDto } from './dto/home-signature.dto'; +import { CreateSignatureDto } from './dto/create-signature.dto'; @Entity() -export default class SignatureEntity extends BaseEntity { +export class SignatureEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; @@ -35,4 +37,37 @@ export default class SignatureEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + /* + async findMySignature(userId: number): Promise { + const signatures = await this.createQueryBuilder('signature') + .leftJoinAndSelect('signature.owner', 'user') + .where('user.id = :userId', { userId }) + .getMany(); + + //결과를 HomeSignatureDto로 매핑 + const result: HomeSignatureDto[] = signatures.map((signature) => ({ + title: signature.title, + date: signature.created, + // 페이지 1장 사진 추가해야 + })); + + return result; + } + */ + + static async createSignature( + newSignature: CreateSignatureDto, + ): Promise { + try { + const { title } = newSignature; + + const signature: SignatureEntity = new SignatureEntity(); + signature.title = newSignature.title; + + return await signature.save(); + } catch (error) { + throw new Error('Failed to create Signature'); + } + } } diff --git a/src/signature/signature.like.entity.ts b/src/signature/signature.like.entity.ts index 61ff1b7..79ca194 100644 --- a/src/signature/signature.like.entity.ts +++ b/src/signature/signature.like.entity.ts @@ -11,7 +11,7 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import SignatureEntity from './signature.entity'; +import { SignatureEntity } from './signature.entity'; import { UserEntity } from 'src/user/user.entity'; @Entity() diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 4e02839..98c765e 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -3,8 +3,7 @@ import { Module } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { SignatureController } from './signature.controller'; -import { SignatureEntityRepository } from './signature.repository'; -import SignatureEntity from './signature.entity'; +import { SignatureEntity } from './signature.entity'; import { TypeOrmModule } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { EntityManager } from 'typeorm'; @@ -12,6 +11,6 @@ import { EntityManager } from 'typeorm'; @Module({ //imports: [TypeOrmModule.forFeature([SignatureEntity])], controllers: [SignatureController], - providers: [SignatureService, SignatureEntityRepository, EntityManager], + providers: [SignatureService, EntityManager], }) export class SignatureModule {} diff --git a/src/signature/signature.page.entity.ts b/src/signature/signature.page.entity.ts index 6847388..8856a19 100644 --- a/src/signature/signature.page.entity.ts +++ b/src/signature/signature.page.entity.ts @@ -11,7 +11,7 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import SignatureEntity from './signature.entity'; +import { SignatureEntity } from './signature.entity'; @Entity() export class SignaturePageEntity extends BaseEntity { diff --git a/src/signature/signature.repository.ts b/src/signature/signature.repository.ts deleted file mode 100644 index 731dfbb..0000000 --- a/src/signature/signature.repository.ts +++ /dev/null @@ -1,44 +0,0 @@ -//signature.repository.ts - -import { Repository, DataSource, EntityManager } from 'typeorm'; -import SignatureEntity from './signature.entity'; -import { CreateSignatureDto } from './dto/create-signature.dto'; -import { HomeSignatureDto } from './dto/home-signature.dto'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class SignatureEntityRepository extends Repository { - constructor(private readonly entityManager: EntityManager) { - super(SignatureEntity, entityManager); - } - - async findMySignature(userId: number): Promise { - const signatures = await this.createQueryBuilder('signature') - .leftJoinAndSelect('signature.owner', 'user') - .where('user.id = :userId', { userId }) - .getMany(); - - //결과를 HomeSignatureDto로 매핑 - const result: HomeSignatureDto[] = signatures.map((signature) => ({ - title: signature.title, - date: signature.created, - // 페이지 1장 사진 추가해야 - })); - - return result; - } - - async createSignature(newSignature: CreateSignatureDto): Promise { - const { title } = newSignature; - /* - const signature = this.signatureRepository.create({ - title: title, - liked_cnt: 0, - }); - - await this.signatureRepository.save(signature); - */ - - return true; - } -} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 50e26f5..66c491b 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -1,24 +1,32 @@ // signature.service.ts -import { HttpException, Injectable } from '@nestjs/common'; +import { BadRequestException, HttpException, Injectable } from '@nestjs/common'; import bcrypt from 'bcrypt'; import jsonwebtoken from 'jsonwebtoken'; -import { SignatureEntityRepository } from './signature.repository'; import { CreateSignatureDto } from './dto/create-signature.dto'; -import SignatureEntity from './signature.entity'; +import { SignatureEntity } from './signature.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { HomeSignatureDto } from './dto/home-signature.dto'; import { UserEntity } from 'src/user/user.entity'; @Injectable() export class SignatureService { - constructor( - @InjectRepository(SignatureEntity) - private signatureRepository: SignatureEntityRepository, - ) {} + async createSignature( + createSignatureDto: CreateSignatureDto, + ): Promise { + const signature: SignatureEntity = await SignatureEntity.createSignature( + createSignatureDto, + ); + + if (!signature) { + throw new BadRequestException(); + } + return signature.id; + } + /* async homeSignature(userId: number): Promise { - /* 사용자 토큰 가져오기 구현 + // 사용자 토큰 가져오기 구현 const getByUserId = getRepository(UserEntity) .createQueryBuilder('user') .where('user.userId = :userId', {userId }); @@ -27,23 +35,14 @@ export class SignatureService { if(!getUser){ } - */ + console.log('홈 서비스 진입'); - const signatures: HomeSignatureDto[] = - await this.signatureRepository.findMySignature(userId); + const signatures: HomeSignatureDto[] = await SignatureEntity.findMySignature(userId); return signatures; } catch(error) { // 예외 처리 throw new HttpException('Internal Server Error', 500); } - - async createSignature(newSignature: CreateSignatureDto): Promise { - const signature = await this.signatureRepository.createSignature( - newSignature, - ); - console.log(signature); - if (signature == true) return true; - else return false; - } + */ } From 8999c1b45d4c44deb440187b45ef1dc7ce4e9e6f Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 31 Jan 2024 05:53:08 +0900 Subject: [PATCH 016/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=83=9D=EC=84=B1=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 포스트맨 테스트 완료 --- .eslintrc.js | 4 +- src/database/database.module.ts | 2 +- src/diary/diary.entity.ts | 10 ++- src/diary/diary.image.entity.ts | 5 +- src/location/location.entity.ts | 5 +- src/place/place.entity.ts | 9 +-- src/place/place.image.entity.ts | 11 ++-- src/place/place.tag.entity.ts | 11 ++-- src/schedule/schedule.detail.entity.ts | 9 ++- src/schedule/schedule.entity.ts | 20 ++++-- src/schedule/schedule.group.entity.ts | 7 ++- .../{ => domain}/signature.entity.ts | 46 ++++++++++---- .../{ => domain}/signature.like.entity.ts | 17 +++--- src/signature/domain/signature.page.entity.ts | 61 +++++++++++++++++++ src/signature/dto/create-signature.dto.ts | 4 +- src/signature/dto/page-signature.dto.ts | 4 +- src/signature/signature.controller.ts | 20 +++++- src/signature/signature.module.ts | 5 +- src/signature/signature.page.entity.ts | 44 ------------- src/signature/signature.service.ts | 19 ++++-- src/types/express.d.ts | 2 +- src/user/user.dto.ts | 2 +- src/user/user.entity.ts | 20 ++++-- src/user/user.following.entity.ts | 14 +++-- src/user/user.profile.image.entity.ts | 11 ++-- src/user/user.service.ts | 15 +++++ 26 files changed, 255 insertions(+), 122 deletions(-) rename src/signature/{ => domain}/signature.entity.ts (50%) rename src/signature/{ => domain}/signature.like.entity.ts (63%) create mode 100644 src/signature/domain/signature.page.entity.ts delete mode 100644 src/signature/signature.page.entity.ts diff --git a/.eslintrc.js b/.eslintrc.js index 0d6419a..0666d04 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,9 @@ module.exports = { tsconfigRootDir: __dirname, sourceType: 'module', }, - plugins: ['@typescript-eslint/eslint-plugin'], + plugins: [ + '@typescript-eslint/eslint-plugin' + ], extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', diff --git a/src/database/database.module.ts b/src/database/database.module.ts index ff423a1..857bc80 100644 --- a/src/database/database.module.ts +++ b/src/database/database.module.ts @@ -5,4 +5,4 @@ import { databaseProviders } from './database.providers'; providers: [...databaseProviders], exports: [...databaseProviders], }) -export class DatabaseModule {} \ No newline at end of file +export class DatabaseModule {} diff --git a/src/diary/diary.entity.ts b/src/diary/diary.entity.ts index 8e797e9..155e09a 100644 --- a/src/diary/diary.entity.ts +++ b/src/diary/diary.entity.ts @@ -5,7 +5,8 @@ import { DeleteDateColumn, Entity, JoinColumn, - ManyToOne, OneToMany, + ManyToOne, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -32,7 +33,10 @@ export class DiaryEntity extends BaseEntity { @Column({ type: 'enum', enum: ['SUNNY', 'RAINY', 'SNOWY', 'CLOUDY'] }) weather: 'SUNNY' | 'RAINY' | 'SNOWY' | 'CLOUDY'; - @Column({ type: 'enum', enum: ['HAPPY', 'SAD', 'ANGRY', 'SHOCKED', 'SLEEPY', 'WINK'] }) + @Column({ + type: 'enum', + enum: ['HAPPY', 'SAD', 'ANGRY', 'SHOCKED', 'SLEEPY', 'WINK'], + }) mood: 'HAPPY' | 'SAD' | 'ANGRY' | 'SHOCKED' | 'SLEEPY' | 'WINK'; @Column({ type: 'mediumtext' }) @@ -49,4 +53,4 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/diary/diary.image.entity.ts b/src/diary/diary.image.entity.ts index f57e444..a18772d 100644 --- a/src/diary/diary.image.entity.ts +++ b/src/diary/diary.image.entity.ts @@ -1,5 +1,6 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, Entity, @@ -30,4 +31,4 @@ export class DiaryImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index c057809..57fdcc2 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -1,5 +1,6 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, Entity, @@ -32,4 +33,4 @@ export class LocationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/place/place.entity.ts b/src/place/place.entity.ts index f9ceaaf..effb5d5 100644 --- a/src/place/place.entity.ts +++ b/src/place/place.entity.ts @@ -3,7 +3,8 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, OneToMany, + Entity, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -21,10 +22,10 @@ export class PlaceEntity extends BaseEntity { @Column({ type: 'text' }) description: string; - @OneToMany(() => PlaceTagEntity, tag => tag.place) + @OneToMany(() => PlaceTagEntity, (tag) => tag.place) tags: PlaceTagEntity[]; - @OneToMany(() => PlaceImageEntity, image => image.place) + @OneToMany(() => PlaceImageEntity, (image) => image.place) images: PlaceImageEntity[]; @CreateDateColumn() @@ -35,4 +36,4 @@ export class PlaceEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/place/place.image.entity.ts b/src/place/place.image.entity.ts index 52d9100..31dca50 100644 --- a/src/place/place.image.entity.ts +++ b/src/place/place.image.entity.ts @@ -1,8 +1,11 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -14,7 +17,7 @@ export class PlaceImageEntity extends BaseEntity { id: number; @JoinColumn() - @ManyToOne(() => PlaceEntity, place => place.images) + @ManyToOne(() => PlaceEntity, (place) => place.images) place: PlaceEntity; @Column() @@ -28,4 +31,4 @@ export class PlaceImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/place/place.tag.entity.ts b/src/place/place.tag.entity.ts index c245ce9..b3e24c7 100644 --- a/src/place/place.tag.entity.ts +++ b/src/place/place.tag.entity.ts @@ -1,8 +1,11 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -14,7 +17,7 @@ export class PlaceTagEntity extends BaseEntity { id: number; @JoinColumn() - @ManyToOne(() => PlaceEntity, place => place.tags) + @ManyToOne(() => PlaceEntity, (place) => place.tags) place: PlaceEntity; @Column() @@ -28,4 +31,4 @@ export class PlaceTagEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/schedule/schedule.detail.entity.ts b/src/schedule/schedule.detail.entity.ts index 6f48087..6874f11 100644 --- a/src/schedule/schedule.detail.entity.ts +++ b/src/schedule/schedule.detail.entity.ts @@ -1,8 +1,11 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -31,4 +34,4 @@ export class ScheduleDetailEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index ccae4be..ef29894 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -1,8 +1,12 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, OneToMany, + Entity, + JoinColumn, + ManyToOne, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -15,10 +19,16 @@ export class ScheduleEntity extends BaseEntity { id: number; @JoinColumn() - @ManyToOne(() => ScheduleGroupEntity, (scheduleGroup) => scheduleGroup.schedules) + @ManyToOne( + () => ScheduleGroupEntity, + (scheduleGroup) => scheduleGroup.schedules, + ) scheduleGroup: ScheduleGroupEntity; - @OneToMany(() => ScheduleDetailEntity, (scheduleDetail) => scheduleDetail.schedule) + @OneToMany( + () => ScheduleDetailEntity, + (scheduleDetail) => scheduleDetail.schedule, + ) scheduleDetails: ScheduleDetailEntity[]; @Column({ type: 'date' }) @@ -38,4 +48,4 @@ export class ScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/schedule/schedule.group.entity.ts b/src/schedule/schedule.group.entity.ts index 004a5e7..57c644d 100644 --- a/src/schedule/schedule.group.entity.ts +++ b/src/schedule/schedule.group.entity.ts @@ -2,7 +2,10 @@ import { BaseEntity, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, OneToMany, + Entity, + JoinColumn, + ManyToOne, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -29,4 +32,4 @@ export class ScheduleGroupEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/signature/signature.entity.ts b/src/signature/domain/signature.entity.ts similarity index 50% rename from src/signature/signature.entity.ts rename to src/signature/domain/signature.entity.ts index 5f56858..b370771 100644 --- a/src/signature/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -5,15 +5,18 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, + Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; -import { HomeSignatureDto } from './dto/home-signature.dto'; -import { CreateSignatureDto } from './dto/create-signature.dto'; +import { HomeSignatureDto } from '../dto/home-signature.dto'; +import { CreateSignatureDto } from '../dto/create-signature.dto'; +import { UserService } from '../../user/user.service'; +import { SignaturePageEntity } from './signature.page.entity'; +import { SignatureLikeEntity } from './signature.like.entity'; @Entity() export class SignatureEntity extends BaseEntity { @@ -23,11 +26,21 @@ export class SignatureEntity extends BaseEntity { @Column() title: string; - @Column() + @Column({default:0}) liked_cnt: number; - @OneToMany(() => UserEntity, (owner) => owner.id) - owner: UserEntity[]; + @ManyToOne(() => UserEntity, + (user) => user.signatures) + @JoinColumn({ name: 'user_id'}) + user: UserEntity; + + @OneToMany(() => SignaturePageEntity, + (signaturePage) => signaturePage.signature) + signaturePages: SignaturePageEntity[]; + + @OneToMany(() => SignatureLikeEntity, + (signatureLike) => signatureLike.signature) + likes: SignatureLikeEntity[]; @CreateDateColumn() created: Date; @@ -57,16 +70,27 @@ export class SignatureEntity extends BaseEntity { */ static async createSignature( - newSignature: CreateSignatureDto, + createSignatureDto: CreateSignatureDto, ): Promise { try { - const { title } = newSignature; - const signature: SignatureEntity = new SignatureEntity(); - signature.title = newSignature.title; + signature.title = createSignatureDto.title; + + // 현재 로그인한 사용자 아이디로 수정해야함 + const user: UserEntity = await UserEntity.findOne({ where: { id: 1 }}); + + if(!user){ + throw new Error('User not found'); + } + else{ + console.log("user name: "+ user.name); + signature.user = user; + + return await signature.save(); - return await signature.save(); + } } catch (error) { + console.error('Error creating Signature:', error); throw new Error('Failed to create Signature'); } } diff --git a/src/signature/signature.like.entity.ts b/src/signature/domain/signature.like.entity.ts similarity index 63% rename from src/signature/signature.like.entity.ts rename to src/signature/domain/signature.like.entity.ts index 79ca194..bd12691 100644 --- a/src/signature/signature.like.entity.ts +++ b/src/signature/domain/signature.like.entity.ts @@ -2,12 +2,9 @@ import { BaseEntity, - Column, CreateDateColumn, DeleteDateColumn, - Entity, - OneToMany, - OneToOne, + Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -19,11 +16,15 @@ export class SignatureLikeEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @OneToMany(() => SignatureEntity, (signature) => signature.id) - signature: SignatureEntity[]; + @ManyToOne(() => SignatureEntity, + (signature) => signature.likes) + @JoinColumn({name: 'signature_id'}) + signature: SignatureEntity; - @OneToMany(() => UserEntity, (user) => user.id) - user: UserEntity[]; + @ManyToOne(() => UserEntity, + (user) => user.likes) + @JoinColumn({name: 'user_id'}) + user: UserEntity; @CreateDateColumn() created: Date; diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts new file mode 100644 index 0000000..c5fdecb --- /dev/null +++ b/src/signature/domain/signature.page.entity.ts @@ -0,0 +1,61 @@ +// signature.entity.ts + +import { + BaseEntity, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, JoinColumn, ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { SignatureEntity } from './signature.entity'; +import {PageSignatureDto} from '../dto/page-signature.dto'; + +@Entity() +export class SignaturePageEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column() + pageNum: number; + + @Column({ type: 'mediumtext' }) + content: string; + + @Column() + location: string; + + @Column() + image: string; + + @ManyToOne(() => SignatureEntity, + (signature) => signature.signaturePages) + @JoinColumn({name: 'signature_id'}) + signature: SignatureEntity; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; + + static async saveSignaturePages( + pageSignatureDto:PageSignatureDto, + signature:SignatureEntity):Promise { + + const signaturePage:SignaturePageEntity = new SignaturePageEntity(); + + signaturePage.signature = signature; + signaturePage.content = pageSignatureDto.content; + signaturePage.image = pageSignatureDto.image; // base64 이미지 서버에 올려야 + signaturePage.location = pageSignatureDto.location; + signaturePage.pageNum = pageSignatureDto.page; + + return await signaturePage.save(); + + } +} diff --git a/src/signature/dto/create-signature.dto.ts b/src/signature/dto/create-signature.dto.ts index 70cdac4..21b9676 100644 --- a/src/signature/dto/create-signature.dto.ts +++ b/src/signature/dto/create-signature.dto.ts @@ -1,8 +1,8 @@ // create-signature.dto.ts -import { pageSignatureDto } from './page-signature.dto'; +import { PageSignatureDto } from './page-signature.dto'; export class CreateSignatureDto { title: string; - pages: pageSignatureDto[]; + pages: PageSignatureDto[]; } diff --git a/src/signature/dto/page-signature.dto.ts b/src/signature/dto/page-signature.dto.ts index 0674ae4..3a25dea 100644 --- a/src/signature/dto/page-signature.dto.ts +++ b/src/signature/dto/page-signature.dto.ts @@ -1,7 +1,7 @@ // page-signature.dto.ts -export class pageSignatureDto { - pageNum: number; +export class PageSignatureDto { + page: number; content: string; location: string; image: string; diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 4085a58..df976a1 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -22,8 +22,24 @@ export class SignatureController { @Post('/new') async createNewSignature( @Body() newSignature: CreateSignatureDto, - ): Promise { + ): Promise { const result = await this.signatureService.createSignature(newSignature); - return result; + + if(!result){ + return { + status: 500, + success: false, + message: "서버 내부 오류", + }; + + } + else{ + return { + status: 201, + success: true, + message: "시그니처 기록하기 성공", + data: {result} + }; + } } } diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 98c765e..99f6e51 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -3,14 +3,13 @@ import { Module } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { SignatureController } from './signature.controller'; -import { SignatureEntity } from './signature.entity'; +import { SignatureEntity } from './domain/signature.entity'; import { TypeOrmModule } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { EntityManager } from 'typeorm'; @Module({ - //imports: [TypeOrmModule.forFeature([SignatureEntity])], controllers: [SignatureController], - providers: [SignatureService, EntityManager], + providers: [SignatureService], }) export class SignatureModule {} diff --git a/src/signature/signature.page.entity.ts b/src/signature/signature.page.entity.ts deleted file mode 100644 index 8856a19..0000000 --- a/src/signature/signature.page.entity.ts +++ /dev/null @@ -1,44 +0,0 @@ -// signature.entity.ts - -import { - BaseEntity, - Column, - CreateDateColumn, - DeleteDateColumn, - Entity, - OneToMany, - OneToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; -import { SignatureEntity } from './signature.entity'; - -@Entity() -export class SignaturePageEntity extends BaseEntity { - @PrimaryGeneratedColumn() - id: number; - - @Column() - pageNum: number; - - @Column({ type: 'mediumtext' }) - content: string; - - @Column() - location: string; - - @Column() - image: string; - - @OneToMany(() => SignatureEntity, (signature) => signature.id) - signature: SignatureEntity[]; - - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - - @DeleteDateColumn() - deleted: Date; -} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 66c491b..4c72160 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -4,23 +4,34 @@ import { BadRequestException, HttpException, Injectable } from '@nestjs/common'; import bcrypt from 'bcrypt'; import jsonwebtoken from 'jsonwebtoken'; import { CreateSignatureDto } from './dto/create-signature.dto'; -import { SignatureEntity } from './signature.entity'; +import { SignatureEntity } from './domain/signature.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { HomeSignatureDto } from './dto/home-signature.dto'; import { UserEntity } from 'src/user/user.entity'; +import { SignaturePageEntity } from './domain/signature.page.entity'; +import { PageSignatureDto } from './dto/page-signature.dto'; @Injectable() export class SignatureService { async createSignature( createSignatureDto: CreateSignatureDto, ): Promise { - const signature: SignatureEntity = await SignatureEntity.createSignature( - createSignatureDto, - ); + + // [1] 시그니처 저장 + const signature: SignatureEntity = + await SignatureEntity.createSignature( createSignatureDto); if (!signature) { throw new BadRequestException(); } + + else{ + // [2] 각 페이지 저장 + for(const pageSignatureDto of createSignatureDto.pages){ + await SignaturePageEntity.saveSignaturePages(pageSignatureDto, signature); + } + } + return signature.id; } diff --git a/src/types/express.d.ts b/src/types/express.d.ts index 062ca01..7369421 100644 --- a/src/types/express.d.ts +++ b/src/types/express.d.ts @@ -4,4 +4,4 @@ declare namespace Express { id: number; }; } -} \ No newline at end of file +} diff --git a/src/user/user.dto.ts b/src/user/user.dto.ts index 1bf2737..7064a5f 100644 --- a/src/user/user.dto.ts +++ b/src/user/user.dto.ts @@ -1,3 +1,3 @@ export interface IReqUser { id: number; -} \ No newline at end of file +} diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index a2150fd..ffb90c7 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -3,12 +3,16 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, OneToMany, OneToOne, + Entity, ManyToOne, + OneToMany, + OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { UserProfileImageEntity } from './user.profile.image.entity'; import { UserFollowingEntity } from './user.following.entity'; +import { SignatureEntity } from '../signature/domain/signature.entity'; +import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; @Entity() export class UserEntity extends BaseEntity { @@ -39,15 +43,21 @@ export class UserEntity extends BaseEntity { @Column() oauthToken: string; - @OneToOne(() => UserProfileImageEntity, profileImage => profileImage.user) + @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user) profileImage: UserProfileImageEntity; - @OneToMany(() => UserFollowingEntity, following => following.user) + @OneToMany(() => UserFollowingEntity, (following) => following.user) following: UserFollowingEntity[]; - @OneToMany(() => UserFollowingEntity, followed => followed.followUser) + @OneToMany(() => UserFollowingEntity, (followed) => followed.followUser) follower: UserFollowingEntity[]; + @OneToMany(() => SignatureEntity, (signature) => signature.user) + signatures: SignatureEntity[]; + + @OneToMany(() => SignatureLikeEntity, (signatureLike) => signatureLike.signature) + likes: SignatureLikeEntity[]; + @CreateDateColumn() created: Date; @@ -56,4 +66,4 @@ export class UserEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/user/user.following.entity.ts b/src/user/user.following.entity.ts index 65ed9b7..ad21951 100644 --- a/src/user/user.following.entity.ts +++ b/src/user/user.following.entity.ts @@ -1,4 +1,10 @@ -import { BaseEntity, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { + BaseEntity, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; import { UserEntity } from './user.entity'; @Entity() @@ -7,10 +13,10 @@ export class UserFollowingEntity extends BaseEntity { id: number; @JoinColumn() - @ManyToOne(() => UserEntity, user => user.following) + @ManyToOne(() => UserEntity, (user) => user.following) user: UserEntity; @JoinColumn() - @ManyToOne(() => UserEntity, user => user.follower) + @ManyToOne(() => UserEntity, (user) => user.follower) followUser: UserEntity; -} \ No newline at end of file +} diff --git a/src/user/user.profile.image.entity.ts b/src/user/user.profile.image.entity.ts index 8ad1f51..959a9bc 100644 --- a/src/user/user.profile.image.entity.ts +++ b/src/user/user.profile.image.entity.ts @@ -1,8 +1,11 @@ import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, OneToOne, + Entity, + JoinColumn, + OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -14,7 +17,7 @@ export class UserProfileImageEntity extends BaseEntity { id: number; @JoinColumn() - @OneToOne(() => UserEntity, user => user.profileImage) + @OneToOne(() => UserEntity, (user) => user.profileImage) user: UserEntity; @Column() @@ -28,4 +31,4 @@ export class UserProfileImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/user/user.service.ts b/src/user/user.service.ts index c304b5c..23fd05e 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -3,6 +3,7 @@ import bcrypt from 'bcrypt'; import jsonwebtoken from 'jsonwebtoken'; import { UserEntity } from './user.entity'; import { IReqUser } from './user.dto'; +import { UserFollowingEntity } from './user.following.entity'; @Injectable() export class UserService { @@ -42,4 +43,18 @@ export class UserService { }), }; } + + async FindUser(id: number) { + const user: UserEntity = await UserEntity.findOne({ + where: { + id: id + } + }); + + if (!user) { + throw new HttpException('Invalid user', 403); + } + + return user; + } } From 0b64e36c0c793245384b2ada71b13e0869a1462b Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 31 Jan 2024 19:21:03 +0900 Subject: [PATCH 017/316] =?UTF-8?q?feat:=20=EB=82=B4=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98=20=EB=AA=A9=EB=A1=9D=20=EB=B6=88=EB=9F=AC?= =?UTF-8?q?=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 41 +++++++++++++-- package.json | 1 + src/response/response-code.enum.ts | 49 ++++++++++++++++++ src/response/response.dto.ts | 27 ++++++++++ src/signature/domain/signature.entity.ts | 40 ++++++++------- src/signature/domain/signature.page.entity.ts | 11 ++++ src/signature/dto/home-signature.dto.ts | 6 ++- src/signature/signature.controller.ts | 50 +++++++++++-------- src/signature/signature.service.ts | 26 ++++------ 9 files changed, 190 insertions(+), 61 deletions(-) create mode 100644 src/response/response-code.enum.ts create mode 100644 src/response/response.dto.ts diff --git a/package-lock.json b/package-lock.json index f1d481d..1aac640 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", @@ -1864,6 +1865,37 @@ "typescript": ">=4.8.2" } }, + "node_modules/@nestjs/swagger": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.2.0.tgz", + "integrity": "sha512-W7WPq561/79w27ZEgViXS7c5hqPwT7QXhsLsSeu2jeBROUhMM825QKDFKbMmtb643IW5dznJ4PjherlZZgtMvg==", + "dependencies": { + "@nestjs/mapped-types": "2.0.4", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "5.11.0" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.0.tgz", @@ -2914,8 +2946,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -6303,7 +6334,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -8393,6 +8423,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.0.tgz", + "integrity": "sha512-j0PIATqQSEFGOLmiJOJZj1X1Jt6bFIur3JpY7+ghliUnfZs0fpWDdHEkn9q7QUlBtKbkn6TepvSxTqnE8l3s0A==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 9a39206..dba731e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts new file mode 100644 index 0000000..377cf0e --- /dev/null +++ b/src/response/response-code.enum.ts @@ -0,0 +1,49 @@ +import { HttpStatus } from '@nestjs/common'; + +export enum ResponseCode { + + /* 200 OK : 요청 성공 */ + SIGNIN_SUCCESS = 'OK', + SIGNOUT_SUCCESS = 'OK', + REISSUE_TOKEN_SUCCESS = 'OK', + GET_MY_SIGNATURES_SUCCESS = 'OK', + GET_SIGNATURE_DETAIL_SUCCESS = 'OK', + + /* 201 CREATED : 요청 성공, 자원 생성 */ + SIGNUP_SUCCESS = 'CREATED', + SIGNATURE_CREATED = 'CREATED', + + + /* 400 BAD_REQUEST : 잘못된 요청 */ + AUTH_NUMBER_INCORRECT = 'BAD_REQUEST', + RESET_PASSWORD_FAIL_MATCH = 'BAD_REQUEST', + SIGNATURE_CREATION_FAIL = 'BAD_REQUEST', + + + /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ + INVALID_AUTH_TOKEN = 'UNAUTHORIZED', + INVALID_ACCOUNT = 'UNAUTHORIZED', + UNKNOWN_AUTHENTICATION_ERROR = 'UNAUTHORIZED', + + + /* 403 FORBIDDEN : 권한이 없는 사용자 */ + INVALID_REFRESH_TOKEN = 'FORBIDDEN', + HOLDING_WITHDRAWAL = 'FORBIDDEN', + SIGNOUT_FAIL_REFRESH_TOKEN = 'FORBIDDEN', + + + /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ + ACCOUNT_NOT_FOUND = 'NOT_FOUND', + REFRESH_TOKEN_NOT_FOUND = 'NOT_FOUND', + SIGNATURE_NOT_FOUND = 'NOT_FOUND', + + + /* 409 CONFLICT : Resource 의 현재 상태와 충돌. 보통 중복된 데이터 존재 */ + EMAIL_DUPLICATION = 'CONFLICT', + USERNAME_DUPLICATION = 'CONFLICT', + NICKNAME_DUPLICATION = 'CONFLICT', + + + /* 500 INTERNAL_SERVER_ERROR */ + INTERNAL_SERVEr_ERROR = 'INTERNAL_SERVER_ERROR' +} diff --git a/src/response/response.dto.ts b/src/response/response.dto.ts new file mode 100644 index 0000000..0045117 --- /dev/null +++ b/src/response/response.dto.ts @@ -0,0 +1,27 @@ +import { HttpStatus } from '@nestjs/common'; +import { ApiProperty } from '@nestjs/swagger'; +import {ResponseCode} from './response-code.enum'; + +export class ResponseDto { + @ApiProperty({ description: '응답 시간'}) + timestamp: Date = new Date(); + + @ApiProperty({ description: 'http status code'}) + code: ResponseCode; + + @ApiProperty() + success: boolean + + @ApiProperty({ example: '데이터 불러오기 성공', description: '응답 메시지'}) + message: string; + + @ApiProperty({ required: false, nullable: true , description: 'Response Body'}) + data?: T; + + public constructor(code: ResponseCode, success:boolean, message: string, data: T) { + this.code = code; + this.success = success; + this.message = message; + this.data = data; + } +} diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index b370771..3e311c1 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -51,23 +51,6 @@ export class SignatureEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - /* - async findMySignature(userId: number): Promise { - const signatures = await this.createQueryBuilder('signature') - .leftJoinAndSelect('signature.owner', 'user') - .where('user.id = :userId', { userId }) - .getMany(); - - //결과를 HomeSignatureDto로 매핑 - const result: HomeSignatureDto[] = signatures.map((signature) => ({ - title: signature.title, - date: signature.created, - // 페이지 1장 사진 추가해야 - })); - - return result; - } - */ static async createSignature( createSignatureDto: CreateSignatureDto, @@ -77,7 +60,9 @@ export class SignatureEntity extends BaseEntity { signature.title = createSignatureDto.title; // 현재 로그인한 사용자 아이디로 수정해야함 - const user: UserEntity = await UserEntity.findOne({ where: { id: 1 }}); + const user: UserEntity = await UserEntity.findOne({ + where: { id: 1 } + }); if(!user){ throw new Error('User not found'); @@ -94,4 +79,23 @@ export class SignatureEntity extends BaseEntity { throw new Error('Failed to create Signature'); } } + + static async findMySignature(user_id: number):Promise { + const mySignatureList:HomeSignatureDto[] = []; + const signatures = await SignatureEntity.find({ + where: { user: { id: user_id} }, + }); + + for(const signature of signatures){ + const homeSignature:HomeSignatureDto = new HomeSignatureDto(); + + homeSignature.id = signature.id; + homeSignature.title = signature.title; + homeSignature.date = signature.created; + homeSignature.image = await SignaturePageEntity.findThumbnail(signature.id); + + mySignatureList.push(homeSignature); + } + return mySignatureList; + } } diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index c5fdecb..3686ac5 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -58,4 +58,15 @@ export class SignaturePageEntity extends BaseEntity { return await signaturePage.save(); } + + static async findThumbnail(id: number) { + // 각 시그니처의 첫 번째 페이지의 이미지 가져오기 + const firstPage=await SignaturePageEntity.findOne({ + where: { + signature: { id: id }, + pageNum: 1 + } + }); + return firstPage.image; + } } diff --git a/src/signature/dto/home-signature.dto.ts b/src/signature/dto/home-signature.dto.ts index ca9154c..5b272ae 100644 --- a/src/signature/dto/home-signature.dto.ts +++ b/src/signature/dto/home-signature.dto.ts @@ -1,6 +1,8 @@ // home-signature.dto.ts export class HomeSignatureDto { - title: string; - date: Date; + id: number; // 시그니처 id + title: string; // 시그니처 제목 + date: Date; // 시그니처 발행일 + image: string; // 시그니처 첫번째 페이지의 이미지(썸네일) } diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index df976a1..7fee1c0 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -1,45 +1,53 @@ // signature.controller.ts -import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Post } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { CreateSignatureDto } from './dto/create-signature.dto'; +import { ResponseCode } from '../response/response-code.enum'; +import { ResponseDto } from '../response/response.dto'; import { HomeSignatureDto } from './dto/home-signature.dto'; + @Controller('signature') //@UseGuards(new AuthGuard()) export class SignatureController { constructor(private readonly signatureService: SignatureService) {} - /* - @Get('/') - getMySignature(@Body() userId: number) { - // 임시로 토큰이 아닌 유저 아이디 받도록 구현 - console.log('signature/: 내 시그니처 요청'); - return this.signatureService.homeSignature(userId); + @Get('/') // 시그니처 탭 메인: 내 시그니처 목록 + async getMySignature(@Body() user_id: number): Promise> { + + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 + const result = await this.signatureService.homeSignature(user_id); + console.log("내 시그니처 조회 결과: ", result); + + return new ResponseDto( + ResponseCode.GET_MY_SIGNATURES_SUCCESS, + true, + "내 시그니처 가져오기 성공", + result + ); } - */ @Post('/new') - async createNewSignature( + async createNewSignature( // 시그니처 생성하기 @Body() newSignature: CreateSignatureDto, - ): Promise { + ): Promise> { const result = await this.signatureService.createSignature(newSignature); if(!result){ - return { - status: 500, - success: false, - message: "서버 내부 오류", - }; + return new ResponseDto( + ResponseCode.SIGNATURE_CREATION_FAIL, + false, + "시그니처 생성에 실패했습니다", + null); } else{ - return { - status: 201, - success: true, - message: "시그니처 기록하기 성공", - data: {result} - }; + return new ResponseDto( + ResponseCode.SIGNATURE_CREATED, + true, + "시그니처 기록하기 성공", + result); } } } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 4c72160..8dd1717 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -35,25 +35,17 @@ export class SignatureService { return signature.id; } - /* - async homeSignature(userId: number): Promise { - // 사용자 토큰 가져오기 구현 - const getByUserId = getRepository(UserEntity) - .createQueryBuilder('user') - .where('user.userId = :userId', {userId }); - const getUser = await getByUserId.getOne(); - if(!getUser){ + async homeSignature(user_id: number): Promise { + try { + const homeSignatureList: HomeSignatureDto[] = await SignatureEntity.findMySignature(user_id); + return homeSignatureList; + } catch (error) { + // 예외 처리 + console.error('Error on HomeSignature: ', error); + throw new HttpException('Internal Server Error', 500); } - - console.log('홈 서비스 진입'); - const signatures: HomeSignatureDto[] = await SignatureEntity.findMySignature(userId); - return signatures; } - catch(error) { - // 예외 처리 - throw new HttpException('Internal Server Error', 500); - } - */ + } From af868e4644a1d6d5e32919f7d4fabdab456d1be1 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 1 Feb 2024 01:47:00 +0900 Subject: [PATCH 018/316] =?UTF-8?q?feat=20:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=20=EC=83=9D=EC=84=B1=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 ++ package-lock.json | 41 +++++++++++++++++++++++-- package.json | 1 + src/app.module.ts | 5 --- src/constants/response.ts | 6 ---- src/date-group/date-group.controller.ts | 11 ++++++- src/date-group/date-group.entity.ts | 28 +++++++++++++++-- src/date-group/date-group.module.ts | 7 +---- src/date-group/date-group.repository.ts | 26 ---------------- src/date-group/date-group.service.ts | 20 +++++------- src/response/response.status.ts | 7 +++++ src/response/response.ts | 16 ++++++++++ 12 files changed, 109 insertions(+), 62 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/constants/response.ts delete mode 100644 src/date-group/date-group.repository.ts create mode 100644 src/response/response.status.ts create mode 100644 src/response/response.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1ea4962 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "CodeGPT.apiKey": "CodeGPT Plus Beta" +} diff --git a/package-lock.json b/package-lock.json index 3d8581f..ea29772 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@nestjs/core": "^10.3.1", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", "class-validator": "^0.14.1", @@ -1865,6 +1866,37 @@ "typescript": ">=4.8.2" } }, + "node_modules/@nestjs/swagger": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.2.0.tgz", + "integrity": "sha512-W7WPq561/79w27ZEgViXS7c5hqPwT7QXhsLsSeu2jeBROUhMM825QKDFKbMmtb643IW5dznJ4PjherlZZgtMvg==", + "dependencies": { + "@nestjs/mapped-types": "2.0.4", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "5.11.0" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.0.tgz", @@ -2920,8 +2952,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -6319,7 +6350,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -8414,6 +8444,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.0.tgz", + "integrity": "sha512-j0PIATqQSEFGOLmiJOJZj1X1Jt6bFIur3JpY7+ghliUnfZs0fpWDdHEkn9q7QUlBtKbkn6TepvSxTqnE8l3s0A==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 0b51719..8b6f779 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@nestjs/core": "^10.3.1", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", "class-validator": "^0.14.1", diff --git a/src/app.module.ts b/src/app.module.ts index 948838b..955eb19 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,9 +10,7 @@ import { LocationModule } from './location/location.module'; import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; import { JourneyModule } from './journey/journey.module'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { DateGroupModule } from './date-group/date-group.module'; -import { DateGroupEntity } from './date-group/date-group.entity'; @Module({ imports: [ @@ -27,9 +25,6 @@ import { DateGroupEntity } from './date-group/date-group.entity'; PlaceModule, JourneyModule, DateGroupModule, - TypeOrmModule.forRoot({ - entities: [DateGroupEntity], - }), ], controllers: [AppController], providers: [AppService], diff --git a/src/constants/response.ts b/src/constants/response.ts deleted file mode 100644 index 02a996f..0000000 --- a/src/constants/response.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const MESSAGE = { - WRONG_INPUT: { - code: 500, - message: '잘못된 입력 값입니다.', - }, -} as const; diff --git a/src/date-group/date-group.controller.ts b/src/date-group/date-group.controller.ts index 85013df..90c9770 100644 --- a/src/date-group/date-group.controller.ts +++ b/src/date-group/date-group.controller.ts @@ -3,13 +3,22 @@ import { Controller, Post, Body } from '@nestjs/common'; import { DateGroupService } from './date-group.service'; import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; +import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; @Controller() export class DateGroupController { constructor(private readonly dateGroupService: DateGroupService) {} + @ApiOperation({ + summary: '날짜 그룹 생성하기', + description: 'startDate와 endDate 저장', + }) + @ApiOkResponse({ + description: '날짜 그룹 생성 성공', + }) @Post('/api/post/create-date-group') createDateGroup(@Body() createJourneyDto: CreateJourneyDto) { - return this.dateGroupService.createDateGroup(createJourneyDto); + const result = this.dateGroupService.createDateGroup(createJourneyDto); + return result; } } diff --git a/src/date-group/date-group.entity.ts b/src/date-group/date-group.entity.ts index ecc767b..701917b 100644 --- a/src/date-group/date-group.entity.ts +++ b/src/date-group/date-group.entity.ts @@ -1,9 +1,18 @@ // journey-date-group.entity.ts -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; +import { + Entity, + PrimaryGeneratedColumn, + Column, + OneToMany, + BaseEntity, +} from 'typeorm'; +import { errResponse } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; import { JourneyEntity } from '../journey/model/journey.entity'; +import { CreateJourneyDto } from 'src/journey/dtos/create-journey.dto'; @Entity() -export class DateGroupEntity { +export class DateGroupEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; @@ -15,4 +24,19 @@ export class DateGroupEntity { @OneToMany(() => JourneyEntity, (journey) => journey.dateGroup) journeys: JourneyEntity[]; + + // 날짜 그룹 생성 + static async createDateGroup( + createJourneyDto: CreateJourneyDto, + ): Promise { + try { + const dateGroup: DateGroupEntity = new DateGroupEntity(); + dateGroup.startDate = createJourneyDto.startDate; + dateGroup.endDate = createJourneyDto.endDate; + + return await dateGroup.save(); + } catch (error) { + throw new Error(error); + } + } } diff --git a/src/date-group/date-group.module.ts b/src/date-group/date-group.module.ts index 7a1ea3a..d83e0b6 100644 --- a/src/date-group/date-group.module.ts +++ b/src/date-group/date-group.module.ts @@ -1,15 +1,10 @@ // dateGroup.module.ts import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { DateGroupController } from './date-group.controller'; import { DateGroupService } from './date-group.service'; -import { DateGroupRepository } from './date-group.repository'; -import { EntityManager } from 'typeorm'; -import { DateGroupEntity } from './date-group.entity'; @Module({ - imports: [TypeOrmModule.forFeature([DateGroupEntity])], controllers: [DateGroupController], - providers: [DateGroupService, DateGroupRepository, EntityManager], + providers: [DateGroupService], }) export class DateGroupModule {} diff --git a/src/date-group/date-group.repository.ts b/src/date-group/date-group.repository.ts deleted file mode 100644 index 9fa26d0..0000000 --- a/src/date-group/date-group.repository.ts +++ /dev/null @@ -1,26 +0,0 @@ -// date-group.repository.ts -import { Injectable } from '@nestjs/common'; -import { Repository, EntityManager } from 'typeorm'; -import { DateGroupEntity } from './date-group.entity'; -import { CreateJourneyDto } from 'src/journey/dtos/create-journey.dto'; - -@Injectable() -export class DateGroupRepository extends Repository { - constructor(private readonly entityManager: EntityManager) { - super(DateGroupEntity, entityManager); - } - // 날짜 그룹 생성 - async createDateGroup( - createJourneyDto: CreateJourneyDto, - ): Promise { - try { - const dateGroup = new DateGroupEntity(); - dateGroup.startDate = createJourneyDto.startDate; - dateGroup.endDate = createJourneyDto.endDate; - - return await this.entityManager.save(dateGroup); - } catch (error) { - throw new Error('Failed to create DateGroup'); - } - } -} diff --git a/src/date-group/date-group.service.ts b/src/date-group/date-group.service.ts index b184ce5..cf7d8a7 100644 --- a/src/date-group/date-group.service.ts +++ b/src/date-group/date-group.service.ts @@ -1,27 +1,21 @@ // data-group.service.ts import { BadRequestException, Injectable } from '@nestjs/common'; -import { MESSAGE } from '../constants/response'; -import { DateGroupRepository } from './date-group.repository'; +import { response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; -import { InjectRepository } from '@nestjs/typeorm'; import { DateGroupEntity } from './date-group.entity'; @Injectable() export class DateGroupService { - constructor( - @InjectRepository(DateGroupEntity) - private readonly dateGroupRepository: DateGroupRepository, - ) {} - - async createDateGroup(createJourneyDto: CreateJourneyDto): Promise { + async createDateGroup(createJourneyDto: CreateJourneyDto) { // DateGroup 생성 - const dateGroup = await this.dateGroupRepository.createDateGroup( + const dateGroup: DateGroupEntity = await DateGroupEntity.createDateGroup( createJourneyDto, ); if (!dateGroup) { - throw new BadRequestException(MESSAGE.WRONG_INPUT); + throw new BadRequestException(); } - - return dateGroup.id; + console.log(dateGroup.id); + return response(BaseResponse.DATEGROUP_CREATED, dateGroup); } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts new file mode 100644 index 0000000..89950aa --- /dev/null +++ b/src/response/response.status.ts @@ -0,0 +1,7 @@ +export const BaseResponse = { + DATEGROUP_CREATED: { + success: true, + code: 201, + message: '날짜 그룹이 생성되었습니다.', + }, +}; diff --git a/src/response/response.ts b/src/response/response.ts new file mode 100644 index 0000000..3ee415f --- /dev/null +++ b/src/response/response.ts @@ -0,0 +1,16 @@ +export const response = ({ success, code, message }, data) => { + return { + success: success, + code: code, + message: message, + data: data, + }; +}; + +export const errResponse = ({ success, code, message }) => { + return { + success: success, + code: code, + message: message, + }; +}; From 849e329b4bd5faf4ebf30b600db00036073c9a84 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 1 Feb 2024 18:39:07 +0900 Subject: [PATCH 019/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/date-group/date-group.controller.ts | 22 ++------------ src/date-group/date-group.entity.ts | 8 ++--- src/date-group/date-group.service.ts | 20 ++----------- src/date-group/dtos/create-date-group.dto.ts | 10 +++++++ src/journey/dtos/create-journey.dto.ts | 5 +++- src/journey/journey.controller.ts | 20 +++++++++++-- src/journey/journey.module.ts | 3 +- src/journey/journey.repository.ts | 10 ------- src/journey/journey.service.ts | 29 +++++++++++++++++- src/journey/model/journey.entity.ts | 15 +++++++++- src/place/place.entity.ts | 9 +++--- src/response/response.status.ts | 5 ++++ src/response/response.ts | 2 +- src/schedule/schedule.entity.ts | 31 ++++++++++++++------ 14 files changed, 115 insertions(+), 74 deletions(-) create mode 100644 src/date-group/dtos/create-date-group.dto.ts delete mode 100644 src/journey/journey.repository.ts diff --git a/src/date-group/date-group.controller.ts b/src/date-group/date-group.controller.ts index 90c9770..b9f9676 100644 --- a/src/date-group/date-group.controller.ts +++ b/src/date-group/date-group.controller.ts @@ -1,24 +1,6 @@ // DateGroup.controller.ts -import { Controller, Post, Body } from '@nestjs/common'; -import { DateGroupService } from './date-group.service'; -import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; -import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; +import { Controller } from '@nestjs/common'; @Controller() -export class DateGroupController { - constructor(private readonly dateGroupService: DateGroupService) {} - - @ApiOperation({ - summary: '날짜 그룹 생성하기', - description: 'startDate와 endDate 저장', - }) - @ApiOkResponse({ - description: '날짜 그룹 생성 성공', - }) - @Post('/api/post/create-date-group') - createDateGroup(@Body() createJourneyDto: CreateJourneyDto) { - const result = this.dateGroupService.createDateGroup(createJourneyDto); - return result; - } -} +export class DateGroupController {} diff --git a/src/date-group/date-group.entity.ts b/src/date-group/date-group.entity.ts index 701917b..9be11f5 100644 --- a/src/date-group/date-group.entity.ts +++ b/src/date-group/date-group.entity.ts @@ -9,7 +9,7 @@ import { import { errResponse } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { JourneyEntity } from '../journey/model/journey.entity'; -import { CreateJourneyDto } from 'src/journey/dtos/create-journey.dto'; +import { CreateDateGroupDto } from './dtos/create-date-group.dto'; @Entity() export class DateGroupEntity extends BaseEntity { @@ -27,12 +27,12 @@ export class DateGroupEntity extends BaseEntity { // 날짜 그룹 생성 static async createDateGroup( - createJourneyDto: CreateJourneyDto, + createDateGroupDto: CreateDateGroupDto, ): Promise { try { const dateGroup: DateGroupEntity = new DateGroupEntity(); - dateGroup.startDate = createJourneyDto.startDate; - dateGroup.endDate = createJourneyDto.endDate; + dateGroup.startDate = createDateGroupDto.startDate; + dateGroup.endDate = createDateGroupDto.endDate; return await dateGroup.save(); } catch (error) { diff --git a/src/date-group/date-group.service.ts b/src/date-group/date-group.service.ts index cf7d8a7..42c283d 100644 --- a/src/date-group/date-group.service.ts +++ b/src/date-group/date-group.service.ts @@ -1,21 +1,5 @@ // data-group.service.ts -import { BadRequestException, Injectable } from '@nestjs/common'; -import { response } from 'src/response/response'; -import { BaseResponse } from 'src/response/response.status'; -import { CreateJourneyDto } from '../journey/dtos/create-journey.dto'; -import { DateGroupEntity } from './date-group.entity'; +import { Injectable } from '@nestjs/common'; @Injectable() -export class DateGroupService { - async createDateGroup(createJourneyDto: CreateJourneyDto) { - // DateGroup 생성 - const dateGroup: DateGroupEntity = await DateGroupEntity.createDateGroup( - createJourneyDto, - ); - if (!dateGroup) { - throw new BadRequestException(); - } - console.log(dateGroup.id); - return response(BaseResponse.DATEGROUP_CREATED, dateGroup); - } -} +export class DateGroupService {} diff --git a/src/date-group/dtos/create-date-group.dto.ts b/src/date-group/dtos/create-date-group.dto.ts new file mode 100644 index 0000000..2e79e58 --- /dev/null +++ b/src/date-group/dtos/create-date-group.dto.ts @@ -0,0 +1,10 @@ +// create-journey.dto.ts +import { IsDateString } from 'class-validator'; + +export class CreateDateGroupDto { + @IsDateString() + startDate: string; + + @IsDateString() + endDate: string; +} diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts index 2e5c4b1..dbbec90 100644 --- a/src/journey/dtos/create-journey.dto.ts +++ b/src/journey/dtos/create-journey.dto.ts @@ -1,7 +1,10 @@ // create-journey.dto.ts -import { IsDateString } from 'class-validator'; +import { IsString, IsDateString } from 'class-validator'; export class CreateJourneyDto { + @IsString() + journey_title: string; + @IsDateString() startDate: string; diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 338a4a4..44f15a5 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,9 +1,23 @@ -// journey.controller.ts - -import { Controller } from '@nestjs/common'; +import { Controller, Post, Body } from '@nestjs/common'; import { JourneyService } from './journey.service'; +import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +import { CreateJourneyDto } from './dtos/create-journey.dto'; +import { response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; @Controller('api/journey') export class JourneyController { constructor(private readonly journeyService: JourneyService) {} + @ApiOperation({ + summary: '여정 저장하기', + description: '날짜와 제목을 포함합니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Post('create') + async createJourney(@Body() createJourneyDto: CreateJourneyDto) { + const result = await this.journeyService.createJourney(createJourneyDto); + return result; + } } diff --git a/src/journey/journey.module.ts b/src/journey/journey.module.ts index 5614375..1d56977 100644 --- a/src/journey/journey.module.ts +++ b/src/journey/journey.module.ts @@ -2,10 +2,9 @@ import { Module } from '@nestjs/common'; import { JourneyController } from './journey.controller'; import { JourneyService } from './journey.service'; -import { JourneyRepository } from './journey.repository'; @Module({ controllers: [JourneyController], - providers: [JourneyService, JourneyRepository], + providers: [JourneyService], }) export class JourneyModule {} diff --git a/src/journey/journey.repository.ts b/src/journey/journey.repository.ts deleted file mode 100644 index 5196099..0000000 --- a/src/journey/journey.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -// journey.repository.ts -import { Injectable } from '@nestjs/common'; -// import { Repository } from 'typeorm'; - -// import { JourneyEntity } from './model/journey.entity'; - -@Injectable() -export class JourneyRepository { - // constructor(private readonly journey: Repository) {} -} diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 06664b1..1d97cc8 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,5 +1,32 @@ // journey.service.ts import { Injectable } from '@nestjs/common'; +import { JourneyEntity } from './model/journey.entity'; +import { response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; +import { DateGroupEntity } from 'src/date-group/date-group.entity'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { CreateJourneyDto } from './dtos/create-journey.dto'; +import { CreateDateGroupDto } from 'src/date-group/dtos/create-date-group.dto'; @Injectable() -export class JourneyService {} +export class JourneyService { + async createJourney(createJourneyDto: CreateJourneyDto) { + const dates: CreateDateGroupDto = new CreateDateGroupDto(); + { + (dates.startDate = createJourneyDto.startDate), + (dates.endDate = createJourneyDto.endDate); + } + const dateGroup = await DateGroupEntity.createDateGroup(dates); + if (!dateGroup) { + throw new Error(); + } + const journey = await JourneyEntity.createJourney( + createJourneyDto, + dateGroup.id, + ); + + const schedule = await ScheduleEntity.createSchedule(dates); + console.log(journey, schedule); + return response(BaseResponse.JOURNEY_CREATED); + } +} diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 6dc3b17..cc96235 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -10,13 +10,14 @@ import { } from 'typeorm'; import { DateGroupEntity } from '../../date-group/date-group.entity'; import { MonthlyJourneyEntity } from './monthly-journey.entity'; +import { CreateJourneyDto } from '../dtos/create-journey.dto'; @Entity() export class JourneyEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column({ length: 255, nullable: false }) + @Column() journey_title: string; @ManyToOne(() => DateGroupEntity, (dateGroup) => dateGroup.journeys) @@ -29,4 +30,16 @@ export class JourneyEntity extends BaseEntity { ) @JoinColumn({ name: 'monthly_id' }) monthlyJourney: MonthlyJourneyEntity; + + static async createJourney(createJourneyDto: CreateJourneyDto, dateGroupId) { + try { + const journey: JourneyEntity = new JourneyEntity(); + journey.journey_title = createJourneyDto.journey_title; + journey.dateGroup = dateGroupId; + + return await journey.save(); + } catch (error) { + throw new Error(error); + } + } } diff --git a/src/place/place.entity.ts b/src/place/place.entity.ts index f9ceaaf..effb5d5 100644 --- a/src/place/place.entity.ts +++ b/src/place/place.entity.ts @@ -3,7 +3,8 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, OneToMany, + Entity, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -21,10 +22,10 @@ export class PlaceEntity extends BaseEntity { @Column({ type: 'text' }) description: string; - @OneToMany(() => PlaceTagEntity, tag => tag.place) + @OneToMany(() => PlaceTagEntity, (tag) => tag.place) tags: PlaceTagEntity[]; - @OneToMany(() => PlaceImageEntity, image => image.place) + @OneToMany(() => PlaceImageEntity, (image) => image.place) images: PlaceImageEntity[]; @CreateDateColumn() @@ -35,4 +36,4 @@ export class PlaceEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 89950aa..08a6b95 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -4,4 +4,9 @@ export const BaseResponse = { code: 201, message: '날짜 그룹이 생성되었습니다.', }, + JOURNEY_CREATED: { + success: true, + code: 201, + message: '여정이 저장되었습니다.', + }, }; diff --git a/src/response/response.ts b/src/response/response.ts index 3ee415f..aacddc4 100644 --- a/src/response/response.ts +++ b/src/response/response.ts @@ -1,4 +1,4 @@ -export const response = ({ success, code, message }, data) => { +export const response = ({ success, code, message }, data = null) => { return { success: success, code: code, diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index cf483b9..688f002 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -16,21 +16,21 @@ export class ScheduleEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; + @Column({ nullable: true }) + date: string; + + @Column({ nullable: true }) + title: string; + + @Column({ nullable: true }) + participants: string; + @OneToMany( () => ScheduleDetailEntity, (scheduleDetail) => scheduleDetail.schedule, ) scheduleDetails: ScheduleDetailEntity[]; - @Column({ type: 'date' }) - date: Date; - - @Column() - title: string; - - @Column() - participants: string; - @CreateDateColumn() created: Date; @@ -39,4 +39,17 @@ export class ScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async createSchedule(dates) { + let currentDate = new Date(dates.startDate); + const lastDate = new Date(dates.endDate); + + while (currentDate <= lastDate) { + const schedule = new ScheduleEntity(); + schedule.date = currentDate.toISOString().split('T')[0]; + await schedule.save(); + currentDate = new Date(currentDate); + currentDate.setDate(currentDate.getDate() + 1); + } + } } From 2f9cc8ff9bf6ca7c8abadd0d0b0bcd2091fee415 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 1 Feb 2024 18:55:23 +0900 Subject: [PATCH 020/316] bug : merge --- src/schedule/schedule.group.entity.ts | 35 --------------------------- src/user/user.entity.ts | 14 ++++++----- 2 files changed, 8 insertions(+), 41 deletions(-) delete mode 100644 src/schedule/schedule.group.entity.ts diff --git a/src/schedule/schedule.group.entity.ts b/src/schedule/schedule.group.entity.ts deleted file mode 100644 index 57c644d..0000000 --- a/src/schedule/schedule.group.entity.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - BaseEntity, - CreateDateColumn, - DeleteDateColumn, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; -import { ScheduleEntity } from './schedule.entity'; -import { UserEntity } from '../user/user.entity'; - -@Entity() -export class ScheduleGroupEntity extends BaseEntity { - @PrimaryGeneratedColumn() - id: number; - - @OneToMany(() => ScheduleEntity, (schedule) => schedule.scheduleGroup) - schedules: ScheduleEntity[]; - - @JoinColumn() - @ManyToOne(() => UserEntity) - user: UserEntity; - - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - - @DeleteDateColumn() - deleted: Date; -} diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 8096eec..1410f6c 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -3,7 +3,7 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, ManyToOne, + Entity, OneToMany, OneToOne, PrimaryGeneratedColumn, @@ -43,19 +43,22 @@ export class UserEntity extends BaseEntity { @Column() oauthToken: string; - @OneToOne(() => UserProfileImageEntity, ((profileImage)) => profileImage.user) + @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user) profileImage: UserProfileImageEntity; - @OneToMany(() => UserFollowingEntity, ((following)) => following.user) + @OneToMany(() => UserFollowingEntity, (following) => following.user) following: UserFollowingEntity[]; - @OneToMany(() => UserFollowingEntity, ((followed)) => followed.followUser) + @OneToMany(() => UserFollowingEntity, (followed) => followed.followUser) follower: UserFollowingEntity[]; @OneToMany(() => SignatureEntity, (signature) => signature.user) signatures: SignatureEntity[]; - @OneToMany(() => SignatureLikeEntity, (signatureLike) => signatureLike.signature) + @OneToMany( + () => SignatureLikeEntity, + (signatureLike) => signatureLike.signature, + ) likes: SignatureLikeEntity[]; @CreateDateColumn() @@ -67,4 +70,3 @@ export class UserEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; } - From f382e81902a2a03102faf7c23eaf268ea1548639 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 1 Feb 2024 20:09:11 +0900 Subject: [PATCH 021/316] =?UTF-8?q?fix=20:=20schedule,=20location=20?= =?UTF-8?q?=EC=97=94=ED=84=B0=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/location/location.entity.ts | 12 +++++++----- src/schedule/schedule.entity.ts | 7 +++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 57fdcc2..35370d0 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -1,3 +1,4 @@ +import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { BaseEntity, Column, @@ -6,6 +7,7 @@ import { Entity, PrimaryGeneratedColumn, UpdateDateColumn, + OneToOne, } from 'typeorm'; @Entity() @@ -16,14 +18,14 @@ export class LocationEntity extends BaseEntity { @Column() name: string; - @Column() - address: string; - @Column({ type: 'decimal', precision: 10, scale: 6 }) - latitude: string; + latitude: number; @Column({ type: 'decimal', precision: 10, scale: 6 }) - longitude: string; + longitude: number; + + @OneToOne(() => ScheduleEntity, (schedule) => schedule.location) + schedule: ScheduleEntity; @CreateDateColumn() created: Date; diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 688f002..88f0825 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -4,12 +4,15 @@ import { CreateDateColumn, DeleteDateColumn, Entity, + JoinColumn, + OneToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { ScheduleDetailEntity } from './schedule.detail.entity'; +import { LocationEntity } from 'src/location/location.entity'; @Entity() export class ScheduleEntity extends BaseEntity { @@ -31,6 +34,10 @@ export class ScheduleEntity extends BaseEntity { ) scheduleDetails: ScheduleDetailEntity[]; + @OneToOne(() => LocationEntity, { eager: true }) // eager 옵션을 사용하여 즉시 로드 + @JoinColumn({ name: 'location_id' }) // 외래 키에 대한 컬럼명 설정 + location: LocationEntity; + @CreateDateColumn() created: Date; From 157504516a010def6c58f590caeb1404076ebefe Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 1 Feb 2024 21:02:14 +0900 Subject: [PATCH 022/316] =?UTF-8?q?feat=20:=20CreateScheduleDto=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schedule/dtos/create-schedule-dto.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/schedule/dtos/create-schedule-dto.ts diff --git a/src/schedule/dtos/create-schedule-dto.ts b/src/schedule/dtos/create-schedule-dto.ts new file mode 100644 index 0000000..662d5fb --- /dev/null +++ b/src/schedule/dtos/create-schedule-dto.ts @@ -0,0 +1,16 @@ +// create-schedule.dto.ts +import { IsString, IsNumber } from 'class-validator'; + +export class CreateScheduleDto { + @IsString() + title: string; + + @IsString() + participant: string; + + @IsNumber() + latitude: number; + + @IsNumber() + longitude: number; +} From e057d8d26bd5fc752654e579889e1234c2f78e75 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 1 Feb 2024 23:43:39 +0900 Subject: [PATCH 023/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/date-group/date-group.entity.ts | 2 -- src/diary/diary.entity.ts | 4 --- src/journey/journey.controller.ts | 2 -- src/location/location.entity.ts | 15 +++++++-- src/response/response.status.ts | 12 +++++++ ...schedule-dto.ts => update-schedule-dto.ts} | 7 ++-- src/schedule/schedule.controller.ts | 25 ++++++++++++-- src/schedule/schedule.entity.ts | 10 ++++-- src/schedule/schedule.service.ts | 33 +++++++++++++++++-- 9 files changed, 87 insertions(+), 23 deletions(-) rename src/schedule/dtos/{create-schedule-dto.ts => update-schedule-dto.ts} (62%) diff --git a/src/date-group/date-group.entity.ts b/src/date-group/date-group.entity.ts index 9be11f5..77d1199 100644 --- a/src/date-group/date-group.entity.ts +++ b/src/date-group/date-group.entity.ts @@ -6,8 +6,6 @@ import { OneToMany, BaseEntity, } from 'typeorm'; -import { errResponse } from 'src/response/response'; -import { BaseResponse } from 'src/response/response.status'; import { JourneyEntity } from '../journey/model/journey.entity'; import { CreateDateGroupDto } from './dtos/create-date-group.dto'; diff --git a/src/diary/diary.entity.ts b/src/diary/diary.entity.ts index e91c65b..a8d510c 100644 --- a/src/diary/diary.entity.ts +++ b/src/diary/diary.entity.ts @@ -29,10 +29,6 @@ export class DiaryEntity extends BaseEntity { @Column() place: string; - @JoinColumn() - @ManyToOne(() => LocationEntity) - location: LocationEntity; - @Column({ type: 'enum', enum: ['CLOUDY', 'RAINY', 'SNOWY', 'PARTLY_CLOUDY', 'SUNNY'], diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 44f15a5..71ebf51 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -2,8 +2,6 @@ import { Controller, Post, Body } from '@nestjs/common'; import { JourneyService } from './journey.service'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { CreateJourneyDto } from './dtos/create-journey.dto'; -import { response } from 'src/response/response'; -import { BaseResponse } from 'src/response/response.status'; @Controller('api/journey') export class JourneyController { diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 35370d0..bbd9437 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -15,9 +15,6 @@ export class LocationEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column() - name: string; - @Column({ type: 'decimal', precision: 10, scale: 6 }) latitude: number; @@ -35,4 +32,16 @@ export class LocationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async createLocation(latitude, longitude) { + try { + const location: LocationEntity = new LocationEntity(); + location.latitude = latitude; + location.longitude = longitude; + + return await location.save(); + } catch (error) { + throw new Error(error); + } + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 08a6b95..718db81 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -9,4 +9,16 @@ export const BaseResponse = { code: 201, message: '여정이 저장되었습니다.', }, + SCHEDULE_UPDATED: { + success: true, + code: 201, + message: '일정을 작성했습니다.', + }, + + /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ + SCHEDULE_NOT_FOUND: { + success: false, + code: 404, + message: '스케줄이 없습니다.', + }, }; diff --git a/src/schedule/dtos/create-schedule-dto.ts b/src/schedule/dtos/update-schedule-dto.ts similarity index 62% rename from src/schedule/dtos/create-schedule-dto.ts rename to src/schedule/dtos/update-schedule-dto.ts index 662d5fb..a139cbe 100644 --- a/src/schedule/dtos/create-schedule-dto.ts +++ b/src/schedule/dtos/update-schedule-dto.ts @@ -1,13 +1,10 @@ -// create-schedule.dto.ts +// Update-schedule.dto.ts import { IsString, IsNumber } from 'class-validator'; -export class CreateScheduleDto { +export class UpdateScheduleDto { @IsString() title: string; - @IsString() - participant: string; - @IsNumber() latitude: number; diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index 8eb1a3f..d009e21 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -1,7 +1,28 @@ -import { Controller } from '@nestjs/common'; +import { Body, Controller, Put, Param } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { ScheduleService } from './schedule.service'; +import { UpdateScheduleDto } from './dtos/update-schedule-dto'; -@Controller('schedule') +@Controller('api/schedule') export class ScheduleController { constructor(private readonly scheduleService: ScheduleService) {} + + @ApiOperation({ + summary: '여정 작성하기', + description: '제목, 위치를 작성합니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Put('update/:scheduleId') + async updateSchedule( + @Param('scheduleId') scheduleId: number, + @Body() updateScheduleDto: UpdateScheduleDto, + ) { + const result = await this.scheduleService.updateSchedule( + scheduleId, + updateScheduleDto, + ); + return result; + } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 88f0825..1433406 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -25,9 +25,6 @@ export class ScheduleEntity extends BaseEntity { @Column({ nullable: true }) title: string; - @Column({ nullable: true }) - participants: string; - @OneToMany( () => ScheduleDetailEntity, (scheduleDetail) => scheduleDetail.schedule, @@ -59,4 +56,11 @@ export class ScheduleEntity extends BaseEntity { currentDate.setDate(currentDate.getDate() + 1); } } + + static async updateSchedule(schedule, updateScheduleDto, location) { + schedule.title = updateScheduleDto.title; + schedule.location = location.id; + + return await schedule.save(); + } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 2e22997..d88ad5c 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -1,4 +1,33 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; +import { errResponse, response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; +import { UpdateScheduleDto } from './dtos/update-schedule-dto'; +import { LocationEntity } from 'src/location/location.entity'; +import { ScheduleEntity } from './schedule.entity'; @Injectable() -export class ScheduleService {} +export class ScheduleService { + async updateSchedule( + scheduleId: number, + updateScheduleDto: UpdateScheduleDto, + ) { + const location = await LocationEntity.createLocation( + updateScheduleDto.latitude, + updateScheduleDto.longitude, + ); + const schedule = await this.findExistSchedule(scheduleId); + await ScheduleEntity.updateSchedule(schedule, updateScheduleDto, location); + + return response(BaseResponse.SCHEDULE_UPDATED); + } + + async findExistSchedule(scheduleId) { + const schedule = await ScheduleEntity.findOne({ + where: { id: scheduleId }, + }); + if (!schedule) { + throw new NotFoundException(BaseResponse.SCHEDULE_NOT_FOUND); + } + return schedule; + } +} From 1b5453b56fa98aa2a7df605a30c24ab74658e8c3 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 00:35:37 +0900 Subject: [PATCH 024/316] =?UTF-8?q?fix=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0=20-=20=EB=82=A0=EC=A7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schedule/dtos/update-schedule-dto.ts | 8 +------- src/schedule/schedule.controller.ts | 12 ++++++------ src/schedule/schedule.entity.ts | 7 +++---- src/schedule/schedule.service.ts | 16 ++++------------ 4 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/schedule/dtos/update-schedule-dto.ts b/src/schedule/dtos/update-schedule-dto.ts index a139cbe..1b69873 100644 --- a/src/schedule/dtos/update-schedule-dto.ts +++ b/src/schedule/dtos/update-schedule-dto.ts @@ -1,13 +1,7 @@ // Update-schedule.dto.ts import { IsString, IsNumber } from 'class-validator'; -export class UpdateScheduleDto { +export class UpdateScheduleTitleDto { @IsString() title: string; - - @IsNumber() - latitude: number; - - @IsNumber() - longitude: number; } diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index d009e21..cef5260 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Put, Param } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { ScheduleService } from './schedule.service'; -import { UpdateScheduleDto } from './dtos/update-schedule-dto'; +import { UpdateScheduleTitleDto } from './dtos/update-schedule-dto'; @Controller('api/schedule') export class ScheduleController { @@ -9,19 +9,19 @@ export class ScheduleController { @ApiOperation({ summary: '여정 작성하기', - description: '제목, 위치를 작성합니다.', + description: '제목을 작성합니다.', }) @ApiOkResponse({ description: '성공 ', }) - @Put('update/:scheduleId') + @Put('update-title/:scheduleId') async updateSchedule( @Param('scheduleId') scheduleId: number, - @Body() updateScheduleDto: UpdateScheduleDto, + @Body() updateScheduleTitleDto: UpdateScheduleTitleDto, ) { - const result = await this.scheduleService.updateSchedule( + const result = await this.scheduleService.updateScheduleTitle( scheduleId, - updateScheduleDto, + updateScheduleTitleDto, ); return result; } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 1433406..129a322 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -57,10 +57,9 @@ export class ScheduleEntity extends BaseEntity { } } - static async updateSchedule(schedule, updateScheduleDto, location) { - schedule.title = updateScheduleDto.title; - schedule.location = location.id; - + static async updateSchedule(schedule, title) { + schedule.title = title; + console.log(title); return await schedule.save(); } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index d88ad5c..3e02252 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -1,22 +1,14 @@ import { Injectable, NotFoundException } from '@nestjs/common'; -import { errResponse, response } from 'src/response/response'; +import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; -import { UpdateScheduleDto } from './dtos/update-schedule-dto'; -import { LocationEntity } from 'src/location/location.entity'; + import { ScheduleEntity } from './schedule.entity'; @Injectable() export class ScheduleService { - async updateSchedule( - scheduleId: number, - updateScheduleDto: UpdateScheduleDto, - ) { - const location = await LocationEntity.createLocation( - updateScheduleDto.latitude, - updateScheduleDto.longitude, - ); + async updateScheduleTitle(scheduleId, scheduleTitle) { const schedule = await this.findExistSchedule(scheduleId); - await ScheduleEntity.updateSchedule(schedule, updateScheduleDto, location); + await ScheduleEntity.updateSchedule(schedule, scheduleTitle.title); return response(BaseResponse.SCHEDULE_UPDATED); } From 08d5305a4449794c4a9fb9537ebe69d90f46b3fe Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 01:06:49 +0900 Subject: [PATCH 025/316] =?UTF-8?q?fix=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0=20-=20=EC=9C=84=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/location/dtos/create-location.dto.ts | 9 +++++++++ src/location/location.entity.ts | 6 +++--- src/schedule/dtos/update-schedule-dto.ts | 2 +- src/schedule/schedule.controller.ts | 22 +++++++++++++++++++++- src/schedule/schedule.entity.ts | 8 ++++++-- src/schedule/schedule.service.ts | 11 +++++++++-- 6 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 src/location/dtos/create-location.dto.ts diff --git a/src/location/dtos/create-location.dto.ts b/src/location/dtos/create-location.dto.ts new file mode 100644 index 0000000..1530830 --- /dev/null +++ b/src/location/dtos/create-location.dto.ts @@ -0,0 +1,9 @@ +import { IsNumber } from 'class-validator'; + +export class CreateLocationDto { + @IsNumber() + latitude: number; + + @IsNumber() + longitude: number; +} diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index bbd9437..4e76118 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -33,11 +33,11 @@ export class LocationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createLocation(latitude, longitude) { + static async createLocation(scheduleLocation) { try { const location: LocationEntity = new LocationEntity(); - location.latitude = latitude; - location.longitude = longitude; + location.latitude = scheduleLocation.latitude; + location.longitude = scheduleLocation.longitude; return await location.save(); } catch (error) { diff --git a/src/schedule/dtos/update-schedule-dto.ts b/src/schedule/dtos/update-schedule-dto.ts index 1b69873..eddb290 100644 --- a/src/schedule/dtos/update-schedule-dto.ts +++ b/src/schedule/dtos/update-schedule-dto.ts @@ -1,5 +1,5 @@ // Update-schedule.dto.ts -import { IsString, IsNumber } from 'class-validator'; +import { IsString } from 'class-validator'; export class UpdateScheduleTitleDto { @IsString() diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index cef5260..152cb5f 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Put, Param } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { ScheduleService } from './schedule.service'; import { UpdateScheduleTitleDto } from './dtos/update-schedule-dto'; +import { CreateLocationDto } from 'src/location/dtos/create-location.dto'; @Controller('api/schedule') export class ScheduleController { @@ -15,7 +16,7 @@ export class ScheduleController { description: '성공 ', }) @Put('update-title/:scheduleId') - async updateSchedule( + async updateScheduleTitle( @Param('scheduleId') scheduleId: number, @Body() updateScheduleTitleDto: UpdateScheduleTitleDto, ) { @@ -25,4 +26,23 @@ export class ScheduleController { ); return result; } + + @ApiOperation({ + summary: '여정 작성하기', + description: '위치를 태그합니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Put('update-location/:scheduleId') + async updateScheduleLocation( + @Param('scheduleId') scheduleId: number, + @Body() createLocationDto: CreateLocationDto, + ) { + const result = await this.scheduleService.updateScheduleLocation( + scheduleId, + createLocationDto, + ); + return result; + } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 129a322..8bbd9b8 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -57,9 +57,13 @@ export class ScheduleEntity extends BaseEntity { } } - static async updateSchedule(schedule, title) { + static async updateScheduleTitle(schedule, title) { schedule.title = title; - console.log(title); + return await schedule.save(); + } + + static async updateScheduleLocation(schedule, location) { + schedule.location = location.id; return await schedule.save(); } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 3e02252..e41dd7a 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -1,18 +1,25 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; - +import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; @Injectable() export class ScheduleService { async updateScheduleTitle(scheduleId, scheduleTitle) { const schedule = await this.findExistSchedule(scheduleId); - await ScheduleEntity.updateSchedule(schedule, scheduleTitle.title); + await ScheduleEntity.updateScheduleTitle(schedule, scheduleTitle.title); return response(BaseResponse.SCHEDULE_UPDATED); } + async updateScheduleLocation(scheduleId, scheduleLocation) { + const schedule = await this.findExistSchedule(scheduleId); + const location = await LocationEntity.createLocation(scheduleLocation); + await ScheduleEntity.updateScheduleLocation(schedule, location); + return response(BaseResponse.SCHEDULE_UPDATED); + } + async findExistSchedule(scheduleId) { const schedule = await ScheduleEntity.findOne({ where: { id: scheduleId }, From ae1d8f3885a8bf3fec093565c2f2611659027646 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 02:31:28 +0900 Subject: [PATCH 026/316] =?UTF-8?q?fix=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0=20-=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=ED=95=A0=EB=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/location/location.entity.ts | 14 ++++++++++++++ src/schedule/schedule.entity.ts | 2 +- src/schedule/schedule.service.ts | 13 +++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 4e76118..b187994 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -44,4 +44,18 @@ export class LocationEntity extends BaseEntity { throw new Error(error); } } + + static async updateLocation(schedule, scheduleLocation) { + try { + const location = await LocationEntity.findOneOrFail({ + where: { id: schedule.location_id }, + }); + location.latitude = scheduleLocation.latitude; + location.longitude = scheduleLocation.longitude; + + return await location.save(); + } catch (error) { + throw new Error(error); + } + } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 8bbd9b8..08339aa 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -63,7 +63,7 @@ export class ScheduleEntity extends BaseEntity { } static async updateScheduleLocation(schedule, location) { - schedule.location = location.id; + schedule.location_id = location.id; return await schedule.save(); } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index e41dd7a..b692dfd 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -15,8 +15,17 @@ export class ScheduleService { async updateScheduleLocation(scheduleId, scheduleLocation) { const schedule = await this.findExistSchedule(scheduleId); - const location = await LocationEntity.createLocation(scheduleLocation); - await ScheduleEntity.updateScheduleLocation(schedule, location); + if (schedule.location) { + const location = LocationEntity.updateLocation( + schedule, + scheduleLocation, + ); + await ScheduleEntity.updateScheduleLocation(schedule, location); + } else { + const location = await LocationEntity.createLocation(scheduleLocation); + await ScheduleEntity.updateScheduleLocation(schedule, location); + } + return response(BaseResponse.SCHEDULE_UPDATED); } From d15f86c3a9fb2cb4b4eb0ba5b0f1eee3072d7808 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 03:09:19 +0900 Subject: [PATCH 027/316] =?UTF-8?q?fix=20:=20=EC=84=B8=EB=B6=80=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=97=94=ED=84=B0=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 2 +- src/location/location.entity.ts | 2 +- .../detail-schedule.ts} | 16 ++++++++-------- src/schedule/{ => models}/schedule.entity.ts | 8 ++++---- src/schedule/schedule.service.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) rename src/schedule/{schedule.detail.entity.ts => models/detail-schedule.ts} (64%) rename src/schedule/{ => models}/schedule.entity.ts (88%) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 1d97cc8..d29b1fb 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -4,7 +4,7 @@ import { JourneyEntity } from './model/journey.entity'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { DateGroupEntity } from 'src/date-group/date-group.entity'; -import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { ScheduleEntity } from 'src/schedule/models/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; import { CreateDateGroupDto } from 'src/date-group/dtos/create-date-group.dto'; diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index b187994..71d6cae 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -1,4 +1,4 @@ -import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { ScheduleEntity } from 'src/schedule/models/schedule.entity'; import { BaseEntity, Column, diff --git a/src/schedule/schedule.detail.entity.ts b/src/schedule/models/detail-schedule.ts similarity index 64% rename from src/schedule/schedule.detail.entity.ts rename to src/schedule/models/detail-schedule.ts index 6874f11..6f1136c 100644 --- a/src/schedule/schedule.detail.entity.ts +++ b/src/schedule/models/detail-schedule.ts @@ -12,20 +12,20 @@ import { import { ScheduleEntity } from './schedule.entity'; @Entity() -export class ScheduleDetailEntity extends BaseEntity { +export class DetailScheduleEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @JoinColumn() - @ManyToOne(() => ScheduleEntity, (schedule) => schedule.scheduleDetails) - schedule: ScheduleEntity; - - @Column() - name: string; + @Column({ nullable: true }) + content: string; - @Column() + @Column({ default: false }) isDone: boolean; + @JoinColumn({ name: 'schedule_id' }) + @ManyToOne(() => ScheduleEntity, (schedule) => schedule.detailSchedules) + schedule: ScheduleEntity; + @CreateDateColumn() created: Date; diff --git a/src/schedule/schedule.entity.ts b/src/schedule/models/schedule.entity.ts similarity index 88% rename from src/schedule/schedule.entity.ts rename to src/schedule/models/schedule.entity.ts index 08339aa..7935958 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/models/schedule.entity.ts @@ -11,7 +11,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { ScheduleDetailEntity } from './schedule.detail.entity'; +import { DetailScheduleEntity } from './detail-schedule'; import { LocationEntity } from 'src/location/location.entity'; @Entity() @@ -26,10 +26,10 @@ export class ScheduleEntity extends BaseEntity { title: string; @OneToMany( - () => ScheduleDetailEntity, - (scheduleDetail) => scheduleDetail.schedule, + () => DetailScheduleEntity, + (detailSchedule) => DetailSchedule.schedule, ) - scheduleDetails: ScheduleDetailEntity[]; + detailSchedules: DetailScheduleEntity[]; @OneToOne(() => LocationEntity, { eager: true }) // eager 옵션을 사용하여 즉시 로드 @JoinColumn({ name: 'location_id' }) // 외래 키에 대한 컬럼명 설정 diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index b692dfd..8dbb847 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -2,7 +2,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { LocationEntity } from 'src/location/location.entity'; -import { ScheduleEntity } from './schedule.entity'; +import { ScheduleEntity } from './models/schedule.entity'; @Injectable() export class ScheduleService { From 3740b8df22e00b2015d36732ee14315484f55f8b Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 03:18:41 +0900 Subject: [PATCH 028/316] =?UTF-8?q?docs=20:=20=EC=84=B8=EB=B6=80=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=ED=8F=B4=EB=8D=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/detail-schedule/detail-schedule.controller.ts | 7 +++++++ .../detail-schedule.entity.ts} | 2 +- src/detail-schedule/detail-schedule.module.ts | 8 ++++++++ src/detail-schedule/detail-schedule.service.ts | 4 ++++ src/journey/journey.service.ts | 2 +- src/location/location.entity.ts | 2 +- src/schedule/{models => }/schedule.entity.ts | 4 ++-- src/schedule/schedule.service.ts | 2 +- 8 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/detail-schedule/detail-schedule.controller.ts rename src/{schedule/models/detail-schedule.ts => detail-schedule/detail-schedule.entity.ts} (91%) create mode 100644 src/detail-schedule/detail-schedule.module.ts create mode 100644 src/detail-schedule/detail-schedule.service.ts rename src/schedule/{models => }/schedule.entity.ts (92%) diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts new file mode 100644 index 0000000..7f82a96 --- /dev/null +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -0,0 +1,7 @@ +import { Controller } from '@nestjs/common'; +import { DetailScheduleService } from './detail-schedule.service'; + +@Controller('api/detail-schedule') +export class DetailScheduleController { + constructor(private readonly detailScheduleService: DetailScheduleService) {} +} diff --git a/src/schedule/models/detail-schedule.ts b/src/detail-schedule/detail-schedule.entity.ts similarity index 91% rename from src/schedule/models/detail-schedule.ts rename to src/detail-schedule/detail-schedule.entity.ts index 6f1136c..8a0d372 100644 --- a/src/schedule/models/detail-schedule.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -9,7 +9,7 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import { ScheduleEntity } from './schedule.entity'; +import { ScheduleEntity } from '../schedule/schedule.entity'; @Entity() export class DetailScheduleEntity extends BaseEntity { diff --git a/src/detail-schedule/detail-schedule.module.ts b/src/detail-schedule/detail-schedule.module.ts new file mode 100644 index 0000000..ff7ccb9 --- /dev/null +++ b/src/detail-schedule/detail-schedule.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { DetailScheduleService } from './detail-schedule.service'; +import { DetailScheduleController } from './detail-schedule.controller'; +@Module({ + controllers: [DetailScheduleController], + providers: [DetailScheduleService], +}) +export class LocationModule {} diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts new file mode 100644 index 0000000..da519f8 --- /dev/null +++ b/src/detail-schedule/detail-schedule.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class DetailScheduleService {} diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index d29b1fb..1d97cc8 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -4,7 +4,7 @@ import { JourneyEntity } from './model/journey.entity'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { DateGroupEntity } from 'src/date-group/date-group.entity'; -import { ScheduleEntity } from 'src/schedule/models/schedule.entity'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; import { CreateDateGroupDto } from 'src/date-group/dtos/create-date-group.dto'; diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 71d6cae..b187994 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -1,4 +1,4 @@ -import { ScheduleEntity } from 'src/schedule/models/schedule.entity'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { BaseEntity, Column, diff --git a/src/schedule/models/schedule.entity.ts b/src/schedule/schedule.entity.ts similarity index 92% rename from src/schedule/models/schedule.entity.ts rename to src/schedule/schedule.entity.ts index 7935958..ba1e4e0 100644 --- a/src/schedule/models/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -11,7 +11,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { DetailScheduleEntity } from './detail-schedule'; +import { DetailScheduleEntity } from '../detail-schedule/detail-schedule'; import { LocationEntity } from 'src/location/location.entity'; @Entity() @@ -27,7 +27,7 @@ export class ScheduleEntity extends BaseEntity { @OneToMany( () => DetailScheduleEntity, - (detailSchedule) => DetailSchedule.schedule, + (detailSchedule) => detailSchedule.schedule, ) detailSchedules: DetailScheduleEntity[]; diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 8dbb847..b692dfd 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -2,7 +2,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { LocationEntity } from 'src/location/location.entity'; -import { ScheduleEntity } from './models/schedule.entity'; +import { ScheduleEntity } from './schedule.entity'; @Injectable() export class ScheduleService { From 92911be34d0bff031180395ecb8c863ae0ba766d Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 04:43:17 +0900 Subject: [PATCH 029/316] =?UTF-8?q?feat=20:=20=EC=84=B8=EB=B6=80=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=B6=94=EA=B0=80=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 ++ .../detail-schedule-info.dto.ts | 17 +++++++++++++++++ .../detail-schedule.controller.ts | 19 ++++++++++++++++++- src/detail-schedule/detail-schedule.entity.ts | 7 +++++++ src/detail-schedule/detail-schedule.module.ts | 3 ++- .../detail-schedule.service.ts | 17 ++++++++++++++++- src/response/response.status.ts | 5 +++++ src/schedule/schedule.entity.ts | 15 +++++++++++++-- src/schedule/schedule.service.ts | 14 ++------------ 9 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 src/detail-schedule/detail-schedule-info.dto.ts diff --git a/src/app.module.ts b/src/app.module.ts index bee435b..6d7bed3 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { PlaceModule } from './place/place.module'; import { JourneyModule } from './journey/journey.module'; import { DateGroupModule } from './date-group/date-group.module'; import { SignatureModule } from './signature/signature.module'; +import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; @Module({ imports: [ @@ -23,6 +24,7 @@ import { SignatureModule } from './signature/signature.module'; DiaryModule, LocationModule, ScheduleModule, + DetailScheduleModule, PlaceModule, JourneyModule, DateGroupModule, diff --git a/src/detail-schedule/detail-schedule-info.dto.ts b/src/detail-schedule/detail-schedule-info.dto.ts new file mode 100644 index 0000000..b6b33c5 --- /dev/null +++ b/src/detail-schedule/detail-schedule-info.dto.ts @@ -0,0 +1,17 @@ +import { IsOptional, IsNumber, IsString, IsBoolean } from 'class-validator'; + +export class DetailScheduleInfoDto { + @IsNumber() + id: number; + + @IsOptional() + @IsString() + content: string; + + @IsOptional() + @IsBoolean() + isDone: boolean; + + @IsNumber() + schedule_id: number; +} diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index 7f82a96..a348b5b 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -1,7 +1,24 @@ -import { Controller } from '@nestjs/common'; +import { Controller, Post, Param, Body } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { DetailScheduleService } from './detail-schedule.service'; @Controller('api/detail-schedule') export class DetailScheduleController { constructor(private readonly detailScheduleService: DetailScheduleService) {} + + //세부일정 추가하기 + @ApiOperation({ + summary: '세부 일정 추가하기', + description: '일정 배너에서 세부 일정을 추가할 수 있습니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Post('create/:scheduleId') + async createDetailSchedule(@Param('scheduleId') scheduleId: number) { + const result = await this.detailScheduleService.createDetailSchedule( + scheduleId, + ); + return result; + } } diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index 8a0d372..0a2f08d 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -10,6 +10,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { ScheduleEntity } from '../schedule/schedule.entity'; +import { DetailScheduleInfoDto } from './detail-schedule-info.dto'; @Entity() export class DetailScheduleEntity extends BaseEntity { @@ -34,4 +35,10 @@ export class DetailScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async createDetailSchedule(scheduleId) { + const detailSchedule = new DetailScheduleEntity(); + detailSchedule.schedule = scheduleId; + return await detailSchedule.save(); + } } diff --git a/src/detail-schedule/detail-schedule.module.ts b/src/detail-schedule/detail-schedule.module.ts index ff7ccb9..c0b643e 100644 --- a/src/detail-schedule/detail-schedule.module.ts +++ b/src/detail-schedule/detail-schedule.module.ts @@ -1,8 +1,9 @@ import { Module } from '@nestjs/common'; import { DetailScheduleService } from './detail-schedule.service'; import { DetailScheduleController } from './detail-schedule.controller'; + @Module({ controllers: [DetailScheduleController], providers: [DetailScheduleService], }) -export class LocationModule {} +export class DetailScheduleModule {} diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index da519f8..fe3d788 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -1,4 +1,19 @@ import { Injectable } from '@nestjs/common'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { DetailScheduleEntity } from './detail-schedule.entity'; +import { response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; @Injectable() -export class DetailScheduleService {} +export class DetailScheduleService { + //세부일정 추가하기 + async createDetailSchedule(scheduleId: number) { + const schedule = await ScheduleEntity.findExistSchedule(scheduleId); + console.log(schedule.id); + const detailSchedule = await DetailScheduleEntity.createDetailSchedule( + schedule.id, + ); + console.log(detailSchedule); + return response(BaseResponse.DETAIL_SCHEDULE_CREATED); + } +} diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 718db81..094e49e 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -14,6 +14,11 @@ export const BaseResponse = { code: 201, message: '일정을 작성했습니다.', }, + DETAIL_SCHEDULE_CREATED: { + success: true, + code: 201, + message: '세부 일정을 추가했습니다.', + }, /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ SCHEDULE_NOT_FOUND: { diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index ba1e4e0..905092d 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -10,8 +10,9 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; - -import { DetailScheduleEntity } from '../detail-schedule/detail-schedule'; +import { NotFoundException } from '@nestjs/common'; +import { BaseResponse } from 'src/response/response.status'; +import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity'; import { LocationEntity } from 'src/location/location.entity'; @Entity() @@ -66,4 +67,14 @@ export class ScheduleEntity extends BaseEntity { schedule.location_id = location.id; return await schedule.save(); } + + static async findExistSchedule(scheduleId) { + const schedule = await ScheduleEntity.findOne({ + where: { id: scheduleId }, + }); + if (!schedule) { + throw new NotFoundException(BaseResponse.SCHEDULE_NOT_FOUND); + } + return schedule; + } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index b692dfd..ea548e1 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -7,14 +7,14 @@ import { ScheduleEntity } from './schedule.entity'; @Injectable() export class ScheduleService { async updateScheduleTitle(scheduleId, scheduleTitle) { - const schedule = await this.findExistSchedule(scheduleId); + const schedule = await ScheduleEntity.findExistSchedule(scheduleId); await ScheduleEntity.updateScheduleTitle(schedule, scheduleTitle.title); return response(BaseResponse.SCHEDULE_UPDATED); } async updateScheduleLocation(scheduleId, scheduleLocation) { - const schedule = await this.findExistSchedule(scheduleId); + const schedule = await ScheduleEntity.findExistSchedule(scheduleId); if (schedule.location) { const location = LocationEntity.updateLocation( schedule, @@ -28,14 +28,4 @@ export class ScheduleService { return response(BaseResponse.SCHEDULE_UPDATED); } - - async findExistSchedule(scheduleId) { - const schedule = await ScheduleEntity.findOne({ - where: { id: scheduleId }, - }); - if (!schedule) { - throw new NotFoundException(BaseResponse.SCHEDULE_NOT_FOUND); - } - return schedule; - } } From 54a8e4108f0f7cae8ffa14e817dc89a4af30b588 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 05:17:34 +0900 Subject: [PATCH 030/316] =?UTF-8?q?feat=20:=20=EC=84=B8=EB=B6=80=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-schedule-info.dto.ts | 6 +++++ .../detail-schedule.controller.ts | 23 ++++++++++++++++++- src/detail-schedule/detail-schedule.entity.ts | 18 +++++++++++++++ .../detail-schedule.service.ts | 11 +++++++++ src/response/response.status.ts | 11 +++++++++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/detail-schedule/detail-schedule-info.dto.ts b/src/detail-schedule/detail-schedule-info.dto.ts index b6b33c5..eb2fae3 100644 --- a/src/detail-schedule/detail-schedule-info.dto.ts +++ b/src/detail-schedule/detail-schedule-info.dto.ts @@ -15,3 +15,9 @@ export class DetailScheduleInfoDto { @IsNumber() schedule_id: number; } + +export class DetailContentDto { + @IsOptional() + @IsString() + content: string; +} diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index a348b5b..e4fdff4 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -1,6 +1,7 @@ -import { Controller, Post, Param, Body } from '@nestjs/common'; +import { Controller, Post, Put, Param, Body } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { DetailScheduleService } from './detail-schedule.service'; +import { DetailContentDto } from './detail-schedule-info.dto'; @Controller('api/detail-schedule') export class DetailScheduleController { @@ -21,4 +22,24 @@ export class DetailScheduleController { ); return result; } + + //세부 일정 작성하기 + @ApiOperation({ + summary: '세부 일정 작성하기', + description: '일정 배너에 세부 일정을 작성할 수 있습니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Put('update/:detailId') + async updateDetailSchedule( + @Param('detailId') detailId: number, + @Body() detailContentDto: DetailContentDto, + ) { + const result = await this.detailScheduleService.updateDetailSchedule( + detailId, + detailContentDto.content, + ); + return result; + } } diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index 0a2f08d..415ae0f 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -9,6 +9,8 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { NotFoundException } from '@nestjs/common'; +import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from '../schedule/schedule.entity'; import { DetailScheduleInfoDto } from './detail-schedule-info.dto'; @@ -36,9 +38,25 @@ export class DetailScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + //세부 일정 추가하기 static async createDetailSchedule(scheduleId) { const detailSchedule = new DetailScheduleEntity(); detailSchedule.schedule = scheduleId; return await detailSchedule.save(); } + //세부 일정 작성하기 + static async updateDetailSchedule(detailSchedule, content) { + detailSchedule.content = content; + return await detailSchedule.save(); + } + + static async findExistDetail(detailId) { + const detail = await DetailScheduleEntity.findOne({ + where: { id: detailId }, + }); + if (!detail) { + throw new NotFoundException(BaseResponse.DETAIL_SCHEDULE_NOT_FOUND); + } + return detail; + } } diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index fe3d788..99d191a 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -16,4 +16,15 @@ export class DetailScheduleService { console.log(detailSchedule); return response(BaseResponse.DETAIL_SCHEDULE_CREATED); } + + //세부일정 작성하기 + async updateDetailSchedule(detailId, content) { + const detailSchedule = await DetailScheduleEntity.findExistDetail(detailId); + const updateContent = await DetailScheduleEntity.updateDetailSchedule( + detailSchedule, + content, + ); + console.log(updateContent); + return response(BaseResponse.DETAIL_SCHEDULE_UPDATED); + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 094e49e..2c31f43 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -14,11 +14,17 @@ export const BaseResponse = { code: 201, message: '일정을 작성했습니다.', }, + DETAIL_SCHEDULE_CREATED: { success: true, code: 201, message: '세부 일정을 추가했습니다.', }, + DETAIL_SCHEDULE_UPDATED: { + success: true, + code: 201, + message: '세부 일정을 작성했습니다.', + }, /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ SCHEDULE_NOT_FOUND: { @@ -26,4 +32,9 @@ export const BaseResponse = { code: 404, message: '스케줄이 없습니다.', }, + DETAIL_SCHEDULE_NOT_FOUND: { + success: false, + code: 404, + message: '세부 일정이 없습니다.', + }, }; From 88901f8cb439f21ffc448b640d3675f24356641d Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 05:45:57 +0900 Subject: [PATCH 031/316] =?UTF-8?q?feat=20:=20=EC=84=B8=EB=B6=80=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=82=AD=EC=A0=9C=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-schedule.controller.ts | 19 ++++++++++++++++++- src/detail-schedule/detail-schedule.entity.ts | 6 +++++- .../detail-schedule.service.ts | 8 ++++++++ src/response/response.status.ts | 8 ++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index e4fdff4..71fe58e 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Put, Param, Body } from '@nestjs/common'; +import { Controller, Post, Put, Delete, Param, Body } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { DetailScheduleService } from './detail-schedule.service'; import { DetailContentDto } from './detail-schedule-info.dto'; @@ -42,4 +42,21 @@ export class DetailScheduleController { ); return result; } + + //세부 일정 삭제하기 + /*remove로 할지, softremove로 할지 고민 */ + @ApiOperation({ + summary: '세부 일정 삭제하기', + description: '일정 배너에 세부 일정을 삭제할 수 있습니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Delete('delete/:detailId') + async deleteDetailSchedule(@Param('detailId') detailId: number) { + const result = await this.detailScheduleService.deleteDetailSchedule( + detailId, + ); + return result; + } } diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index 415ae0f..ea7439e 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -49,8 +49,12 @@ export class DetailScheduleEntity extends BaseEntity { detailSchedule.content = content; return await detailSchedule.save(); } + //세부 일정 삭제하기 + static async deleteDetailSchedule(detailSchedule) { + return await DetailScheduleEntity.softRemove(detailSchedule); + } - static async findExistDetail(detailId) { + static async findExistDetail(detailId: number) { const detail = await DetailScheduleEntity.findOne({ where: { id: detailId }, }); diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index 99d191a..fc9c6c3 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -27,4 +27,12 @@ export class DetailScheduleService { console.log(updateContent); return response(BaseResponse.DETAIL_SCHEDULE_UPDATED); } + + //세부일정 작성하기 + async deleteDetailSchedule(detailId: number) { + const detailSchedule = await DetailScheduleEntity.findExistDetail(detailId); + const deleteDetailSchedule = + await DetailScheduleEntity.deleteDetailSchedule(detailSchedule); + return response(BaseResponse.DELETE_DETAIL_SCHEDULE_SUCCESS); + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 2c31f43..10bbc6d 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -1,4 +1,12 @@ export const BaseResponse = { + /* 200 OK : 요청 성공 */ + DELETE_DETAIL_SCHEDULE_SUCCESS: { + success: true, + code: 200, + message: '세부 일정을 삭제했습니다.', + }, + + /* 201 CREATED : 요청 성공, 자원 생성 */ DATEGROUP_CREATED: { success: true, code: 201, From e04db72876ee495bdbf1b1d5d4e5f9eee524384d Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 06:18:34 +0900 Subject: [PATCH 032/316] =?UTF-8?q?feat=20:=20=EC=84=B8=EB=B6=80=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-schedule.controller.ts | 26 ++++++++++++++++++- src/detail-schedule/detail-schedule.entity.ts | 5 ++++ .../detail-schedule.service.ts | 15 ++++++++++- src/response/response.status.ts | 5 ++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index 71fe58e..ab97458 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -1,4 +1,12 @@ -import { Controller, Post, Put, Delete, Param, Body } from '@nestjs/common'; +import { + Controller, + Post, + Put, + Patch, + Delete, + Param, + Body, +} from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { DetailScheduleService } from './detail-schedule.service'; import { DetailContentDto } from './detail-schedule-info.dto'; @@ -43,6 +51,22 @@ export class DetailScheduleController { return result; } + //세부 일정 상태 업데이트 + @ApiOperation({ + summary: '세부 일정 상태 업데이트', + description: 'true면 false로, false면 true로', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Patch('update-status/:detailId') + async updateDetailStatus(@Param('detailId') detailId: number) { + const result = await this.detailScheduleService.updateDetailStatus( + detailId, + ); + return result; + } + //세부 일정 삭제하기 /*remove로 할지, softremove로 할지 고민 */ @ApiOperation({ diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index ea7439e..f066383 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -49,6 +49,11 @@ export class DetailScheduleEntity extends BaseEntity { detailSchedule.content = content; return await detailSchedule.save(); } + //세부 일정 상태 업데이트하기 + static async updateDetailStatus(detailSchedule) { + detailSchedule.isDone = !detailSchedule.isDone; + return await detailSchedule.save(); + } //세부 일정 삭제하기 static async deleteDetailSchedule(detailSchedule) { return await DetailScheduleEntity.softRemove(detailSchedule); diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index fc9c6c3..496997a 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -28,7 +28,20 @@ export class DetailScheduleService { return response(BaseResponse.DETAIL_SCHEDULE_UPDATED); } - //세부일정 작성하기 + //세부일정 상태 업데이트하기 + async updateDetailStatus(detailId) { + const detailSchedule = await DetailScheduleEntity.findExistDetail(detailId); + const updateStatus = await DetailScheduleEntity.updateDetailStatus( + detailSchedule, + ); + console.log(updateStatus); + return response( + BaseResponse.UPDATE_DETAIL_SCHEDULE_STATUS_SUCCESS, + updateStatus.isDone, + ); + } + + //세부일정 삭제하기 async deleteDetailSchedule(detailId: number) { const detailSchedule = await DetailScheduleEntity.findExistDetail(detailId); const deleteDetailSchedule = diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 10bbc6d..7b54cf4 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -5,6 +5,11 @@ export const BaseResponse = { code: 200, message: '세부 일정을 삭제했습니다.', }, + UPDATE_DETAIL_SCHEDULE_STATUS_SUCCESS: { + success: true, + code: 200, + message: '세부 일정 상태를 변경했습니다', + }, /* 201 CREATED : 요청 성공, 자원 생성 */ DATEGROUP_CREATED: { From d608d1dbfb35344adc4ea0217c729749c8e26a0d Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 11:25:39 +0900 Subject: [PATCH 033/316] =?UTF-8?q?chore=20:=20diary=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=ED=98=95=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 25 ++++++++++++++++-- src/diary/diary.service.ts | 4 ++- src/diary/dtos/diary-info-dto.ts | 12 +++++++++ src/diary/{ => models}/diary.entity.ts | 13 ++++++---- src/diary/{ => models}/diary.image.entity.ts | 12 ++++----- src/journey/dtos/create-journey.dto.ts | 2 +- src/journey/model/journey.entity.ts | 8 +++--- src/location/location.entity.ts | 2 +- src/schedule/schedule.entity.ts | 4 +-- src/signature/domain/signature.page.entity.ts | 26 +++++++++---------- 10 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 src/diary/dtos/diary-info-dto.ts rename src/diary/{ => models}/diary.entity.ts (78%) rename src/diary/{ => models}/diary.image.entity.ts (79%) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 3b42be4..212cd17 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -1,7 +1,28 @@ -import { Controller } from '@nestjs/common'; +import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; +import { Controller, Post, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; +import { CreateDiaryInfoDto } from './dtos/diary-info-dto'; -@Controller('diary') +@Controller('api/diary') export class DiaryController { constructor(private readonly diaryService: DiaryService) {} + + @ApiOperation({ + summary: '일지 작성하기', + description: '일지를 작성하고 저장한 상태', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Post('create/:scheduleId') + async createJourney( + @Param('scheduleId') scheduleId: number, + @Body() createDiaryInfoDto: CreateDiaryInfoDto, + ) { + const result = await this.diaryService.createDiary( + scheduleId, + createDiaryInfoDto, + ); + return result; + } } diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index bd66641..f8d21c1 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -1,4 +1,6 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class DiaryService {} +export class DiaryService { + async createDiary(scheduleId, createDiaryInfoDto) {} +} diff --git a/src/diary/dtos/diary-info-dto.ts b/src/diary/dtos/diary-info-dto.ts new file mode 100644 index 0000000..63420cf --- /dev/null +++ b/src/diary/dtos/diary-info-dto.ts @@ -0,0 +1,12 @@ +import { IsString, IsDateString, IsEnum } from 'class-validator'; +import { PickType } from '@nestjs/swagger'; +import { DiaryEntity } from '../models/diary.entity'; + +export class CreateDiaryInfoDto extends PickType(DiaryEntity, [ + 'title', + 'place', + 'weather', + 'mood', + 'content', + 'image', +]) {} diff --git a/src/diary/diary.entity.ts b/src/diary/models/diary.entity.ts similarity index 78% rename from src/diary/diary.entity.ts rename to src/diary/models/diary.entity.ts index a8d510c..d91d64f 100644 --- a/src/diary/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -6,13 +6,13 @@ import { Entity, JoinColumn, ManyToOne, - OneToMany, + OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import { UserEntity } from '../user/user.entity'; +import { UserEntity } from '../../user/user.entity'; import { DiaryImageEntity } from './diary.image.entity'; -import { LocationEntity } from '../location/location.entity'; +import { LocationEntity } from '../../location/location.entity'; @Entity() export class DiaryEntity extends BaseEntity { @@ -44,8 +44,11 @@ export class DiaryEntity extends BaseEntity { @Column({ type: 'mediumtext' }) content: string; - @OneToMany(() => DiaryImageEntity, (image) => image.diary) - images: DiaryImageEntity[]; + @OneToOne(() => DiaryImageEntity, (image) => image.diary, { + nullable: true, + cascade: true, + }) + image: DiaryImageEntity; @CreateDateColumn() created: Date; diff --git a/src/diary/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts similarity index 79% rename from src/diary/diary.image.entity.ts rename to src/diary/models/diary.image.entity.ts index a18772d..ac93130 100644 --- a/src/diary/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -5,7 +5,7 @@ import { DeleteDateColumn, Entity, JoinColumn, - ManyToOne, + OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -16,12 +16,12 @@ export class DiaryImageEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @JoinColumn() - @ManyToOne(() => DiaryEntity, (diary) => diary.images) - diary: DiaryEntity; - @Column() - imageKey: string; + imageUrl: string; + + @JoinColumn({ name: 'diaryId' }) + @OneToOne(() => DiaryEntity, (diary) => diary.image) + diary: DiaryEntity; @CreateDateColumn() created: Date; diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts index dbbec90..5cd6b20 100644 --- a/src/journey/dtos/create-journey.dto.ts +++ b/src/journey/dtos/create-journey.dto.ts @@ -3,7 +3,7 @@ import { IsString, IsDateString } from 'class-validator'; export class CreateJourneyDto { @IsString() - journey_title: string; + title: string; @IsDateString() startDate: string; diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index cc96235..a79984a 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -18,23 +18,23 @@ export class JourneyEntity extends BaseEntity { id: number; @Column() - journey_title: string; + title: string; @ManyToOne(() => DateGroupEntity, (dateGroup) => dateGroup.journeys) - @JoinColumn({ name: 'date_group_id' }) + @JoinColumn({ name: 'dateGroupId' }) dateGroup: DateGroupEntity; @ManyToOne( () => MonthlyJourneyEntity, (monthlyJourney) => monthlyJourney.journeys, ) - @JoinColumn({ name: 'monthly_id' }) + @JoinColumn({ name: 'monthlyId' }) monthlyJourney: MonthlyJourneyEntity; static async createJourney(createJourneyDto: CreateJourneyDto, dateGroupId) { try { const journey: JourneyEntity = new JourneyEntity(); - journey.journey_title = createJourneyDto.journey_title; + journey.title = createJourneyDto.title; journey.dateGroup = dateGroupId; return await journey.save(); diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index b187994..986b08f 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -48,7 +48,7 @@ export class LocationEntity extends BaseEntity { static async updateLocation(schedule, scheduleLocation) { try { const location = await LocationEntity.findOneOrFail({ - where: { id: schedule.location_id }, + where: { id: schedule.locationId }, }); location.latitude = scheduleLocation.latitude; location.longitude = scheduleLocation.longitude; diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 905092d..549539d 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -33,7 +33,7 @@ export class ScheduleEntity extends BaseEntity { detailSchedules: DetailScheduleEntity[]; @OneToOne(() => LocationEntity, { eager: true }) // eager 옵션을 사용하여 즉시 로드 - @JoinColumn({ name: 'location_id' }) // 외래 키에 대한 컬럼명 설정 + @JoinColumn({ name: 'locationId' }) // 외래 키에 대한 컬럼명 설정 location: LocationEntity; @CreateDateColumn() @@ -64,7 +64,7 @@ export class ScheduleEntity extends BaseEntity { } static async updateScheduleLocation(schedule, location) { - schedule.location_id = location.id; + schedule.locationId = location.id; return await schedule.save(); } diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 3686ac5..0e9be30 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -5,12 +5,14 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { SignatureEntity } from './signature.entity'; -import {PageSignatureDto} from '../dto/page-signature.dto'; +import { PageSignatureDto } from '../dto/page-signature.dto'; @Entity() export class SignaturePageEntity extends BaseEntity { @@ -29,9 +31,8 @@ export class SignaturePageEntity extends BaseEntity { @Column() image: string; - @ManyToOne(() => SignatureEntity, - (signature) => signature.signaturePages) - @JoinColumn({name: 'signature_id'}) + @ManyToOne(() => SignatureEntity, (signature) => signature.signaturePages) + @JoinColumn({ name: 'signature_id' }) signature: SignatureEntity; @CreateDateColumn() @@ -44,10 +45,10 @@ export class SignaturePageEntity extends BaseEntity { deleted: Date; static async saveSignaturePages( - pageSignatureDto:PageSignatureDto, - signature:SignatureEntity):Promise { - - const signaturePage:SignaturePageEntity = new SignaturePageEntity(); + pageSignatureDto: PageSignatureDto, + signature: SignatureEntity, + ): Promise { + const signaturePage: SignaturePageEntity = new SignaturePageEntity(); signaturePage.signature = signature; signaturePage.content = pageSignatureDto.content; @@ -56,16 +57,15 @@ export class SignaturePageEntity extends BaseEntity { signaturePage.pageNum = pageSignatureDto.page; return await signaturePage.save(); - } static async findThumbnail(id: number) { // 각 시그니처의 첫 번째 페이지의 이미지 가져오기 - const firstPage=await SignaturePageEntity.findOne({ + const firstPage = await SignaturePageEntity.findOne({ where: { signature: { id: id }, - pageNum: 1 - } + pageNum: 1, + }, }); return firstPage.image; } From c3b64d553770e14877439333fd62622324c717a1 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 2 Feb 2024 12:38:18 +0900 Subject: [PATCH 034/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 6 +++--- src/diary/diary.service.ts | 12 +++++++++++- ...{diary-info-dto.ts => create-diary.dto.ts} | 2 +- src/diary/models/diary.entity.ts | 19 ++++++++++++++++++- src/response/response.status.ts | 15 +++++++++++++++ src/schedule/schedule.entity.ts | 6 +++++- 6 files changed, 53 insertions(+), 7 deletions(-) rename src/diary/dtos/{diary-info-dto.ts => create-diary.dto.ts} (78%) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 212cd17..cd6103f 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -1,7 +1,7 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; import { Controller, Post, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; -import { CreateDiaryInfoDto } from './dtos/diary-info-dto'; +import { CreateDiaryDto } from './dtos/create-diary.dto'; @Controller('api/diary') export class DiaryController { @@ -17,11 +17,11 @@ export class DiaryController { @Post('create/:scheduleId') async createJourney( @Param('scheduleId') scheduleId: number, - @Body() createDiaryInfoDto: CreateDiaryInfoDto, + @Body() createDiaryDto: CreateDiaryDto, ) { const result = await this.diaryService.createDiary( scheduleId, - createDiaryInfoDto, + createDiaryDto, ); return result; } diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index f8d21c1..afb3b90 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -1,6 +1,16 @@ import { Injectable } from '@nestjs/common'; +import { response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { DiaryEntity } from './models/diary.entity'; +import { CreateDiaryDto } from './dtos/create-diary.dto'; @Injectable() export class DiaryService { - async createDiary(scheduleId, createDiaryInfoDto) {} + async createDiary(scheduleId, diaryInfo: CreateDiaryDto) { + const schedule = await ScheduleEntity.findExistSchedule(scheduleId); + const diary = await DiaryEntity.createDiary(schedule, diaryInfo); + console.log(diary); + return response(BaseResponse.DIARY_CREATED); + } } diff --git a/src/diary/dtos/diary-info-dto.ts b/src/diary/dtos/create-diary.dto.ts similarity index 78% rename from src/diary/dtos/diary-info-dto.ts rename to src/diary/dtos/create-diary.dto.ts index 63420cf..b0ecf70 100644 --- a/src/diary/dtos/diary-info-dto.ts +++ b/src/diary/dtos/create-diary.dto.ts @@ -2,7 +2,7 @@ import { IsString, IsDateString, IsEnum } from 'class-validator'; import { PickType } from '@nestjs/swagger'; import { DiaryEntity } from '../models/diary.entity'; -export class CreateDiaryInfoDto extends PickType(DiaryEntity, [ +export class CreateDiaryDto extends PickType(DiaryEntity, [ 'title', 'place', 'weather', diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index d91d64f..db79048 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -12,7 +12,8 @@ import { } from 'typeorm'; import { UserEntity } from '../../user/user.entity'; import { DiaryImageEntity } from './diary.image.entity'; -import { LocationEntity } from '../../location/location.entity'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { CreateDiaryDto } from '../dtos/create-diary.dto'; @Entity() export class DiaryEntity extends BaseEntity { @@ -50,6 +51,10 @@ export class DiaryEntity extends BaseEntity { }) image: DiaryImageEntity; + @OneToOne(() => ScheduleEntity, (schedule) => schedule.diary) + @JoinColumn({ name: 'scheduleId' }) + schedule: ScheduleEntity; + @CreateDateColumn() created: Date; @@ -58,4 +63,16 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async createDiary(schedule, diaryInfo: CreateDiaryDto) { + const diary = new DiaryEntity(); + diary.title = diaryInfo.title; + diary.place = diaryInfo.place; + diary.weather = diaryInfo.weather; + diary.mood = diaryInfo.mood; + diary.content = diaryInfo.content; + diary.schedule = schedule.id; + + return await diary.save(); + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 7b54cf4..2be8679 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -38,6 +38,16 @@ export const BaseResponse = { code: 201, message: '세부 일정을 작성했습니다.', }, + DIARY_CREATED: { + success: true, + code: 201, + message: '일지를 작성했습니다.', + }, + DIARY_UPDATED: { + success: true, + code: 201, + message: '일지를 수정했습니다.', + }, /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ SCHEDULE_NOT_FOUND: { @@ -50,4 +60,9 @@ export const BaseResponse = { code: 404, message: '세부 일정이 없습니다.', }, + DIARY_NOT_FOUND: { + success: false, + code: 404, + message: '일지가 없습니다.', + }, }; diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 549539d..311acfa 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -14,6 +14,7 @@ import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity'; import { LocationEntity } from 'src/location/location.entity'; +import { DiaryEntity } from 'src/diary/models/diary.entity'; @Entity() export class ScheduleEntity extends BaseEntity { @@ -36,6 +37,9 @@ export class ScheduleEntity extends BaseEntity { @JoinColumn({ name: 'locationId' }) // 외래 키에 대한 컬럼명 설정 location: LocationEntity; + @OneToOne(() => DiaryEntity, (diary) => diary.schedule, { cascade: true }) + diary: DiaryEntity; + @CreateDateColumn() created: Date; @@ -64,7 +68,7 @@ export class ScheduleEntity extends BaseEntity { } static async updateScheduleLocation(schedule, location) { - schedule.locationId = location.id; + schedule.location = location.id; return await schedule.save(); } From 8f2af3f25f4508cce66a0d82581f4da091371033 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 03:14:10 +0900 Subject: [PATCH 035/316] =?UTF-8?q?feat=20:=20S3=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aws-s3/aws-s3.module.ts | 27 + aws-s3/aws-s3.service.ts | 30 + package-lock.json | 1913 +++++++++++++++++++++-- package.json | 5 + src/database/database.providers.ts | 2 +- src/diary/dtos/create-diary.dto.ts | 33 +- src/diary/dtos/get-diary-img-url.dto.ts | 13 + 7 files changed, 1867 insertions(+), 156 deletions(-) create mode 100644 aws-s3/aws-s3.module.ts create mode 100644 aws-s3/aws-s3.service.ts create mode 100644 src/diary/dtos/get-diary-img-url.dto.ts diff --git a/aws-s3/aws-s3.module.ts b/aws-s3/aws-s3.module.ts new file mode 100644 index 0000000..9ce9044 --- /dev/null +++ b/aws-s3/aws-s3.module.ts @@ -0,0 +1,27 @@ +import { S3Client } from '@aws-sdk/client-s3'; +import { Module } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { AwsS3Service } from './aws-s3.service'; + +@Module({ + imports: [], + controllers: [], + providers: [ + { + provide: 'S3_CLIENT', + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + return new S3Client({ + region: configService.get('AWS_REGION'), + credentials: { + accessKeyId: configService.get('S3_ACCESS_KEY')!, + secretAccessKey: configService.get('S3_SECRET_KEY')!, + }, + }); + }, + }, + AwsS3Service, + ], + exports: [AwsS3Service], +}) +export class AwsS3Module {} diff --git a/aws-s3/aws-s3.service.ts b/aws-s3/aws-s3.service.ts new file mode 100644 index 0000000..d13334d --- /dev/null +++ b/aws-s3/aws-s3.service.ts @@ -0,0 +1,30 @@ +import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; +import { Inject, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { GetDiaryImgUrlDto } from 'src/diary/dtos/get-diary-img-url.dto'; + +@Injectable() +export class AwsS3Service { + constructor( + @Inject('S3_CLIENT') + private readonly s3Client: S3Client, + + private readonly configService: ConfigService, + ) {} + async getDiaryImgUrl(getDiaryImgUrlDto: GetDiaryImgUrlDto, diaryId: number) { + // 저장될 파일 이름 + const fileName = `profile/${diaryId}/${Date.now()}${ + getDiaryImgUrlDto.fileName + }`; + + // Put. 즉, s3에 데이터를 집어넣는 작업에 대한 url 생성 + const command = new PutObjectCommand({ + Bucket: this.configService.get('S3_BUCKET'), + Key: fileName, + }); + + const postedImgUrl = await getSignedUrl(this.s3Client, command); + return postedImgUrl; + } +} diff --git a/package-lock.json b/package-lock.json index 647a560..e6f78e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@aws-sdk/client-s3": "^3.504.0", + "@aws-sdk/s3-request-presigner": "^3.504.0", "@nestjs/common": "^10.3.1", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.3.1", @@ -19,6 +21,8 @@ "bcrypt": "^5.1.1", "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", + "multer": "^1.4.5-lts.1", + "multer-s3": "^3.0.1", "mysql2": "^3.8.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", @@ -32,6 +36,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsonwebtoken": "^9.0.5", + "@types/multer-s3": "^3.0.3", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^5.59.11", @@ -244,6 +249,866 @@ "node": ">=0.12.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/crc32c": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", + "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32c/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", + "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.504.0.tgz", + "integrity": "sha512-J8xPsnk7EDwalFSaDxPFNT2+x99nG2uQTpsLXAV3bWbT1nD/JZ+fase9GqxM11v6WngzqRvTQg26ljMn5hQSKA==", + "dependencies": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.504.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.504.0", + "@aws-sdk/middleware-bucket-endpoint": "3.502.0", + "@aws-sdk/middleware-expect-continue": "3.502.0", + "@aws-sdk/middleware-flexible-checksums": "3.502.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-location-constraint": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-sdk-s3": "3.502.0", + "@aws-sdk/middleware-signing": "3.502.0", + "@aws-sdk/middleware-ssec": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/signature-v4-multi-region": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@aws-sdk/xml-builder": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/eventstream-serde-browser": "^2.1.1", + "@smithy/eventstream-serde-config-resolver": "^2.1.1", + "@smithy/eventstream-serde-node": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-blob-browser": "^2.1.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/hash-stream-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/md5-js": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-stream": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.502.0.tgz", + "integrity": "sha512-OZAYal1+PQgUUtWiHhRayDtX0OD+XpXHKAhjYgEIPbyhQaCMp3/Bq1xDX151piWXvXqXLJHFKb8DUEqzwGO9QA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.504.0.tgz", + "integrity": "sha512-ODA33/nm2srhV08EW0KZAP577UgV0qjyr7Xp2yEo8MXWL4ZqQZprk1c+QKBhjr4Djesrm0VPmSD/np0mtYP68A==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.504.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-signing": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.504.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.504.0.tgz", + "integrity": "sha512-IESs8FkL7B/uY+ml4wgoRkrr6xYo4PizcNw6JX17eveq1gRBCPKeGMjE6HTDOcIYZZ8rqz/UeuH3JD4UhrMOnA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.504.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.496.0.tgz", + "integrity": "sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A==", + "dependencies": { + "@smithy/core": "^1.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.502.0.tgz", + "integrity": "sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.503.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.503.1.tgz", + "integrity": "sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.504.0.tgz", + "integrity": "sha512-ODICLXfr8xTUd3wweprH32Ge41yuBa+u3j0JUcLdTUO1N9ldczSMdo8zOPlP0z4doqD3xbnqMkjNQWgN/Q+5oQ==", + "dependencies": { + "@aws-sdk/client-sts": "3.504.0", + "@aws-sdk/credential-provider-env": "3.502.0", + "@aws-sdk/credential-provider-process": "3.502.0", + "@aws-sdk/credential-provider-sso": "3.504.0", + "@aws-sdk/credential-provider-web-identity": "3.504.0", + "@aws-sdk/types": "3.502.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.504.0.tgz", + "integrity": "sha512-6+V5hIh+tILmUjf2ZQWQINR3atxQVgH/bFrGdSR/sHSp/tEgw3m0xWL3IRslWU1e4/GtXrfg1iYnMknXy68Ikw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.502.0", + "@aws-sdk/credential-provider-http": "3.503.1", + "@aws-sdk/credential-provider-ini": "3.504.0", + "@aws-sdk/credential-provider-process": "3.502.0", + "@aws-sdk/credential-provider-sso": "3.504.0", + "@aws-sdk/credential-provider-web-identity": "3.504.0", + "@aws-sdk/types": "3.502.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.502.0.tgz", + "integrity": "sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.504.0.tgz", + "integrity": "sha512-4MgH2or2SjPzaxM08DCW+BjaX4DSsEGJlicHKmz6fh+w9JmLh750oXcTnbvgUeVz075jcs6qTKjvUcsdGM/t8Q==", + "dependencies": { + "@aws-sdk/client-sso": "3.502.0", + "@aws-sdk/token-providers": "3.504.0", + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.504.0.tgz", + "integrity": "sha512-L1ljCvGpIEFdJk087ijf2ohg7HBclOeB1UgBxUBBzf4iPRZTQzd2chGaKj0hm2VVaXz7nglswJeURH5PFcS5oA==", + "dependencies": { + "@aws-sdk/client-sts": "3.504.0", + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.504.0.tgz", + "integrity": "sha512-A2h/yHy+2JFhqiCL1vfSlKxLRIZyyQte58O8s0yAV/TDt7ElzeXMTVtCUvhcOrnjtdHKfh4F36jeZSh1ja/9HA==", + "dependencies": { + "@smithy/abort-controller": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/smithy-client": "^2.3.1", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.0.0" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.502.0.tgz", + "integrity": "sha512-mUSP2DUcjhO5zM2b21CvZ9AqwI8DaAeZA6NYHOxWGTV9BUxHcdGWXEjDkcVj9CQ0gvNwTtw6B5L/q52rVAnZbw==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.502.0.tgz", + "integrity": "sha512-DxfAuBVuPSt8as9xP57o8ks6ySVSjwO2NNNAdpLwk4KhEAPYEpHlf2yWYorYLrS+dDmwfYgOhRNoguuBdCu6ow==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.502.0.tgz", + "integrity": "sha512-kCt2zQDFumz/LnJJJOSd2GW4dr8oT8YMJKgxC/pph3aRXoSHXRwhrMbFnQ8swEE9vjywxtcED8sym0b0tNhhoA==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/types": "3.502.0", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.502.0.tgz", + "integrity": "sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.502.0.tgz", + "integrity": "sha512-fLRwPuTZvEWQkPjys03m3D6tYN4kf7zU6+c8mJxwvEg+yfBuv2RBsbd+Vn2bTisUjXvIg1kyBzONlpHoIyFneg==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.502.0.tgz", + "integrity": "sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.502.0.tgz", + "integrity": "sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.502.0.tgz", + "integrity": "sha512-GbGugrfyL5bNA/zw8iQll92yXBONfWSC8Ns00DtkOU1saPXp4/7WHtyyZGYdvPa73T1IsuZy9egpoYRBmRcd5Q==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.502.0.tgz", + "integrity": "sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.502.0.tgz", + "integrity": "sha512-1nidVTIba6/aVjjzD/WNqWdzSyTrXOHO3Ddz2MGD8S1yGSrYz4iYaq4Bm/uosfdr8B1L0Ws0pjdRXrNfzSw/DQ==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.502.0.tgz", + "integrity": "sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.502.0.tgz", + "integrity": "sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.504.0.tgz", + "integrity": "sha512-5FxVdRufiFLSUDJ/Qul5JFPHjhFFzo+C6u53bzbi7gaSshA6lLLhJ9KbVk2LmKE1mTR+nh2+JebI6y+3njtkzw==", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-format-url": "3.502.0", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.502.0.tgz", + "integrity": "sha512-NpOXtUXH0ZAgnyI3Y3s2fPrgwbsWoNMwdoXdFZvH0eDzzX80tim7Yuy6dzVA5zrxSzOYs1xjcOhM+4CmM0QZiw==", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.504.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.504.0.tgz", + "integrity": "sha512-YIJWWsZi2ClUiILS1uh5L6VjmCUSTI6KKMuL9DkGjYqJ0aI6M8bd8fT9Wm7QmXCyjcArTgr/Atkhia4T7oKvzQ==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.504.0", + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.502.0.tgz", + "integrity": "sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.495.0.tgz", + "integrity": "sha512-hwdA3XAippSEUxs7jpznwD63YYFR+LtQvlEcebPTgWR9oQgG9TfS+39PUfbnEeje1ICuOrN3lrFqFbmP9uzbMg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.502.0.tgz", + "integrity": "sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "@smithy/util-endpoints": "^1.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.502.0.tgz", + "integrity": "sha512-4+0zBD0ZIJqtTzSE6VRruRwUx3lG+is8Egv+LN99X5y7i6OdrS9ePYHbCJ9FxkzTThgbkUq6k2W7psEDYvn4VA==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", + "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.502.0.tgz", + "integrity": "sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.502.0.tgz", + "integrity": "sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw==", + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.496.0.tgz", + "integrity": "sha512-GvEjh537IIeOw1ZkZuB37sV12u+ipS5Z1dwjEC/HAvhl5ac23ULtTr1/n+U1gLNN+BAKSWjKiQ2ksj8DiUzeyw==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", @@ -1850,190 +2715,852 @@ "@nestjs/core": "^10.0.0" } }, - "node_modules/@nestjs/schematics": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.0.tgz", - "integrity": "sha512-HQWvD3F7O0Sv3qHS2jineWxPLmBTLlyjT6VdSw2EAIXulitmV+ErxB3TCVQQORlNkl5p5cwRYWyBaOblDbNFIQ==", - "dev": true, + "node_modules/@nestjs/platform-express/node_modules/multer": { + "version": "1.4.4-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", + "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.0.tgz", + "integrity": "sha512-HQWvD3F7O0Sv3qHS2jineWxPLmBTLlyjT6VdSw2EAIXulitmV+ErxB3TCVQQORlNkl5p5cwRYWyBaOblDbNFIQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "17.0.9", + "@angular-devkit/schematics": "17.0.9", + "comment-json": "4.2.3", + "jsonc-parser": "3.2.0", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/swagger": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.2.0.tgz", + "integrity": "sha512-W7WPq561/79w27ZEgViXS7c5hqPwT7QXhsLsSeu2jeBROUhMM825QKDFKbMmtb643IW5dznJ4PjherlZZgtMvg==", + "dependencies": { + "@nestjs/mapped-types": "2.0.4", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "5.11.0" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/testing": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.0.tgz", + "integrity": "sha512-8DM+bw1qASCvaEnoHUQhypCOf54+G5R21MeFBMvnSk5DtKaWVZuzDP2GjLeYCpTH19WeP6LrrjHv3rX2LKU02A==", + "dev": true, + "dependencies": { + "tslib": "2.6.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", + "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", + "dependencies": { + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, + "node_modules/@nestjs/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.1.tgz", + "integrity": "sha512-1+qdrUqLhaALYL0iOcN43EP6yAXXQ2wWZ6taf4S2pNGowmOc5gx+iMQv+E42JizNJjB0+gEadOXeV1Bf7JWL1Q==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.1.1.tgz", + "integrity": "sha512-NjNFCKxC4jVvn+lUr3Yo4/PmUJj3tbyqH6GNHueyTGS5Q27vlEJ1MkNhUDV8QGxJI7Bodnc2pD18lU2zRfhHlQ==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.1.1.tgz", + "integrity": "sha512-zNW+43dltfNMUrBEYLMWgI8lQr0uhtTcUyxkgC9EP4j17WREzgSFMPUFVrVV6Rc2+QtWERYjb4tzZnQGa7R9fQ==", + "dependencies": { + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.1.tgz", + "integrity": "sha512-lxfLDpZm+AWAHPFZps5JfDoO9Ux1764fOgvRUBpHIO8HWHcSN1dkgsago1qLRVgm1BZ8RCm8cgv99QvtaOWIhw==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.1.tgz", + "integrity": "sha512-tf+NIu9FkOh312b6M9G4D68is4Xr7qptzaZGZUREELF8ysE1yLKphqt7nsomjKZVwW7WE5pDDex9idowNGRQ/Q==", + "dependencies": { + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.1.tgz", + "integrity": "sha512-7XHjZUxmZYnONheVQL7j5zvZXga+EWNgwEAP6OPZTi7l8J4JTeNh9aIOfE5fKHZ/ee2IeNOh54ZrSna+Vc6TFA==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.1.tgz", + "integrity": "sha512-E8KYBxBIuU4c+zrpR22VsVrOPoEDzk35bQR3E+xm4k6Pa6JqzkDOdMyf9Atac5GPNKHJBdVaQ4JtjdWX2rl/nw==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.1.1.tgz", + "integrity": "sha512-JvEdCmGlZUay5VtlT8/kdR6FlvqTDUiJecMjXsBb0+k1H/qc9ME5n2XKPo8q/MZwEIA1GmGgYMokKGjVvMiDow==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.1.1.tgz", + "integrity": "sha512-EqNqXYp3+dk//NmW3NAgQr9bEQ7fsu/CcxQmTiq07JlaIcne/CBWpMZETyXm9w5LXkhduBsdXdlMscfDUDn2fA==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.1.1.tgz", + "integrity": "sha512-LF882q/aFidFNDX7uROAGxq3H0B7rjyPkV6QDn6/KDQ+CG7AFkRccjxRf1xqajq/Pe4bMGGr+VKAaoF6lELIQw==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.1.1.tgz", + "integrity": "sha512-LR0mMT+XIYTxk4k2fIxEA1BPtW3685QlqufUEUAX1AJcfFfxNDKEvuCRZbO8ntJb10DrIFVJR9vb0MhDCi0sAQ==", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.1.tgz", + "integrity": "sha512-VYGLinPsFqH68lxfRhjQaSkjXM7JysUOJDTNjHBuN/ykyRb2f1gyavN9+VhhPTWCy32L4yZ2fdhpCs/nStEicg==", + "dependencies": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.1.1.tgz", + "integrity": "sha512-jizu1+2PAUjiGIfRtlPEU8Yo6zn+d78ti/ZHDesdf1SUn2BuZW433JlPoCOLH3dBoEEvTgLvQ8tUGSoTTALA+A==", + "dependencies": { + "@smithy/chunked-blob-reader": "^2.1.1", + "@smithy/chunked-blob-reader-native": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.1.tgz", + "integrity": "sha512-Qhoq0N8f2OtCnvUpCf+g1vSyhYQrZjhSwvJ9qvR8BUGOtTXiyv2x1OD2e6jVGmlpC4E4ax1USHoyGfV9JFsACg==", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.1.1.tgz", + "integrity": "sha512-VgDaKcfCy0iHcmtAZgZ3Yw9g37Gkn2JsQiMtFQXUh8Wmo3GfNgDwLOtdhJ272pOT7DStzpe9cNr+eV5Au8KfQA==", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.1.tgz", + "integrity": "sha512-7WTgnKw+VPg8fxu2v9AlNOQ5yaz6RA54zOVB4f6vQuR0xFKd+RzlCpt0WidYTsye7F+FYDIaS/RnJW4pxjNInw==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.1.1.tgz", + "integrity": "sha512-L3MbIYBIdLlT+MWTYrdVSv/dow1+6iZ1Ad7xS0OHxTTs17d753ZcpOV4Ro7M7tRAVWML/sg2IAp/zzCb6aAttg==", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.1.tgz", + "integrity": "sha512-rSr9ezUl9qMgiJR0UVtVOGEZElMdGFyl8FzWEF5iEKTlcWxGr2wTqGfDwtH3LAB7h+FPkxqv4ZU4cpuCN9Kf/g==", + "dependencies": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.1.tgz", + "integrity": "sha512-XPZTb1E2Oav60Ven3n2PFx+rX9EDsU/jSTA8VDamt7FXks67ekjPY/XrmmPDQaFJOTUHJNKjd8+kZxVO5Ael4Q==", + "dependencies": { + "@smithy/middleware-serde": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.1.tgz", + "integrity": "sha512-eMIHOBTXro6JZ+WWzZWd/8fS8ht5nS5KDQjzhNMHNRcG5FkNTqcKpYhw7TETMYzbLfhO5FYghHy1vqDWM4FLDA==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.1.tgz", + "integrity": "sha512-D8Gq0aQBeE1pxf3cjWVkRr2W54t+cdM2zx78tNrVhqrDykRA7asq8yVJij1u5NDtKzKqzBSPYh7iW0svUKg76g==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.1.tgz", + "integrity": "sha512-KPJhRlhsl8CjgGXK/DoDcrFGfAqoqvuwlbxy+uOO4g2Azn1dhH+GVfC3RAp+6PoL5PWPb+vt6Z23FP+Mr6qeCw==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.1.tgz", + "integrity": "sha512-epzK3x1xNxA9oJgHQ5nz+2j6DsJKdHfieb+YgJ7ATWxzNcB7Hc+Uya2TUck5MicOPhDV8HZImND7ZOecVr+OWg==", + "dependencies": { + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.3.1.tgz", + "integrity": "sha512-gLA8qK2nL9J0Rk/WEZSvgin4AppvuCYRYg61dcUo/uKxvMZsMInL5I5ZdJTogOvdfVug3N2dgI5ffcUfS4S9PA==", + "dependencies": { + "@smithy/abort-controller": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.1.tgz", + "integrity": "sha512-FX7JhhD/o5HwSwg6GLK9zxrMUrGnb3PzNBrcthqHKBc3dH0UfgEAU24xnJ8F0uow5mj17UeBEOI6o3CF2k7Mhw==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.1.1.tgz", + "integrity": "sha512-6ZRTSsaXuSL9++qEwH851hJjUA0OgXdQFCs+VDw4tGH256jQ3TjYY/i34N4vd24RV3nrjNsgd1yhb57uMoKbzQ==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.1.tgz", + "integrity": "sha512-C/ko/CeEa8jdYE4gt6nHO5XDrlSJ3vdCG0ZAc6nD5ZIE7LBp0jCx4qoqp7eoutBu7VrGMXERSRoPqwi1WjCPbg==", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.1.tgz", + "integrity": "sha512-H4+6jKGVhG1W4CIxfBaSsbm98lOO88tpDWmZLgkJpt8Zkk/+uG0FmmqMuCAc3HNM2ZDV+JbErxr0l5BcuIf/XQ==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.1.tgz", + "integrity": "sha512-txEdZxPUgM1PwGvDvHzqhXisrc5LlRWYCf2yyHfvITWioAKat7srQvpjMAvgzf0t6t7j8yHrryXU9xt7RZqFpw==", + "dependencies": { + "@smithy/types": "^2.9.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.1.tgz", + "integrity": "sha512-2E2kh24igmIznHLB6H05Na4OgIEilRu0oQpYXo3LCNRrawHAcfDKq9004zJs+sAMt2X5AbY87CUCJ7IpqpSgdw==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.1.tgz", + "integrity": "sha512-Hb7xub0NHuvvQD3YwDSdanBmYukoEkhqBjqoxo+bSdC0ryV9cTfgmNjuAQhTPYB6yeU7hTR+sPRiFMlxqv6kmg==", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.3.1.tgz", + "integrity": "sha512-YsTdU8xVD64r2pLEwmltrNvZV6XIAC50LN6ivDopdt+YiF/jGH6PY9zUOu0CXD/d8GMB8gbhnpPsdrjAXHS9QA==", "dependencies": { - "@angular-devkit/core": "17.0.9", - "@angular-devkit/schematics": "17.0.9", - "comment-json": "4.2.3", - "jsonc-parser": "3.2.0", - "pluralize": "8.0.0" + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", + "tslib": "^2.5.0" }, - "peerDependencies": { - "typescript": ">=4.8.2" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@nestjs/swagger": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.2.0.tgz", - "integrity": "sha512-W7WPq561/79w27ZEgViXS7c5hqPwT7QXhsLsSeu2jeBROUhMM825QKDFKbMmtb643IW5dznJ4PjherlZZgtMvg==", + "node_modules/@smithy/types": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.9.1.tgz", + "integrity": "sha512-vjXlKNXyprDYDuJ7UW5iobdmyDm6g8dDG+BFUncAg/3XJaN45Gy5RWWWUVgrzIK7S4R1KWgIX5LeJcfvSI24bw==", "dependencies": { - "@nestjs/mapped-types": "2.0.4", - "js-yaml": "4.1.0", - "lodash": "4.17.21", - "path-to-regexp": "3.2.0", - "swagger-ui-dist": "5.11.0" - }, - "peerDependencies": { - "@fastify/static": "^6.0.0", - "@nestjs/common": "^9.0.0 || ^10.0.0", - "@nestjs/core": "^9.0.0 || ^10.0.0", - "class-transformer": "*", - "class-validator": "*", - "reflect-metadata": "^0.1.12" + "tslib": "^2.5.0" }, - "peerDependenciesMeta": { - "@fastify/static": { - "optional": true - }, - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@nestjs/testing": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.0.tgz", - "integrity": "sha512-8DM+bw1qASCvaEnoHUQhypCOf54+G5R21MeFBMvnSk5DtKaWVZuzDP2GjLeYCpTH19WeP6LrrjHv3rX2LKU02A==", - "dev": true, + "node_modules/@smithy/url-parser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.1.tgz", + "integrity": "sha512-qC9Bv8f/vvFIEkHsiNrUKYNl8uKQnn4BdhXl7VzQRP774AwIjiSMMwkbT+L7Fk8W8rzYVifzJNYxv1HwvfBo3Q==", "dependencies": { - "tslib": "2.6.2" + "@smithy/querystring-parser": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", + "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", + "dependencies": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", + "dependencies": { + "tslib": "^2.5.0" }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0", - "@nestjs/microservices": "^10.0.0", - "@nestjs/platform-express": "^10.0.0" + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", + "dependencies": { + "@smithy/is-array-buffer": "^2.1.1", + "tslib": "^2.5.0" }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - } + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@nestjs/typeorm": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", - "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", + "node_modules/@smithy/util-config-provider": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", "dependencies": { - "uuid": "9.0.1" + "tslib": "^2.5.0" }, - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", - "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "typeorm": "^0.3.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@nestjs/typeorm/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.1.tgz", + "integrity": "sha512-lqLz/9aWRO6mosnXkArtRuQqqZBhNpgI65YDpww4rVQBuUT7qzKbDLG5AmnQTCiU4rOquaZO/Kt0J7q9Uic7MA==", + "dependencies": { + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.1.1.tgz", + "integrity": "sha512-tYVrc+w+jSBfBd267KDnvSGOh4NMz+wVH7v4CClDbkdPfnjvImBZsOURncT5jsFwR9KCuDyPoSZq4Pa6+eCUrA==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@smithy/config-resolver": "^2.1.1", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 8" + "node": ">= 10.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@smithy/util-endpoints": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.1.tgz", + "integrity": "sha512-sI4d9rjoaekSGEtq3xSb2nMjHMx8QXcz2cexnVyRWsy4yQ9z3kbDpX+7fN0jnbdOp0b3KSTZJZ2Yb92JWSanLw==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, "engines": { - "node": ">= 8" + "node": ">= 14.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 8" + "node": ">=14.0.0" } }, - "node_modules/@nuxtjs/opencollective": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", - "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "node_modules/@smithy/util-middleware": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.1.tgz", + "integrity": "sha512-mKNrk8oz5zqkNcbcgAAepeJbmfUW6ogrT2Z2gDbIUzVzNAHKJQTYmH9jcy0jbWb+m7ubrvXKb6uMjkSgAqqsFA==", "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" }, - "bin": { - "opencollective": "bin/opencollective.js" + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.1.tgz", + "integrity": "sha512-Mg+xxWPTeSPrthpC5WAamJ6PW4Kbo01Fm7lWM1jmGRvmrRdsd3192Gz2fBXAMURyXpaNxyZf6Hr/nQ4q70oVEA==", + "dependencies": { + "@smithy/service-error-classification": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" + "node": ">= 14.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, + "node_modules/@smithy/util-stream": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.1.tgz", + "integrity": "sha512-J7SMIpUYvU4DQN55KmBtvaMc7NM3CZ2iWICdcgaovtLzseVhAqFRYqloT3mh0esrFw+3VEK6nQFteFsTqZSECQ==", + "dependencies": { + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=14" + "node": ">=14.0.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "node_modules/@smithy/util-uri-escape": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, + "node_modules/@smithy/util-utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", + "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", "dependencies": { - "type-detect": "4.0.8" + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, + "node_modules/@smithy/util-waiter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.1.1.tgz", + "integrity": "sha512-kYy6BLJJNif+uqNENtJqWdXcpqo1LS+nj1AfXcDhOpqpSHJSAkVySLyZV9fkmuVO21lzGoxjvd1imGGJHph/IA==", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@smithy/abort-controller": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/@sqltools/formatter": { @@ -2266,6 +3793,26 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/multer": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", + "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/multer-s3": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/multer-s3/-/multer-s3-3.0.3.tgz", + "integrity": "sha512-VgWygI9UwyS7loLithUUi0qAMIDWdNrERS2Sb06UuPYiLzKuIFn2NgL7satyl4v8sh/LLoU7DiPanvbQaRg9Yg==", + "dev": true, + "dependencies": { + "@aws-sdk/client-s3": "^3.0.0", + "@types/multer": "*", + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.10.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz", @@ -3209,6 +4756,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4469,7 +6021,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "engines": { "node": ">=0.8.x" } @@ -4683,6 +6234,27 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", @@ -4737,6 +6309,14 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5298,6 +6878,11 @@ "node": "*" } }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6846,9 +8431,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multer": { - "version": "1.4.4-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", - "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.0.0", @@ -6862,6 +8447,23 @@ "node": ">= 6.0.0" } }, + "node_modules/multer-s3": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-3.0.1.tgz", + "integrity": "sha512-BFwSO80a5EW4GJRBdUuSHblz2jhVSAze33ZbnGpcfEicoT0iRolx4kWR+AJV07THFRCQ78g+kelKFdjkCCaXeQ==", + "dependencies": { + "@aws-sdk/lib-storage": "^3.46.0", + "file-type": "^3.3.0", + "html-comment-regex": "^1.1.2", + "run-parallel": "^1.1.6" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.0.0" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -7554,7 +9156,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -7860,7 +9461,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -8261,6 +9861,28 @@ "node": ">= 0.8" } }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -8375,6 +9997,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "node_modules/superagent": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", diff --git a/package.json b/package.json index 7b1e9d3..326ddf9 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@aws-sdk/client-s3": "^3.504.0", + "@aws-sdk/s3-request-presigner": "^3.504.0", "@nestjs/common": "^10.3.1", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.3.1", @@ -30,6 +32,8 @@ "bcrypt": "^5.1.1", "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", + "multer": "^1.4.5-lts.1", + "multer-s3": "^3.0.1", "mysql2": "^3.8.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", @@ -43,6 +47,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsonwebtoken": "^9.0.5", + "@types/multer-s3": "^3.0.3", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^5.59.11", diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index 08cb027..ec934c7 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: false, + synchronize: true, }); return dataSource.initialize(); diff --git a/src/diary/dtos/create-diary.dto.ts b/src/diary/dtos/create-diary.dto.ts index b0ecf70..2443384 100644 --- a/src/diary/dtos/create-diary.dto.ts +++ b/src/diary/dtos/create-diary.dto.ts @@ -1,12 +1,21 @@ -import { IsString, IsDateString, IsEnum } from 'class-validator'; -import { PickType } from '@nestjs/swagger'; -import { DiaryEntity } from '../models/diary.entity'; - -export class CreateDiaryDto extends PickType(DiaryEntity, [ - 'title', - 'place', - 'weather', - 'mood', - 'content', - 'image', -]) {} +import { IsString, IsOptional, IsEnum } from 'class-validator'; + +export class CreateDiaryDto { + @IsString() + title: string; + + @IsString() + place: string; + + @IsEnum(['CLOUDY', 'RAINY', 'SNOWY', 'PARTLY_CLOUDY', 'SUNNY']) + weather: 'CLOUDY' | 'RAINY' | 'SNOWY' | 'PARTLY_CLOUDY' | 'SUNNY'; + + @IsEnum(['ANGRY', 'SAD', 'SMILE', 'HAPPY', 'SHOCKED']) + mood: 'ANGRY' | 'SAD' | 'SMILE' | 'HAPPY' | 'SHOCKED'; + + @IsString() + content: string; + + @IsString() + image: string; +} diff --git a/src/diary/dtos/get-diary-img-url.dto.ts b/src/diary/dtos/get-diary-img-url.dto.ts new file mode 100644 index 0000000..ebfdd5f --- /dev/null +++ b/src/diary/dtos/get-diary-img-url.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class GetDiaryImgUrlDto { + @ApiProperty({ + example: 'abc.png', + description: '파일명', + required: true, + }) + @IsString() + @IsNotEmpty() + fileName: string; +} From 363975c26a5fcee31253aad25c578746376f65af Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 04:10:49 +0900 Subject: [PATCH 036/316] =?UTF-8?q?feat=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20url=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aws-s3/aws-s3.service.ts | 2 +- src/database/database.providers.ts | 2 +- src/diary/diary.controller.ts | 29 ++++++++++++++++++++++------- src/diary/diary.module.ts | 2 ++ src/diary/diary.service.ts | 14 ++++++++++++++ src/response/response.status.ts | 5 +++++ 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/aws-s3/aws-s3.service.ts b/aws-s3/aws-s3.service.ts index d13334d..a521914 100644 --- a/aws-s3/aws-s3.service.ts +++ b/aws-s3/aws-s3.service.ts @@ -12,7 +12,7 @@ export class AwsS3Service { private readonly configService: ConfigService, ) {} - async getDiaryImgUrl(getDiaryImgUrlDto: GetDiaryImgUrlDto, diaryId: number) { + async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { // 저장될 파일 이름 const fileName = `profile/${diaryId}/${Date.now()}${ getDiaryImgUrlDto.fileName diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index ec934c7..08cb027 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: true, + synchronize: false, }); return dataSource.initialize(); diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index cd6103f..fb78b42 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -2,11 +2,13 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; import { Controller, Post, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; import { CreateDiaryDto } from './dtos/create-diary.dto'; +import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; @Controller('api/diary') export class DiaryController { constructor(private readonly diaryService: DiaryService) {} + /*일지 작성하기 */ @ApiOperation({ summary: '일지 작성하기', description: '일지를 작성하고 저장한 상태', @@ -14,15 +16,28 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) - @Post('create/:scheduleId') + @Post('create/:diaryId') async createJourney( - @Param('scheduleId') scheduleId: number, - @Body() createDiaryDto: CreateDiaryDto, + @Param('diaryId') diaryId: number, + @Body() body: CreateDiaryDto, ) { - const result = await this.diaryService.createDiary( - scheduleId, - createDiaryDto, - ); + const result = await this.diaryService.createDiary(diaryId, body); + return result; + } + /*일지 사진 url 발급 */ + @ApiOperation({ + summary: '일지 사진 업로드 위한 presigned Url 발급', + description: '일지를 작성하고 저장한 상태', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Post('image-url/:diaryId') + async getDiaryImageUrl( + @Param('diaryId') diaryId: number, + @Body() body: GetDiaryImgUrlDto, + ) { + const result = await this.diaryService.getDiaryImgUrl(diaryId, body); return result; } } diff --git a/src/diary/diary.module.ts b/src/diary/diary.module.ts index 36441d7..145a1ee 100644 --- a/src/diary/diary.module.ts +++ b/src/diary/diary.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { DiaryService } from './diary.service'; import { DiaryController } from './diary.controller'; +import { AwsS3Module } from 'aws-s3/aws-s3.module'; @Module({ + imports: [AwsS3Module], controllers: [DiaryController], providers: [DiaryService], }) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index afb3b90..9a910f8 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -4,13 +4,27 @@ import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { DiaryEntity } from './models/diary.entity'; import { CreateDiaryDto } from './dtos/create-diary.dto'; +import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; +import { AwsS3Service } from '../../aws-s3/aws-s3.service'; @Injectable() export class DiaryService { + constructor(private readonly awsS3Service: AwsS3Service) {} + + //일지 작성하기 async createDiary(scheduleId, diaryInfo: CreateDiaryDto) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); const diary = await DiaryEntity.createDiary(schedule, diaryInfo); console.log(diary); return response(BaseResponse.DIARY_CREATED); } + //일지 사진 S3에 업로드 후 url 받기 + async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { + const postImgUrl = await this.awsS3Service.getDiaryImgUrl( + diaryId, + getDiaryImgUrlDto, + ); + console.log(postImgUrl); + return response(BaseResponse.DIARY_IMG_URL_CREATED); + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 2be8679..b7649e2 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -48,6 +48,11 @@ export const BaseResponse = { code: 201, message: '일지를 수정했습니다.', }, + DIARY_IMG_URL_CREATED: { + success: true, + code: 201, + message: '이미지 url이 발급되었습니다.', + }, /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ SCHEDULE_NOT_FOUND: { From 8dd29bc8b61b1c0dc0d57908ea0d967081a1473f Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 05:23:08 +0900 Subject: [PATCH 037/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20API=20?= =?UTF-8?q?=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/domain/signature.like.entity.ts | 8 -- src/signature/domain/signature.page.entity.ts | 37 +++++- src/signature/dto/author-signature.dto.ts | 8 ++ src/signature/dto/detail-signature.dto.ts | 11 ++ src/signature/dto/header-signature.dto.ts | 9 ++ src/signature/dto/page-signature.dto.ts | 1 + src/signature/dto/tmp-userId.dto.ts | 4 + src/signature/signature.controller.ts | 49 ++++++-- src/signature/signature.module.ts | 3 +- src/signature/signature.service.ts | 116 ++++++++++++++++-- src/user/user.service.ts | 43 +++++-- 11 files changed, 242 insertions(+), 47 deletions(-) create mode 100644 src/signature/dto/author-signature.dto.ts create mode 100644 src/signature/dto/detail-signature.dto.ts create mode 100644 src/signature/dto/header-signature.dto.ts create mode 100644 src/signature/dto/tmp-userId.dto.ts diff --git a/src/signature/domain/signature.like.entity.ts b/src/signature/domain/signature.like.entity.ts index bd12691..3173e77 100644 --- a/src/signature/domain/signature.like.entity.ts +++ b/src/signature/domain/signature.like.entity.ts @@ -26,12 +26,4 @@ export class SignatureLikeEntity extends BaseEntity { @JoinColumn({name: 'user_id'}) user: UserEntity; - @CreateDateColumn() - created: Date; - - @UpdateDateColumn() - updated: Date; - - @DeleteDateColumn() - deleted: Date; } diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 3686ac5..b09ac0a 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -11,6 +11,7 @@ import { } from 'typeorm'; import { SignatureEntity } from './signature.entity'; import {PageSignatureDto} from '../dto/page-signature.dto'; +import { errorContext } from 'rxjs/internal/util/errorContext'; @Entity() export class SignaturePageEntity extends BaseEntity { @@ -18,7 +19,7 @@ export class SignaturePageEntity extends BaseEntity { id: number; @Column() - pageNum: number; + page: number; @Column({ type: 'mediumtext' }) content: string; @@ -53,7 +54,7 @@ export class SignaturePageEntity extends BaseEntity { signaturePage.content = pageSignatureDto.content; signaturePage.image = pageSignatureDto.image; // base64 이미지 서버에 올려야 signaturePage.location = pageSignatureDto.location; - signaturePage.pageNum = pageSignatureDto.page; + signaturePage.page = pageSignatureDto.page; return await signaturePage.save(); @@ -61,12 +62,36 @@ export class SignaturePageEntity extends BaseEntity { static async findThumbnail(id: number) { // 각 시그니처의 첫 번째 페이지의 이미지 가져오기 - const firstPage=await SignaturePageEntity.findOne({ + try{ + const firstPage=await SignaturePageEntity.findOne({ + where: { + signature: { id: id }, + page: 1 + } + }); + + if (firstPage && firstPage.signature) { + console.log("썸네일 아이디: ", firstPage.id, " signatureId: ", firstPage.signature.id); + return firstPage.image; + } else { + console.log("썸네일을 찾을 수 없습니다."); + return null; + } + + }catch (error){ + console.log("Error on findThumbnail: ",error); + throw error; + } + + } + + static async findSignaturePages(signatureId: number){ + const pages: SignaturePageEntity[] = await SignaturePageEntity.find({ where: { - signature: { id: id }, - pageNum: 1 + signature: {id: signatureId} } }); - return firstPage.image; + + return pages; } } diff --git a/src/signature/dto/author-signature.dto.ts b/src/signature/dto/author-signature.dto.ts new file mode 100644 index 0000000..6490c54 --- /dev/null +++ b/src/signature/dto/author-signature.dto.ts @@ -0,0 +1,8 @@ +// author-signature.dto.ts + +export class AuthorSignatureDto { // 시그니처 작성자 정보 + _id: number; // 메이트 아이디 + name: string; // 메이트 이름 + image: string; // 메이트 프로필 이미지 + is_followed: boolean; // 해당 메이트 팔로우 여부 +} \ No newline at end of file diff --git a/src/signature/dto/detail-signature.dto.ts b/src/signature/dto/detail-signature.dto.ts new file mode 100644 index 0000000..fe5267a --- /dev/null +++ b/src/signature/dto/detail-signature.dto.ts @@ -0,0 +1,11 @@ +// detail-signature.dto.ts + +import { AuthorSignatureDto } from './author-signature.dto'; +import { HeaderSignatureDto } from './header-signature.dto'; +import { PageSignatureDto } from './page-signature.dto'; + +export class DetailSignatureDto { // 시그니처 상세 보기 + author: AuthorSignatureDto; // 시그니처 작성자 정보 + header: HeaderSignatureDto; // 시그니처 제목 및 좋아요 정보 + pages: PageSignatureDto[]; // 시그니처 각 페이지 내용 +} \ No newline at end of file diff --git a/src/signature/dto/header-signature.dto.ts b/src/signature/dto/header-signature.dto.ts new file mode 100644 index 0000000..d320d67 --- /dev/null +++ b/src/signature/dto/header-signature.dto.ts @@ -0,0 +1,9 @@ +// header-signature.dto.ts + +export class HeaderSignatureDto{ + _id: number // 시그니처 아이디 + title: string; // 시그니처 제목 + is_liked: boolean; // 해당 시그니처 좋아요 여부 + like_cnt: number; // 좋아요 개수 + date: string; // 발행일 +} \ No newline at end of file diff --git a/src/signature/dto/page-signature.dto.ts b/src/signature/dto/page-signature.dto.ts index 3a25dea..74bf120 100644 --- a/src/signature/dto/page-signature.dto.ts +++ b/src/signature/dto/page-signature.dto.ts @@ -1,6 +1,7 @@ // page-signature.dto.ts export class PageSignatureDto { + _id: number; page: number; content: string; location: string; diff --git a/src/signature/dto/tmp-userId.dto.ts b/src/signature/dto/tmp-userId.dto.ts new file mode 100644 index 0000000..bee0c18 --- /dev/null +++ b/src/signature/dto/tmp-userId.dto.ts @@ -0,0 +1,4 @@ + +export class TmpUserIdDto { + userId: number +} \ No newline at end of file diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 7fee1c0..5820565 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -1,11 +1,13 @@ // signature.controller.ts -import { Body, Controller, Get, Post } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { CreateSignatureDto } from './dto/create-signature.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { HomeSignatureDto } from './dto/home-signature.dto'; +import { DetailSignatureDto } from './dto/detail-signature.dto'; +import { TmpUserIdDto } from './dto/tmp-userId.dto'; @Controller('signature') @@ -14,18 +16,27 @@ export class SignatureController { constructor(private readonly signatureService: SignatureService) {} @Get('/') // 시그니처 탭 메인: 내 시그니처 목록 - async getMySignature(@Body() user_id: number): Promise> { + async getMySignature(@Body() user_id: TmpUserIdDto): Promise> { // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.homeSignature(user_id); - console.log("내 시그니처 조회 결과: ", result); + const result = await this.signatureService.homeSignature(user_id.userId); - return new ResponseDto( - ResponseCode.GET_MY_SIGNATURES_SUCCESS, - true, - "내 시그니처 가져오기 성공", - result - ); + if(!result){ + return new ResponseDto( + ResponseCode.GET_MY_SIGNATURE_FAIL, + false, + "내 시그니처 가져오기 실패", + null + ); + } + else{ + return new ResponseDto( + ResponseCode.GET_MY_SIGNATURES_SUCCESS, + true, + "내 시그니처 가져오기 성공", + result + ); + } } @Post('/new') @@ -50,4 +61,22 @@ export class SignatureController { result); } } + + @Get('/:signatureId') // 시그니처 상세보기 + async getSignatureDetail( + @Body() user_id: TmpUserIdDto, + @Param('signatureId') signatureId: number + ): Promise> { + + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 + const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공", + result + ); + } + } diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 99f6e51..0b71923 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -7,9 +7,10 @@ import { SignatureEntity } from './domain/signature.entity'; import { TypeOrmModule } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { EntityManager } from 'typeorm'; +import { UserService } from '../user/user.service'; @Module({ controllers: [SignatureController], - providers: [SignatureService], + providers: [SignatureService,UserService], }) export class SignatureModule {} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 8dd1717..d3ad86d 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -10,23 +10,23 @@ import { HomeSignatureDto } from './dto/home-signature.dto'; import { UserEntity } from 'src/user/user.entity'; import { SignaturePageEntity } from './domain/signature.page.entity'; import { PageSignatureDto } from './dto/page-signature.dto'; +import { DetailSignatureDto } from './dto/detail-signature.dto'; +import { AuthorSignatureDto } from './dto/author-signature.dto'; +import { HeaderSignatureDto } from './dto/header-signature.dto'; +import { UserService } from '../user/user.service'; @Injectable() export class SignatureService { - async createSignature( - createSignatureDto: CreateSignatureDto, - ): Promise { - // [1] 시그니처 저장 - const signature: SignatureEntity = - await SignatureEntity.createSignature( createSignatureDto); + constructor(private readonly userService: UserService) {} - if (!signature) { - throw new BadRequestException(); - } + async createSignature(createSignatureDto: CreateSignatureDto): Promise { - else{ - // [2] 각 페이지 저장 + // [1] 시그니처 저장 + const signature: SignatureEntity = await SignatureEntity.createSignature( createSignatureDto); + + if (!signature) throw new BadRequestException(); + else{ // [2] 각 페이지 저장 for(const pageSignatureDto of createSignatureDto.pages){ await SignaturePageEntity.saveSignaturePages(pageSignatureDto, signature); } @@ -36,9 +36,10 @@ export class SignatureService { } - async homeSignature(user_id: number): Promise { + async homeSignature(userId: number): Promise { try { - const homeSignatureList: HomeSignatureDto[] = await SignatureEntity.findMySignature(user_id); + console.log("userId; ",userId); + const homeSignatureList: HomeSignatureDto[] = await SignatureEntity.findMySignature(userId); return homeSignatureList; } catch (error) { @@ -48,4 +49,93 @@ export class SignatureService { } } + async checkIfLiked(user: UserEntity, signatureId: number): Promise { + // user가 해당 시그니처에 좋아요 눌렀는지 확인 + + const likesArray = user.likes || []; + + const isLiked = likesArray.some( + (signatureLike) => signatureLike.id === signatureId + ); + + return isLiked; + } + + async detailSignature(userId: number, signatureId: number):Promise { + try{ + const detailSignatureDto:DetailSignatureDto = new DetailSignatureDto(); + + // [1] 시그니처 객체, 로그인 유저 객체 가져오기 + const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); + console.log("시그니처 정보: ", signature); + + const loginUser:UserEntity = await this.userService.findUserById(userId); + console.log("로그인한 유저 정보: ", loginUser); + + /****************************************/ + + // [2] 시그니처 작성자 정보 가져오기 + const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); + + console.log("시그니처 작성자 id: ",signature.user.id); + console.log("로그인한 유저 id: ",loginUser.id); + // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 + if(loginUser.id != signature.user.id) { + authorDto._id = signature.user.id; + authorDto.name = signature.user.name; + + const image = await this.userService.getProfileImage(signature.user.id); + console.log("시그니처 작성자 프로필 이미지: ",image); + + authorDto.image = image.imageKey; + + // 해당 시그니처 작성자를 팔로우하고 있는지 확인 + authorDto.is_followed = await this.userService.checkIfFollowing(loginUser,signature.user.id); + detailSignatureDto.author = authorDto; + } + + /****************************************/ + + // [3] 시그니처 헤더 정보 담기 + const headerSignatureDto: HeaderSignatureDto = new HeaderSignatureDto(); + headerSignatureDto._id = signature.id; + headerSignatureDto.title = signature.title; + headerSignatureDto.like_cnt = signature.liked; + + // 발행일 가공하기 + const date = signature.created; + headerSignatureDto.date = await SignatureEntity.formatDateString(date); + + // 해당 시그니처 좋아요 눌렀는지 확인하기 + headerSignatureDto.is_liked = await this.checkIfLiked(loginUser,signatureId); + + detailSignatureDto.header = headerSignatureDto; + + /****************************************/ + + // [4] 각 페이지 내용 가져오기 + const signaturePageDto: PageSignatureDto[] = []; + const pages: SignaturePageEntity[] = await SignaturePageEntity.findSignaturePages(signatureId); + + for(const page of pages){ + const pageDto:PageSignatureDto = new PageSignatureDto(); + pageDto._id = page.id; + pageDto.page = page.page; + pageDto.content = page.content; + pageDto.image = page.image; + pageDto.location = page.location; + + signaturePageDto.push(pageDto); + } + detailSignatureDto.pages = signaturePageDto; + + + return detailSignatureDto; + } + catch(error){ + // 예외 처리 + console.error('Error on DetailSignature: ', error); + throw new HttpException('Internal Server Error', 500); + } + } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 23fd05e..a112a03 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -4,6 +4,7 @@ import jsonwebtoken from 'jsonwebtoken'; import { UserEntity } from './user.entity'; import { IReqUser } from './user.dto'; import { UserFollowingEntity } from './user.following.entity'; +import { UserProfileImageEntity } from './user.profile.image.entity'; @Injectable() export class UserService { @@ -44,17 +45,41 @@ export class UserService { }; } - async FindUser(id: number) { - const user: UserEntity = await UserEntity.findOne({ - where: { - id: id - } - }); + async checkIfFollowing(user: UserEntity, targetUserId: number): Promise { + // user가 targetUser를 팔로우하고 있는지 확인 - if (!user) { - throw new HttpException('Invalid user', 403); + const followingArray = user.following||[]; + + const isFollowing = followingArray.some( + (following) => following.followUser.id === targetUserId); + + return isFollowing; + } + + async findUserById(userId: number): Promise { + try{ + const user:UserEntity = await UserEntity.findOne({ + where: { id: userId }, + }); + return user; + + }catch (error){ + console.log("Error on findUserById: ", error); + throw error; } + } - return user; + async getProfileImage(userId: number) { + try{ + const profileImageEntity = await UserProfileImageEntity.findOne({ + where:{ user:{ id: userId } } + }); + + console.log("겟프로필이미지: ",profileImageEntity); + return profileImageEntity; + + }catch (error){ + console.log("Error on getProfileImage: "+error); + } } } From 4b199f56cf0b4a6d4fedb0926adb72e77331d8e1 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 05:24:07 +0900 Subject: [PATCH 038/316] =?UTF-8?q?[Refactor]=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=ED=8F=AC=EB=A7=B7=20=EB=B0=8F=20=EC=9D=91=EB=8B=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 1 + src/signature/domain/signature.entity.ts | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 377cf0e..318dd87 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -18,6 +18,7 @@ export enum ResponseCode { AUTH_NUMBER_INCORRECT = 'BAD_REQUEST', RESET_PASSWORD_FAIL_MATCH = 'BAD_REQUEST', SIGNATURE_CREATION_FAIL = 'BAD_REQUEST', + GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index 3e311c1..43acbf4 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -27,7 +27,7 @@ export class SignatureEntity extends BaseEntity { title: string; @Column({default:0}) - liked_cnt: number; + liked: number; @ManyToOne(() => UserEntity, (user) => user.signatures) @@ -51,6 +51,13 @@ export class SignatureEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + static async formatDateString(date: Date): Promise { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + + return `${year}.${month}.${day}`; + } static async createSignature( createSignatureDto: CreateSignatureDto, @@ -98,4 +105,13 @@ export class SignatureEntity extends BaseEntity { } return mySignatureList; } + + static async findSignatureById(signatureId: number): Promise { + const signature:SignatureEntity = await SignatureEntity.findOne({ + where: { id: signatureId }, + relations: ['user'] // user 관계를 포함 + }); + + return signature; + } } From 41e305f134268db32fc5729b2d58b82e48502e9a Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 10:08:30 +0900 Subject: [PATCH 039/316] docs : pull request template --- aws-s3/aws-s3.service.ts | 2 +- docs/pull-request-template.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 docs/pull-request-template.md diff --git a/aws-s3/aws-s3.service.ts b/aws-s3/aws-s3.service.ts index a521914..819038f 100644 --- a/aws-s3/aws-s3.service.ts +++ b/aws-s3/aws-s3.service.ts @@ -14,7 +14,7 @@ export class AwsS3Service { ) {} async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { // 저장될 파일 이름 - const fileName = `profile/${diaryId}/${Date.now()}${ + const fileName = `diary/${diaryId}/${Date.now()}${ getDiaryImgUrlDto.fileName }`; diff --git a/docs/pull-request-template.md b/docs/pull-request-template.md new file mode 100644 index 0000000..cf303c9 --- /dev/null +++ b/docs/pull-request-template.md @@ -0,0 +1,19 @@ +# Check + +- [ ] 잘 동작하는 코드인가요? +- [ ] 테스트를 충분히 해봤나요? +- [ ] 컨벤션을 준수 했나요? + +# Description + +어떤 작업을 했나요? + +# Key Changes + +핵심적인 부분을 간단하게 알려주세요! + +# To Reviewers + +이런 부분을 집중적으로 봐주세요. + +### 아직 해결하지 못한 사항이 있다면 issue에 남기고 팀원들과 공유해주세요~ From e70f091cb0f9e2796edab587def4376bf8e093de Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 14:05:00 +0900 Subject: [PATCH 040/316] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=EB=A5=BC=20=EC=B6=94=EA=B0=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20=EC=A0=80=EC=9E=A5=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.entity.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index ffb90c7..6a0f4d2 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -28,8 +28,11 @@ export class UserEntity extends BaseEntity { @Column() password: string; + @Column() + nickname: string; + @Column({ type: 'text' }) - bio: string; + introduction: string; @Column() age: number; From a226af9a4822b421a9ad0807414c782792c5b2bc Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 14:05:26 +0900 Subject: [PATCH 041/316] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=EB=A5=BC=20=EC=B6=94=EA=B0=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20=EC=A0=80=EC=9E=A5=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 6a0f4d2..de64b27 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -3,7 +3,7 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, ManyToOne, + Entity, OneToMany, OneToOne, PrimaryGeneratedColumn, From af8294c5b679cf5a52aa925e43a5d31a829ea83b Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 14:21:53 +0900 Subject: [PATCH 042/316] =?UTF-8?q?=20chore=20:=20=EC=97=AC=EC=A0=95=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EC=8B=9C,=20=EC=9D=BC=EC=A0=95=EA=B3=BC?= =?UTF-8?q?=20=EC=9D=BC=EC=A7=80=EB=8F=84=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.providers.ts | 2 +- src/diary/diary.controller.ts | 6 ++++-- src/diary/models/diary.entity.ts | 14 +++++++++++--- src/journey/journey.service.ts | 13 +++++++++++-- src/schedule/schedule.entity.ts | 15 ++++----------- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index 08cb027..ec934c7 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: false, + synchronize: true, }); return dataSource.initialize(); diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index fb78b42..9e67fda 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -16,8 +16,8 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) - @Post('create/:diaryId') - async createJourney( + @Post('post/:diaryId') + async postJourney( @Param('diaryId') diaryId: number, @Body() body: CreateDiaryDto, ) { @@ -40,4 +40,6 @@ export class DiaryController { const result = await this.diaryService.getDiaryImgUrl(diaryId, body); return result; } + + /*일지 사진 업로드*/ } diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index db79048..2c132c2 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -24,25 +24,27 @@ export class DiaryEntity extends BaseEntity { @ManyToOne(() => UserEntity) author: UserEntity; - @Column() + @Column({ nullable: true }) title: string; - @Column() + @Column({ nullable: true }) place: string; @Column({ type: 'enum', enum: ['CLOUDY', 'RAINY', 'SNOWY', 'PARTLY_CLOUDY', 'SUNNY'], + nullable: true, }) weather: 'CLOUDY' | 'RAINY' | 'SNOWY' | 'PARTLY_CLOUDY' | 'SUNNY'; @Column({ type: 'enum', enum: ['ANGRY', 'SAD', 'SMILE', 'HAPPY', 'SHOCKED'], + nullable: true, }) mood: 'ANGRY' | 'SAD' | 'SMILE' | 'HAPPY' | 'SHOCKED'; - @Column({ type: 'mediumtext' }) + @Column({ nullable: true, type: 'mediumtext' }) content: string; @OneToOne(() => DiaryImageEntity, (image) => image.diary, { @@ -64,6 +66,12 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + static async preDiary(schedule) { + const diary = new DiaryEntity(); + diary.schedule = schedule.id; + await diary.save(); + } + static async createDiary(schedule, diaryInfo: CreateDiaryDto) { const diary = new DiaryEntity(); diary.title = diaryInfo.title; diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 1d97cc8..eb4f848 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -7,6 +7,7 @@ import { DateGroupEntity } from 'src/date-group/date-group.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; import { CreateDateGroupDto } from 'src/date-group/dtos/create-date-group.dto'; +import { DiaryEntity } from 'src/diary/models/diary.entity'; @Injectable() export class JourneyService { @@ -25,8 +26,16 @@ export class JourneyService { dateGroup.id, ); - const schedule = await ScheduleEntity.createSchedule(dates); - console.log(journey, schedule); + // let schedule = await ScheduleEntity.createSchedule(dates); + let currentDate = new Date(dates.startDate); + const lastDate = new Date(dates.endDate); + + while (currentDate <= lastDate) { + const schedule = await ScheduleEntity.createSchedule(currentDate); + const diary = await DiaryEntity.preDiary(schedule); + currentDate = new Date(currentDate); + currentDate.setDate(currentDate.getDate() + 1); + } return response(BaseResponse.JOURNEY_CREATED); } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 311acfa..aa86915 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -49,17 +49,10 @@ export class ScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createSchedule(dates) { - let currentDate = new Date(dates.startDate); - const lastDate = new Date(dates.endDate); - - while (currentDate <= lastDate) { - const schedule = new ScheduleEntity(); - schedule.date = currentDate.toISOString().split('T')[0]; - await schedule.save(); - currentDate = new Date(currentDate); - currentDate.setDate(currentDate.getDate() + 1); - } + static async createSchedule(currentDate) { + const schedule = new ScheduleEntity(); + schedule.date = currentDate.toISOString().split('T')[0]; + return await schedule.save(); } static async updateScheduleTitle(schedule, title) { From 01742d82a19153b89acad5a7fa4494f0193772a2 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 14:26:11 +0900 Subject: [PATCH 043/316] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EB=A5=BC=20=EC=9D=B8=EC=A6=9D=ED=95=98=EB=8A=94=20Guard?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.guard.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/user/user.guard.ts diff --git a/src/user/user.guard.ts b/src/user/user.guard.ts new file mode 100644 index 0000000..bb5aea5 --- /dev/null +++ b/src/user/user.guard.ts @@ -0,0 +1,32 @@ +import Express from 'express'; +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import jsonwebtoken from 'jsonwebtoken'; +import { IReqUser } from './user.dto'; + +@Injectable() +export class UserGuard implements CanActivate { + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + const authorization = request.headers['authorization']?.split(' '); + + if (authorization.length === 2 && authorization[0] === 'Bearer') { + const token = authorization[1]; + + try { + request.user = jsonwebtoken.verify( + token, + process.env.JWT_SECRET, + ) as IReqUser; + } catch (error) { + return false; + } + } else { + return false; + } + + return true; + } +} From e09d238dbbb4baf0e23bf65be48ba420df1d501e Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 14:26:18 +0900 Subject: [PATCH 044/316] =?UTF-8?q?refactor=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.providers.ts | 2 +- src/diary/diary.controller.ts | 6 +++--- src/diary/diary.service.ts | 6 +++--- src/diary/dtos/{create-diary.dto.ts => post-diary.dto.ts} | 2 +- src/diary/models/diary.entity.ts | 6 +++--- src/journey/journey.service.ts | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) rename src/diary/dtos/{create-diary.dto.ts => post-diary.dto.ts} (93%) diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index ec934c7..08cb027 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: true, + synchronize: false, }); return dataSource.initialize(); diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 9e67fda..61229f0 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -1,7 +1,7 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; import { Controller, Post, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; -import { CreateDiaryDto } from './dtos/create-diary.dto'; +import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; @Controller('api/diary') @@ -19,9 +19,9 @@ export class DiaryController { @Post('post/:diaryId') async postJourney( @Param('diaryId') diaryId: number, - @Body() body: CreateDiaryDto, + @Body() body: PostDiaryDto, ) { - const result = await this.diaryService.createDiary(diaryId, body); + const result = await this.diaryService.postDiary(diaryId, body); return result; } /*일지 사진 url 발급 */ diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 9a910f8..16a74db 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -3,7 +3,7 @@ import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { DiaryEntity } from './models/diary.entity'; -import { CreateDiaryDto } from './dtos/create-diary.dto'; +import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; import { AwsS3Service } from '../../aws-s3/aws-s3.service'; @@ -12,9 +12,9 @@ export class DiaryService { constructor(private readonly awsS3Service: AwsS3Service) {} //일지 작성하기 - async createDiary(scheduleId, diaryInfo: CreateDiaryDto) { + async postDiary(scheduleId, diaryInfo: PostDiaryDto) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); - const diary = await DiaryEntity.createDiary(schedule, diaryInfo); + const diary = await DiaryEntity.postDiary(schedule, diaryInfo); console.log(diary); return response(BaseResponse.DIARY_CREATED); } diff --git a/src/diary/dtos/create-diary.dto.ts b/src/diary/dtos/post-diary.dto.ts similarity index 93% rename from src/diary/dtos/create-diary.dto.ts rename to src/diary/dtos/post-diary.dto.ts index 2443384..bb4253f 100644 --- a/src/diary/dtos/create-diary.dto.ts +++ b/src/diary/dtos/post-diary.dto.ts @@ -1,6 +1,6 @@ import { IsString, IsOptional, IsEnum } from 'class-validator'; -export class CreateDiaryDto { +export class PostDiaryDto { @IsString() title: string; diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 2c132c2..66fcaa8 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -13,7 +13,7 @@ import { import { UserEntity } from '../../user/user.entity'; import { DiaryImageEntity } from './diary.image.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; -import { CreateDiaryDto } from '../dtos/create-diary.dto'; +import { PostDiaryDto } from '../dtos/post-diary.dto'; @Entity() export class DiaryEntity extends BaseEntity { @@ -66,13 +66,13 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async preDiary(schedule) { + static async createDiary(schedule) { const diary = new DiaryEntity(); diary.schedule = schedule.id; await diary.save(); } - static async createDiary(schedule, diaryInfo: CreateDiaryDto) { + static async postDiary(schedule, diaryInfo: PostDiaryDto) { const diary = new DiaryEntity(); diary.title = diaryInfo.title; diary.place = diaryInfo.place; diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index eb4f848..b730679 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -32,7 +32,7 @@ export class JourneyService { while (currentDate <= lastDate) { const schedule = await ScheduleEntity.createSchedule(currentDate); - const diary = await DiaryEntity.preDiary(schedule); + const diary = await DiaryEntity.createDiary(schedule); currentDate = new Date(currentDate); currentDate.setDate(currentDate.getDate() + 1); } From d1996e4cd82f0d5056f52ff5501ca0b3051421ad Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 14:26:20 +0900 Subject: [PATCH 045/316] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=ED=95=98=EB=8A=94=20API=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 1 + src/user/user.controller.ts | 11 +++- src/user/user.dto.ts | 5 ++ src/user/user.service.ts | 91 ++++++++++++++++++++++++------ 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 318dd87..a97615b 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -8,6 +8,7 @@ export enum ResponseCode { REISSUE_TOKEN_SUCCESS = 'OK', GET_MY_SIGNATURES_SUCCESS = 'OK', GET_SIGNATURE_DETAIL_SUCCESS = 'OK', + UPDATE_PROFILE_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index b934774..ff4f408 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,5 +1,8 @@ -import { Body, Controller, Post } from '@nestjs/common'; +import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; +import { IUserProfile } from './user.dto'; +import { UserGuard } from './user.guard'; +import { Request } from 'express'; @Controller('user') export class UserController { @@ -9,4 +12,10 @@ export class UserController { Login(@Body('email') email: string, @Body('password') password: string) { return this.userService.Login(email, password); } + + @Post('/profile') + @UseGuards(UserGuard) + UpdateProfile(@Body() body: Partial, @Req() req: Request) { + return this.userService.updateUserProfile(req.user.id, body); + } } diff --git a/src/user/user.dto.ts b/src/user/user.dto.ts index 7064a5f..0d19ad7 100644 --- a/src/user/user.dto.ts +++ b/src/user/user.dto.ts @@ -1,3 +1,8 @@ export interface IReqUser { id: number; } + +export interface IUserProfile { + nickname: string; + introduction: string; +} diff --git a/src/user/user.service.ts b/src/user/user.service.ts index a112a03..eee0cf5 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,13 +1,16 @@ -import { HttpException, Injectable } from '@nestjs/common'; +import { HttpException, Injectable, Logger } from '@nestjs/common'; import bcrypt from 'bcrypt'; import jsonwebtoken from 'jsonwebtoken'; import { UserEntity } from './user.entity'; -import { IReqUser } from './user.dto'; -import { UserFollowingEntity } from './user.following.entity'; +import { IReqUser, IUserProfile } from './user.dto'; import { UserProfileImageEntity } from './user.profile.image.entity'; +import { ResponseDto } from '../response/response.dto'; +import { ResponseCode } from '../response/response-code.enum'; @Injectable() export class UserService { + private readonly logger: Logger = new Logger(UserService.name); + private _hashPassword(password: string): string { return bcrypt.hashSync(password, 10); } @@ -45,41 +48,97 @@ export class UserService { }; } - async checkIfFollowing(user: UserEntity, targetUserId: number): Promise { + async checkIfFollowing( + user: UserEntity, + targetUserId: number, + ): Promise { // user가 targetUser를 팔로우하고 있는지 확인 - const followingArray = user.following||[]; + const followingArray = user.following || []; const isFollowing = followingArray.some( - (following) => following.followUser.id === targetUserId); + (following) => following.followUser.id === targetUserId, + ); return isFollowing; } async findUserById(userId: number): Promise { - try{ - const user:UserEntity = await UserEntity.findOne({ + try { + const user: UserEntity = await UserEntity.findOne({ where: { id: userId }, }); return user; - - }catch (error){ - console.log("Error on findUserById: ", error); + } catch (error) { + console.log('Error on findUserById: ', error); throw error; } } async getProfileImage(userId: number) { - try{ + try { const profileImageEntity = await UserProfileImageEntity.findOne({ - where:{ user:{ id: userId } } + where: { user: { id: userId } }, }); - console.log("겟프로필이미지: ",profileImageEntity); + console.log('겟프로필이미지: ', profileImageEntity); return profileImageEntity; + } catch (error) { + console.log('Error on getProfileImage: ' + error); + } + } + + async updateUserProfile(userId: number, profile: Partial) { + try { + const user = await UserEntity.findOne({ + where: { + id: Number(userId), + }, + }); - }catch (error){ - console.log("Error on getProfileImage: "+error); + if (profile.introduction !== undefined) { + user.introduction = profile.introduction; + } + if (profile.nickname !== undefined) { + // Todo: 닉네임 중복 체크를 트랜잭션으로 처리하라 + const existingNickname = await UserEntity.count({ + where: { + nickname: profile.nickname.toString(), + }, + }); + + if (existingNickname > 0) { + return new ResponseDto( + ResponseCode.NICKNAME_DUPLICATION, + false, + '중복된 닉네임 존재', + null, + ); + } + + user.nickname = profile.nickname; + } + + await user.save(); + + return new ResponseDto( + ResponseCode.UPDATE_PROFILE_SUCCESS, + true, + '추가정보 입력 성공', + null, + ); + } catch (error) { + this.logger.error(error); + + if (error instanceof HttpException) { + throw error; + } + return new ResponseDto( + ResponseCode.INTERNAL_SERVEr_ERROR, + false, + '서버 내부 오류', + null, + ); } } } From 0669966f45229aee6ba00c96bb7eaabb80fa2b82 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 14:55:50 +0900 Subject: [PATCH 046/316] =?UTF-8?q?chore:=20Nest=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20=EC=A0=9C=EA=B1=B0=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.controller.ts | 11 ++--------- src/app.service.ts | 6 +----- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index cce879e..08e392f 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,12 +1,5 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller } from '@nestjs/common'; import { AppService } from './app.service'; @Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} +export class AppController {} diff --git a/src/app.service.ts b/src/app.service.ts index 927d7cc..7263d33 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -1,8 +1,4 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class AppService { - getHello(): string { - return 'Hello World!'; - } -} +export class AppService {} From cd4b79e7989ea1bb5038856bdc06a4e44f9a2bea Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 14:57:32 +0900 Subject: [PATCH 047/316] =?UTF-8?q?bug=20:=20s3=EC=97=90=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=95=88?= =?UTF-8?q?=EB=90=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.service.ts | 9 +++++++-- src/diary/models/diary.entity.ts | 3 ++- src/diary/models/diary.image.entity.ts | 9 ++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 16a74db..16a38f9 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -3,6 +3,7 @@ import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { DiaryEntity } from './models/diary.entity'; +import { DiaryImageEntity } from './models/diary.image.entity'; import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; import { AwsS3Service } from '../../aws-s3/aws-s3.service'; @@ -20,11 +21,15 @@ export class DiaryService { } //일지 사진 S3에 업로드 후 url 받기 async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { - const postImgUrl = await this.awsS3Service.getDiaryImgUrl( + const createImgUrl = await this.awsS3Service.getDiaryImgUrl( diaryId, getDiaryImgUrlDto, ); - console.log(postImgUrl); + const createDiaryImg = await DiaryImageEntity.createDiaryImg( + diaryId, + createImgUrl, + ); + console.log(createImgUrl); return response(BaseResponse.DIARY_IMG_URL_CREATED); } } diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 66fcaa8..aea0eab 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -66,12 +66,13 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + /*일지 생성하기*/ static async createDiary(schedule) { const diary = new DiaryEntity(); diary.schedule = schedule.id; await diary.save(); } - + /*일지 작성하기*/ static async postDiary(schedule, diaryInfo: PostDiaryDto) { const diary = new DiaryEntity(); diary.title = diaryInfo.title; diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index ac93130..ddc49ad 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -16,7 +16,7 @@ export class DiaryImageEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column() + @Column({ type: 'mediumtext' }) imageUrl: string; @JoinColumn({ name: 'diaryId' }) @@ -31,4 +31,11 @@ export class DiaryImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async createDiaryImg(diaryId, ImgUrl: string) { + const diaryImg = new DiaryImageEntity(); + diaryImg.imageUrl = ImgUrl; + diaryImg.diary = diaryId; + await diaryImg.save(); + } } From 3ce89b320f4caf27e8ba5963bf32d7e64da53de1 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 15:00:04 +0900 Subject: [PATCH 048/316] =?UTF-8?q?chore:=20Global=20API=20prefix=EB=A5=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.ts b/src/main.ts index 13cad38..0c38314 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.setGlobalPrefix('api/v1'); await app.listen(3000); } bootstrap(); From 1a6af88020b28319163d17ca6b1f74840527599c Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 15:25:50 +0900 Subject: [PATCH 049/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EA=B8=B0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 4 ++-- src/diary/diary.service.ts | 10 +++++----- src/diary/models/diary.entity.ts | 20 +++++++++++++++----- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 61229f0..4dcdb00 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -1,5 +1,5 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; -import { Controller, Post, Body, Param } from '@nestjs/common'; +import { Controller, Put, Post, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; @@ -16,7 +16,7 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) - @Post('post/:diaryId') + @Put('post/:diaryId') async postJourney( @Param('diaryId') diaryId: number, @Body() body: PostDiaryDto, diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 16a38f9..c2e3f61 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -12,14 +12,14 @@ import { AwsS3Service } from '../../aws-s3/aws-s3.service'; export class DiaryService { constructor(private readonly awsS3Service: AwsS3Service) {} - //일지 작성하기 - async postDiary(scheduleId, diaryInfo: PostDiaryDto) { - const schedule = await ScheduleEntity.findExistSchedule(scheduleId); - const diary = await DiaryEntity.postDiary(schedule, diaryInfo); + /*일지 작성하기*/ + async postDiary(diaryId, diaryInfo: PostDiaryDto) { + const diary = await DiaryEntity.postDiary(diaryId, diaryInfo); console.log(diary); return response(BaseResponse.DIARY_CREATED); } - //일지 사진 S3에 업로드 후 url 받기 + + /*일지 사진 S3에 업로드 후 url 받기*/ async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { const createImgUrl = await this.awsS3Service.getDiaryImgUrl( diaryId, diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index aea0eab..ac317c8 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -10,9 +10,11 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { NotFoundException } from '@nestjs/common'; +import { BaseResponse } from 'src/response/response.status'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from '../../user/user.entity'; import { DiaryImageEntity } from './diary.image.entity'; -import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { PostDiaryDto } from '../dtos/post-diary.dto'; @Entity() @@ -48,7 +50,6 @@ export class DiaryEntity extends BaseEntity { content: string; @OneToOne(() => DiaryImageEntity, (image) => image.diary, { - nullable: true, cascade: true, }) image: DiaryImageEntity; @@ -73,15 +74,24 @@ export class DiaryEntity extends BaseEntity { await diary.save(); } /*일지 작성하기*/ - static async postDiary(schedule, diaryInfo: PostDiaryDto) { - const diary = new DiaryEntity(); + static async postDiary(diaryId, diaryInfo: PostDiaryDto) { + const diary = await this.findExistDairy(diaryId); diary.title = diaryInfo.title; diary.place = diaryInfo.place; diary.weather = diaryInfo.weather; diary.mood = diaryInfo.mood; diary.content = diaryInfo.content; - diary.schedule = schedule.id; return await diary.save(); } + + static async findExistDairy(diaryId) { + const diary = await DiaryEntity.findOne({ + where: { id: diaryId }, + }); + if (!diary) { + throw new NotFoundException(BaseResponse.DIARY_NOT_FOUND); + } + return diary; + } } From faf4c50368143d865dd35ec43d58f455ed0381ed Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 16:07:51 +0900 Subject: [PATCH 050/316] =?UTF-8?q?feat:=20S3=20=EC=9C=A0=ED=8B=B8?= =?UTF-8?q?=EC=9D=84=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 22 ++++ package-lock.json | 265 +++++++++++++++++++++++++++++++++++++--- package.json | 5 +- src/utils/S3.module.ts | 8 ++ src/utils/S3.service.ts | 58 +++++++++ 5 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 .env.example create mode 100644 src/utils/S3.module.ts create mode 100644 src/utils/S3.service.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..59f0aed --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +# Database Connection Config +DB_HOST= +DB_PORT= +DB_USER= +DB_PASS= +DB_NAME= + +# If DB_SYNC is true, the database will be synced with the entities on every start +# Set to false in production +DB_SYNC= + +# JWT Secret +JWT_SECRET= + +# S3 Config +S3_ENDPOINT= +S3_ACCESS_KEY= +S3_SECRET_ACCESS_KEY= +S3_BUCKET_NAME= + +# It can be CDN url or public accessible bucket url +S3_PUBLIC_URL= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1aac640..445bdbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,14 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", + "aws-sdk": "^2.1550.0", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "typeorm": "^0.3.20" + "typeorm": "^0.3.20", + "uuid": "^9.0.1" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -33,6 +35,7 @@ "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.59.11", "@typescript-eslint/parser": "^5.59.11", "eslint": "^8.42.0", @@ -1773,6 +1776,14 @@ "reflect-metadata": "^0.1.13" } }, + "node_modules/@nestjs/config/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nestjs/core": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.0.tgz", @@ -1938,18 +1949,6 @@ "typeorm": "^0.3.0" } }, - "node_modules/@nestjs/typeorm/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2339,6 +2338,12 @@ "@types/superagent": "*" } }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -2980,6 +2985,68 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/available-typed-arrays": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", + "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1550.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1550.0.tgz", + "integrity": "sha512-abkbOeaL7iV085UqO8Y7/Ep7VYONK32chhKejhMbPYSqTp2YgNeqOSQfSaVZWeWCwqJxujEyoXFGTNgTt46D0g==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4834,6 +4901,14 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -5249,6 +5324,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -5464,6 +5553,21 @@ "node": ">= 0.10" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5482,6 +5586,17 @@ "node": ">=8" } }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -5520,6 +5635,20 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -5576,6 +5705,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -6324,6 +6467,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7529,6 +7680,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7890,6 +8050,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, "node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", @@ -9151,6 +9316,32 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9165,9 +9356,13 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -9324,6 +9519,24 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "dependencies": { + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -9387,6 +9600,26 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index dba731e..d859436 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,14 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", + "aws-sdk": "^2.1550.0", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "typeorm": "^0.3.20" + "typeorm": "^0.3.20", + "uuid": "^9.0.1" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -44,6 +46,7 @@ "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.59.11", "@typescript-eslint/parser": "^5.59.11", "eslint": "^8.42.0", diff --git a/src/utils/S3.module.ts b/src/utils/S3.module.ts new file mode 100644 index 0000000..ff879cc --- /dev/null +++ b/src/utils/S3.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { S3UtilService } from './S3.service'; + +@Module({ + providers: [S3UtilService], + exports: [S3UtilService], +}) +export class S3Module {} diff --git a/src/utils/S3.service.ts b/src/utils/S3.service.ts new file mode 100644 index 0000000..f203612 --- /dev/null +++ b/src/utils/S3.service.ts @@ -0,0 +1,58 @@ +import * as S3 from 'aws-sdk/clients/s3.js'; +import { v4 as uuidv4 } from 'uuid'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class S3UtilService { + private readonly s3 = new S3({ + endpoint: process.env.S3_ENDPOINT ?? undefined, + accessKeyId: process.env.S3_ACCESS_KEY, + secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, + signatureVersion: 'v4', + }); + + public async listObjects() { + return this.s3 + .listObjectsV2({ Bucket: process.env.S3_BUCKET_NAME }) + .promise(); + } + + public async putObject(key: string, body: Buffer) { + return this.s3 + .putObject({ + Bucket: process.env.S3_BUCKET_NAME, + Key: key, + Body: body, + }) + .promise(); + } + + public async putObjectFromBase64(key: string, body: string) { + return this.s3 + .putObject({ + Bucket: process.env.S3_BUCKET_NAME, + Key: key, + Body: Buffer.from(body, 'base64'), + }) + .promise(); + } + + public async getPresignedUrl(key: string) { + return this.s3.getSignedUrlPromise('getObject', { + Bucket: process.env.S3_BUCKET_NAME, + Key: key, + Expires: 60, + }); + } + + public async getImageUrl(key: string) { + return `${process.env.S3_PUBLIC_URL}${key}`; + } + + public generateRandomImageKey(originalName: string) { + const ext = originalName.split('.').pop(); + const uuid = uuidv4(); + + return `${uuid}.${ext}`; + } +} From f704ce2cdf5c9776c7e9481bbf41867427d561d1 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:10:45 +0900 Subject: [PATCH 051/316] =?UTF-8?q?[Refactor]=20UserGuard=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit by. 민준님 --- src/user/user.guard.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/user/user.guard.ts diff --git a/src/user/user.guard.ts b/src/user/user.guard.ts new file mode 100644 index 0000000..bb5aea5 --- /dev/null +++ b/src/user/user.guard.ts @@ -0,0 +1,32 @@ +import Express from 'express'; +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import jsonwebtoken from 'jsonwebtoken'; +import { IReqUser } from './user.dto'; + +@Injectable() +export class UserGuard implements CanActivate { + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + const authorization = request.headers['authorization']?.split(' '); + + if (authorization.length === 2 && authorization[0] === 'Bearer') { + const token = authorization[1]; + + try { + request.user = jsonwebtoken.verify( + token, + process.env.JWT_SECRET, + ) as IReqUser; + } catch (error) { + return false; + } + } else { + return false; + } + + return true; + } +} From 43d2643c6b2fcfbf90bc187afcd3dff31f579074 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:11:20 +0900 Subject: [PATCH 052/316] =?UTF-8?q?[Refactor]=20UserFollowing=20=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit by 민준님 --- src/user/user.following.entity.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/user/user.following.entity.ts b/src/user/user.following.entity.ts index ad21951..ba9034f 100644 --- a/src/user/user.following.entity.ts +++ b/src/user/user.following.entity.ts @@ -1,9 +1,9 @@ import { - BaseEntity, + BaseEntity, CreateDateColumn, DeleteDateColumn, Entity, JoinColumn, ManyToOne, - PrimaryGeneratedColumn, + PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { UserEntity } from './user.entity'; @@ -19,4 +19,14 @@ export class UserFollowingEntity extends BaseEntity { @JoinColumn() @ManyToOne(() => UserEntity, (user) => user.follower) followUser: UserEntity; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; + } From 082a3b95abf05d1dbd77e3bfc117fd5412524a1e Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:11:56 +0900 Subject: [PATCH 053/316] =?UTF-8?q?[Feat]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?API=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 3 +- src/signature/domain/signature.entity.ts | 44 ++++++++--- src/signature/domain/signature.like.entity.ts | 22 ++++++ src/signature/dto/like-signature.dto.ts | 6 ++ src/signature/signature.controller.ts | 73 ++++++++++++++++--- src/signature/signature.service.ts | 51 ++++++++++++- 6 files changed, 175 insertions(+), 24 deletions(-) create mode 100644 src/signature/dto/like-signature.dto.ts diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 318dd87..cf13530 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -8,11 +8,12 @@ export enum ResponseCode { REISSUE_TOKEN_SUCCESS = 'OK', GET_MY_SIGNATURES_SUCCESS = 'OK', GET_SIGNATURE_DETAIL_SUCCESS = 'OK', + DELETE_LIKE_ON_SIGNATURE_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', SIGNATURE_CREATED = 'CREATED', - + LIKE_ON_SIGNATURE_CREATED = 'CREATED', /* 400 BAD_REQUEST : 잘못된 요청 */ AUTH_NUMBER_INCORRECT = 'BAD_REQUEST', diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index 43acbf4..c017b3b 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -5,10 +5,10 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, EntitySubscriberInterface, EventSubscriber, InsertEvent, JoinColumn, ManyToOne, OneToMany, OneToOne, - PrimaryGeneratedColumn, + PrimaryGeneratedColumn, RemoveEvent, UpdateDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; @@ -17,31 +17,51 @@ import { CreateSignatureDto } from '../dto/create-signature.dto'; import { UserService } from '../../user/user.service'; import { SignaturePageEntity } from './signature.page.entity'; import { SignatureLikeEntity } from './signature.like.entity'; - @Entity() -export class SignatureEntity extends BaseEntity { +@EventSubscriber() +export class SignatureEntity extends BaseEntity implements EntitySubscriberInterface{ @PrimaryGeneratedColumn() id: number; @Column() title: string; - @Column({default:0}) + @Column({ default: 0 }) liked: number; - @ManyToOne(() => UserEntity, - (user) => user.signatures) - @JoinColumn({ name: 'user_id'}) + @ManyToOne(() => UserEntity, (user) => user.signatures) + @JoinColumn({ name: 'user_id' }) user: UserEntity; - @OneToMany(() => SignaturePageEntity, - (signaturePage) => signaturePage.signature) + @OneToMany(() => SignaturePageEntity, (signaturePage) => signaturePage.signature) signaturePages: SignaturePageEntity[]; - @OneToMany(() => SignatureLikeEntity, - (signatureLike) => signatureLike.signature) + @OneToMany(() => SignatureLikeEntity, (signatureLike) => signatureLike.signature, { + cascade: true, // 관계에 대한 연산을 가능하게 합니다. + }) likes: SignatureLikeEntity[]; + listenTo() { + return SignatureLikeEntity; + } + + // SignatureLikeEntity 삽입 이벤트에 대한 이벤트 리스너 + beforeInsert(event: InsertEvent): void { + this.updateLikedCount(event.entity, 1); + } + + // SignatureLikeEntity 삭제 이벤트에 대한 이벤트 리스너 + beforeRemove(event: RemoveEvent): void { + this.updateLikedCount(event.entity, -1); + } + + // 변경된 값에 따라 liked 카운트 업데이트 + private updateLikedCount(entity: SignatureLikeEntity, change: number): void { + this.liked += change; + this.save(); // 업데이트된 liked 카운트를 데이터베이스에 저장합니다. + } + + @CreateDateColumn() created: Date; diff --git a/src/signature/domain/signature.like.entity.ts b/src/signature/domain/signature.like.entity.ts index 3173e77..dcffa4c 100644 --- a/src/signature/domain/signature.like.entity.ts +++ b/src/signature/domain/signature.like.entity.ts @@ -26,4 +26,26 @@ export class SignatureLikeEntity extends BaseEntity { @JoinColumn({name: 'user_id'}) user: UserEntity; + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; + + static async createLike(signature: SignatureEntity, loginUser: UserEntity) { + try{ + const signatureLike = new SignatureLikeEntity(); + signatureLike.signature = signature; + signatureLike.user = loginUser; + + return SignatureLikeEntity.save(signatureLike); + + }catch(error){ + console.error('Error on likeSignature: ', error); + throw new Error('Failed to like Signature'); + } + } } diff --git a/src/signature/dto/like-signature.dto.ts b/src/signature/dto/like-signature.dto.ts new file mode 100644 index 0000000..1965a7f --- /dev/null +++ b/src/signature/dto/like-signature.dto.ts @@ -0,0 +1,6 @@ +// like-signature.dto.ts + +export class LikeSignatureDto { + signatureId: number; + liked: number; +} \ No newline at end of file diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 5820565..8d6660d 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -1,6 +1,6 @@ // signature.controller.ts -import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { CreateSignatureDto } from './dto/create-signature.dto'; import { ResponseCode } from '../response/response-code.enum'; @@ -8,6 +8,9 @@ import { ResponseDto } from '../response/response.dto'; import { HomeSignatureDto } from './dto/home-signature.dto'; import { DetailSignatureDto } from './dto/detail-signature.dto'; import { TmpUserIdDto } from './dto/tmp-userId.dto'; +import { SignatureEntity } from './domain/signature.entity'; +import { SignatureLikeEntity } from './domain/signature.like.entity'; +import { LikeSignatureDto } from './dto/like-signature.dto'; @Controller('signature') @@ -68,15 +71,65 @@ export class SignatureController { @Param('signatureId') signatureId: number ): Promise> { - // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.detailSignature(user_id.userId, signatureId); - - return new ResponseDto( - ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, - true, - "시그니처 상세보기 성공", - result - ); + try{ + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 + const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공", + result + ); + } + catch(error){ + console.log('Error on signatureId: ',error); + throw error; + } + } + @Patch('/like/:signatureId') // 시그니처 상세보기 + async addSignatureLike( + @Body() user_id: TmpUserIdDto, + @Param('signatureId') signatureId: number + ): Promise> { + try{ + + // [1] 이미 좋아요 했는지 확인 + const liked: SignatureLikeEntity= await this.signatureService.findIfAlreadyLiked(user_id.userId,signatureId); + let result = new SignatureEntity(); + const likeSignatureDto= new LikeSignatureDto(); + + if(liked){ // 이미 좋아요했던 시그니처라면 좋아요 삭제 + result = await this.signatureService.deleteLikeOnSignature(liked,signatureId); + likeSignatureDto.liked = result.liked; + likeSignatureDto.signatureId = result.id; + + return new ResponseDto( + ResponseCode.DELETE_LIKE_ON_SIGNATURE_SUCCESS, + true, + '시그니처 좋아요 취소하기 성공', + likeSignatureDto + ); + + + }else{ // 좋아요 한적 없으면 시그니처 좋아요 추가 + result = await this.signatureService.addLikeOnSignature(user_id.userId,signatureId); + likeSignatureDto.liked = result.liked; + likeSignatureDto.signatureId = result.id; + + return new ResponseDto( + ResponseCode.LIKE_ON_SIGNATURE_CREATED, + true, + '시그니처 좋아요 성공', + likeSignatureDto + ); + } + + }catch(error){ + console.log('addSignatureLike: ', error ); + throw error; + } + } } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index d3ad86d..8ec8cb8 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -14,6 +14,7 @@ import { DetailSignatureDto } from './dto/detail-signature.dto'; import { AuthorSignatureDto } from './dto/author-signature.dto'; import { HeaderSignatureDto } from './dto/header-signature.dto'; import { UserService } from '../user/user.service'; +import { SignatureLikeEntity } from './domain/signature.like.entity'; @Injectable() export class SignatureService { @@ -23,7 +24,7 @@ export class SignatureService { async createSignature(createSignatureDto: CreateSignatureDto): Promise { // [1] 시그니처 저장 - const signature: SignatureEntity = await SignatureEntity.createSignature( createSignatureDto); + const signature: SignatureEntity = await SignatureEntity.createSignature(createSignatureDto); if (!signature) throw new BadRequestException(); else{ // [2] 각 페이지 저장 @@ -138,4 +139,52 @@ export class SignatureService { throw new HttpException('Internal Server Error', 500); } } + + async findIfAlreadyLiked(userId: number, signatureId: number): Promise { + + const signatureLike = await SignatureLikeEntity.findOne({ + where:{ + user: { id: userId }, + signature: {id: signatureId} + } + }); + + if(signatureLike) return signatureLike + else null; + + } + + + async addLikeOnSignature(userId: number, signatureId: number) { + + // [1] 시그니처 객체, 로그인 유저 객체 가져오기 + const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); + console.log("시그니처 정보: ", signature); + + const loginUser:UserEntity = await this.userService.findUserById(userId); + console.log("로그인한 유저 정보: ", loginUser); + + // [2] 좋아요 테이블에 인스턴스 추가하기 + const signatureLike = await SignatureLikeEntity.createLike(signature,loginUser); + + // [3] 해당 시그니처 좋아요 개수 추가하기 + signature.liked ++; + const newSignature = await SignatureEntity.save(signature); + + return signature; + + } + + async deleteLikeOnSignature(signatureLike:SignatureLikeEntity, signatureId:number) { + + // [1] 해당 좋아요 기록 삭제 + const deleted_like = await SignatureLikeEntity.softRemove(signatureLike); + + // [2] 시그니처 좋아요 개수 -1 + const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); + signature.liked --; + const newSignature = await SignatureEntity.save(signature); + + return signature + } } From 8a51ad45106c85c1b95097f743382f7386e8a38a Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 3 Feb 2024 16:38:52 +0900 Subject: [PATCH 054/316] =?UTF-8?q?chore=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 3 +-- src/diary/dtos/post-diary.dto.ts | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 4dcdb00..74bdd46 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -25,6 +25,7 @@ export class DiaryController { return result; } /*일지 사진 url 발급 */ + /*일지 사진 업로드*/ @ApiOperation({ summary: '일지 사진 업로드 위한 presigned Url 발급', description: '일지를 작성하고 저장한 상태', @@ -40,6 +41,4 @@ export class DiaryController { const result = await this.diaryService.getDiaryImgUrl(diaryId, body); return result; } - - /*일지 사진 업로드*/ } diff --git a/src/diary/dtos/post-diary.dto.ts b/src/diary/dtos/post-diary.dto.ts index bb4253f..33bbec3 100644 --- a/src/diary/dtos/post-diary.dto.ts +++ b/src/diary/dtos/post-diary.dto.ts @@ -15,7 +15,4 @@ export class PostDiaryDto { @IsString() content: string; - - @IsString() - image: string; } From 1f2a24455135c9b998e7a57ccde15804c2239aac Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sat, 3 Feb 2024 17:16:03 +0900 Subject: [PATCH 055/316] =?UTF-8?q?=EC=97=AC=ED=96=89=20=EA=B7=9C=EC=B9=99?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 여행 규칙 작성하기 --- package-lock.json | 35 +++++++++++++++ package.json | 2 + src/app.module.ts | 3 ++ src/rule/domain/rule.invitation.entity.ts | 21 +++++++++ src/rule/domain/rule.main.entity.ts | 27 ++++++++++++ src/rule/domain/rule.sub.entity.ts | 18 ++++++++ src/rule/dto/create-rule.dto.ts | 36 +++++++++++++++ src/rule/rule.controller.ts | 16 +++++++ src/rule/rule.module.ts | 10 +++++ src/rule/rule.service.ts | 54 +++++++++++++++++++++++ src/user/user.entity.ts | 7 +++ 11 files changed, 229 insertions(+) create mode 100644 src/rule/domain/rule.invitation.entity.ts create mode 100644 src/rule/domain/rule.main.entity.ts create mode 100644 src/rule/domain/rule.sub.entity.ts create mode 100644 src/rule/dto/create-rule.dto.ts create mode 100644 src/rule/rule.controller.ts create mode 100644 src/rule/rule.module.ts create mode 100644 src/rule/rule.service.ts diff --git a/package-lock.json b/package-lock.json index 1aac640..57f1101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", @@ -2339,6 +2341,11 @@ "@types/superagent": "*" } }, + "node_modules/@types/validator": { + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3476,6 +3483,21 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6487,6 +6509,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.54", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz", + "integrity": "sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -9192,6 +9219,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index dba731e..23f3ce5 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", diff --git a/src/app.module.ts b/src/app.module.ts index 78e1f62..6bd58ef 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -9,6 +9,8 @@ import { LocationModule } from './location/location.module'; import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; import { SignatureModule } from './signature/signature.module'; +import { RuleModule } from './rule/rule.module'; + @Module({ imports: [ @@ -22,6 +24,7 @@ import { SignatureModule } from './signature/signature.module'; ScheduleModule, PlaceModule, SignatureModule, + RuleModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts new file mode 100644 index 0000000..8bb4a81 --- /dev/null +++ b/src/rule/domain/rule.invitation.entity.ts @@ -0,0 +1,21 @@ +import { BaseEntity, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; +import { UserEntity } from '../../user/user.entity'; +import { RuleMainEntity } from './rule.main.entity' + +@Entity() +export class RuleInvitationEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.invitations) + @JoinColumn({name: 'rule_id'}) + rule: RuleMainEntity; + + @ManyToOne(() => UserEntity, user => user.invitationsSent) + @JoinColumn({name: 'inviter_id'}) + inviter: UserEntity; + + @ManyToOne(() => UserEntity, user => user.invitationsReceived) + @JoinColumn({name: 'invited_id'}) + invited: UserEntity; +} \ No newline at end of file diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts new file mode 100644 index 0000000..15a17ce --- /dev/null +++ b/src/rule/domain/rule.main.entity.ts @@ -0,0 +1,27 @@ +import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, OneToMany } from 'typeorm'; +import { RuleSubEntity } from './rule.sub.entity'; +import { RuleInvitationEntity } from './rule.invitation.entity' + +@Entity() +export class RuleMainEntity extends BaseEntity { + @PrimaryGeneratedColumn({ type: 'bigint' }) + id: number; + + @Column({ type: 'varchar', length: 255 }) + mainTitle: string; + + @CreateDateColumn({ type: 'timestamp' }) + created: Date; + + @UpdateDateColumn({ type: 'timestamp' }) + updated: Date; + + @Column({ type: 'timestamp', nullable: true }) + deleted: Date; + + @OneToMany(() => RuleSubEntity, ruleSub => ruleSub.main) + rules: RuleSubEntity[]; + + @OneToMany(() => RuleInvitationEntity, ruleInvitation => ruleInvitation.rule) + invitations: RuleInvitationEntity[]; +} \ No newline at end of file diff --git a/src/rule/domain/rule.sub.entity.ts b/src/rule/domain/rule.sub.entity.ts new file mode 100644 index 0000000..c45a646 --- /dev/null +++ b/src/rule/domain/rule.sub.entity.ts @@ -0,0 +1,18 @@ +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; +import { RuleMainEntity } from './rule.main.entity'; + +@Entity() +export class RuleSubEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ type: 'varchar', length: 255 }) + ruleTitle: string; + + @Column({ type: 'text' }) + ruleDetail: string; + + @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.rules) + @JoinColumn({ name: 'rule_id'}) + main: RuleMainEntity; +} \ No newline at end of file diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts new file mode 100644 index 0000000..5dcd0e1 --- /dev/null +++ b/src/rule/dto/create-rule.dto.ts @@ -0,0 +1,36 @@ +import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; + +class RulePairDto { + @IsNotEmpty() + @IsString() + ruleTitle: string; + + @IsNotEmpty() + @IsString() + ruleDetail: string; +} + +export class CreateRuleDto { + @IsNotEmpty() + @IsString() + mainTitle: string; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => RulePairDto) + rulePairs: RulePairDto[]; + + @IsNotEmpty() + @IsNumber() + inviterId: number; + + @IsNotEmpty() + @IsArray() + @IsNumber({}, { each: true }) + invitedId: number[]; + + @IsNotEmpty() + @IsNumber() + ruleId: number; +} diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts new file mode 100644 index 0000000..003cac5 --- /dev/null +++ b/src/rule/rule.controller.ts @@ -0,0 +1,16 @@ +import { Controller, Post, Body, Param } from '@nestjs/common'; +import { RuleService } from './rule.service'; +import { CreateRuleDto } from './dto/create-rule.dto'; + +@Controller('mate/rule') +export class RuleController { + constructor( + private readonly ruleCreateService: RuleService, + ) {} + + @Post('/write/:userId') + async createRule(@Body() createRuleDto: CreateRuleDto, @Param('userId') userId: number) { + return this.ruleCreateService.createRule(createRuleDto, userId); + } + +} \ No newline at end of file diff --git a/src/rule/rule.module.ts b/src/rule/rule.module.ts new file mode 100644 index 0000000..e18fec5 --- /dev/null +++ b/src/rule/rule.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { RuleService } from './rule.service'; +import { RuleController } from './rule.controller'; +// import { RuleConverter } from './rule.converter'; + +@Module({ + controllers: [RuleController], + providers: [RuleService], +}) +export class RuleModule {} diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts new file mode 100644 index 0000000..23df9da --- /dev/null +++ b/src/rule/rule.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@nestjs/common'; +import { CreateRuleDto } from './dto/create-rule.dto'; +// import { RuleConverter } from './rule.converter'; + +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { RuleMainEntity } from './domain/rule.main.entity'; +import { RuleSubEntity } from './domain/rule.sub.entity'; +import { RuleInvitationEntity } from './domain/rule.invitation.entity'; + +@Injectable() +export class RuleService { + constructor( + private ruleConverter: RuleConverter, + ) {} + + async createRule(createRuleDto: CreateRuleDto, userId: number): Promise { + // Extract necessary information from the DTO + const { main, rules, invitations } = await this.ruleConverter.toEntity(createRuleDto); + + // Save main rule entity + const savedMain = await this.ruleMainRepository.save(main); + + // Save rule sub entities + const savedRules = await this.ruleSubRepository.save(rules); + + // Save rule invitation entities + const savedInvitations = await this.ruleInvitationRepository.save(invitations); + + // Return response + return { + status: 201, + success: true, + message: '여행 규칙 작성 및 메이트 초대 성공', + data: { + write: { + id: savedMain.id, + mainTitle: savedMain.mainTitle, + created: savedMain.created.getTime(), + rules: savedRules.map(rule => ({ + id: rule.id, + ruleTitle: rule.ruleTitle, + ruleDetail: rule.ruleDetail, + })), + }, + invitations: savedInvitations.map(invitation => ({ + id: invitation.id, + inviterId: userId, + invitedId: invitation.invited.id, + })), + } + }; + } +} diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index ffb90c7..7383dc5 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -13,6 +13,7 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { UserFollowingEntity } from './user.following.entity'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; +import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; @Entity() export class UserEntity extends BaseEntity { @@ -58,6 +59,12 @@ export class UserEntity extends BaseEntity { @OneToMany(() => SignatureLikeEntity, (signatureLike) => signatureLike.signature) likes: SignatureLikeEntity[]; + @OneToMany(() => RuleInvitationEntity, (invitation) => invitation.inviter) + invitationsSent: RuleInvitationEntity[]; + + @OneToMany(() => RuleInvitationEntity, (invitation) => invitation.invited) + invitationsReceived: RuleInvitationEntity[]; + @CreateDateColumn() created: Date; From fbee690ad1da029ff2baebc8bfd9652067ac8903 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:21:13 +0900 Subject: [PATCH 056/316] =?UTF-8?q?[Refactor]=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 8d6660d..495d46c 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -10,7 +10,7 @@ import { DetailSignatureDto } from './dto/detail-signature.dto'; import { TmpUserIdDto } from './dto/tmp-userId.dto'; import { SignatureEntity } from './domain/signature.entity'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { LikeSignatureDto } from './dto/like-signature.dto'; +import { LikeSignatureDto } from './dto/Like-signature.dto'; @Controller('signature') From d4faf260703f47c94ea7dc90690e7bbf6d506846 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 17:31:03 +0900 Subject: [PATCH 057/316] =?UTF-8?q?chore:=20=EB=B0=B0=ED=8F=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-dev.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/deploy-dev.yml diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 0000000..153c2bd --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -0,0 +1,27 @@ +name: Development Server Deploy +on: + push: + branches: + - feature/#32 + - develop + pull_request: + branches: + - develop +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + + steps: + - name: SSH + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USER }} + password: ${{ secrets.SSH_PASSWORD }} + script: | + cd /root/hereyou + git pull + npm install + npm run build + pm2 restart all \ No newline at end of file From cacd8d791e545cb7fce4119c5822fe64a3a65888 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:48:19 +0900 Subject: [PATCH 058/316] =?UTF-8?q?[Feat]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=82=AD=EC=A0=9C=ED=95=98=EA=B8=B0=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 4 + src/signature/signature.controller.ts | 108 +++++++++++++++++++------- src/signature/signature.service.ts | 11 +++ 3 files changed, 97 insertions(+), 26 deletions(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 13c9511..c1a54d0 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -10,6 +10,8 @@ export enum ResponseCode { GET_SIGNATURE_DETAIL_SUCCESS = 'OK', DELETE_LIKE_ON_SIGNATURE_SUCCESS = 'OK', UPDATE_PROFILE_SUCCESS = 'OK', + DELETE_SIGNATURE_SUCCESS = 'OK', + PATCH_SIGNATURE_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', @@ -21,6 +23,8 @@ export enum ResponseCode { RESET_PASSWORD_FAIL_MATCH = 'BAD_REQUEST', SIGNATURE_CREATION_FAIL = 'BAD_REQUEST', GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', + SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', + SIGNATURE_PATCH_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 495d46c..9b76b68 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -1,6 +1,6 @@ // signature.controller.ts -import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { CreateSignatureDto } from './dto/create-signature.dto'; import { ResponseCode } from '../response/response-code.enum'; @@ -65,31 +65,7 @@ export class SignatureController { } } - @Get('/:signatureId') // 시그니처 상세보기 - async getSignatureDetail( - @Body() user_id: TmpUserIdDto, - @Param('signatureId') signatureId: number - ): Promise> { - - try{ - // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.detailSignature(user_id.userId, signatureId); - - return new ResponseDto( - ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, - true, - "시그니처 상세보기 성공", - result - ); - } - catch(error){ - console.log('Error on signatureId: ',error); - throw error; - } - - } - - @Patch('/like/:signatureId') // 시그니처 상세보기 + @Patch('/like/:signatureId') // 시그니처 좋아요 등록 or 취소 async addSignatureLike( @Body() user_id: TmpUserIdDto, @Param('signatureId') signatureId: number @@ -132,4 +108,84 @@ export class SignatureController { throw error; } } + + @Get('/:signatureId') // 시그니처 상세보기 + async getSignatureDetail( + @Body() user_id: TmpUserIdDto, + @Param('signatureId') signatureId: number + ): Promise> { + + try{ + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 + const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공", + result + ); + } + catch(error){ + console.log('Error on signatureId: ',error); + throw error; + } + + } + + @Patch('/:signatureId') // 시그니처 수정하기 + async patchSignatureDetail( + @Body() user_id: TmpUserIdDto, + @Param('signatureId') signatureId: number + ): Promise> { + try{ + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 + const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공", + result + ); + } + catch(error){ + console.log('Error on signatureId: ',error); + throw error; + } + + } + + @Delete('/:signatureId') // 시그니처 삭제하기 + async deleteSignatureDetail( + @Body() user_id: TmpUserIdDto, + @Param('signatureId') signatureId: number + ): Promise> { + try{ + // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 + + // [1] 시그니처 가져오기 + const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); + console.log("시그니처 정보: ", signature); + + // [2] 시그니처 삭제하기 + const result = await this.signatureService.deleteSignature(signature); + + return new ResponseDto( + ResponseCode.DELETE_SIGNATURE_SUCCESS, + true, + "시그니처 삭제 성공", + null + ); + } + catch(error){ + return new ResponseDto( + ResponseCode.SIGNATURE_DELETE_FAIL, + false, + "시그니처 삭제 실패", + null + ); + } + + } } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 8ec8cb8..e184969 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -187,4 +187,15 @@ export class SignatureService { return signature } + + async deleteSignature(signature){ + try{ + const result = await SignatureEntity.softRemove(signature); + return result; + } + catch(error){ + console.log("Error on deleting Signature: ",error); + throw error; + } + } } From ba7fe4b4d414294be015a5867227d27adaecec03 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Sat, 3 Feb 2024 18:02:16 +0900 Subject: [PATCH 059/316] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=9D=B4=20=EB=8F=99=EC=9E=91=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.guard.ts | 2 +- src/user/user.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/user/user.guard.ts b/src/user/user.guard.ts index bb5aea5..6abfd5a 100644 --- a/src/user/user.guard.ts +++ b/src/user/user.guard.ts @@ -1,7 +1,7 @@ import Express from 'express'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Observable } from 'rxjs'; -import jsonwebtoken from 'jsonwebtoken'; +import * as jsonwebtoken from 'jsonwebtoken'; import { IReqUser } from './user.dto'; @Injectable() diff --git a/src/user/user.service.ts b/src/user/user.service.ts index eee0cf5..4e1fb6b 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,6 +1,6 @@ import { HttpException, Injectable, Logger } from '@nestjs/common'; import bcrypt from 'bcrypt'; -import jsonwebtoken from 'jsonwebtoken'; +import * as jsonwebtoken from 'jsonwebtoken'; import { UserEntity } from './user.entity'; import { IReqUser, IUserProfile } from './user.dto'; import { UserProfileImageEntity } from './user.profile.image.entity'; From fea3e5b7ab989e0b9e0a95d3e3e51897198e0cc9 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 4 Feb 2024 00:58:03 +0900 Subject: [PATCH 060/316] =?UTF-8?q?bug=20:=20=EC=97=94=ED=84=B0=ED=8B=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.providers.ts | 2 +- src/date-group/date-group.controller.ts | 6 --- src/date-group/date-group.entity.ts | 40 ------------------ src/date-group/date-group.module.ts | 10 ----- src/date-group/date-group.service.ts | 5 --- src/date-group/dtos/create-date-group.dto.ts | 10 ----- src/detail-schedule/detail-schedule.entity.ts | 5 +-- src/diary/models/diary.entity.ts | 13 +++--- src/diary/models/diary.image.entity.ts | 5 +-- src/journey/journey.service.ts | 24 +++++------ src/journey/model/journey.entity.ts | 42 +++++++++++++------ src/journey/model/monthly-journey.entity.ts | 18 -------- src/journey/monthly-journey.module.ts | 8 ---- src/journey/monthly-journey.repository.ts | 5 --- src/location/location.entity.ts | 2 +- src/schedule/schedule.entity.ts | 21 ++++++---- src/schedule/schedule.service.ts | 2 +- src/user/user.entity.ts | 4 ++ 18 files changed, 72 insertions(+), 150 deletions(-) delete mode 100644 src/date-group/date-group.controller.ts delete mode 100644 src/date-group/date-group.entity.ts delete mode 100644 src/date-group/date-group.module.ts delete mode 100644 src/date-group/date-group.service.ts delete mode 100644 src/date-group/dtos/create-date-group.dto.ts delete mode 100644 src/journey/model/monthly-journey.entity.ts delete mode 100644 src/journey/monthly-journey.module.ts delete mode 100644 src/journey/monthly-journey.repository.ts diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index 08cb027..ec934c7 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: false, + synchronize: true, }); return dataSource.initialize(); diff --git a/src/date-group/date-group.controller.ts b/src/date-group/date-group.controller.ts deleted file mode 100644 index b9f9676..0000000 --- a/src/date-group/date-group.controller.ts +++ /dev/null @@ -1,6 +0,0 @@ -// DateGroup.controller.ts - -import { Controller } from '@nestjs/common'; - -@Controller() -export class DateGroupController {} diff --git a/src/date-group/date-group.entity.ts b/src/date-group/date-group.entity.ts deleted file mode 100644 index 77d1199..0000000 --- a/src/date-group/date-group.entity.ts +++ /dev/null @@ -1,40 +0,0 @@ -// journey-date-group.entity.ts -import { - Entity, - PrimaryGeneratedColumn, - Column, - OneToMany, - BaseEntity, -} from 'typeorm'; -import { JourneyEntity } from '../journey/model/journey.entity'; -import { CreateDateGroupDto } from './dtos/create-date-group.dto'; - -@Entity() -export class DateGroupEntity extends BaseEntity { - @PrimaryGeneratedColumn() - id: number; - - @Column() - startDate: string; - - @Column() - endDate: string; - - @OneToMany(() => JourneyEntity, (journey) => journey.dateGroup) - journeys: JourneyEntity[]; - - // 날짜 그룹 생성 - static async createDateGroup( - createDateGroupDto: CreateDateGroupDto, - ): Promise { - try { - const dateGroup: DateGroupEntity = new DateGroupEntity(); - dateGroup.startDate = createDateGroupDto.startDate; - dateGroup.endDate = createDateGroupDto.endDate; - - return await dateGroup.save(); - } catch (error) { - throw new Error(error); - } - } -} diff --git a/src/date-group/date-group.module.ts b/src/date-group/date-group.module.ts deleted file mode 100644 index d83e0b6..0000000 --- a/src/date-group/date-group.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -// dateGroup.module.ts -import { Module } from '@nestjs/common'; -import { DateGroupController } from './date-group.controller'; -import { DateGroupService } from './date-group.service'; - -@Module({ - controllers: [DateGroupController], - providers: [DateGroupService], -}) -export class DateGroupModule {} diff --git a/src/date-group/date-group.service.ts b/src/date-group/date-group.service.ts deleted file mode 100644 index 42c283d..0000000 --- a/src/date-group/date-group.service.ts +++ /dev/null @@ -1,5 +0,0 @@ -// data-group.service.ts -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class DateGroupService {} diff --git a/src/date-group/dtos/create-date-group.dto.ts b/src/date-group/dtos/create-date-group.dto.ts deleted file mode 100644 index 2e79e58..0000000 --- a/src/date-group/dtos/create-date-group.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -// create-journey.dto.ts -import { IsDateString } from 'class-validator'; - -export class CreateDateGroupDto { - @IsDateString() - startDate: string; - - @IsDateString() - endDate: string; -} diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index f066383..dfa8068 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -25,9 +25,8 @@ export class DetailScheduleEntity extends BaseEntity { @Column({ default: false }) isDone: boolean; - @JoinColumn({ name: 'schedule_id' }) @ManyToOne(() => ScheduleEntity, (schedule) => schedule.detailSchedules) - schedule: ScheduleEntity; + scheduleId: ScheduleEntity; @CreateDateColumn() created: Date; @@ -41,7 +40,7 @@ export class DetailScheduleEntity extends BaseEntity { //세부 일정 추가하기 static async createDetailSchedule(scheduleId) { const detailSchedule = new DetailScheduleEntity(); - detailSchedule.schedule = scheduleId; + detailSchedule.scheduleId = scheduleId; return await detailSchedule.save(); } //세부 일정 작성하기 diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index ac317c8..742b5c2 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -14,6 +14,7 @@ import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from '../../user/user.entity'; +import { JourneyEntity } from 'src/journey/model/journey.entity'; import { DiaryImageEntity } from './diary.image.entity'; import { PostDiaryDto } from '../dtos/post-diary.dto'; @@ -49,14 +50,13 @@ export class DiaryEntity extends BaseEntity { @Column({ nullable: true, type: 'mediumtext' }) content: string; - @OneToOne(() => DiaryImageEntity, (image) => image.diary, { + @OneToOne(() => DiaryImageEntity, (image) => image.diaryId, { cascade: true, }) image: DiaryImageEntity; @OneToOne(() => ScheduleEntity, (schedule) => schedule.diary) - @JoinColumn({ name: 'scheduleId' }) - schedule: ScheduleEntity; + scheduleId: ScheduleEntity; @CreateDateColumn() created: Date; @@ -68,10 +68,11 @@ export class DiaryEntity extends BaseEntity { deleted: Date; /*일지 생성하기*/ - static async createDiary(schedule) { + static async createDiary(journey, schedule) { const diary = new DiaryEntity(); - diary.schedule = schedule.id; - await diary.save(); + diary.scheduleId = schedule.id; + + return await diary.save(); } /*일지 작성하기*/ static async postDiary(diaryId, diaryInfo: PostDiaryDto) { diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index ddc49ad..de049f4 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -19,9 +19,8 @@ export class DiaryImageEntity extends BaseEntity { @Column({ type: 'mediumtext' }) imageUrl: string; - @JoinColumn({ name: 'diaryId' }) @OneToOne(() => DiaryEntity, (diary) => diary.image) - diary: DiaryEntity; + diaryId: DiaryEntity; @CreateDateColumn() created: Date; @@ -35,7 +34,7 @@ export class DiaryImageEntity extends BaseEntity { static async createDiaryImg(diaryId, ImgUrl: string) { const diaryImg = new DiaryImageEntity(); diaryImg.imageUrl = ImgUrl; - diaryImg.diary = diaryId; + diaryImg.diaryId = diaryId; await diaryImg.save(); } } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index b730679..6cfdbfa 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -3,16 +3,14 @@ import { Injectable } from '@nestjs/common'; import { JourneyEntity } from './model/journey.entity'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; -import { DateGroupEntity } from 'src/date-group/date-group.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; -import { CreateDateGroupDto } from 'src/date-group/dtos/create-date-group.dto'; import { DiaryEntity } from 'src/diary/models/diary.entity'; @Injectable() export class JourneyService { async createJourney(createJourneyDto: CreateJourneyDto) { - const dates: CreateDateGroupDto = new CreateDateGroupDto(); + /*const dates: CreateDateGroupDto = new CreateDateGroupDto(); { (dates.startDate = createJourneyDto.startDate), (dates.endDate = createJourneyDto.endDate); @@ -20,22 +18,24 @@ export class JourneyService { const dateGroup = await DateGroupEntity.createDateGroup(dates); if (!dateGroup) { throw new Error(); - } - const journey = await JourneyEntity.createJourney( - createJourneyDto, - dateGroup.id, - ); + }*/ + + const journey = await JourneyEntity.createJourney(createJourneyDto); // let schedule = await ScheduleEntity.createSchedule(dates); - let currentDate = new Date(dates.startDate); - const lastDate = new Date(dates.endDate); + let currentDate = new Date(createJourneyDto.startDate); + const lastDate = new Date(createJourneyDto.endDate); while (currentDate <= lastDate) { - const schedule = await ScheduleEntity.createSchedule(currentDate); - const diary = await DiaryEntity.createDiary(schedule); + const schedule = await ScheduleEntity.createSchedule( + journey, + currentDate, + ); + const diary = await DiaryEntity.createDiary(journey, schedule); currentDate = new Date(currentDate); currentDate.setDate(currentDate.getDate() + 1); } + return response(BaseResponse.JOURNEY_CREATED); } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index a79984a..cf3c59c 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -5,12 +5,17 @@ import { Entity, PrimaryGeneratedColumn, Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + OneToMany, ManyToOne, JoinColumn, } from 'typeorm'; -import { DateGroupEntity } from '../../date-group/date-group.entity'; -import { MonthlyJourneyEntity } from './monthly-journey.entity'; + import { CreateJourneyDto } from '../dtos/create-journey.dto'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { UserEntity } from 'src/user/user.entity'; @Entity() export class JourneyEntity extends BaseEntity { @@ -20,22 +25,33 @@ export class JourneyEntity extends BaseEntity { @Column() title: string; - @ManyToOne(() => DateGroupEntity, (dateGroup) => dateGroup.journeys) - @JoinColumn({ name: 'dateGroupId' }) - dateGroup: DateGroupEntity; + @Column() + startDate: string; + + @Column() + endDate: string; + + @ManyToOne(() => UserEntity, (user) => user.journeys) + userId: UserEntity; + + @OneToMany(() => ScheduleEntity, (schedule) => schedule.journeyId) + schedules: ScheduleEntity[]; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; - @ManyToOne( - () => MonthlyJourneyEntity, - (monthlyJourney) => monthlyJourney.journeys, - ) - @JoinColumn({ name: 'monthlyId' }) - monthlyJourney: MonthlyJourneyEntity; + @DeleteDateColumn() + deleted: Date; - static async createJourney(createJourneyDto: CreateJourneyDto, dateGroupId) { + static async createJourney(createJourneyDto: CreateJourneyDto) { try { const journey: JourneyEntity = new JourneyEntity(); journey.title = createJourneyDto.title; - journey.dateGroup = dateGroupId; + journey.startDate = createJourneyDto.startDate; + journey.endDate = createJourneyDto.endDate; return await journey.save(); } catch (error) { diff --git a/src/journey/model/monthly-journey.entity.ts b/src/journey/model/monthly-journey.entity.ts deleted file mode 100644 index b583e32..0000000 --- a/src/journey/model/monthly-journey.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -// monthly-journey.entity.ts -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; -import { JourneyEntity } from './journey.entity'; - -@Entity() -export class MonthlyJourneyEntity { - @PrimaryGeneratedColumn() - id: number; - - @Column({ name: 'year' }) - year: number; - - @Column({ name: 'month' }) - month: number; - - @OneToMany(() => JourneyEntity, (journey) => journey.monthlyJourney) - journeys: JourneyEntity[]; -} diff --git a/src/journey/monthly-journey.module.ts b/src/journey/monthly-journey.module.ts deleted file mode 100644 index c89a474..0000000 --- a/src/journey/monthly-journey.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -// journey.module.ts -import { Module } from '@nestjs/common'; -import { MonthlyJourneyRepository } from './monthly-journey.repository'; - -@Module({ - providers: [MonthlyJourneyRepository], -}) -export class JourneyModule {} diff --git a/src/journey/monthly-journey.repository.ts b/src/journey/monthly-journey.repository.ts deleted file mode 100644 index 5374a5a..0000000 --- a/src/journey/monthly-journey.repository.ts +++ /dev/null @@ -1,5 +0,0 @@ -// journey.repository.ts -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class MonthlyJourneyRepository {} diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 986b08f..c99290e 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -21,7 +21,7 @@ export class LocationEntity extends BaseEntity { @Column({ type: 'decimal', precision: 10, scale: 6 }) longitude: number; - @OneToOne(() => ScheduleEntity, (schedule) => schedule.location) + @OneToOne(() => ScheduleEntity, (schedule) => schedule.locationId) schedule: ScheduleEntity; @CreateDateColumn() diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index aa86915..803458e 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -2,19 +2,21 @@ import { BaseEntity, Column, CreateDateColumn, + UpdateDateColumn, DeleteDateColumn, Entity, JoinColumn, + ManyToOne, OneToOne, OneToMany, PrimaryGeneratedColumn, - UpdateDateColumn, } from 'typeorm'; import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity'; import { LocationEntity } from 'src/location/location.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; +import { JourneyEntity } from 'src/journey/model/journey.entity'; @Entity() export class ScheduleEntity extends BaseEntity { @@ -27,17 +29,19 @@ export class ScheduleEntity extends BaseEntity { @Column({ nullable: true }) title: string; + @ManyToOne(() => LocationEntity, (location) => location.id) + locationId: Location; + + @ManyToOne(() => JourneyEntity, (journey) => journey.schedules) + journeyId: JourneyEntity; + @OneToMany( () => DetailScheduleEntity, - (detailSchedule) => detailSchedule.schedule, + (detailSchedule) => detailSchedule.scheduleId, ) detailSchedules: DetailScheduleEntity[]; - @OneToOne(() => LocationEntity, { eager: true }) // eager 옵션을 사용하여 즉시 로드 - @JoinColumn({ name: 'locationId' }) // 외래 키에 대한 컬럼명 설정 - location: LocationEntity; - - @OneToOne(() => DiaryEntity, (diary) => diary.schedule, { cascade: true }) + @OneToOne(() => DiaryEntity, (diary) => diary.scheduleId, { cascade: true }) diary: DiaryEntity; @CreateDateColumn() @@ -49,9 +53,10 @@ export class ScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createSchedule(currentDate) { + static async createSchedule(journey, currentDate) { const schedule = new ScheduleEntity(); schedule.date = currentDate.toISOString().split('T')[0]; + return await schedule.save(); } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index ea548e1..7f813fb 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -15,7 +15,7 @@ export class ScheduleService { async updateScheduleLocation(scheduleId, scheduleLocation) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); - if (schedule.location) { + if (schedule.locationId) { const location = LocationEntity.updateLocation( schedule, scheduleLocation, diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 1410f6c..3ed5e03 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -13,6 +13,7 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { UserFollowingEntity } from './user.following.entity'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; +import { JourneyEntity } from 'src/journey/model/journey.entity'; @Entity() export class UserEntity extends BaseEntity { @@ -61,6 +62,9 @@ export class UserEntity extends BaseEntity { ) likes: SignatureLikeEntity[]; + @OneToMany(() => JourneyEntity, (journey) => journey.userId) + journeys: JourneyEntity[]; + @CreateDateColumn() created: Date; From 37ec46e0c25a4d65a4452d3c6d17d6020d09851c Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 4 Feb 2024 02:25:13 +0900 Subject: [PATCH 061/316] =?UTF-8?q?feat:=20=EC=97=AC=ED=96=89=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EC=83=9D=EC=84=B1=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 2 ++ src/rule/rule.controller.ts | 28 ++++++++++++++++++----- src/rule/rule.converter.ts | 36 ++++++++++++++++++++++++++++++ src/rule/rule.module.ts | 4 ++-- src/rule/rule.service.ts | 36 +++++------------------------- src/user/user.service.ts | 1 - 6 files changed, 69 insertions(+), 38 deletions(-) create mode 100644 src/rule/rule.converter.ts diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 377cf0e..f3885b2 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -12,12 +12,14 @@ export enum ResponseCode { /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', SIGNATURE_CREATED = 'CREATED', + RULE_CREATED = 'CREATED', /* 400 BAD_REQUEST : 잘못된 요청 */ AUTH_NUMBER_INCORRECT = 'BAD_REQUEST', RESET_PASSWORD_FAIL_MATCH = 'BAD_REQUEST', SIGNATURE_CREATION_FAIL = 'BAD_REQUEST', + RULE_CREATION_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 003cac5..4db9727 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,6 +1,8 @@ -import { Controller, Post, Body, Param } from '@nestjs/common'; +import { Controller, Post, Body } from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; +import { ResponseCode } from '../response/response-code.enum'; +import { ResponseDto } from '../response/response.dto' @Controller('mate/rule') export class RuleController { @@ -8,9 +10,25 @@ export class RuleController { private readonly ruleCreateService: RuleService, ) {} - @Post('/write/:userId') - async createRule(@Body() createRuleDto: CreateRuleDto, @Param('userId') userId: number) { - return this.ruleCreateService.createRule(createRuleDto, userId); - } + // 여행 규칙 생성하기 + @Post('/write') + async createRule(@Body() createRuleDto: CreateRuleDto): Promise> { + const result = await this.ruleCreateService.createRule(createRuleDto); + + if(!result){ + return new ResponseDto( + ResponseCode.RULE_CREATION_FAIL, + false, + "여행 규칙 생성 실패", + null); + } + else{ + return new ResponseDto( + ResponseCode.RULE_CREATED, + true, + "여행 규칙 생성 성공", + result); + } + } } \ No newline at end of file diff --git a/src/rule/rule.converter.ts b/src/rule/rule.converter.ts new file mode 100644 index 0000000..34e9de8 --- /dev/null +++ b/src/rule/rule.converter.ts @@ -0,0 +1,36 @@ +import { Injectable } from '@nestjs/common'; +import { RuleMainEntity } from './domain/rule.main.entity'; +import { RuleSubEntity } from './domain/rule.sub.entity'; +import { RuleInvitationEntity } from './domain/rule.invitation.entity'; +import { UserEntity } from 'src/user/user.entity'; +import { CreateRuleDto } from './dto/create-rule.dto'; + +@Injectable() +export class RuleConverter { + + async toEntity(dto: CreateRuleDto): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleInvitationEntity[] }> { + const main = new RuleMainEntity(); + main.mainTitle = dto.mainTitle; + + const rules = dto.rulePairs.map(pair => { + const sub = new RuleSubEntity(); + sub.ruleTitle = pair.ruleTitle; + sub.ruleDetail = pair.ruleDetail; + return sub; + }); + + const inviter = await UserEntity.findOneOrFail({ where: { id: dto.inviterId } }); + const rule = await RuleMainEntity.findOneOrFail({ where: { id: dto.inviterId } }); + const invitations = await Promise.all(dto.invitedId.map(async invitedId => { + const invited = await UserEntity.findOneOrFail({ where: { id: invitedId } }); + + const invitation = new RuleInvitationEntity(); + invitation.inviter = inviter; + invitation.rule = rule; + invitation.invited = invited; + return invitation; + })); + + return { main, rules, invitations }; + } +} diff --git a/src/rule/rule.module.ts b/src/rule/rule.module.ts index e18fec5..2bc25de 100644 --- a/src/rule/rule.module.ts +++ b/src/rule/rule.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { RuleService } from './rule.service'; import { RuleController } from './rule.controller'; -// import { RuleConverter } from './rule.converter'; +import { RuleConverter } from './rule.converter'; @Module({ controllers: [RuleController], - providers: [RuleService], + providers: [RuleService, RuleConverter], }) export class RuleModule {} diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 23df9da..3295767 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,9 +1,6 @@ import { Injectable } from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; -// import { RuleConverter } from './rule.converter'; - -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { RuleConverter } from './rule.converter'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; import { RuleInvitationEntity } from './domain/rule.invitation.entity'; @@ -14,41 +11,20 @@ export class RuleService { private ruleConverter: RuleConverter, ) {} - async createRule(createRuleDto: CreateRuleDto, userId: number): Promise { + async createRule(createRuleDto: CreateRuleDto): Promise { // Extract necessary information from the DTO const { main, rules, invitations } = await this.ruleConverter.toEntity(createRuleDto); // Save main rule entity - const savedMain = await this.ruleMainRepository.save(main); + const savedMain = await RuleMainEntity.save(main); // Save rule sub entities - const savedRules = await this.ruleSubRepository.save(rules); + const savedRules = await RuleSubEntity.save(rules); // Save rule invitation entities - const savedInvitations = await this.ruleInvitationRepository.save(invitations); + const savedInvitations = await RuleInvitationEntity.save(invitations); // Return response - return { - status: 201, - success: true, - message: '여행 규칙 작성 및 메이트 초대 성공', - data: { - write: { - id: savedMain.id, - mainTitle: savedMain.mainTitle, - created: savedMain.created.getTime(), - rules: savedRules.map(rule => ({ - id: rule.id, - ruleTitle: rule.ruleTitle, - ruleDetail: rule.ruleDetail, - })), - }, - invitations: savedInvitations.map(invitation => ({ - id: invitation.id, - inviterId: userId, - invitedId: invitation.invited.id, - })), - } - }; + return savedMain.id; } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 23fd05e..0e64e3c 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -3,7 +3,6 @@ import bcrypt from 'bcrypt'; import jsonwebtoken from 'jsonwebtoken'; import { UserEntity } from './user.entity'; import { IReqUser } from './user.dto'; -import { UserFollowingEntity } from './user.following.entity'; @Injectable() export class UserService { From 98ef971f67687d58c6bd5e22d26e401a63435558 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 4 Feb 2024 02:25:55 +0900 Subject: [PATCH 062/316] =?UTF-8?q?feat=20:=20journey=EC=97=90=20userId=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 -- src/database/database.providers.ts | 2 +- src/detail-schedule/detail-schedule.entity.ts | 4 +-- src/diary/models/diary.entity.ts | 10 +++--- src/diary/models/diary.image.entity.ts | 4 +-- src/journey/dtos/create-journey.dto.ts | 5 ++- src/journey/journey.controller.ts | 14 ++++++-- src/journey/journey.service.ts | 26 ++++++++------- src/journey/model/journey.entity.ts | 7 ++-- src/location/location.entity.ts | 2 +- src/schedule/schedule.entity.ts | 16 ++++------ src/schedule/schedule.service.ts | 2 +- src/user/user.entity.ts | 18 +++++------ src/user/user.guard.ts | 32 +++++++++++++++++++ 14 files changed, 93 insertions(+), 51 deletions(-) create mode 100644 src/user/user.guard.ts diff --git a/src/app.module.ts b/src/app.module.ts index 6d7bed3..491a288 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,7 +10,6 @@ import { LocationModule } from './location/location.module'; import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; import { JourneyModule } from './journey/journey.module'; -import { DateGroupModule } from './date-group/date-group.module'; import { SignatureModule } from './signature/signature.module'; import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; @@ -27,7 +26,6 @@ import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; DetailScheduleModule, PlaceModule, JourneyModule, - DateGroupModule, SignatureModule, ], controllers: [AppController], diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index ec934c7..08cb027 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: true, + synchronize: false, }); return dataSource.initialize(); diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index dfa8068..9a79505 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -26,7 +26,7 @@ export class DetailScheduleEntity extends BaseEntity { isDone: boolean; @ManyToOne(() => ScheduleEntity, (schedule) => schedule.detailSchedules) - scheduleId: ScheduleEntity; + schedule: ScheduleEntity; @CreateDateColumn() created: Date; @@ -40,7 +40,7 @@ export class DetailScheduleEntity extends BaseEntity { //세부 일정 추가하기 static async createDetailSchedule(scheduleId) { const detailSchedule = new DetailScheduleEntity(); - detailSchedule.scheduleId = scheduleId; + detailSchedule.schedule = scheduleId; return await detailSchedule.save(); } //세부 일정 작성하기 diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 742b5c2..8d1c1cc 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -50,13 +50,13 @@ export class DiaryEntity extends BaseEntity { @Column({ nullable: true, type: 'mediumtext' }) content: string; - @OneToOne(() => DiaryImageEntity, (image) => image.diaryId, { + @OneToOne(() => DiaryImageEntity, (image) => image.diary, { cascade: true, }) image: DiaryImageEntity; - @OneToOne(() => ScheduleEntity, (schedule) => schedule.diary) - scheduleId: ScheduleEntity; + @ManyToOne(() => ScheduleEntity, (schedule) => schedule.diary) + schedule: ScheduleEntity; @CreateDateColumn() created: Date; @@ -68,9 +68,9 @@ export class DiaryEntity extends BaseEntity { deleted: Date; /*일지 생성하기*/ - static async createDiary(journey, schedule) { + static async createDiary(schedule) { const diary = new DiaryEntity(); - diary.scheduleId = schedule.id; + diary.schedule = schedule.id; return await diary.save(); } diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index de049f4..1796c12 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -20,7 +20,7 @@ export class DiaryImageEntity extends BaseEntity { imageUrl: string; @OneToOne(() => DiaryEntity, (diary) => diary.image) - diaryId: DiaryEntity; + diary: DiaryEntity; @CreateDateColumn() created: Date; @@ -34,7 +34,7 @@ export class DiaryImageEntity extends BaseEntity { static async createDiaryImg(diaryId, ImgUrl: string) { const diaryImg = new DiaryImageEntity(); diaryImg.imageUrl = ImgUrl; - diaryImg.diaryId = diaryId; + diaryImg.diary = diaryId; await diaryImg.save(); } } diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts index 5cd6b20..a7c2975 100644 --- a/src/journey/dtos/create-journey.dto.ts +++ b/src/journey/dtos/create-journey.dto.ts @@ -1,7 +1,10 @@ // create-journey.dto.ts -import { IsString, IsDateString } from 'class-validator'; +import { IsString, IsDateString, IsNumber } from 'class-validator'; export class CreateJourneyDto { + @IsNumber() + userId: number; + @IsString() title: string; diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 71ebf51..48f2ef9 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,11 +1,14 @@ -import { Controller, Post, Body } from '@nestjs/common'; -import { JourneyService } from './journey.service'; +import { Controller, Post, Body, Req, UseGuards } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +// import { Request } from 'express'; +// import { UserGuard } from 'src/user/user.guard'; +import { JourneyService } from './journey.service'; import { CreateJourneyDto } from './dtos/create-journey.dto'; @Controller('api/journey') export class JourneyController { constructor(private readonly journeyService: JourneyService) {} + /*여정 저장하기*/ @ApiOperation({ summary: '여정 저장하기', description: '날짜와 제목을 포함합니다.', @@ -13,8 +16,13 @@ export class JourneyController { @ApiOkResponse({ description: '성공 ', }) + // @UseGuards(UserGuard) @Post('create') - async createJourney(@Body() createJourneyDto: CreateJourneyDto) { + async createJourney( + @Body() createJourneyDto: CreateJourneyDto, + // @Req() req: Request, + ) { + // const user = req.user; const result = await this.journeyService.createJourney(createJourneyDto); return result; } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 6cfdbfa..054504f 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -9,20 +9,12 @@ import { DiaryEntity } from 'src/diary/models/diary.entity'; @Injectable() export class JourneyService { + //여정 생성하기 - 일정, 일지 함께 생성 async createJourney(createJourneyDto: CreateJourneyDto) { - /*const dates: CreateDateGroupDto = new CreateDateGroupDto(); - { - (dates.startDate = createJourneyDto.startDate), - (dates.endDate = createJourneyDto.endDate); - } - const dateGroup = await DateGroupEntity.createDateGroup(dates); - if (!dateGroup) { - throw new Error(); - }*/ - + //여정 제목, 날짜 저장하기 const journey = await JourneyEntity.createJourney(createJourneyDto); - // let schedule = await ScheduleEntity.createSchedule(dates); + //일정 배너 생성하기 let currentDate = new Date(createJourneyDto.startDate); const lastDate = new Date(createJourneyDto.endDate); @@ -31,7 +23,8 @@ export class JourneyService { journey, currentDate, ); - const diary = await DiaryEntity.createDiary(journey, schedule); + //일지 생성하기 + const diary = await DiaryEntity.createDiary(schedule); currentDate = new Date(currentDate); currentDate.setDate(currentDate.getDate() + 1); } @@ -39,3 +32,12 @@ export class JourneyService { return response(BaseResponse.JOURNEY_CREATED); } } +/*const dates: CreateDateGroupDto = new CreateDateGroupDto(); + { + (dates.startDate = createJourneyDto.startDate), + (dates.endDate = createJourneyDto.endDate); + } + const dateGroup = await DateGroupEntity.createDateGroup(dates); + if (!dateGroup) { + throw new Error(); + }*/ diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index cf3c59c..ea2c5a3 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -32,9 +32,9 @@ export class JourneyEntity extends BaseEntity { endDate: string; @ManyToOne(() => UserEntity, (user) => user.journeys) - userId: UserEntity; + user: UserEntity; - @OneToMany(() => ScheduleEntity, (schedule) => schedule.journeyId) + @OneToMany(() => ScheduleEntity, (schedule) => schedule.journey) schedules: ScheduleEntity[]; @CreateDateColumn() @@ -46,12 +46,13 @@ export class JourneyEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createJourney(createJourneyDto: CreateJourneyDto) { + static async createJourney(createJourneyDto) { try { const journey: JourneyEntity = new JourneyEntity(); journey.title = createJourneyDto.title; journey.startDate = createJourneyDto.startDate; journey.endDate = createJourneyDto.endDate; + journey.user = createJourneyDto.userId; return await journey.save(); } catch (error) { diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index c99290e..986b08f 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -21,7 +21,7 @@ export class LocationEntity extends BaseEntity { @Column({ type: 'decimal', precision: 10, scale: 6 }) longitude: number; - @OneToOne(() => ScheduleEntity, (schedule) => schedule.locationId) + @OneToOne(() => ScheduleEntity, (schedule) => schedule.location) schedule: ScheduleEntity; @CreateDateColumn() diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 803458e..79b3a6e 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -5,9 +5,7 @@ import { UpdateDateColumn, DeleteDateColumn, Entity, - JoinColumn, ManyToOne, - OneToOne, OneToMany, PrimaryGeneratedColumn, } from 'typeorm'; @@ -29,20 +27,20 @@ export class ScheduleEntity extends BaseEntity { @Column({ nullable: true }) title: string; - @ManyToOne(() => LocationEntity, (location) => location.id) - locationId: Location; + @ManyToOne(() => LocationEntity, (location) => location.schedule) + location: LocationEntity; @ManyToOne(() => JourneyEntity, (journey) => journey.schedules) - journeyId: JourneyEntity; + journey: JourneyEntity; @OneToMany( () => DetailScheduleEntity, - (detailSchedule) => detailSchedule.scheduleId, + (detailSchedule) => detailSchedule.schedule, ) detailSchedules: DetailScheduleEntity[]; - @OneToOne(() => DiaryEntity, (diary) => diary.scheduleId, { cascade: true }) - diary: DiaryEntity; + @OneToMany(() => DiaryEntity, (diary) => diary.schedule) + diary: DiaryEntity[]; @CreateDateColumn() created: Date; @@ -56,7 +54,7 @@ export class ScheduleEntity extends BaseEntity { static async createSchedule(journey, currentDate) { const schedule = new ScheduleEntity(); schedule.date = currentDate.toISOString().split('T')[0]; - + schedule.journey = journey.id; return await schedule.save(); } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 7f813fb..ea548e1 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -15,7 +15,7 @@ export class ScheduleService { async updateScheduleLocation(scheduleId, scheduleLocation) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); - if (schedule.locationId) { + if (schedule.location) { const location = LocationEntity.updateLocation( schedule, scheduleLocation, diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 3ed5e03..25d01e8 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -20,28 +20,28 @@ export class UserEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column() + @Column({ nullable: true }) name: string; - @Column() + @Column({ nullable: true }) email: string; - @Column() + @Column({ nullable: true }) password: string; - @Column({ type: 'text' }) + @Column({ type: 'text', nullable: true }) bio: string; - @Column() + @Column({ nullable: true }) age: number; - @Column({ type: 'enum', enum: ['MALE', 'FEMALE', 'UNKNOWN'] }) + @Column({ type: 'enum', enum: ['MALE', 'FEMALE', 'UNKNOWN'], nullable: true }) gender: 'MALE' | 'FEMALE' | 'UNKNOWN'; - @Column({ type: 'enum', enum: ['KAKAO', 'GOOGLE'] }) + @Column({ type: 'enum', enum: ['KAKAO', 'GOOGLE'], nullable: true }) oauthType: 'KAKAO' | 'GOOGLE'; - @Column() + @Column({ nullable: true }) oauthToken: string; @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user) @@ -62,7 +62,7 @@ export class UserEntity extends BaseEntity { ) likes: SignatureLikeEntity[]; - @OneToMany(() => JourneyEntity, (journey) => journey.userId) + @OneToMany(() => JourneyEntity, (journey) => journey.user) journeys: JourneyEntity[]; @CreateDateColumn() diff --git a/src/user/user.guard.ts b/src/user/user.guard.ts new file mode 100644 index 0000000..6abfd5a --- /dev/null +++ b/src/user/user.guard.ts @@ -0,0 +1,32 @@ +import Express from 'express'; +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import * as jsonwebtoken from 'jsonwebtoken'; +import { IReqUser } from './user.dto'; + +@Injectable() +export class UserGuard implements CanActivate { + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + const authorization = request.headers['authorization']?.split(' '); + + if (authorization.length === 2 && authorization[0] === 'Bearer') { + const token = authorization[1]; + + try { + request.user = jsonwebtoken.verify( + token, + process.env.JWT_SECRET, + ) as IReqUser; + } catch (error) { + return false; + } + } else { + return false; + } + + return true; + } +} From 0bcface89fb4f1fde7848ae202396ce046ed597c Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 4 Feb 2024 03:55:16 +0900 Subject: [PATCH 063/316] chore: Enable CORS for development purpose --- src/main.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.ts b/src/main.ts index 0c38314..26e06ad 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,8 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + // Todo: Enable CORS only for development, specify the origin in production + app.enableCors(); app.setGlobalPrefix('api/v1'); await app.listen(3000); } From 9b67a0635b809db97edf01a93cd8c3d8157a730f Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 4 Feb 2024 05:58:27 +0900 Subject: [PATCH 064/316] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/domain/rule.invitation.entity.ts | 2 +- src/rule/rule.controller.ts | 1 - src/rule/rule.service.ts | 5 ----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index 8bb4a81..2a9900f 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; -import { UserEntity } from '../../user/user.entity'; +import { UserEntity } from 'src/user/user.entity'; import { RuleMainEntity } from './rule.main.entity' @Entity() diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 4db9727..fe4f88c 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -10,7 +10,6 @@ export class RuleController { private readonly ruleCreateService: RuleService, ) {} - // 여행 규칙 생성하기 @Post('/write') async createRule(@Body() createRuleDto: CreateRuleDto): Promise> { const result = await this.ruleCreateService.createRule(createRuleDto); diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 3295767..3e3316c 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -12,19 +12,14 @@ export class RuleService { ) {} async createRule(createRuleDto: CreateRuleDto): Promise { - // Extract necessary information from the DTO const { main, rules, invitations } = await this.ruleConverter.toEntity(createRuleDto); - // Save main rule entity const savedMain = await RuleMainEntity.save(main); - // Save rule sub entities const savedRules = await RuleSubEntity.save(rules); - // Save rule invitation entities const savedInvitations = await RuleInvitationEntity.save(invitations); - // Return response return savedMain.id; } } From c480e8cdb596ae061ef7d93c673d15dd42b4ec15 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 4 Feb 2024 11:58:17 +0900 Subject: [PATCH 065/316] =?UTF-8?q?fix=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=98=EA=B8=B0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 22 +++-------- src/location/dtos/location-info.dto.ts | 0 src/location/location.entity.ts | 24 +++++++++--- src/main.ts | 2 +- src/schedule/dtos/update-schedule-dto.ts | 13 ++++++- src/schedule/schedule.controller.ts | 34 +++------------- src/schedule/schedule.entity.ts | 4 +- src/schedule/schedule.service.ts | 49 +++++++++++++++++++----- 8 files changed, 82 insertions(+), 66 deletions(-) create mode 100644 src/location/dtos/location-info.dto.ts diff --git a/package-lock.json b/package-lock.json index cc89104..0e5e9b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2832,18 +2832,6 @@ "typeorm": "^0.3.0" } }, - "node_modules/@nestjs/typeorm/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3898,17 +3886,17 @@ "@types/superagent": "*" } }, - "node_modules/@types/validator": { - "version": "13.11.8", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", - "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" - }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", diff --git a/src/location/dtos/location-info.dto.ts b/src/location/dtos/location-info.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 986b08f..16b39d0 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -33,11 +33,11 @@ export class LocationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createLocation(scheduleLocation) { + static async createLocation(updateScheduleDto) { try { const location: LocationEntity = new LocationEntity(); - location.latitude = scheduleLocation.latitude; - location.longitude = scheduleLocation.longitude; + location.latitude = updateScheduleDto.latitude; + location.longitude = updateScheduleDto.longitude; return await location.save(); } catch (error) { @@ -45,17 +45,29 @@ export class LocationEntity extends BaseEntity { } } - static async updateLocation(schedule, scheduleLocation) { + static async updateLocation(schedule, updateScheduleDto) { try { const location = await LocationEntity.findOneOrFail({ where: { id: schedule.locationId }, }); - location.latitude = scheduleLocation.latitude; - location.longitude = scheduleLocation.longitude; + location.latitude = updateScheduleDto.latitude; + location.longitude = updateScheduleDto.longitude; return await location.save(); } catch (error) { throw new Error(error); } } + + static async findExistLocation(updateScheduleDto) { + { + } + const location = await LocationEntity.findOne({ + where: { + latitude: updateScheduleDto.latitude, + longitude: updateScheduleDto.longitude, + }, + }); + return location; + } } diff --git a/src/main.ts b/src/main.ts index 0c38314..5da35ea 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); - app.setGlobalPrefix('api/v1'); + // app.setGlobalPrefix('api/v1'); await app.listen(3000); } bootstrap(); diff --git a/src/schedule/dtos/update-schedule-dto.ts b/src/schedule/dtos/update-schedule-dto.ts index eddb290..dfaa492 100644 --- a/src/schedule/dtos/update-schedule-dto.ts +++ b/src/schedule/dtos/update-schedule-dto.ts @@ -1,7 +1,16 @@ // Update-schedule.dto.ts -import { IsString } from 'class-validator'; +import { IsString, IsNumber, IsOptional } from 'class-validator'; -export class UpdateScheduleTitleDto { +export class UpdateScheduleDto { @IsString() + @IsOptional() title: string; + + @IsOptional() + @IsNumber() + latitude: number; + + @IsOptional() + @IsNumber() + longitude: number; } diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index 152cb5f..bfa1701 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Put, Param } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { ScheduleService } from './schedule.service'; -import { UpdateScheduleTitleDto } from './dtos/update-schedule-dto'; +import { UpdateScheduleDto } from './dtos/update-schedule-dto'; import { CreateLocationDto } from 'src/location/dtos/create-location.dto'; @Controller('api/schedule') @@ -10,39 +10,17 @@ export class ScheduleController { @ApiOperation({ summary: '여정 작성하기', - description: '제목을 작성합니다.', + description: '제목과 위치를 작성합니다.', }) @ApiOkResponse({ description: '성공 ', }) - @Put('update-title/:scheduleId') - async updateScheduleTitle( + @Put('update/:scheduleId') + async updateSchedule( @Param('scheduleId') scheduleId: number, - @Body() updateScheduleTitleDto: UpdateScheduleTitleDto, + @Body() body: UpdateScheduleDto, ) { - const result = await this.scheduleService.updateScheduleTitle( - scheduleId, - updateScheduleTitleDto, - ); - return result; - } - - @ApiOperation({ - summary: '여정 작성하기', - description: '위치를 태그합니다.', - }) - @ApiOkResponse({ - description: '성공 ', - }) - @Put('update-location/:scheduleId') - async updateScheduleLocation( - @Param('scheduleId') scheduleId: number, - @Body() createLocationDto: CreateLocationDto, - ) { - const result = await this.scheduleService.updateScheduleLocation( - scheduleId, - createLocationDto, - ); + const result = await this.scheduleService.updateSchedule(scheduleId, body); return result; } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 79b3a6e..0b3f705 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -58,8 +58,8 @@ export class ScheduleEntity extends BaseEntity { return await schedule.save(); } - static async updateScheduleTitle(schedule, title) { - schedule.title = title; + static async updateScheduleTitle(schedule, updateScheduleDto) { + schedule.title = updateScheduleDto.title; return await schedule.save(); } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index ea548e1..b85395d 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -3,29 +3,58 @@ import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; +import { UpdateScheduleDto } from './dtos/update-schedule-dto'; @Injectable() export class ScheduleService { - async updateScheduleTitle(scheduleId, scheduleTitle) { + //일정 작성하기 + async updateSchedule(scheduleId, updateScheduleDto) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); - await ScheduleEntity.updateScheduleTitle(schedule, scheduleTitle.title); + + if (!updateScheduleDto.latitude || !updateScheduleDto.longitude) { + await ScheduleEntity.updateScheduleTitle(schedule, updateScheduleDto); + } else if (!updateScheduleDto.title) { + await this.updateScheduleLocation(schedule, updateScheduleDto); + } else { + await this.updateScheduleLocation(schedule, updateScheduleDto); + await ScheduleEntity.updateScheduleTitle(schedule, updateScheduleDto); + } return response(BaseResponse.SCHEDULE_UPDATED); } - async updateScheduleLocation(scheduleId, scheduleLocation) { - const schedule = await ScheduleEntity.findExistSchedule(scheduleId); - if (schedule.location) { + async updateScheduleLocation(schedule, updateScheduleDto) { + const existLocation = await LocationEntity.findExistLocation( + updateScheduleDto, + ); + if (existLocation) { + await ScheduleEntity.updateScheduleLocation(schedule, existLocation); + } else if (schedule.location) { const location = LocationEntity.updateLocation( schedule, - scheduleLocation, + updateScheduleDto, ); - await ScheduleEntity.updateScheduleLocation(schedule, location); + console.log(location); } else { - const location = await LocationEntity.createLocation(scheduleLocation); + const location = await LocationEntity.createLocation(updateScheduleDto); await ScheduleEntity.updateScheduleLocation(schedule, location); + console.log(location); } - - return response(BaseResponse.SCHEDULE_UPDATED); } + // const existLocation = LocationEntity.findExistLocation(updateScheduleDto); + // if (!existLocation) { + // if (!schedule.location) { + // const location = await LocationEntity.createLocation(updateScheduleDto); + // await ScheduleEntity.updateScheduleLocation(schedule, location); + // console.log(location); + // } else { + // const location = LocationEntity.updateLocation( + // schedule, + // updateScheduleDto, + // ); + // console.log(location); + // } + // } + // await ScheduleEntity.updateScheduleLocation(schedule, existLocation); + // } } From 95380411265af0e7ad4b03f4d123571d8f364147 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 4 Feb 2024 11:59:21 +0900 Subject: [PATCH 066/316] =?UTF-8?q?chore:=EC=A3=BC=EC=84=9D=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schedule/schedule.service.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index b85395d..292b93b 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -41,20 +41,4 @@ export class ScheduleService { console.log(location); } } - // const existLocation = LocationEntity.findExistLocation(updateScheduleDto); - // if (!existLocation) { - // if (!schedule.location) { - // const location = await LocationEntity.createLocation(updateScheduleDto); - // await ScheduleEntity.updateScheduleLocation(schedule, location); - // console.log(location); - // } else { - // const location = LocationEntity.updateLocation( - // schedule, - // updateScheduleDto, - // ); - // console.log(location); - // } - // } - // await ScheduleEntity.updateScheduleLocation(schedule, existLocation); - // } } From 6191ab11e6f1d7640063f4998cdcae2bd6d800f3 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 4 Feb 2024 16:57:13 +0900 Subject: [PATCH 067/316] Update signature.controller.ts --- src/signature/signature.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 495d46c..8d6660d 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -10,7 +10,7 @@ import { DetailSignatureDto } from './dto/detail-signature.dto'; import { TmpUserIdDto } from './dto/tmp-userId.dto'; import { SignatureEntity } from './domain/signature.entity'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { LikeSignatureDto } from './dto/Like-signature.dto'; +import { LikeSignatureDto } from './dto/like-signature.dto'; @Controller('signature') From 916b20548bb46decc42ecbcdb60b1a14fc22431d Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 4 Feb 2024 17:45:56 +0900 Subject: [PATCH 068/316] =?UTF-8?q?feat=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20url=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aws-s3/aws-s3.module.ts | 27 ----------------------- aws-s3/aws-s3.service.ts | 30 -------------------------- src/app.module.ts | 2 ++ src/diary/diary.controller.ts | 4 ++-- src/diary/diary.module.ts | 4 ++-- src/diary/diary.service.ts | 22 ++++++++----------- src/diary/models/diary.entity.ts | 8 +++---- src/diary/models/diary.image.entity.ts | 6 ++++-- src/main.ts | 1 + src/utils/S3.service.ts | 10 ++++++--- 10 files changed, 30 insertions(+), 84 deletions(-) delete mode 100644 aws-s3/aws-s3.module.ts delete mode 100644 aws-s3/aws-s3.service.ts diff --git a/aws-s3/aws-s3.module.ts b/aws-s3/aws-s3.module.ts deleted file mode 100644 index 9ce9044..0000000 --- a/aws-s3/aws-s3.module.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { S3Client } from '@aws-sdk/client-s3'; -import { Module } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { AwsS3Service } from './aws-s3.service'; - -@Module({ - imports: [], - controllers: [], - providers: [ - { - provide: 'S3_CLIENT', - inject: [ConfigService], - useFactory: (configService: ConfigService) => { - return new S3Client({ - region: configService.get('AWS_REGION'), - credentials: { - accessKeyId: configService.get('S3_ACCESS_KEY')!, - secretAccessKey: configService.get('S3_SECRET_KEY')!, - }, - }); - }, - }, - AwsS3Service, - ], - exports: [AwsS3Service], -}) -export class AwsS3Module {} diff --git a/aws-s3/aws-s3.service.ts b/aws-s3/aws-s3.service.ts deleted file mode 100644 index 819038f..0000000 --- a/aws-s3/aws-s3.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { Inject, Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { GetDiaryImgUrlDto } from 'src/diary/dtos/get-diary-img-url.dto'; - -@Injectable() -export class AwsS3Service { - constructor( - @Inject('S3_CLIENT') - private readonly s3Client: S3Client, - - private readonly configService: ConfigService, - ) {} - async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { - // 저장될 파일 이름 - const fileName = `diary/${diaryId}/${Date.now()}${ - getDiaryImgUrlDto.fileName - }`; - - // Put. 즉, s3에 데이터를 집어넣는 작업에 대한 url 생성 - const command = new PutObjectCommand({ - Bucket: this.configService.get('S3_BUCKET'), - Key: fileName, - }); - - const postedImgUrl = await getSignedUrl(this.s3Client, command); - return postedImgUrl; - } -} diff --git a/src/app.module.ts b/src/app.module.ts index 491a288..fd6b0f8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { PlaceModule } from './place/place.module'; import { JourneyModule } from './journey/journey.module'; import { SignatureModule } from './signature/signature.module'; import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; +import { S3Module } from './utils/S3.module'; @Module({ imports: [ @@ -27,6 +28,7 @@ import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; PlaceModule, JourneyModule, SignatureModule, + S3Module, ], controllers: [AppController], providers: [AppService], diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 74bdd46..ac95308 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -36,9 +36,9 @@ export class DiaryController { @Post('image-url/:diaryId') async getDiaryImageUrl( @Param('diaryId') diaryId: number, - @Body() body: GetDiaryImgUrlDto, + @Body('fileName') fileName: string, ) { - const result = await this.diaryService.getDiaryImgUrl(diaryId, body); + const result = await this.diaryService.getDiaryImgUrl(diaryId, fileName); return result; } } diff --git a/src/diary/diary.module.ts b/src/diary/diary.module.ts index 145a1ee..82161b7 100644 --- a/src/diary/diary.module.ts +++ b/src/diary/diary.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { DiaryService } from './diary.service'; import { DiaryController } from './diary.controller'; -import { AwsS3Module } from 'aws-s3/aws-s3.module'; +import { S3Module } from 'src/utils/S3.module'; @Module({ - imports: [AwsS3Module], + imports: [S3Module], controllers: [DiaryController], providers: [DiaryService], }) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index c2e3f61..d8779f6 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -1,16 +1,15 @@ import { Injectable } from '@nestjs/common'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; -import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { DiaryEntity } from './models/diary.entity'; import { DiaryImageEntity } from './models/diary.image.entity'; import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; -import { AwsS3Service } from '../../aws-s3/aws-s3.service'; +import { S3UtilService } from 'src/utils/S3.service'; @Injectable() export class DiaryService { - constructor(private readonly awsS3Service: AwsS3Service) {} + constructor(private readonly s3UtilService: S3UtilService) {} /*일지 작성하기*/ async postDiary(diaryId, diaryInfo: PostDiaryDto) { @@ -20,16 +19,13 @@ export class DiaryService { } /*일지 사진 S3에 업로드 후 url 받기*/ - async getDiaryImgUrl(diaryId: number, getDiaryImgUrlDto: GetDiaryImgUrlDto) { - const createImgUrl = await this.awsS3Service.getDiaryImgUrl( - diaryId, - getDiaryImgUrlDto, - ); - const createDiaryImg = await DiaryImageEntity.createDiaryImg( - diaryId, - createImgUrl, - ); - console.log(createImgUrl); + async getDiaryImgUrl(diaryId, fileName: string) { + const diary = await DiaryEntity.findExistDiary(diaryId); + const imageKey = this.s3UtilService.generateRandomImageKey(fileName); + // await this.s3UtilService.putObjectFromBase64(imageKey, fileName); + const imageUrl = await this.s3UtilService.getImageUrl(imageKey); + console.log('url', imageUrl); + await DiaryImageEntity.createDiaryImg(diary, imageUrl); return response(BaseResponse.DIARY_IMG_URL_CREATED); } } diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 8d1c1cc..3d86ef7 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -50,9 +50,7 @@ export class DiaryEntity extends BaseEntity { @Column({ nullable: true, type: 'mediumtext' }) content: string; - @OneToOne(() => DiaryImageEntity, (image) => image.diary, { - cascade: true, - }) + @OneToOne(() => DiaryImageEntity, (image) => image.diary, {}) image: DiaryImageEntity; @ManyToOne(() => ScheduleEntity, (schedule) => schedule.diary) @@ -76,7 +74,7 @@ export class DiaryEntity extends BaseEntity { } /*일지 작성하기*/ static async postDiary(diaryId, diaryInfo: PostDiaryDto) { - const diary = await this.findExistDairy(diaryId); + const diary = await this.findExistDiary(diaryId); diary.title = diaryInfo.title; diary.place = diaryInfo.place; diary.weather = diaryInfo.weather; @@ -86,7 +84,7 @@ export class DiaryEntity extends BaseEntity { return await diary.save(); } - static async findExistDairy(diaryId) { + static async findExistDiary(diaryId) { const diary = await DiaryEntity.findOne({ where: { id: diaryId }, }); diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index 1796c12..200c2d4 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -19,6 +19,7 @@ export class DiaryImageEntity extends BaseEntity { @Column({ type: 'mediumtext' }) imageUrl: string; + @JoinColumn() @OneToOne(() => DiaryEntity, (diary) => diary.image) diary: DiaryEntity; @@ -31,10 +32,11 @@ export class DiaryImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createDiaryImg(diaryId, ImgUrl: string) { + static async createDiaryImg(diary, ImgUrl: string) { const diaryImg = new DiaryImageEntity(); diaryImg.imageUrl = ImgUrl; - diaryImg.diary = diaryId; + diaryImg.diary = diary.id; + console.log(diaryImg.diary); await diaryImg.save(); } } diff --git a/src/main.ts b/src/main.ts index 5da35ea..566b6b5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.enableCors(); // app.setGlobalPrefix('api/v1'); await app.listen(3000); } diff --git a/src/utils/S3.service.ts b/src/utils/S3.service.ts index f203612..5438d0b 100644 --- a/src/utils/S3.service.ts +++ b/src/utils/S3.service.ts @@ -5,10 +5,13 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class S3UtilService { private readonly s3 = new S3({ - endpoint: process.env.S3_ENDPOINT ?? undefined, - accessKeyId: process.env.S3_ACCESS_KEY, - secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, signatureVersion: 'v4', + endpoint: process.env.S3_ENDPOINT, + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.S3_ACCESS_KEY, + secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, + }, }); public async listObjects() { @@ -51,6 +54,7 @@ export class S3UtilService { public generateRandomImageKey(originalName: string) { const ext = originalName.split('.').pop(); + console.log(ext); const uuid = uuidv4(); return `${uuid}.${ext}`; From 958a124cc83d8b4a8fb6eda670c7b8b626624b7f Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 4 Feb 2024 18:43:05 +0900 Subject: [PATCH 069/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=BD=94=EB=A9=98=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 46 +++++++++++++++++++++++++++ package.json | 2 ++ src/app.module.ts | 3 +- src/comment/comment.controller.ts | 36 +++++++++++++++++++++ src/comment/comment.converter.ts | 23 ++++++++++++++ src/comment/comment.module.ts | 10 ++++++ src/comment/comment.service.ts | 19 +++++++++++ src/comment/domain/comment.entity.ts | 29 +++++++++++++++++ src/comment/dto/create-comment.dto.ts | 11 +++++++ src/response/response-code.enum.ts | 2 ++ src/rule/domain/rule.main.entity.ts | 4 +++ src/rule/dto/create-rule.dto.ts | 4 --- src/rule/rule.controller.ts | 2 +- src/rule/rule.service.ts | 5 --- src/user/user.entity.ts | 4 +++ 15 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 src/comment/comment.controller.ts create mode 100644 src/comment/comment.converter.ts create mode 100644 src/comment/comment.module.ts create mode 100644 src/comment/comment.service.ts create mode 100644 src/comment/domain/comment.entity.ts create mode 100644 src/comment/dto/create-comment.dto.ts diff --git a/package-lock.json b/package-lock.json index a71d0ff..1ba6fbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", @@ -22,6 +23,7 @@ "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", "mysql2": "^3.9.0", + "passport": "^0.7.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.20", @@ -1842,6 +1844,15 @@ } } }, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.0.tgz", @@ -2346,6 +2357,11 @@ "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -7403,6 +7419,31 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7471,6 +7512,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", diff --git a/package.json b/package.json index 787055c..e6a5963 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/mapped-types": "*", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.2.0", "@nestjs/typeorm": "^10.0.1", @@ -33,6 +34,7 @@ "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", "mysql2": "^3.9.0", + "passport": "^0.7.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.20", diff --git a/src/app.module.ts b/src/app.module.ts index 6bd58ef..ba6deb8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,7 +10,7 @@ import { ScheduleModule } from './schedule/schedule.module'; import { PlaceModule } from './place/place.module'; import { SignatureModule } from './signature/signature.module'; import { RuleModule } from './rule/rule.module'; - +import { CommentModule } from './comment/comment.module'; @Module({ imports: [ @@ -25,6 +25,7 @@ import { RuleModule } from './rule/rule.module'; PlaceModule, SignatureModule, RuleModule, + CommentModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts new file mode 100644 index 0000000..8268bdd --- /dev/null +++ b/src/comment/comment.controller.ts @@ -0,0 +1,36 @@ +import { Controller, Post, Body, Req, UseGuards, Param } from '@nestjs/common'; +import { CommentService } from './comment.service'; +import { CreateCommentDto } from './dto/create-comment.dto'; +import { ResponseCode } from '../response/response-code.enum'; +import { ResponseDto } from '../response/response.dto'; +// import { UserGuard } from 'src/user/user.guard'; + +// @UseGuards(UserGuard) +@Controller('mate/rule') +export class CommentController { + constructor( + private readonly commentService: CommentService, + ) {} + + // 여행 규칙 코멘트 생성 + @Post('/:ruleId') + async createComment(@Body() createCommentDto: CreateCommentDto, @Param('ruleId') ruleId: number): Promise> { + const result = await this.commentService.createComment(createCommentDto, ruleId); + + if(!result){ + return new ResponseDto( + ResponseCode.COMMENT_CREATION_FAIL, + false, + "여행 규칙 코멘트 생성 실패", + null); + + } + else{ + return new ResponseDto( + ResponseCode.COMMENT_CREATED, + true, + "여행 규칙 코멘트 생성 성공", + result); + } + } +} \ No newline at end of file diff --git a/src/comment/comment.converter.ts b/src/comment/comment.converter.ts new file mode 100644 index 0000000..ca171f7 --- /dev/null +++ b/src/comment/comment.converter.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { CommentEntity } from './domain/comment.entity'; +import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; +import { UserEntity } from 'src/user/user.entity'; +import { CreateCommentDto } from './dto/create-comment.dto'; + +@Injectable() +export class CommentConverter { + + async toEntity(dto: CreateCommentDto, ruleId:number): Promise { + const comment = new CommentEntity(); + + comment.content = dto.content; + console.log(comment.content); + const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId } }); + comment.rule = rule; + console.log(comment.rule); + const user = await UserEntity.findOneOrFail({ where: { id: dto.userId } }); + comment.user = user; + + return comment; + } +} \ No newline at end of file diff --git a/src/comment/comment.module.ts b/src/comment/comment.module.ts new file mode 100644 index 0000000..f078682 --- /dev/null +++ b/src/comment/comment.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { CommentService } from './comment.service'; +import { CommentController } from './comment.controller'; +import { CommentConverter } from './comment.converter'; + +@Module({ + controllers: [CommentController], + providers: [CommentService, CommentConverter], +}) +export class CommentModule {} diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts new file mode 100644 index 0000000..72c5c66 --- /dev/null +++ b/src/comment/comment.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { CreateCommentDto } from './dto/create-comment.dto'; +import { CommentConverter } from './comment.converter'; +import { CommentEntity } from './domain/comment.entity'; + +@Injectable() +export class CommentService { + constructor( + private commentConverter: CommentConverter + ) {} + + async createComment(createCommentDto: CreateCommentDto, ruleId: number): Promise { + const comment = await this.commentConverter.toEntity(createCommentDto, ruleId); + + const savedComment = await CommentEntity.save(comment); + + return savedComment.id; + } +} diff --git a/src/comment/domain/comment.entity.ts b/src/comment/domain/comment.entity.ts new file mode 100644 index 0000000..64a0aae --- /dev/null +++ b/src/comment/domain/comment.entity.ts @@ -0,0 +1,29 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; +import { UserEntity } from 'src/user/user.entity'; + +@Entity() +export class CommentEntity extends BaseEntity { + @PrimaryGeneratedColumn({ type: 'bigint' }) + id: number; + + @Column({ type: 'varchar', length: 255 }) + content: string; + + @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.comments) + @JoinColumn({ name: 'rule_id'}) + rule: RuleMainEntity; + + @ManyToOne(() => UserEntity, user => user.comments) + @JoinColumn({ name: 'user_id'}) + user: UserEntity; + + @CreateDateColumn({ type: 'timestamp' }) + created: Date; + + @UpdateDateColumn({ type: 'timestamp' }) + updated: Date; + + @Column({ type: 'timestamp', nullable: true }) + deleted: Date; +} \ No newline at end of file diff --git a/src/comment/dto/create-comment.dto.ts b/src/comment/dto/create-comment.dto.ts new file mode 100644 index 0000000..182ca0f --- /dev/null +++ b/src/comment/dto/create-comment.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; + +export class CreateCommentDto { + @IsNotEmpty() + @IsNumber() + userId: number; + + @IsNotEmpty() + @IsString() + content: string; +} \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 8f4e687..ce55a30 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -17,6 +17,7 @@ export enum ResponseCode { SIGNATURE_CREATED = 'CREATED', RULE_CREATED = 'CREATED', LIKE_ON_SIGNATURE_CREATED = 'CREATED', + COMMENT_CREATED = 'CREATED', /* 400 BAD_REQUEST : 잘못된 요청 */ @@ -25,6 +26,7 @@ export enum ResponseCode { SIGNATURE_CREATION_FAIL = 'BAD_REQUEST', RULE_CREATION_FAIL = 'BAD_REQUEST', GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', + COMMENT_CREATION_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index 15a17ce..3a61e33 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -1,6 +1,7 @@ import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, OneToMany } from 'typeorm'; import { RuleSubEntity } from './rule.sub.entity'; import { RuleInvitationEntity } from './rule.invitation.entity' +import { CommentEntity } from 'src/comment/domain/comment.entity'; @Entity() export class RuleMainEntity extends BaseEntity { @@ -24,4 +25,7 @@ export class RuleMainEntity extends BaseEntity { @OneToMany(() => RuleInvitationEntity, ruleInvitation => ruleInvitation.rule) invitations: RuleInvitationEntity[]; + + @OneToMany(() => CommentEntity, comment => comment.rule) + comments: CommentEntity[]; } \ No newline at end of file diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index 5dcd0e1..185bfab 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -29,8 +29,4 @@ export class CreateRuleDto { @IsArray() @IsNumber({}, { each: true }) invitedId: number[]; - - @IsNotEmpty() - @IsNumber() - ruleId: number; } diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 4db9727..357d806 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -10,7 +10,7 @@ export class RuleController { private readonly ruleCreateService: RuleService, ) {} - // 여행 규칙 생성하기 + // 여행 규칙 생성 @Post('/write') async createRule(@Body() createRuleDto: CreateRuleDto): Promise> { const result = await this.ruleCreateService.createRule(createRuleDto); diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 3295767..3e3316c 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -12,19 +12,14 @@ export class RuleService { ) {} async createRule(createRuleDto: CreateRuleDto): Promise { - // Extract necessary information from the DTO const { main, rules, invitations } = await this.ruleConverter.toEntity(createRuleDto); - // Save main rule entity const savedMain = await RuleMainEntity.save(main); - // Save rule sub entities const savedRules = await RuleSubEntity.save(rules); - // Save rule invitation entities const savedInvitations = await RuleInvitationEntity.save(invitations); - // Return response return savedMain.id; } } diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 8366687..06ef313 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -14,6 +14,7 @@ import { UserFollowingEntity } from './user.following.entity'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; +import { CommentEntity } from 'src/comment/domain/comment.entity'; @Entity() export class UserEntity extends BaseEntity { @@ -68,6 +69,9 @@ export class UserEntity extends BaseEntity { @OneToMany(() => RuleInvitationEntity, (invitation) => invitation.invited) invitationsReceived: RuleInvitationEntity[]; + @OneToMany(() => CommentEntity, (comment) => comment.user) + comments: CommentEntity[]; + @CreateDateColumn() created: Date; From 23a4127b572585899c2a41e2f3c1d4908182be7f Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 4 Feb 2024 19:07:47 +0900 Subject: [PATCH 070/316] =?UTF-8?q?fix:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=EC=9D=B4=20=EC=A0=95=EC=83=81?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=9E=91=EB=8F=99=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 4e1fb6b..6861035 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,5 +1,5 @@ import { HttpException, Injectable, Logger } from '@nestjs/common'; -import bcrypt from 'bcrypt'; +import * as bcrypt from 'bcrypt'; import * as jsonwebtoken from 'jsonwebtoken'; import { UserEntity } from './user.entity'; import { IReqUser, IUserProfile } from './user.dto'; From 8fc880045e2fa3584158f1fa4c5376f665b995e0 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 4 Feb 2024 19:51:02 +0900 Subject: [PATCH 071/316] =?UTF-8?q?fix=20:=20invitedId=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createRuleDto 에서 invitedId 삭제 --- src/rule/dto/create-rule.dto.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index 5dcd0e1..185bfab 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -29,8 +29,4 @@ export class CreateRuleDto { @IsArray() @IsNumber({}, { each: true }) invitedId: number[]; - - @IsNotEmpty() - @IsNumber() - ruleId: number; } From 53d76990aef14c92c52ac101fdab0648369d4894 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 4 Feb 2024 23:04:36 +0900 Subject: [PATCH 072/316] =?UTF-8?q?feat=20:=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/detail-schedule/detail-schedule.controller.ts | 2 +- src/diary/diary.controller.ts | 2 +- src/journey/journey.controller.ts | 2 +- src/main.ts | 2 +- src/schedule/schedule.controller.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index ab97458..0ec7490 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -11,7 +11,7 @@ import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { DetailScheduleService } from './detail-schedule.service'; import { DetailContentDto } from './detail-schedule-info.dto'; -@Controller('api/detail-schedule') +@Controller('detail-schedule') export class DetailScheduleController { constructor(private readonly detailScheduleService: DetailScheduleService) {} diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index ac95308..fe889d6 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -4,7 +4,7 @@ import { DiaryService } from './diary.service'; import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; -@Controller('api/diary') +@Controller('diary') export class DiaryController { constructor(private readonly diaryService: DiaryService) {} diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 48f2ef9..b48ea32 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -5,7 +5,7 @@ import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { JourneyService } from './journey.service'; import { CreateJourneyDto } from './dtos/create-journey.dto'; -@Controller('api/journey') +@Controller('journey') export class JourneyController { constructor(private readonly journeyService: JourneyService) {} /*여정 저장하기*/ diff --git a/src/main.ts b/src/main.ts index 566b6b5..e8bc486 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors(); - // app.setGlobalPrefix('api/v1'); + app.setGlobalPrefix('api/v1'); await app.listen(3000); } bootstrap(); diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index bfa1701..e08bb7c 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -4,7 +4,7 @@ import { ScheduleService } from './schedule.service'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; import { CreateLocationDto } from 'src/location/dtos/create-location.dto'; -@Controller('api/schedule') +@Controller('schedule') export class ScheduleController { constructor(private readonly scheduleService: ScheduleService) {} From cf47d65890b25c01e301f3496d2d30df289f2af9 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 5 Feb 2024 00:12:40 +0900 Subject: [PATCH 073/316] =?UTF-8?q?chore=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B0=80=EB=8A=A5=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.controller.ts | 15 +++++++++------ src/journey/journey.service.ts | 6 +++--- src/journey/model/journey.entity.ts | 6 +++--- src/response/response.status.ts | 7 +++++++ src/user/user.entity.ts | 14 +++++++------- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index b48ea32..60486e1 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,7 +1,7 @@ import { Controller, Post, Body, Req, UseGuards } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; -// import { Request } from 'express'; -// import { UserGuard } from 'src/user/user.guard'; +import { Request } from 'express'; +import { UserGuard } from 'src/user/user.guard'; import { JourneyService } from './journey.service'; import { CreateJourneyDto } from './dtos/create-journey.dto'; @@ -16,14 +16,17 @@ export class JourneyController { @ApiOkResponse({ description: '성공 ', }) - // @UseGuards(UserGuard) + @UseGuards(UserGuard) @Post('create') async createJourney( @Body() createJourneyDto: CreateJourneyDto, - // @Req() req: Request, + @Req() req: Request, ) { - // const user = req.user; - const result = await this.journeyService.createJourney(createJourneyDto); + const user = req.user; + const result = await this.journeyService.createJourney( + user, + createJourneyDto, + ); return result; } } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 054504f..f59b2a5 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -10,11 +10,11 @@ import { DiaryEntity } from 'src/diary/models/diary.entity'; @Injectable() export class JourneyService { //여정 생성하기 - 일정, 일지 함께 생성 - async createJourney(createJourneyDto: CreateJourneyDto) { + async createJourney(user, createJourneyDto: CreateJourneyDto) { //여정 제목, 날짜 저장하기 - const journey = await JourneyEntity.createJourney(createJourneyDto); + const journey = await JourneyEntity.createJourney(user, createJourneyDto); - //일정 배너 생성하기 + //일정 배너 생성하기 : (startDate - endDate + 1)개 let currentDate = new Date(createJourneyDto.startDate); const lastDate = new Date(createJourneyDto.endDate); diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index ea2c5a3..919c330 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -28,7 +28,7 @@ export class JourneyEntity extends BaseEntity { @Column() startDate: string; - @Column() + @Column({ nullable: true }) endDate: string; @ManyToOne(() => UserEntity, (user) => user.journeys) @@ -46,13 +46,13 @@ export class JourneyEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async createJourney(createJourneyDto) { + static async createJourney(user, createJourneyDto) { try { const journey: JourneyEntity = new JourneyEntity(); journey.title = createJourneyDto.title; journey.startDate = createJourneyDto.startDate; journey.endDate = createJourneyDto.endDate; - journey.user = createJourneyDto.userId; + journey.user = user.id; return await journey.save(); } catch (error) { diff --git a/src/response/response.status.ts b/src/response/response.status.ts index b7649e2..eb3bf88 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -55,6 +55,13 @@ export const BaseResponse = { }, /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ + + JOURNEY_NOT_FOUND: { + success: false, + code: 404, + message: '아직 작성한 여정이 없어요!', + }, + SCHEDULE_NOT_FOUND: { success: false, code: 404, diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 1c756ed..8c00544 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -21,19 +21,19 @@ export class UserEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column({ nullable: true }) + @Column() name: string; - @Column({ nullable: true }) + @Column() email: string; - @Column({ nullable: true }) + @Column() password: string; @Column() nickname: string; - @Column({ type: 'text', nullable: true }) + @Column({ type: 'text' }) bio: string; @Column({ type: 'text' }) introduction: string; @@ -41,13 +41,13 @@ export class UserEntity extends BaseEntity { @Column() age: number; - @Column({ type: 'enum', enum: ['MALE', 'FEMALE', 'UNKNOWN'], nullable: true }) + @Column({ type: 'enum', enum: ['MALE', 'FEMALE', 'UNKNOWN'] }) gender: 'MALE' | 'FEMALE' | 'UNKNOWN'; - @Column({ type: 'enum', enum: ['KAKAO', 'GOOGLE'], nullable: true }) + @Column({ type: 'enum', enum: ['KAKAO', 'GOOGLE'] }) oauthType: 'KAKAO' | 'GOOGLE'; - @Column({ nullable: true }) + @Column() oauthToken: string; @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user) From 8f0d1d126f49b0fff183ffd3cb39312df1dbebdc Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 5 Feb 2024 03:16:26 +0900 Subject: [PATCH 074/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=B0=8F=20=EB=8C=93=EA=B8=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #45 : 여행 규칙 및 댓글 조회 (브랜치 오류) --- src/response/response-code.enum.ts | 2 + src/rule/domain/detail.rule.entity.ts | 0 src/rule/domain/rule.invitation.entity.ts | 12 +++- src/rule/domain/rule.main.entity.ts | 9 +++ src/rule/dto/comment-pair.dto.ts | 19 ++++++ src/rule/dto/detail-comment.dto.ts | 11 ++++ src/rule/dto/detail-member.dto.ts | 10 +++ src/rule/dto/detail-page.dto.ts | 12 ++++ src/rule/dto/detail-rule.dto.ts | 18 ++++++ src/rule/dto/member-pair.dto.ts | 15 +++++ src/rule/dto/meta-to-back.dto.ts | 16 +++++ src/rule/dto/meta-to-front.dto.ts | 20 ++++++ src/rule/dto/rule-pair.dto.ts | 11 ++++ src/rule/rule.controller.ts | 70 ++++++++++++++++++++- src/rule/rule.converter.ts | 77 +++++++++++++++++++++++ src/rule/rule.service.ts | 20 +++++- src/user/user.profile.image.entity.ts | 8 +++ 17 files changed, 325 insertions(+), 5 deletions(-) create mode 100644 src/rule/domain/detail.rule.entity.ts create mode 100644 src/rule/dto/comment-pair.dto.ts create mode 100644 src/rule/dto/detail-comment.dto.ts create mode 100644 src/rule/dto/detail-member.dto.ts create mode 100644 src/rule/dto/detail-page.dto.ts create mode 100644 src/rule/dto/detail-rule.dto.ts create mode 100644 src/rule/dto/member-pair.dto.ts create mode 100644 src/rule/dto/meta-to-back.dto.ts create mode 100644 src/rule/dto/meta-to-front.dto.ts create mode 100644 src/rule/dto/rule-pair.dto.ts diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index ce55a30..868f90b 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -10,6 +10,7 @@ export enum ResponseCode { GET_SIGNATURE_DETAIL_SUCCESS = 'OK', DELETE_LIKE_ON_SIGNATURE_SUCCESS = 'OK', UPDATE_PROFILE_SUCCESS = 'OK', + GET_RULE_DETAIL_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ @@ -27,6 +28,7 @@ export enum ResponseCode { RULE_CREATION_FAIL = 'BAD_REQUEST', GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', COMMENT_CREATION_FAIL = 'BAD_REQUEST', + GET_RULE_DETAIL_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/rule/domain/detail.rule.entity.ts b/src/rule/domain/detail.rule.entity.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index 8bb4a81..5000cc5 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -11,11 +11,21 @@ export class RuleInvitationEntity extends BaseEntity { @JoinColumn({name: 'rule_id'}) rule: RuleMainEntity; - @ManyToOne(() => UserEntity, user => user.invitationsSent) + @ManyToOne(() => UserEntity, user => user.invitationsSent) @JoinColumn({name: 'inviter_id'}) @JoinColumn({name: 'inviter_id'}) inviter: UserEntity; @ManyToOne(() => UserEntity, user => user.invitationsReceived) @JoinColumn({name: 'invited_id'}) invited: UserEntity; + + static async findNameById(inviterId: number): Promise<{ memberId : number, name : string }> { + const userEntity : UserEntity = await UserEntity.findOne({ + where: { id: inviterId } + }); + const memberId = inviterId; + const name = userEntity.name; + + return { memberId, name }; + } } \ No newline at end of file diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index 3a61e33..253b22b 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -28,4 +28,13 @@ export class RuleMainEntity extends BaseEntity { @OneToMany(() => CommentEntity, comment => comment.rule) comments: CommentEntity[]; + + static async findMainById(ruleId: number): Promise { + const ruleMain : RuleMainEntity = await RuleMainEntity.findOne({ + where: { id: ruleId }, + relations: ['rules', 'invitations', 'comments'] + }); + + return ruleMain; + } } \ No newline at end of file diff --git a/src/rule/dto/comment-pair.dto.ts b/src/rule/dto/comment-pair.dto.ts new file mode 100644 index 0000000..75989b2 --- /dev/null +++ b/src/rule/dto/comment-pair.dto.ts @@ -0,0 +1,19 @@ +import { IsNotEmpty, IsNumber, IsString, IsDate } from 'class-validator'; + +export class CommentPairDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + image: string; + + @IsNotEmpty() + @IsString() + text: string; + + @IsNotEmpty() + @IsDate() + created: Date; +} \ No newline at end of file diff --git a/src/rule/dto/detail-comment.dto.ts b/src/rule/dto/detail-comment.dto.ts new file mode 100644 index 0000000..7675dda --- /dev/null +++ b/src/rule/dto/detail-comment.dto.ts @@ -0,0 +1,11 @@ +import { IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { CommentPairDto } from './comment-pair.dto'; + +export class DetailCommentDto { + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CommentPairDto) + commentPairs: CommentPairDto[]; +} + diff --git a/src/rule/dto/detail-member.dto.ts b/src/rule/dto/detail-member.dto.ts new file mode 100644 index 0000000..0eccabb --- /dev/null +++ b/src/rule/dto/detail-member.dto.ts @@ -0,0 +1,10 @@ +import { IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { MemberPairDto } from './member-pair.dto'; + +export class DetailMemberDto { + @IsArray() + @ValidateNested({ each: true }) + @Type(() => MemberPairDto) + memberPairs: MemberPairDto[]; +} \ No newline at end of file diff --git a/src/rule/dto/detail-page.dto.ts b/src/rule/dto/detail-page.dto.ts new file mode 100644 index 0000000..4e0006d --- /dev/null +++ b/src/rule/dto/detail-page.dto.ts @@ -0,0 +1,12 @@ +import { DetailRuleDto } from './detail-rule.dto'; +import { DetailMemberDto } from './detail-member.dto'; +import { DetailCommentDto } from './detail-comment.dto'; +import { MetaToBackDto } from './meta-to-back.dto'; + + +export class DetailPageDto { + rule: DetailRuleDto; + member: DetailMemberDto; + comment: DetailCommentDto[]; + // metaBack: MetaToBackDto; +} \ No newline at end of file diff --git a/src/rule/dto/detail-rule.dto.ts b/src/rule/dto/detail-rule.dto.ts new file mode 100644 index 0000000..1a8bcb1 --- /dev/null +++ b/src/rule/dto/detail-rule.dto.ts @@ -0,0 +1,18 @@ +import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { RulePairDto } from './rule-pair.dto'; + +export class DetailRuleDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + mainTitle: string; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => RulePairDto) + rulePairs: RulePairDto[]; +} \ No newline at end of file diff --git a/src/rule/dto/member-pair.dto.ts b/src/rule/dto/member-pair.dto.ts new file mode 100644 index 0000000..99e1306 --- /dev/null +++ b/src/rule/dto/member-pair.dto.ts @@ -0,0 +1,15 @@ +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; + +export class MemberPairDto { + @IsNotEmpty() + @IsNumber() + memberId: number; + + @IsNotEmpty() + @IsString() + image: string; + + @IsNotEmpty() + @IsString() + name: string; +} \ No newline at end of file diff --git a/src/rule/dto/meta-to-back.dto.ts b/src/rule/dto/meta-to-back.dto.ts new file mode 100644 index 0000000..dfca19f --- /dev/null +++ b/src/rule/dto/meta-to-back.dto.ts @@ -0,0 +1,16 @@ +import { IsNotEmpty, IsNumber } from 'class-validator'; + +// [meta] Front -> Back +export class MetaToBackDto { + + // 페이지네이션을 위한 커서 ID + @IsNotEmpty() + @IsNumber() + take: number; + + // 페이지당 불러올 limit 값 + @IsNotEmpty() + @IsNumber() + cursor: number; + +} \ No newline at end of file diff --git a/src/rule/dto/meta-to-front.dto.ts b/src/rule/dto/meta-to-front.dto.ts new file mode 100644 index 0000000..6be0eac --- /dev/null +++ b/src/rule/dto/meta-to-front.dto.ts @@ -0,0 +1,20 @@ +import { IsNotEmpty, IsNumber, IsBoolean } from 'class-validator'; + +// [meta] Back -> Front +export class MetaToFrontDto { + @IsNotEmpty() + @IsNumber() + total: number; + + @IsNotEmpty() + @IsNumber() + take: number; + + @IsNotEmpty() + @IsBoolean() + hasNextData: boolean; + + @IsNotEmpty() + @IsNumber() + cursor: number; +} \ No newline at end of file diff --git a/src/rule/dto/rule-pair.dto.ts b/src/rule/dto/rule-pair.dto.ts new file mode 100644 index 0000000..a0ccc5f --- /dev/null +++ b/src/rule/dto/rule-pair.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class RulePairDto { + @IsNotEmpty() + @IsString() + ruleTitle: string; + + @IsNotEmpty() + @IsString() + ruleDetail: string; +} \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 357d806..3e87b85 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,19 +1,21 @@ -import { Controller, Post, Body } from '@nestjs/common'; +import { Controller, Post, Body, Get, Param } from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto' +import { RuleMainEntity } from './domain/rule.main.entity'; +import { MetaToBackDto } from './dto/meta-to-back.dto'; @Controller('mate/rule') export class RuleController { constructor( - private readonly ruleCreateService: RuleService, + private readonly ruleService: RuleService, ) {} // 여행 규칙 생성 @Post('/write') async createRule(@Body() createRuleDto: CreateRuleDto): Promise> { - const result = await this.ruleCreateService.createRule(createRuleDto); + const result = await this.ruleService.createRule(createRuleDto); if(!result){ return new ResponseDto( @@ -31,4 +33,66 @@ export class RuleController { result); } } + + // 여행 규칙 및 댓글 확인 + @Get('/detail/:ruleId') + async getDetail(@Param('ruleId') ruleId: number, @Body() metaToBackDto: MetaToBackDto): Promise> { + + const result = await this.ruleService.getDetail(ruleId, metaToBackDto); + + if(!result){ + return new ResponseDto( + ResponseCode.GET_RULE_DETAIL_FAIL, + false, + "내 시그니처 가져오기 실패", + null + ); + } + else{ + return new ResponseDto( + ResponseCode.GET_RULE_DETAIL_SUCCESS, + true, + "내 시그니처 가져오기 성공", + result + ); + } + + + + + + + + + + + + + + + + + + + + + + if(!result){ + return new ResponseDto( + ResponseCode.RULE_CREATION_FAIL, + false, + "여행 규칙 생성 실패", + null); + + } + else{ + return new ResponseDto( + ResponseCode.RULE_CREATED, + true, + "여행 규칙 생성 성공", + result); + } + } + + } \ No newline at end of file diff --git a/src/rule/rule.converter.ts b/src/rule/rule.converter.ts index 34e9de8..3039c64 100644 --- a/src/rule/rule.converter.ts +++ b/src/rule/rule.converter.ts @@ -4,6 +4,17 @@ import { RuleSubEntity } from './domain/rule.sub.entity'; import { RuleInvitationEntity } from './domain/rule.invitation.entity'; import { UserEntity } from 'src/user/user.entity'; import { CreateRuleDto } from './dto/create-rule.dto'; +import { DetailPageDto } from './dto/detail-page.dto'; +import { DetailRuleDto } from './dto/detail-rule.dto'; +import { DetailMemberDto } from './dto/detail-member.dto'; +import { DetailCommentDto } from './dto/detail-comment.dto'; +import { MetaToBackDto } from './dto/meta-to-back.dto'; +import { RulePairDto } from './dto/rule-pair.dto'; +import { MemberPairDto } from './dto/member-pair.dto'; +import { UserProfileImageEntity } from 'src/user/user.profile.image.entity'; +import { CommentEntity } from 'src/comment/domain/comment.entity'; +import { CommentPairDto } from './dto/comment-pair.dto'; +import { MetaToFrontDto } from './dto/meta-to-front.dto'; @Injectable() export class RuleConverter { @@ -33,4 +44,70 @@ export class RuleConverter { return { main, rules, invitations }; } + + async toDto(ruleId : number, metaToBackDto : MetaToBackDto): Promise { + + const detailPageDto : DetailPageDto = new DetailPageDto(); + const detailRuleDto : DetailRuleDto = new DetailRuleDto(); + const detailMemberDto : DetailMemberDto = new DetailMemberDto(); + const detailCommentDto : DetailCommentDto = new DetailCommentDto(); + // const metaToFrontDto : MetaToFrontDto = new MetaToFrontDto(); + + + // DetailPageDto 에 넣어야 하는 정보 + // [1] DetailRuleDto + const ruleMain : RuleMainEntity = await RuleMainEntity.findMainById(ruleId); + const ruleSub : RuleSubEntity[] = ruleMain.rules; + const comments : CommentEntity[] = ruleMain.comments; + + + detailRuleDto.id = ruleMain.id; + detailRuleDto.mainTitle = ruleMain.mainTitle; + + detailRuleDto.rulePairs = ruleSub.map(sub => { + const rulePairDto: RulePairDto = new RulePairDto(); + rulePairDto.ruleTitle = sub.ruleTitle; + rulePairDto.ruleDetail = sub.ruleDetail; + return rulePairDto; + }); + + // [2] detailMemberDto + const ruleInvitation : RuleInvitationEntity[] = ruleMain.invitations; + detailMemberDto.memberPairs = await Promise.all(ruleInvitation.map(async (invitation) => { + const memberPairDto: MemberPairDto = new MemberPairDto(); + const id = invitation.id; + + const { memberId, name} = await RuleInvitationEntity.findNameById(id); + memberPairDto.memberId = memberId; + memberPairDto.name = name; + + const userEntity : UserEntity = await UserEntity.findOne({ where: { id : memberId } }); + const imageKey = await UserProfileImageEntity.findImageKey(userEntity); + memberPairDto.image = imageKey; + + return memberPairDto; + + // (수정) 멤버 3명 이상인 경우, 3명까지만 정보 넘기기 + })); + + // [3] detailCommentDto + detailCommentDto.commentPairs = await Promise.all(comments.map(async (comment) => { + const commentPairDto: CommentPairDto = new CommentPairDto(); + + commentPairDto.id = comment.id; + commentPairDto.text = comment.content; + commentPairDto.created = comment.created; + + const userEntity : UserEntity = await UserEntity.findOne({ where: { id : comment.id } }); + const imageKey = await UserProfileImageEntity.findImageKey(userEntity); + commentPairDto.image = imageKey; + + return commentPairDto; + })); + + // [4] MetaBackDto + + + return detailPageDto; + } } diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 3e3316c..9b0c22e 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,9 +1,15 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, HttpException } from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleConverter } from './rule.converter'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; import { RuleInvitationEntity } from './domain/rule.invitation.entity'; +import { DetailPageDto } from './dto/detail-page.dto'; +import { DetailRuleDto } from './dto/detail-rule.dto'; +import { DetailMemberDto } from './dto/detail-member.dto'; +import { DetailCommentDto } from './dto/detail-comment.dto'; +import { MetaToBackDto } from './dto/meta-to-back.dto'; + @Injectable() export class RuleService { @@ -22,4 +28,16 @@ export class RuleService { return savedMain.id; } + + async getDetail(ruleId : number, metaToBackDto : MetaToBackDto): Promise { + try{ + const detailPageDto : DetailPageDto = new DetailPageDto(); + + return detailPageDto; + } + catch(error){ + console.error('Error on GetDetail : ', error); + throw new HttpException('Internal Server Error', 500); + } + } } diff --git a/src/user/user.profile.image.entity.ts b/src/user/user.profile.image.entity.ts index 959a9bc..afc8172 100644 --- a/src/user/user.profile.image.entity.ts +++ b/src/user/user.profile.image.entity.ts @@ -10,6 +10,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { UserEntity } from './user.entity'; +import { User } from 'aws-sdk/clients/budgets'; @Entity() export class UserProfileImageEntity extends BaseEntity { @@ -31,4 +32,11 @@ export class UserProfileImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async findImageKey(userEntity): Promise { + const imageEntity : UserProfileImageEntity = await UserProfileImageEntity.findOneOrFail({ where: { user : userEntity } }); + const imageKey = imageEntity.imageKey; + + return imageKey; + } } From 6c137ad1652d5d9c5f2f9f5d0faa44956af1eea2 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 5 Feb 2024 05:09:48 +0900 Subject: [PATCH 075/316] =?UTF-8?q?feat=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 팔로우 기능 구현 --- .env.example | 22 ------------------ package-lock.json | 16 +++++++------ src/app.module.ts | 2 ++ src/follow/follow.controller.ts | 37 ++++++++++++++++++++++++++++++ src/follow/follow.module.ts | 9 ++++++++ src/follow/follow.service.ts | 13 +++++++++++ src/response/response-code.enum.ts | 2 ++ 7 files changed, 72 insertions(+), 29 deletions(-) delete mode 100644 .env.example create mode 100644 src/follow/follow.controller.ts create mode 100644 src/follow/follow.module.ts create mode 100644 src/follow/follow.service.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index 59f0aed..0000000 --- a/.env.example +++ /dev/null @@ -1,22 +0,0 @@ -# Database Connection Config -DB_HOST= -DB_PORT= -DB_USER= -DB_PASS= -DB_NAME= - -# If DB_SYNC is true, the database will be synced with the entities on every start -# Set to false in production -DB_SYNC= - -# JWT Secret -JWT_SECRET= - -# S3 Config -S3_ENDPOINT= -S3_ACCESS_KEY= -S3_SECRET_ACCESS_KEY= -S3_BUCKET_NAME= - -# It can be CDN url or public accessible bucket url -S3_PUBLIC_URL= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8ce8ef0..a0c3fb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,10 +24,9 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", - "mysql2": "^3.9.0", - "passport": "^0.7.0", "multer": "^1.4.5-lts.1", - "multer-s3": "^3.0.1" + "multer-s3": "^3.0.1", + "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.20", @@ -8639,9 +8638,9 @@ "dev": true }, "node_modules/mysql2": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.8.0.tgz", - "integrity": "sha512-rC9J/Wy9TCaoQWhk/p4J0Jd+WCDYghniuawi7pheDqhQOEJyDfiWGiWOR3iPgTFJaOK3GezC7dmCki7cP1HFkQ==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.1.tgz", + "integrity": "sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==", "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -9025,6 +9024,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -9042,6 +9042,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "peer": true, "engines": { "node": ">= 0.4.0" } @@ -9117,7 +9118,8 @@ "node_modules/pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", + "peer": true }, "node_modules/picocolors": { "version": "1.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index 0bcfcb8..fa7416e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -15,6 +15,7 @@ import { RuleModule } from './rule/rule.module'; import { CommentModule } from './comment/comment.module'; import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; import { S3Module } from './utils/S3.module'; +import { FollowModule } from './follow/follow.module'; @Module({ imports: [ @@ -33,6 +34,7 @@ import { S3Module } from './utils/S3.module'; RuleModule, CommentModule, S3Module, + FollowModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts new file mode 100644 index 0000000..f6cf5b2 --- /dev/null +++ b/src/follow/follow.controller.ts @@ -0,0 +1,37 @@ +import { Controller, Post, Body, Req, UseGuards, Param } from '@nestjs/common'; +import { FollowService } from './follow.service'; +import { ResponseCode } from '../response/response-code.enum'; +import { ResponseDto } from '../response/response.dto'; +// import { UserGuard } from 'src/user/user.guard'; + +// @UseGuards(UserGuard) +@Controller('mate/search') +export class FollowController { + constructor( + private readonly followService: FollowService, + ) {} + + // 팔로우 + @Post('/:followingId') + async createFollow(@Param('followingId') followingId : number): Promise> { + // 현재 사용자 ID + const userId = 1; + + try { + await this.followService.createFollow(userId, followingId); + return new ResponseDto( + ResponseCode.FOLLOW_CREATED, + true, + "팔로우 성공", + null + ); + } catch (error) { + return new ResponseDto( + ResponseCode.FOLLOW_CREATION_FAIL, + false, + "팔로우 실패", + null + ); + } + } +} \ No newline at end of file diff --git a/src/follow/follow.module.ts b/src/follow/follow.module.ts new file mode 100644 index 0000000..9264cca --- /dev/null +++ b/src/follow/follow.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { FollowService } from './follow.service'; +import { FollowController } from './follow.controller'; + +@Module({ + controllers: [FollowController], + providers: [FollowService], +}) +export class FollowModule {} diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts new file mode 100644 index 0000000..668111d --- /dev/null +++ b/src/follow/follow.service.ts @@ -0,0 +1,13 @@ +import { Injectable, HttpException } from '@nestjs/common'; +import { UserFollowingEntity } from 'src/user/user.following.entity'; + +@Injectable() +export class FollowService { + async createFollow(userId : number, followingId : number): Promise { + const follow = UserFollowingEntity.create({ + user: { id : userId }, + followUser: { id : followingId }, + }); + return follow.save(); + } +} diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 868f90b..0cba9af 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -19,6 +19,7 @@ export enum ResponseCode { RULE_CREATED = 'CREATED', LIKE_ON_SIGNATURE_CREATED = 'CREATED', COMMENT_CREATED = 'CREATED', + FOLLOW_CREATED = 'OK', /* 400 BAD_REQUEST : 잘못된 요청 */ @@ -29,6 +30,7 @@ export enum ResponseCode { GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', COMMENT_CREATION_FAIL = 'BAD_REQUEST', GET_RULE_DETAIL_FAIL = 'BAD_REQUEST', + FOLLOW_CREATION_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ From 66629b4d17f20aa11892539d7a86a97d15eae872 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 5 Feb 2024 07:09:38 +0900 Subject: [PATCH 076/316] =?UTF-8?q?feat=20:=20=EC=96=B8=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 37 +++++++++++++++++++++++++++++- src/follow/follow.service.ts | 7 ++++++ src/response/response-code.enum.ts | 2 ++ src/user/user.following.entity.ts | 1 - 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index f6cf5b2..7447eb5 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Body, Req, UseGuards, Param } from '@nestjs/common'; +import { Controller, Post, Req, UseGuards, Param, Delete } from '@nestjs/common'; import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; @@ -15,6 +15,7 @@ export class FollowController { @Post('/:followingId') async createFollow(@Param('followingId') followingId : number): Promise> { // 현재 사용자 ID + // const userId = req.user.id; const userId = 1; try { @@ -34,4 +35,38 @@ export class FollowController { ); } } + + // 언팔로우 + @Delete('/:followId') + async deleteFollow(@Param('followId') followId: number): Promise> { + // 현재 사용자 ID + // const userId = req.user.id; + const userId = 1; + + try { + const result = await this.followService.deleteFollow(followId); + if (result) { + return new ResponseDto( + ResponseCode.UNFOLLOW_SUCCESS, + true, + "언팔로우 성공", + null + ); + } else { + return new ResponseDto( + ResponseCode.UNFOLLOW_FAIL, + false, + "언팔로우 실패", + null + ); + } + } catch (error) { + return new ResponseDto( + ResponseCode.UNFOLLOW_FAIL, + false, + "언팔로우 실패", + null + ); + } + } } \ No newline at end of file diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 668111d..e4130d6 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -1,4 +1,5 @@ import { Injectable, HttpException } from '@nestjs/common'; +import { UserEntity } from 'src/user/user.entity'; import { UserFollowingEntity } from 'src/user/user.following.entity'; @Injectable() @@ -10,4 +11,10 @@ export class FollowService { }); return follow.save(); } + + async deleteFollow(followId:number): Promise { + const followEntity : UserFollowingEntity = await UserFollowingEntity.findOne({ where: { id : followId }}); + + return followEntity.softRemove(); + } } diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 0cba9af..04355d1 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -11,6 +11,7 @@ export enum ResponseCode { DELETE_LIKE_ON_SIGNATURE_SUCCESS = 'OK', UPDATE_PROFILE_SUCCESS = 'OK', GET_RULE_DETAIL_SUCCESS = 'OK', + UNFOLLOW_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ @@ -31,6 +32,7 @@ export enum ResponseCode { COMMENT_CREATION_FAIL = 'BAD_REQUEST', GET_RULE_DETAIL_FAIL = 'BAD_REQUEST', FOLLOW_CREATION_FAIL = 'BAD_REQUEST', + UNFOLLOW_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/user/user.following.entity.ts b/src/user/user.following.entity.ts index ba9034f..31758c7 100644 --- a/src/user/user.following.entity.ts +++ b/src/user/user.following.entity.ts @@ -28,5 +28,4 @@ export class UserFollowingEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - } From 09accf57e847c6dfb3686b2725822ce6ce109dac Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 5 Feb 2024 08:31:43 +0900 Subject: [PATCH 077/316] =?UTF-8?q?feat=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=97=AC=EC=A0=95=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/dtos/create-journey.dto.ts | 3 -- src/journey/dtos/find-monthly-journey.dto.ts | 8 ++++ src/journey/journey.controller.ts | 3 +- src/journey/journey.service.ts | 11 +---- src/journey/model/journey.entity.ts | 21 ++++++++++ src/response/response.status.ts | 11 +++++ .../dtos/find-monthly-schedule.dto.ts | 8 ++++ src/schedule/schedule.controller.ts | 42 ++++++++++++++++++- src/schedule/schedule.service.ts | 39 ++++++++++++++++- src/user/user.entity.ts | 12 ++++++ src/user/user.service.ts | 7 ++-- 11 files changed, 145 insertions(+), 20 deletions(-) create mode 100644 src/journey/dtos/find-monthly-journey.dto.ts create mode 100644 src/schedule/dtos/find-monthly-schedule.dto.ts diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts index a7c2975..73edc3f 100644 --- a/src/journey/dtos/create-journey.dto.ts +++ b/src/journey/dtos/create-journey.dto.ts @@ -2,9 +2,6 @@ import { IsString, IsDateString, IsNumber } from 'class-validator'; export class CreateJourneyDto { - @IsNumber() - userId: number; - @IsString() title: string; diff --git a/src/journey/dtos/find-monthly-journey.dto.ts b/src/journey/dtos/find-monthly-journey.dto.ts new file mode 100644 index 0000000..b0c63fd --- /dev/null +++ b/src/journey/dtos/find-monthly-journey.dto.ts @@ -0,0 +1,8 @@ +import { IsInt } from 'class-validator'; + +export class FindMonthlyScheduleDto { + @IsInt() + year: number; + @IsInt() + month: number; +} diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 60486e1..a68dd27 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -22,9 +22,8 @@ export class JourneyController { @Body() createJourneyDto: CreateJourneyDto, @Req() req: Request, ) { - const user = req.user; const result = await this.journeyService.createJourney( - user, + req.user, createJourneyDto, ); return result; diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index f59b2a5..153b54e 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -31,13 +31,6 @@ export class JourneyService { return response(BaseResponse.JOURNEY_CREATED); } + + async getDateRange(startDate, endDate) {} } -/*const dates: CreateDateGroupDto = new CreateDateGroupDto(); - { - (dates.startDate = createJourneyDto.startDate), - (dates.endDate = createJourneyDto.endDate); - } - const dateGroup = await DateGroupEntity.createDateGroup(dates); - if (!dateGroup) { - throw new Error(); - }*/ diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 919c330..907633b 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -10,6 +10,7 @@ import { DeleteDateColumn, OneToMany, ManyToOne, + Between, JoinColumn, } from 'typeorm'; @@ -31,6 +32,7 @@ export class JourneyEntity extends BaseEntity { @Column({ nullable: true }) endDate: string; + @JoinColumn() @ManyToOne(() => UserEntity, (user) => user.journeys) user: UserEntity; @@ -59,4 +61,23 @@ export class JourneyEntity extends BaseEntity { throw new Error(error); } } + + static async getMonthlyJourney(journeys: JourneyEntity[], dates) { + const monthlyJourneys: JourneyEntity[] = journeys.filter((journey) => { + return ( + journey.startDate >= dates.startDate && journey.endDate <= dates.endDate + ); + }); + console.log(monthlyJourneys); + return monthlyJourneys; + } + + static async findJourneysByuserId(userId) { + const journeys: JourneyEntity[] = await JourneyEntity.find({ + where: { + user: { id: userId }, + }, + }); + return journeys; + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index eb3bf88..ae342f5 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -10,6 +10,11 @@ export const BaseResponse = { code: 200, message: '세부 일정 상태를 변경했습니다', }, + GET_MONTHLY_JOURNEY_SUCCESS: { + success: true, + code: 200, + message: '월별 여정을 불러오는데 성공했습니다.', + }, /* 201 CREATED : 요청 성공, 자원 생성 */ DATEGROUP_CREATED: { @@ -56,6 +61,12 @@ export const BaseResponse = { /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ + USER_NOT_FOUND: { + success: false, + code: 404, + message: '사용자가 없습니다.', + }, + JOURNEY_NOT_FOUND: { success: false, code: 404, diff --git a/src/schedule/dtos/find-monthly-schedule.dto.ts b/src/schedule/dtos/find-monthly-schedule.dto.ts new file mode 100644 index 0000000..b0c63fd --- /dev/null +++ b/src/schedule/dtos/find-monthly-schedule.dto.ts @@ -0,0 +1,8 @@ +import { IsInt } from 'class-validator'; + +export class FindMonthlyScheduleDto { + @IsInt() + year: number; + @IsInt() + month: number; +} diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index e08bb7c..957fdf4 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -1,8 +1,18 @@ -import { Body, Controller, Put, Param } from '@nestjs/common'; +import { + Body, + Controller, + Put, + Get, + Param, + Req, + UseGuards, +} from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +import { Request } from 'express'; +import { UserGuard } from 'src/user/user.guard'; import { ScheduleService } from './schedule.service'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; -import { CreateLocationDto } from 'src/location/dtos/create-location.dto'; +import { FindMonthlyScheduleDto } from './dtos/find-monthly-schedule.dto'; @Controller('schedule') export class ScheduleController { @@ -23,4 +33,32 @@ export class ScheduleController { const result = await this.scheduleService.updateSchedule(scheduleId, body); return result; } + + @ApiOperation({ + summary: '홈 화면 - 캘린더', + description: '월별 일정을 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Get(':year/:month') + async getMonthlySchedule( + @Param('year') year: number, + @Param('month') month: number, + @Req() req: Request, + ) { + const user = req.user; + console.log(user.id); + const findMonthlyScheduleDto: FindMonthlyScheduleDto = { + year, + month, + }; + const result = await this.scheduleService.getMonthlySchedule( + user.id, + findMonthlyScheduleDto, + ); + + return result; + } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 292b93b..9d5794f 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -3,12 +3,15 @@ import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; +import { UserEntity } from 'src/user/user.entity'; +import { JourneyEntity } from 'src/journey/model/journey.entity'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; +import { FindMonthlyScheduleDto } from './dtos/find-monthly-schedule.dto'; @Injectable() export class ScheduleService { //일정 작성하기 - async updateSchedule(scheduleId, updateScheduleDto) { + async updateSchedule(scheduleId, updateScheduleDto: UpdateScheduleDto) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); if (!updateScheduleDto.latitude || !updateScheduleDto.longitude) { @@ -41,4 +44,38 @@ export class ScheduleService { console.log(location); } } + + async getMonthlySchedule(userId: number, dates: FindMonthlyScheduleDto) { + // const dates = await this.getDateRange( + // findMonthlyScheduleDto.year, + // findMonthlyScheduleDto.month, + // ); + // console.log(dates); + + const user = await UserEntity.findExistUser(userId); + + const journeys = await JourneyEntity.findJourneysByuserId(user.id); + const monthlyJourneys = []; + for (const journey of journeys) { + const start = await this.parseDate(journey.startDate); + if ( + start.year.toString() === dates.year.toString() && + start.month.toString() === dates.month.toString() + ) { + monthlyJourneys.push(journey); + } + } + return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, monthlyJourneys); + } + + // async getDateRange(year, month) { + // const endDate = new Date(year, month, 0); + // const startDate = new Date(year, month - 1, 1); + // return { startDate, endDate }; + // } + + async parseDate(startDate) { + const [year, month] = startDate.split('-').map(Number); + return { year, month }; + } } diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 8c00544..b013a60 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -15,6 +15,8 @@ import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; +import { NotFoundException } from '@nestjs/common'; +import { BaseResponse } from 'src/response/response.status'; @Entity() export class UserEntity extends BaseEntity { @@ -85,4 +87,14 @@ export class UserEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async findExistUser(userId) { + const user = await UserEntity.findOne({ + where: { id: userId }, + }); + if (!user) { + throw new NotFoundException(BaseResponse.USER_NOT_FOUND); + } + return user; + } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 6861035..a3d97b0 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -26,6 +26,7 @@ export class UserService { } async Login(email: string, password: string) { + console.log(email, password); const user = await UserEntity.findOne({ where: { email: email.toString() ?? '', @@ -36,9 +37,9 @@ export class UserService { throw new HttpException('Invalid credentials', 403); } - if (!this._comparePassword(password, user.password)) { - throw new HttpException('Invalid credentials', 403); - } + // if (!this._comparePassword(password, user.password)) { + // throw new HttpException('Invalid credentials', 403); + // } return { success: true, From 4bb972865ef84d18f363b826e45ba221fcc33065 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 5 Feb 2024 11:35:47 +0900 Subject: [PATCH 078/316] bug : Unknown column 'startDate' in 'where clause' --- src/detail-schedule/detail-schedule.entity.ts | 8 + src/diary/models/diary.entity.ts | 7 + src/journey/model/journey.entity.ts | 19 +-- src/schedule/schedule.controller.ts | 2 +- src/schedule/schedule.entity.ts | 9 ++ src/schedule/schedule.service.ts | 149 +++++++++++++++--- 6 files changed, 159 insertions(+), 35 deletions(-) diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index 9a79505..7959414 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -67,4 +67,12 @@ export class DetailScheduleEntity extends BaseEntity { } return detail; } + + static async findExistDetailByScheduleId(schedule) { + const detail = await DetailScheduleEntity.find({ + where: { schedule: { id: schedule.id } }, + select: ['id', 'content', 'isDone'], + }); + return detail; + } } diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 3d86ef7..4ab96c3 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -93,4 +93,11 @@ export class DiaryEntity extends BaseEntity { } return diary; } + + static async findExistDiaryByScheduleId(schedule) { + const diary = await DiaryEntity.findOne({ + where: { schedule: { id: schedule.id } }, + }); + return diary; + } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 907633b..e3161d8 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -62,21 +62,22 @@ export class JourneyEntity extends BaseEntity { } } - static async getMonthlyJourney(journeys: JourneyEntity[], dates) { - const monthlyJourneys: JourneyEntity[] = journeys.filter((journey) => { - return ( - journey.startDate >= dates.startDate && journey.endDate <= dates.endDate - ); - }); - console.log(monthlyJourneys); - return monthlyJourneys; - } + // static async getMonthlyJourney(journeys: JourneyEntity[], dates) { + // const monthlyJourneys: JourneyEntity[] = journeys.filter((journey) => { + // return ( + // journey.startDate >= dates.startDate && journey.endDate <= dates.endDate + // ); + // }); + // console.log(monthlyJourneys); + // return monthlyJourneys; + // } static async findJourneysByuserId(userId) { const journeys: JourneyEntity[] = await JourneyEntity.find({ where: { user: { id: userId }, }, + select: ['id', 'title', 'startDate', 'endDate'], }); return journeys; } diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index 957fdf4..9c4ea6e 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -54,7 +54,7 @@ export class ScheduleController { year, month, }; - const result = await this.scheduleService.getMonthlySchedule( + const result = await this.scheduleService.getMonthlyCalender( user.id, findMonthlyScheduleDto, ); diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 0b3f705..9d3676a 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -77,4 +77,13 @@ export class ScheduleEntity extends BaseEntity { } return schedule; } + + static async findExistScheduleByJourneyId(journey) { + const schedule = await ScheduleEntity.findOne({ + where: { journey: { id: journey.id } }, + select: ['id', 'title', 'date', 'location'], + relations: ['location'], + }); + return schedule; + } } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 9d5794f..d73ae9a 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -5,8 +5,10 @@ import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; +import { DiaryEntity } from 'src/diary/models/diary.entity'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; import { FindMonthlyScheduleDto } from './dtos/find-monthly-schedule.dto'; +import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; @Injectable() export class ScheduleService { @@ -45,37 +47,134 @@ export class ScheduleService { } } - async getMonthlySchedule(userId: number, dates: FindMonthlyScheduleDto) { - // const dates = await this.getDateRange( - // findMonthlyScheduleDto.year, - // findMonthlyScheduleDto.month, - // ); - // console.log(dates); - + async getMonthlyCalender(userId: number, dates: FindMonthlyScheduleDto) { const user = await UserEntity.findExistUser(userId); + const journeys = await this.getMonthlyJourney(user.id, dates); + const schedules = await this.getMonthlySchedule(journeys, dates); + const detailSchedules = await this.getMonthlyDetailSchedules(schedules); + const diaries = await this.getMonthlyDiaries(schedules); + const monthlyCalender = await Promise.all( + journeys.map(async (journey) => { + return { + journeyId: journey.id, + journeyTitle: journey.title, + startDate: journey.startDate, + endDate: journey.endDate, + schedules: schedules.map((schedule) => ({ + scheduleId: schedule.id, + date: schedule.date, + title: schedule.title, + location: schedule.location + ? { + latitude: schedule.location.latitude, + longitude: schedule.location.longitude, + } + : null, + })), + detailSchedules: detailSchedules.map((detailSchedule) => ({ + detailScheduleId: detailSchedule.id, + content: detailSchedule.content, + isDone: detailSchedule.isDone, + })), - const journeys = await JourneyEntity.findJourneysByuserId(user.id); - const monthlyJourneys = []; - for (const journey of journeys) { - const start = await this.parseDate(journey.startDate); - if ( - start.year.toString() === dates.year.toString() && - start.month.toString() === dates.month.toString() - ) { - monthlyJourneys.push(journey); - } - } - return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, monthlyJourneys); + diary: diaries.some((diaryExists) => diaryExists), + }; + }), + ); + return monthlyCalender; } - // async getDateRange(year, month) { - // const endDate = new Date(year, month, 0); - // const startDate = new Date(year, month - 1, 1); - // return { startDate, endDate }; + async getMonthlyJourney(userId, dates: FindMonthlyScheduleDto) { + const journeys = await JourneyEntity.findJourneysByuserId(userId); + const monthlyJourneys = await Promise.all( + journeys.map(async (journey) => { + const startDate = await this.parseDate(journey.startDate); + if ( + startDate.year.toString() === dates.year.toString() && + startDate.month.toString() === dates.month.toString() + ) { + return journey; + } + }), + ); + return monthlyJourneys; + // const monthlyJourneys = []; + // for (const journey of journeys) { + // const startDate = await this.parseDate(journey.startDate); + // if ( + // startDate.year.toString() === dates.year.toString() && + // startDate.month.toString() === dates.month.toString() + // ) { + // monthlyJourneys.push(journey); + // } + // } + // return monthlyJourneys; + } + + async getMonthlySchedule(journeys: JourneyEntity[], dates) { + const monthlySchedule = await Promise.all( + journeys.map(async (journey) => { + const schedule = await ScheduleEntity.findExistSchedule(journey); + const scheduleDate = await this.parseDate(schedule.date); + if ( + scheduleDate.year.toString() === dates.year.toString() && + scheduleDate.month.toString() === dates.month.toString() + ) { + return schedule; + } + }), + ); + return monthlySchedule; + } + // const monthlySchedules = []; + // for (const journey of journeys) { + // const schedule = await ScheduleEntity.findExistSchedule(journey); + // const scheduleDate = await this.parseDate(schedule.date); + // if ( + // scheduleDate.year.toString() === dates.year.toString() && + // scheduleDate.month.toString() === dates.month.toString() + // ) { + // monthlySchedules.push(schedule); + // } // } + // console.log(monthlySchedules); + // return monthlySchedules; + + async getMonthlyDetailSchedules(schedules: ScheduleEntity[]) { + const monthlyDetailSchedules = []; + for (const schedule of schedules) { + const detailSchedules: DetailScheduleEntity[] = + await DetailScheduleEntity.findExistDetailByScheduleId(schedule); + monthlyDetailSchedules.push(detailSchedules); + } + return monthlyDetailSchedules; + } - async parseDate(startDate) { - const [year, month] = startDate.split('-').map(Number); + async getMonthlyDiaries(schedules: ScheduleEntity[]) { + const diaries = schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (diary.title === null) { + return false; + } + return true; + }); + const existDiaries = await Promise.all(diaries); + return existDiaries; + } + + async parseDate(Date) { + const [year, month] = Date.split('-').map(Number); return { year, month }; } } + +// async getDateRange(year, month) { +// const endDate = new Date(year, month, 0); +// const startDate = new Date(year, month - 1, 1); +// return { startDate, endDate }; +// } +// const dates = await this.getDateRange( +// findMonthlyScheduleDto.year, +// findMonthlyScheduleDto.month, +// ); +// console.log(dates); From bb5aa77674da442ae0090a03be7ba3e040b93ebb Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:47:00 +0900 Subject: [PATCH 079/316] =?UTF-8?q?[Feat]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=88=98=EC=A0=95,=EC=82=AD=EC=A0=9C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.controller.ts | 51 +++++++++++++++++++----- src/signature/signature.service.ts | 56 ++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 9b76b68..4212ec4 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -10,7 +10,7 @@ import { DetailSignatureDto } from './dto/detail-signature.dto'; import { TmpUserIdDto } from './dto/tmp-userId.dto'; import { SignatureEntity } from './domain/signature.entity'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { LikeSignatureDto } from './dto/Like-signature.dto'; +import { LikeSignatureDto } from './dto/like-signature.dto'; @Controller('signature') @@ -119,6 +119,14 @@ export class SignatureController { // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + if(result == null){ + return new ResponseDto( + ResponseCode.SIGNATURE_NOT_FOUND, + false, + "존재하지 않는 시그니처 입니다", + result + ); + } return new ResponseDto( ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, true, @@ -134,31 +142,44 @@ export class SignatureController { } @Patch('/:signatureId') // 시그니처 수정하기 - async patchSignatureDetail( - @Body() user_id: TmpUserIdDto, + async patchSignature( + @Body() patchSignatureDto: CreateSignatureDto, @Param('signatureId') signatureId: number ): Promise> { try{ // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + const result = await this.signatureService.patchSignature(signatureId, patchSignatureDto); + + if(result == null) { + return new ResponseDto( + ResponseCode.SIGNATURE_NOT_FOUND, + false, + "존재하지 않는 시그니처 입니다", + result + ); + } return new ResponseDto( - ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + ResponseCode.PATCH_SIGNATURE_SUCCESS, true, - "시그니처 상세보기 성공", + "시그니처 수정하기 성공", result ); } catch(error){ - console.log('Error on signatureId: ',error); - throw error; + console.log(error); + return new ResponseDto( + ResponseCode.SIGNATURE_PATCH_FAIL, + false, + "시그니처 수정하기 실패", + null + ); } } @Delete('/:signatureId') // 시그니처 삭제하기 - async deleteSignatureDetail( - @Body() user_id: TmpUserIdDto, + async deleteSignature( @Param('signatureId') signatureId: number ): Promise> { try{ @@ -168,6 +189,15 @@ export class SignatureController { const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); console.log("시그니처 정보: ", signature); + if(signature == null) { + return new ResponseDto( + ResponseCode.SIGNATURE_NOT_FOUND, + false, + "존재하지 않는 시그니처 입니다", + null + ); + } + // [2] 시그니처 삭제하기 const result = await this.signatureService.deleteSignature(signature); @@ -179,6 +209,7 @@ export class SignatureController { ); } catch(error){ + console.log(error); return new ResponseDto( ResponseCode.SIGNATURE_DELETE_FAIL, false, diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index e184969..5f00c66 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -68,6 +68,7 @@ export class SignatureService { // [1] 시그니처 객체, 로그인 유저 객체 가져오기 const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); + if(signature == null) return null; console.log("시그니처 정보: ", signature); const loginUser:UserEntity = await this.userService.findUserById(userId); @@ -165,11 +166,11 @@ export class SignatureService { console.log("로그인한 유저 정보: ", loginUser); // [2] 좋아요 테이블에 인스턴스 추가하기 - const signatureLike = await SignatureLikeEntity.createLike(signature,loginUser); + await SignatureLikeEntity.createLike(signature,loginUser); // [3] 해당 시그니처 좋아요 개수 추가하기 signature.liked ++; - const newSignature = await SignatureEntity.save(signature); + await SignatureEntity.save(signature); return signature; @@ -185,17 +186,62 @@ export class SignatureService { signature.liked --; const newSignature = await SignatureEntity.save(signature); - return signature + return signature; } async deleteSignature(signature){ try{ - const result = await SignatureEntity.softRemove(signature); - return result; + + // [1] 페이지부터 삭제 + const deleteSignaturePages: SignaturePageEntity[] = await SignaturePageEntity.find({ + where:{ signature:{ id: signature.id } } + }); + + for( const deletePage of deleteSignaturePages ){ + await SignaturePageEntity.softRemove(deletePage); + } + + // [2] 시그니처 삭제 + await SignatureEntity.softRemove(signature); + } catch(error){ console.log("Error on deleting Signature: ",error); throw error; } } + + async patchSignature(signatureId: number, patchSignatureDto: CreateSignatureDto) { + + // [1] 시그니처 객체 가져오기 + const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); + if(signature == null) return null; + console.log("시그니처 정보: ", signature); + + // [2] 시그니처 수정 + signature.title = patchSignatureDto.title; + await SignatureEntity.save(signature); + + // [3] 기존 페이지 가져오기 + const originalSignaturePages: SignaturePageEntity[] = await SignaturePageEntity.find({ + where:{ signature:{ id: signature.id } } + }); + + // [4] 기존 페이지 수정하기 + for(const patchedPage of patchSignatureDto.pages){ + for( const originalPage of originalSignaturePages ){ + if(patchedPage._id == originalPage.id){ + originalPage.content = patchedPage.content; + originalPage.location = patchedPage.location; + + // 이미지 수정 필요 + originalPage.image = patchedPage.image; + } + await SignaturePageEntity.save(originalPage); + } + } + + return signatureId; + + } } From 18034536efe5b0837c3f334c535e93b72f758a27 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:32:27 +0900 Subject: [PATCH 080/316] =?UTF-8?q?[Refactor]=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98=20=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0:=20?= =?UTF-8?q?=EC=83=88=EB=A1=9C=EC=9A=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 5f00c66..9427cf6 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -29,7 +29,7 @@ export class SignatureService { if (!signature) throw new BadRequestException(); else{ // [2] 각 페이지 저장 for(const pageSignatureDto of createSignatureDto.pages){ - await SignaturePageEntity.saveSignaturePages(pageSignatureDto, signature); + await SignaturePageEntity.saveSignaturePage(pageSignatureDto, signature); } } @@ -227,8 +227,11 @@ export class SignatureService { where:{ signature:{ id: signature.id } } }); - // [4] 기존 페이지 수정하기 + // [4] 기존 페이지 수정 및 새로운 페이지 추가하기 for(const patchedPage of patchSignatureDto.pages){ + if(!patchedPage._id){ // id가 없으면 새로 추가할 페이지 + await SignaturePageEntity.saveSignaturePage(patchedPage,signature); + } for( const originalPage of originalSignaturePages ){ if(patchedPage._id == originalPage.id){ originalPage.content = patchedPage.content; From 0675cfd8b0a268fe8c38b7074ab36ee795d9812c Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 5 Feb 2024 20:48:38 +0900 Subject: [PATCH 081/316] =?UTF-8?q?feat=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 현재 로그인한 사용자가 팔로우하는 메이트 리스트 --- src/follow/dto/follow.dto.ts | 27 +++++++++++++++++ src/follow/follow.controller.ts | 37 ++++++++++++++++++++---- src/follow/follow.list.converter.ts | 45 +++++++++++++++++++++++++++++ src/follow/follow.module.ts | 4 ++- src/follow/follow.service.ts | 38 ++++++++++++++++-------- src/response/response-code.enum.ts | 5 ++++ src/rule/rule.controller.ts | 2 +- src/user/user.entity.ts | 3 ++ src/user/user.service.ts | 14 +++++++++ 9 files changed, 156 insertions(+), 19 deletions(-) create mode 100644 src/follow/dto/follow.dto.ts create mode 100644 src/follow/follow.list.converter.ts diff --git a/src/follow/dto/follow.dto.ts b/src/follow/dto/follow.dto.ts new file mode 100644 index 0000000..c1af144 --- /dev/null +++ b/src/follow/dto/follow.dto.ts @@ -0,0 +1,27 @@ +import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class FollowDto { + @IsNotEmpty() + @IsNumber() + mateId: number; + + @IsNotEmpty() + @IsString() + nickName: string; + + @IsNotEmpty() + @IsString() + email: string; + + @IsOptional() + @IsString() + image: string; + + @IsOptional() + @IsString() + introduction: string; + + @IsOptional() + @IsNumber() + followId: number; +} \ No newline at end of file diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 7447eb5..94d5ae9 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -1,7 +1,8 @@ -import { Controller, Post, Req, UseGuards, Param, Delete } from '@nestjs/common'; +import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/common'; import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; +import { FollowDto } from './dto/follow.dto'; // import { UserGuard } from 'src/user/user.guard'; // @UseGuards(UserGuard) @@ -11,7 +12,7 @@ export class FollowController { private readonly followService: FollowService, ) {} - // 팔로우 + // [1] 팔로우 @Post('/:followingId') async createFollow(@Param('followingId') followingId : number): Promise> { // 현재 사용자 ID @@ -34,9 +35,9 @@ export class FollowController { null ); } - } + } - // 언팔로우 + // [2] 언팔로우 @Delete('/:followId') async deleteFollow(@Param('followId') followId: number): Promise> { // 현재 사용자 ID @@ -68,5 +69,31 @@ export class FollowController { null ); } - } + } + + // [3] 팔로우 리스트 조회 + @Get('/followList') + async getFollowList(): Promise> { + // 현재 사용자 ID + // const userId = req.user.id; + const userId = 1; + + try { + const followList = await this.followService.getFollowList(userId); + return new ResponseDto( + ResponseCode.GET_FOLLOWING_LIST_SUCCESS, + true, + "팔로우 리스트 불러오기 성공", + followList + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_FOLLOWING_LIST_FAIL, + false, + "팔로우 리스트 불러오기 실패", + null + ); + } + } + } \ No newline at end of file diff --git a/src/follow/follow.list.converter.ts b/src/follow/follow.list.converter.ts new file mode 100644 index 0000000..8a70d0b --- /dev/null +++ b/src/follow/follow.list.converter.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/user/user.entity'; +import { UserProfileImageEntity } from 'src/user/user.profile.image.entity'; +import { UserFollowingEntity } from 'src/user/user.following.entity'; +import { FollowDto } from './dto/follow.dto'; +import { UserService } from 'src/user/user.service'; + + +@Injectable() +export class FollowListConverter { + + constructor(private readonly userService: UserService) {} + + async toDto(userId: number): Promise { + + // 현재 로그인한 사용자 + const user : UserEntity = await this.userService.findUserById(userId); + console.log('현재 로그인한 사용자 : ',user); + + // 로그인한 사용자의 팔로우 리스트 + const followings : UserFollowingEntity[] = await this.userService.getFollowingList(userId); + console.log('followings : ',followings); + + // 팔로우 사용자들 정보 리스트 + const informs = await Promise.all(followings.map(async (following) => { + const followDto : FollowDto = new FollowDto(); + const mateEntity : UserEntity = following.user; + + followDto.mateId = mateEntity.id; + followDto.nickName = mateEntity.nickname; + followDto.email = mateEntity.email; + followDto.introduction = mateEntity.introduction; + + followDto.followId = following.id; // 팔로우 테이블 ID + const image = await this.userService.getProfileImage(mateEntity.id); + followDto.image = image.imageKey; + + return followDto; + })) + + console.log(informs); + + return informs; + } +} \ No newline at end of file diff --git a/src/follow/follow.module.ts b/src/follow/follow.module.ts index 9264cca..a9b8907 100644 --- a/src/follow/follow.module.ts +++ b/src/follow/follow.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; import { FollowService } from './follow.service'; import { FollowController } from './follow.controller'; +import { FollowListConverter } from './follow.list.converter'; +import { UserService } from 'src/user/user.service'; @Module({ controllers: [FollowController], - providers: [FollowService], + providers: [FollowService, FollowListConverter, UserService], }) export class FollowModule {} diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index e4130d6..f2e8418 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -1,20 +1,34 @@ import { Injectable, HttpException } from '@nestjs/common'; -import { UserEntity } from 'src/user/user.entity'; import { UserFollowingEntity } from 'src/user/user.following.entity'; +import { FollowListConverter } from './follow.list.converter'; +import { FollowDto } from './dto/follow.dto'; @Injectable() export class FollowService { - async createFollow(userId : number, followingId : number): Promise { - const follow = UserFollowingEntity.create({ - user: { id : userId }, - followUser: { id : followingId }, - }); - return follow.save(); - } + constructor( + private followListConverter: FollowListConverter, + ) {} - async deleteFollow(followId:number): Promise { - const followEntity : UserFollowingEntity = await UserFollowingEntity.findOne({ where: { id : followId }}); + // [1] 팔로우 + async createFollow(userId : number, followingId : number): Promise { + const follow = UserFollowingEntity.create({ + user: { id : userId }, + followUser: { id : followingId }, + }); + return follow.save(); + } - return followEntity.softRemove(); - } + // [2] 언팔로우 + async deleteFollow(followId:number): Promise { + const followEntity : UserFollowingEntity = await UserFollowingEntity.findOne({ where: { id : followId }}); + + return followEntity.softRemove(); + } + + // [3] 팔로우 리스트 조회 + async getFollowList(userId: number): Promise { + const followDto: FollowDto[] = await this.followListConverter.toDto(userId); + + return followDto; + } } diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 04355d1..37666b8 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -12,6 +12,9 @@ export enum ResponseCode { UPDATE_PROFILE_SUCCESS = 'OK', GET_RULE_DETAIL_SUCCESS = 'OK', UNFOLLOW_SUCCESS = 'OK', + GET_FOLLOWER_LIST_SUCCESS = 'OK', + GET_FOLLOWING_LIST_SUCCESS = 'OK', + /* 201 CREATED : 요청 성공, 자원 생성 */ @@ -33,6 +36,8 @@ export enum ResponseCode { GET_RULE_DETAIL_FAIL = 'BAD_REQUEST', FOLLOW_CREATION_FAIL = 'BAD_REQUEST', UNFOLLOW_FAIL = 'BAD_REQUEST', + GET_FOLLOWER_LIST_FAIL = 'BAD_REQUEST', + GET_FOLLOWING_LIST_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 202d297..1fc3a31 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -2,7 +2,7 @@ import { Controller, Post, Body, Get, Param } from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; -import { ResponseDto } from '../response/response.dto' +import { ResponseDto } from '../response/response.dto'; import { MetaToBackDto } from './dto/meta-to-back.dto'; @Controller('mate/rule') diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 7892851..d379619 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -34,8 +34,11 @@ export class UserEntity extends BaseEntity { @Column() nickname: string; + /* @Column({ type: 'text', nullable: true }) bio: string; + */ + @Column({ type: 'text' }) introduction: string; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 6861035..9413472 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -6,6 +6,7 @@ import { IReqUser, IUserProfile } from './user.dto'; import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; +import { UserFollowingEntity } from './user.following.entity'; @Injectable() export class UserService { @@ -141,4 +142,17 @@ export class UserService { ); } } + + async getFollowingList(userId: number) { + try { + const userFollowingEntity = await UserFollowingEntity.find({ + where: { user: { id: userId } }, + relations: ['user'], + }); + + return userFollowingEntity; + } catch (error) { + console.log('Error on getFollowingList: ' + error); + } + } } From 3d1a2da8cc7ea831376f3dd16b6da5889e50df74 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 5 Feb 2024 21:07:50 +0900 Subject: [PATCH 082/316] =?UTF-8?q?feat=20:=20=ED=8C=94=EB=A1=9C=EC=9B=8C?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 현재 로그인한 사용자를 팔로우하는 메이트 리스트 --- src/follow/follow.controller.ts | 27 +++++++++++++++++- src/follow/follow.list.converter.ts | 6 +--- src/follow/follow.module.ts | 3 +- src/follow/follow.service.ts | 10 +++++++ src/follow/follower.list.converter.ts | 40 +++++++++++++++++++++++++++ src/user/user.service.ts | 13 ++++++++- 6 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 src/follow/follower.list.converter.ts diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 94d5ae9..ec2f9bb 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -74,7 +74,7 @@ export class FollowController { // [3] 팔로우 리스트 조회 @Get('/followList') async getFollowList(): Promise> { - // 현재 사용자 ID + // 현재 로그인한 사용자 ID // const userId = req.user.id; const userId = 1; @@ -96,4 +96,29 @@ export class FollowController { } } + // [4] 팔로워 리스트 조회 + @Get('/followerList') + async getFollowerList(): Promise> { + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 1; + + try { + const followerList = await this.followService.getFollowerList(userId); + return new ResponseDto( + ResponseCode.GET_FOLLOWER_LIST_SUCCESS, + true, + "팔로워 리스트 불러오기 성공", + followerList + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_FOLLOWER_LIST_FAIL, + false, + "팔로워 리스트 불러오기 실패", + null + ); + } + } + } \ No newline at end of file diff --git a/src/follow/follow.list.converter.ts b/src/follow/follow.list.converter.ts index 8a70d0b..e0290ef 100644 --- a/src/follow/follow.list.converter.ts +++ b/src/follow/follow.list.converter.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { UserEntity } from 'src/user/user.entity'; -import { UserProfileImageEntity } from 'src/user/user.profile.image.entity'; import { UserFollowingEntity } from 'src/user/user.following.entity'; import { FollowDto } from './dto/follow.dto'; import { UserService } from 'src/user/user.service'; @@ -26,19 +25,16 @@ export class FollowListConverter { const followDto : FollowDto = new FollowDto(); const mateEntity : UserEntity = following.user; - followDto.mateId = mateEntity.id; followDto.nickName = mateEntity.nickname; + followDto.mateId = mateEntity.id; followDto.email = mateEntity.email; followDto.introduction = mateEntity.introduction; - followDto.followId = following.id; // 팔로우 테이블 ID const image = await this.userService.getProfileImage(mateEntity.id); followDto.image = image.imageKey; return followDto; })) - - console.log(informs); return informs; } diff --git a/src/follow/follow.module.ts b/src/follow/follow.module.ts index a9b8907..6a9f3a9 100644 --- a/src/follow/follow.module.ts +++ b/src/follow/follow.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { FollowService } from './follow.service'; import { FollowController } from './follow.controller'; import { FollowListConverter } from './follow.list.converter'; +import { FollowerListConverter } from './follower.list.converter'; import { UserService } from 'src/user/user.service'; @Module({ controllers: [FollowController], - providers: [FollowService, FollowListConverter, UserService], + providers: [FollowService, FollowListConverter, FollowerListConverter, UserService], }) export class FollowModule {} diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index f2e8418..0a94e47 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -1,12 +1,15 @@ import { Injectable, HttpException } from '@nestjs/common'; import { UserFollowingEntity } from 'src/user/user.following.entity'; import { FollowListConverter } from './follow.list.converter'; +import { FollowerListConverter } from './follower.list.converter'; import { FollowDto } from './dto/follow.dto'; @Injectable() export class FollowService { constructor( private followListConverter: FollowListConverter, + private followerListConverter: FollowerListConverter, + ) {} // [1] 팔로우 @@ -31,4 +34,11 @@ export class FollowService { return followDto; } + + // [4] 팔로워 리스트 조회 + async getFollowerList(userId: number): Promise { + const followerDto: FollowDto[] = await this.followerListConverter.toDto(userId); + + return followerDto; + } } diff --git a/src/follow/follower.list.converter.ts b/src/follow/follower.list.converter.ts new file mode 100644 index 0000000..2509fcc --- /dev/null +++ b/src/follow/follower.list.converter.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/user/user.entity'; +import { UserFollowingEntity } from 'src/user/user.following.entity'; +import { FollowDto } from './dto/follow.dto'; +import { UserService } from 'src/user/user.service'; + +@Injectable() +export class FollowerListConverter { + + constructor(private readonly userService: UserService) {} + + async toDto(userId: number): Promise { + + // 현재 로그인한 사용자 + const user : UserEntity = await this.userService.findUserById(userId); + console.log('현재 로그인한 사용자 : ', user); + + // 로그인한 사용자의 팔로워 리스트 + const followers : UserFollowingEntity[] = await this.userService.getFollowerList(userId); + console.log('followers : ', followers); + + // 팔로워 사용자들 정보 리스트 + const informs = await Promise.all(followers.map(async (follower) => { + const followerDto : FollowDto = new FollowDto(); + const mateEntity : UserEntity = follower.user; + + followerDto.nickName = mateEntity.nickname; + followerDto.mateId = mateEntity.id; + followerDto.email = mateEntity.email; + followerDto.introduction = mateEntity.introduction; + followerDto.followId = follower.id; // 팔로우 테이블 ID (null인 경우 팔로우 버튼 표시) + const image = await this.userService.getProfileImage(mateEntity.id); + followerDto.image = image.imageKey; + + return followerDto; + })) + + return informs; + } +} \ No newline at end of file diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 9413472..b54f78d 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -149,10 +149,21 @@ export class UserService { where: { user: { id: userId } }, relations: ['user'], }); - return userFollowingEntity; } catch (error) { console.log('Error on getFollowingList: ' + error); } } + + async getFollowerList(userId: number) { + try { + const userFollowerEntity = await UserFollowingEntity.find({ + where: { followUser: { id: userId } }, + relations: ['user'], + }); + return userFollowerEntity; + } catch (error) { + console.log('Error on getFollowingList: ' + error); + } + } } From a88b3810464cab25c1c1609d60f644c00448bb80 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 5 Feb 2024 21:53:58 +0900 Subject: [PATCH 083/316] =?UTF-8?q?feat=20:=20=EC=9D=B4=EB=AF=B8=20?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9A=B0=ED=95=98=EB=8A=94=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=EC=9D=B8=EC=A7=80=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 17 ++++++++++++++++- src/follow/follow.service.ts | 1 - src/response/response-code.enum.ts | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index ec2f9bb..eb0f426 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -2,7 +2,8 @@ import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/co import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -import { FollowDto } from './dto/follow.dto'; +import { UserEntity } from 'src/user/user.entity'; +import { UserService } from 'src/user/user.service'; // import { UserGuard } from 'src/user/user.guard'; // @UseGuards(UserGuard) @@ -10,6 +11,7 @@ import { FollowDto } from './dto/follow.dto'; export class FollowController { constructor( private readonly followService: FollowService, + private readonly userService: UserService, ) {} // [1] 팔로우 @@ -19,6 +21,19 @@ export class FollowController { // const userId = req.user.id; const userId = 1; + // 이미 팔로우하는 사용자인지 검증 + const user : UserEntity = await this.userService.findUserById(userId); + const isAlreadyFollow = this.userService.checkIfFollowing(user, followingId); + + if (isAlreadyFollow) { + return new ResponseDto( + ResponseCode.IS_ALREADY_FOLLOW, + false, + "이미 팔로우하는 사용자입니다", + null + ); + } + try { await this.followService.createFollow(userId, followingId); return new ResponseDto( diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 0a94e47..4c8ca28 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -9,7 +9,6 @@ export class FollowService { constructor( private followListConverter: FollowListConverter, private followerListConverter: FollowerListConverter, - ) {} // [1] 팔로우 diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 37666b8..7d1f767 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -38,6 +38,7 @@ export enum ResponseCode { UNFOLLOW_FAIL = 'BAD_REQUEST', GET_FOLLOWER_LIST_FAIL = 'BAD_REQUEST', GET_FOLLOWING_LIST_FAIL = 'BAD_REQUEST', + IS_ALREADY_FOLLOW = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ From 738467e58ba043ba8bbdc9542162e56b1c38dedf Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 00:12:25 +0900 Subject: [PATCH 084/316] =?UTF-8?q?[Feat]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=A2=8B=EC=95=84=EC=9A=94=ED=95=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20API=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 3 +- src/signature/domain/signature.like.entity.ts | 13 ++++- src/signature/dto/get-like-list.dto.ts | 9 +++ src/signature/dto/like-profile.dto.ts | 9 +++ src/signature/signature.controller.ts | 31 ++++++++++- src/signature/signature.service.ts | 55 ++++++++++++++++++- 6 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 src/signature/dto/get-like-list.dto.ts create mode 100644 src/signature/dto/like-profile.dto.ts diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index c1a54d0..0baf3ba 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -12,6 +12,7 @@ export enum ResponseCode { UPDATE_PROFILE_SUCCESS = 'OK', DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', + GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', @@ -25,7 +26,7 @@ export enum ResponseCode { GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', SIGNATURE_PATCH_FAIL = 'BAD_REQUEST', - + GET_LIKE_SIGNATURE_PROFILES_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ INVALID_AUTH_TOKEN = 'UNAUTHORIZED', diff --git a/src/signature/domain/signature.like.entity.ts b/src/signature/domain/signature.like.entity.ts index dcffa4c..a14c0b5 100644 --- a/src/signature/domain/signature.like.entity.ts +++ b/src/signature/domain/signature.like.entity.ts @@ -40,12 +40,21 @@ export class SignatureLikeEntity extends BaseEntity { const signatureLike = new SignatureLikeEntity(); signatureLike.signature = signature; signatureLike.user = loginUser; - - return SignatureLikeEntity.save(signatureLike); + const signatureLikeEntity = await signatureLike.save(); + console.log("sigLike created: ", signatureLikeEntity); }catch(error){ console.error('Error on likeSignature: ', error); throw new Error('Failed to like Signature'); } } + + static async findSignatureLikes(signatureId: number) { + const signatureLikeEntities = await SignatureLikeEntity.find({ + where:{ signature:{id: signatureId} }, + relations: ['user', 'signature'], + }) + + return signatureLikeEntities; + } } diff --git a/src/signature/dto/get-like-list.dto.ts b/src/signature/dto/get-like-list.dto.ts new file mode 100644 index 0000000..e118369 --- /dev/null +++ b/src/signature/dto/get-like-list.dto.ts @@ -0,0 +1,9 @@ +// get-like-list.dto.ts + +import { LikeProfileDto } from './like-profile.dto'; + +export class GetLikeListDto{ + liked: number; // 좋아요 개수 + profiles: LikeProfileDto[]; // 좋아요한 사용자 프로필 리스트 + +} \ No newline at end of file diff --git a/src/signature/dto/like-profile.dto.ts b/src/signature/dto/like-profile.dto.ts new file mode 100644 index 0000000..ad76feb --- /dev/null +++ b/src/signature/dto/like-profile.dto.ts @@ -0,0 +1,9 @@ +// like-profile.dto.ts + +export class LikeProfileDto{ + _id: number; + nickname: string; + introduction: string; + is_followed: boolean; + image: string; +} \ No newline at end of file diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 4212ec4..39909d8 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -11,7 +11,7 @@ import { TmpUserIdDto } from './dto/tmp-userId.dto'; import { SignatureEntity } from './domain/signature.entity'; import { SignatureLikeEntity } from './domain/signature.like.entity'; import { LikeSignatureDto } from './dto/like-signature.dto'; - +import { GetLikeListDto } from './dto/get-like-list.dto'; @Controller('signature') //@UseGuards(new AuthGuard()) @@ -89,7 +89,6 @@ export class SignatureController { likeSignatureDto ); - }else{ // 좋아요 한적 없으면 시그니처 좋아요 추가 result = await this.signatureService.addLikeOnSignature(user_id.userId,signatureId); likeSignatureDto.liked = result.liked; @@ -199,7 +198,7 @@ export class SignatureController { } // [2] 시그니처 삭제하기 - const result = await this.signatureService.deleteSignature(signature); + await this.signatureService.deleteSignature(signature); return new ResponseDto( ResponseCode.DELETE_SIGNATURE_SUCCESS, @@ -217,6 +216,32 @@ export class SignatureController { null ); } + } + + @Get('/like/:signatureId') // 시그니처에 좋아요한 사용자 목록 + async getSignatureLikeList( + @Body() user_id: TmpUserIdDto, + @Param('signatureId') signatureId: number + ): Promise> { + try{ + const getLikeListDto:GetLikeListDto = await this.signatureService.getSignatureLikeList(user_id.userId, signatureId); + return new ResponseDto( + ResponseCode.GET_LIKE_SIGNATURE_PROFILES_SUCCESS, + true, + "시그니처 좋아요 목록 불러오기 성공", + getLikeListDto + ); + + } + catch(error){ + return new ResponseDto( + ResponseCode.GET_LIKE_SIGNATURE_PROFILES_FAIL, + false, + "시그니처 좋아요 목록 불러오기 실패", + null + ); + } } + } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 9427cf6..2b83cb1 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -1,6 +1,6 @@ // signature.service.ts -import { BadRequestException, HttpException, Injectable } from '@nestjs/common'; +import { BadRequestException, HttpException, Injectable, NotFoundException } from '@nestjs/common'; import bcrypt from 'bcrypt'; import jsonwebtoken from 'jsonwebtoken'; import { CreateSignatureDto } from './dto/create-signature.dto'; @@ -15,6 +15,9 @@ import { AuthorSignatureDto } from './dto/author-signature.dto'; import { HeaderSignatureDto } from './dto/header-signature.dto'; import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; +import { GetLikeListDto } from './dto/get-like-list.dto'; +import { LikeProfileDto } from './dto/like-profile.dto'; +import { errorContext } from 'rxjs/internal/util/errorContext'; @Injectable() export class SignatureService { @@ -247,4 +250,54 @@ export class SignatureService { return signatureId; } + + + async getSignatureLikeList(userId: number, signatureId: number): Promise { + + try{ + + const signature = await SignatureEntity.findSignatureById(signatureId); + if(!signature) { + throw new NotFoundException(`Signature with ID ${signatureId} not found`); + } + + const getLikeListDto: GetLikeListDto = new GetLikeListDto(); + + const signatureLikeEntities = await SignatureLikeEntity.findSignatureLikes(signatureId); + + // 총 좋아요 개수 + getLikeListDto.liked = signatureLikeEntities.length; + + const likeProfileDtos: LikeProfileDto[] = []; + + for(const signatureLikeEntity of signatureLikeEntities){ + const likeProfileDto = new LikeProfileDto(); + + + if (signatureLikeEntity.user) { + const image = await this.userService.getProfileImage(signatureLikeEntity.user.id); + likeProfileDto._id = signatureLikeEntity.user.id; + likeProfileDto.image = image.imageKey; + likeProfileDto.introduction = signatureLikeEntity.user.introduction; + likeProfileDto.nickname = signatureLikeEntity.user.nickname; + + // 만약 좋아요 누른 사용자가 본인이 아니라면 is_followed 값을 체크하고 본인이면 null로 보내준다. + if(signatureLikeEntity.user.id != userId){ + const loginUser= await this.userService.findUserById(userId); + likeProfileDto.is_followed = await this.userService.checkIfFollowing(loginUser,signatureLikeEntity.user.id); + } + else likeProfileDto.is_followed = null; + likeProfileDtos.push(likeProfileDto); + } + } + getLikeListDto.profiles = likeProfileDtos; + + return getLikeListDto; + + }catch(error){ + console.log("Error on GetSignatureLikeList: ", error); + throw error; + } + } + } From 3ded6f2f56594fb82975b802b8b790c36d512496 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 00:13:16 +0900 Subject: [PATCH 085/316] =?UTF-8?q?[Refactor]=20dto=20id=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=EA=B0=92=20=5Fid=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/domain/signature.entity.ts | 14 ++++++-------- src/signature/domain/signature.page.entity.ts | 2 +- src/signature/dto/home-signature.dto.ts | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index c017b3b..2b3f8cf 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -7,14 +7,12 @@ import { DeleteDateColumn, Entity, EntitySubscriberInterface, EventSubscriber, InsertEvent, JoinColumn, ManyToOne, OneToMany, - OneToOne, PrimaryGeneratedColumn, RemoveEvent, UpdateDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; import { HomeSignatureDto } from '../dto/home-signature.dto'; import { CreateSignatureDto } from '../dto/create-signature.dto'; -import { UserService } from '../../user/user.service'; import { SignaturePageEntity } from './signature.page.entity'; import { SignatureLikeEntity } from './signature.like.entity'; @Entity() @@ -29,16 +27,16 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter @Column({ default: 0 }) liked: number; - @ManyToOne(() => UserEntity, (user) => user.signatures) + @ManyToOne(() => UserEntity, + (user) => user.signatures) @JoinColumn({ name: 'user_id' }) user: UserEntity; @OneToMany(() => SignaturePageEntity, (signaturePage) => signaturePage.signature) signaturePages: SignaturePageEntity[]; - @OneToMany(() => SignatureLikeEntity, (signatureLike) => signatureLike.signature, { - cascade: true, // 관계에 대한 연산을 가능하게 합니다. - }) + @OneToMany(() => SignatureLikeEntity, + (signatureLike) => signatureLike.signature) likes: SignatureLikeEntity[]; listenTo() { @@ -58,7 +56,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter // 변경된 값에 따라 liked 카운트 업데이트 private updateLikedCount(entity: SignatureLikeEntity, change: number): void { this.liked += change; - this.save(); // 업데이트된 liked 카운트를 데이터베이스에 저장합니다. + this.save(); // 업데이트된 liked 카운트를 데이터베이스에 저장 } @@ -116,7 +114,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter for(const signature of signatures){ const homeSignature:HomeSignatureDto = new HomeSignatureDto(); - homeSignature.id = signature.id; + homeSignature._id = signature.id; homeSignature.title = signature.title; homeSignature.date = signature.created; homeSignature.image = await SignaturePageEntity.findThumbnail(signature.id); diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index b09ac0a..b76f1df 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -44,7 +44,7 @@ export class SignaturePageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async saveSignaturePages( + static async saveSignaturePage( pageSignatureDto:PageSignatureDto, signature:SignatureEntity):Promise { diff --git a/src/signature/dto/home-signature.dto.ts b/src/signature/dto/home-signature.dto.ts index 5b272ae..1dbf511 100644 --- a/src/signature/dto/home-signature.dto.ts +++ b/src/signature/dto/home-signature.dto.ts @@ -1,7 +1,7 @@ // home-signature.dto.ts export class HomeSignatureDto { - id: number; // 시그니처 id + _id: number; // 시그니처 id title: string; // 시그니처 제목 date: Date; // 시그니처 발행일 image: string; // 시그니처 첫번째 페이지의 이미지(썸네일) From 19ddb7dd09d457a632ff9a01885c561a66ae97a8 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 00:35:54 +0900 Subject: [PATCH 086/316] =?UTF-8?q?[Refactor]=20dto=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 2b83cb1..042e8c4 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -15,7 +15,7 @@ import { AuthorSignatureDto } from './dto/author-signature.dto'; import { HeaderSignatureDto } from './dto/header-signature.dto'; import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { GetLikeListDto } from './dto/get-like-list.dto'; +import { GetLikeListDto } from './dto/get-like-list.dto' import { LikeProfileDto } from './dto/like-profile.dto'; import { errorContext } from 'rxjs/internal/util/errorContext'; From 1fc31577868d89f9fa8ec8afd196c31484328a97 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 6 Feb 2024 00:46:51 +0900 Subject: [PATCH 087/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=A9=A4=EB=B2=84=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 ++ src/member/dto/member.dto.ts | 23 ++++++++++++ src/member/member.controller.ts | 38 ++++++++++++++++++++ src/member/member.list.converter.ts | 54 +++++++++++++++++++++++++++++ src/member/member.module.ts | 13 +++++++ src/member/member.service.ts | 21 +++++++++++ src/response/response-code.enum.ts | 3 +- src/rule/rule.service.ts | 15 ++++++++ 8 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 src/member/dto/member.dto.ts create mode 100644 src/member/member.controller.ts create mode 100644 src/member/member.list.converter.ts create mode 100644 src/member/member.module.ts create mode 100644 src/member/member.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index fa7416e..625c237 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -16,6 +16,7 @@ import { CommentModule } from './comment/comment.module'; import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; import { S3Module } from './utils/S3.module'; import { FollowModule } from './follow/follow.module'; +import { MemberModule } from './member/member.module'; @Module({ imports: [ @@ -35,6 +36,7 @@ import { FollowModule } from './follow/follow.module'; CommentModule, S3Module, FollowModule, + MemberModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/member/dto/member.dto.ts b/src/member/dto/member.dto.ts new file mode 100644 index 0000000..597f71e --- /dev/null +++ b/src/member/dto/member.dto.ts @@ -0,0 +1,23 @@ +import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class MemberDto { + @IsNotEmpty() + @IsNumber() + memberId: number; + + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + nickName: string; + + @IsOptional() + @IsString() + introduction: string; + + @IsOptional() + @IsString() + image: string; +} \ No newline at end of file diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts new file mode 100644 index 0000000..efc6294 --- /dev/null +++ b/src/member/member.controller.ts @@ -0,0 +1,38 @@ +import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/common'; +import { ResponseCode } from '../response/response-code.enum'; +import { ResponseDto } from '../response/response.dto'; +import { MemberService } from './member.service'; +// import { UserGuard } from 'src/user/user.guard'; + +// @UseGuards(UserGuard) +@Controller('mate/rule/member') +export class MemberController { + constructor( + private readonly memberService: MemberService, + ) {} + + // [1] 여행 규칙 멤버 리스트 + @Get('/:ruleId') + async getMember(@Param('ruleId') ruleId : number) : Promise> { + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 1; + + try { + const memberList = await this.memberService.getMemberList(userId, ruleId); + return new ResponseDto( + ResponseCode.GET_MEMBER_LIST_SUCCESS, + true, + "여행 규칙 멤버 리스트 불러오기 성공", + memberList + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_MEMBER_LIST_FAIL, + false, + "여행 규칙 멤버 리스트 불러오기 실패", + null + ); + } + } +} \ No newline at end of file diff --git a/src/member/member.list.converter.ts b/src/member/member.list.converter.ts new file mode 100644 index 0000000..f17986a --- /dev/null +++ b/src/member/member.list.converter.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/user/user.entity'; +import { MemberDto } from './dto/member.dto'; +import { UserService } from 'src/user/user.service'; +import { RuleService } from 'src/rule/rule.service'; +import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; + +@Injectable() +export class MemberListConverter { + + constructor( + private readonly userService: UserService, + private readonly ruleService: RuleService, + + ) {} + + // [1] 여행 규칙 멤버 정보 리스트 DTO 생성 + async toDto(userId: number,ruleId: number): Promise { + + // 여행 초대 객체 생성 + const invitations : RuleInvitationEntity[] = await this.ruleService.getInvitationList(ruleId); + + // 여행 참여 멤버 정보 리스트 생성 + // -1. 팀원 멤버 (invited) 정보 추가 + const informs = await Promise.all(invitations.map(async (invitaiton) => { + const memberDto : MemberDto = new MemberDto(); + const invited : UserEntity = invitaiton.invited; + + memberDto.memberId = invited.id; + memberDto.name = invited.name; + memberDto.nickName = invited.nickname; + memberDto.introduction = invited.introduction; + const image = await this.userService.getProfileImage(invited.id); + memberDto.image = image.imageKey; + + return memberDto; + })) + + // -2. 팀장 멤버 (inviter) 정보 추가 + const inviterDto : MemberDto = new MemberDto(); + const inviter : UserEntity = await this.userService.findUserById(userId); + + inviterDto.memberId = inviter.id; + inviterDto.name = inviter.name; + inviterDto.nickName = inviter.nickname; + inviterDto.introduction = inviter.introduction; + const image = await this.userService.getProfileImage(inviter.id); + inviterDto.image = image.imageKey; + + informs.push(inviterDto); + + return informs; + } +} \ No newline at end of file diff --git a/src/member/member.module.ts b/src/member/member.module.ts new file mode 100644 index 0000000..b653da1 --- /dev/null +++ b/src/member/member.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { MemberService } from './member.service'; +import { RuleService } from 'src/rule/rule.service'; +import { MemberController } from './member.controller'; +import { MemberListConverter } from './member.list.converter'; +import { UserService } from 'src/user/user.service'; +import { RuleConverter } from 'src/rule/rule.converter'; + +@Module({ + controllers: [MemberController], + providers: [MemberService, MemberListConverter, RuleService, UserService, RuleConverter], +}) +export class MemberModule {} diff --git a/src/member/member.service.ts b/src/member/member.service.ts new file mode 100644 index 0000000..9d6b50d --- /dev/null +++ b/src/member/member.service.ts @@ -0,0 +1,21 @@ +import { Injectable, HttpException } from '@nestjs/common'; +import { MemberListConverter } from './member.list.converter'; +import { MemberDto } from './dto/member.dto'; + +@Injectable() +export class MemberService { + constructor( + private memberListConverter: MemberListConverter, + ) {} + + // [1] 여행 규칙 멤버 리스트 조회 + async getMemberList(userId: number, ruleId: number): Promise { + const memberDto: MemberDto[] = await this.memberListConverter.toDto(userId, ruleId); + + return memberDto; + } + + // [2] 여행 규칙 멤버 초대 + + // [3] 여행 규칙 멤버 삭제 +} \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 7d1f767..0b372b0 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -14,7 +14,7 @@ export enum ResponseCode { UNFOLLOW_SUCCESS = 'OK', GET_FOLLOWER_LIST_SUCCESS = 'OK', GET_FOLLOWING_LIST_SUCCESS = 'OK', - + GET_MEMBER_LIST_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ @@ -39,6 +39,7 @@ export enum ResponseCode { GET_FOLLOWER_LIST_FAIL = 'BAD_REQUEST', GET_FOLLOWING_LIST_FAIL = 'BAD_REQUEST', IS_ALREADY_FOLLOW = 'BAD_REQUEST', + GET_MEMBER_LIST_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9b0c22e..8723acb 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -17,6 +17,7 @@ export class RuleService { private ruleConverter: RuleConverter, ) {} + // [1] 여행 규칙 생성 async createRule(createRuleDto: CreateRuleDto): Promise { const { main, rules, invitations } = await this.ruleConverter.toEntity(createRuleDto); @@ -29,6 +30,7 @@ export class RuleService { return savedMain.id; } + // [2] 여행 규칙 조회 async getDetail(ruleId : number, metaToBackDto : MetaToBackDto): Promise { try{ const detailPageDto : DetailPageDto = new DetailPageDto(); @@ -40,4 +42,17 @@ export class RuleService { throw new HttpException('Internal Server Error', 500); } } + + // [member] 초대 받은 멤버 리스트 생성 + async getInvitationList(ruleId: number) { + try { + const invitationEntity = await RuleInvitationEntity.find({ + where: { id : ruleId }, + relations: ['invited'], + }); + return invitationEntity; + } catch (error) { + console.log('Error on getInvitationList: ' + error); + } + } } From 3d8a707771f566986637858d020c99c6bd58bb57 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 6 Feb 2024 02:32:30 +0900 Subject: [PATCH 088/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=A9=A4=EB=B2=84=20=EC=B4=88=EB=8C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이미 초대된 멤버인지 확인하는 테스트 기능 포함 --- src/member/member.controller.ts | 47 ++++++++++++++++++++++++++++-- src/member/member.service.ts | 13 ++++++++- src/response/response-code.enum.ts | 6 +++- src/rule/rule.service.ts | 26 +++++++++++++++++ 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index efc6294..0b3d1f8 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -2,6 +2,7 @@ import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/co import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { MemberService } from './member.service'; +import { RuleService } from 'src/rule/rule.service'; // import { UserGuard } from 'src/user/user.guard'; // @UseGuards(UserGuard) @@ -9,14 +10,15 @@ import { MemberService } from './member.service'; export class MemberController { constructor( private readonly memberService: MemberService, + private readonly ruleService: RuleService, ) {} - // [1] 여행 규칙 멤버 리스트 + // [1] 여행 규칙 멤버 리스트 조회 @Get('/:ruleId') async getMember(@Param('ruleId') ruleId : number) : Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 1; + const userId = 2; try { const memberList = await this.memberService.getMemberList(userId, ruleId); @@ -35,4 +37,45 @@ export class MemberController { ); } } + + // [2] 여행 규칙 멤버 초대 + @Post('/:ruleId/:invitedId') + async createInvitation(@Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 2; + + // 이미 초대된 멤버인지 확인 + const ruleEntity = await this.ruleService.findRuleById(ruleId); + console.log('--이미 참여하는 사용자인지 검증 시작--') + const check = this.ruleService.checkAlreadyMember(ruleEntity, invitedId); + if (check) { + return new ResponseDto( + ResponseCode.IS_ALREADY_MEMBER, + false, + "이미 초대된 사용자입니다", + null + ); + } + console.log('--검증 완료--') + + + // 멤버 초대 + try { + await this.memberService.createInvitation(ruleId, userId, invitedId); + return new ResponseDto( + ResponseCode.INVITATION_CREATED, + true, + "여행 규칙 멤버 초대 성공", + null + ); + } catch (error) { + return new ResponseDto( + ResponseCode.INVITATION_FAIL, + false, + "여행 규칙 멤버 초대 실패", + null + ); + } + } } \ No newline at end of file diff --git a/src/member/member.service.ts b/src/member/member.service.ts index 9d6b50d..bb3e92c 100644 --- a/src/member/member.service.ts +++ b/src/member/member.service.ts @@ -1,6 +1,7 @@ import { Injectable, HttpException } from '@nestjs/common'; import { MemberListConverter } from './member.list.converter'; import { MemberDto } from './dto/member.dto'; +import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; @Injectable() export class MemberService { @@ -16,6 +17,16 @@ export class MemberService { } // [2] 여행 규칙 멤버 초대 - + async createInvitation(ruleId: number, userId: number, invitedId: number): Promise { + + // invitation 객체 생성 + const invitation = RuleInvitationEntity.create({ + rule: { id : ruleId }, + inviter: { id : userId}, + invited: { id : invitedId}, + }) + return invitation.save(); + } + // [3] 여행 규칙 멤버 삭제 } \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 0b372b0..5ddd6c9 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -23,7 +23,8 @@ export enum ResponseCode { RULE_CREATED = 'CREATED', LIKE_ON_SIGNATURE_CREATED = 'CREATED', COMMENT_CREATED = 'CREATED', - FOLLOW_CREATED = 'OK', + FOLLOW_CREATED = 'CREATED', + INVITATION_CREATED = 'CREATED', /* 400 BAD_REQUEST : 잘못된 요청 */ @@ -40,6 +41,9 @@ export enum ResponseCode { GET_FOLLOWING_LIST_FAIL = 'BAD_REQUEST', IS_ALREADY_FOLLOW = 'BAD_REQUEST', GET_MEMBER_LIST_FAIL = 'BAD_REQUEST', + INVITATION_FAIL = 'BAD_REQUEST', + IS_ALREADY_MEMBER = 'BAD_REQUEST', + /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 8723acb..0c979c2 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -43,6 +43,20 @@ export class RuleService { } } + // + + async findRuleById(ruleId: number): Promise { + try { + const rule: RuleMainEntity = await RuleMainEntity.findOne({ + where: { id: ruleId }, + }); + return rule; + } catch (error) { + console.log('Error on findRuleById: ', error); + throw error; + } + } + // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { @@ -55,4 +69,16 @@ export class RuleService { console.log('Error on getInvitationList: ' + error); } } + + // [member] 이미 초대된 멤버인지 확인 + async checkAlreadyMember(rule: RuleMainEntity, targetUserId: number): Promise { + const invitedArray = rule.invitations || []; + + const isAlreadyMember = invitedArray.some( + (invitations) => invitations.invited.id === targetUserId, + ); + + console.log('테스트 결과 : ', isAlreadyMember); + return isAlreadyMember; + } } From 847b4d22cf8236d5f3dde0c5f35f6976ac9a67e0 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 04:54:34 +0900 Subject: [PATCH 089/316] =?UTF-8?q?[Refactor]=20develop=20=EB=B8=8C?= =?UTF-8?q?=EB=9E=9C=EC=B9=98=20=EB=88=84=EB=9D=BD=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 2 +- src/user/user.entity.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index 566b6b5..e8bc486 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors(); - // app.setGlobalPrefix('api/v1'); + app.setGlobalPrefix('api/v1'); await app.listen(3000); } bootstrap(); diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 1c756ed..9815588 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -33,8 +33,6 @@ export class UserEntity extends BaseEntity { @Column() nickname: string; - @Column({ type: 'text', nullable: true }) - bio: string; @Column({ type: 'text' }) introduction: string; From 804e6ec5a62fe08f78ce6ecc2b2665b6f3a264b5 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 04:55:11 +0900 Subject: [PATCH 090/316] =?UTF-8?q?feat:=20=ED=83=90=EC=83=89=ED=83=AD=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=ED=99=94=EB=A9=B4=20api=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 + src/response/response-code.enum.ts | 2 + src/search/dto/cover-signature.dto.ts | 11 ++ src/search/dto/get-search-main.dto.ts | 8 ++ src/search/search.controller.ts | 39 +++++++ src/search/search.module.ts | 13 +++ src/search/search.service.ts | 106 ++++++++++++++++++ src/signature/domain/signature.entity.ts | 32 +++++- src/signature/domain/signature.page.entity.ts | 4 +- src/user/user.service.ts | 15 +++ 10 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 src/search/dto/cover-signature.dto.ts create mode 100644 src/search/dto/get-search-main.dto.ts create mode 100644 src/search/search.controller.ts create mode 100644 src/search/search.module.ts create mode 100644 src/search/search.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 09d44a7..786db82 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -15,6 +15,7 @@ import { RuleModule } from './rule/rule.module'; import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; import { S3Module } from './utils/S3.module'; +import { SearchModule } from './search/search.module'; @Module({ imports: [ @@ -32,6 +33,7 @@ import { S3Module } from './utils/S3.module'; SignatureModule, RuleModule, S3Module, + SearchModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index e4f9965..521bfd5 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -13,6 +13,7 @@ export enum ResponseCode { DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', + GET_SEARCH_MAIN_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ @@ -31,6 +32,7 @@ export enum ResponseCode { SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', SIGNATURE_PATCH_FAIL = 'BAD_REQUEST', GET_LIKE_SIGNATURE_PROFILES_FAIL = 'BAD_REQUEST', + GET_SEARCH_MAIN_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ INVALID_AUTH_TOKEN = 'UNAUTHORIZED', diff --git a/src/search/dto/cover-signature.dto.ts b/src/search/dto/cover-signature.dto.ts new file mode 100644 index 0000000..eb1253a --- /dev/null +++ b/src/search/dto/cover-signature.dto.ts @@ -0,0 +1,11 @@ +// cover-signature.dto.ts + +export class CoverSignatureDto { + _id: number; + title: string; + image: string; // 시그니처 첫 번째 페이지 사진 + userName: string; // 유저 닉네임 + userImage: string; // 유저 프로필 사진 + date: string; + liked: number; +} \ No newline at end of file diff --git a/src/search/dto/get-search-main.dto.ts b/src/search/dto/get-search-main.dto.ts new file mode 100644 index 0000000..a0c1635 --- /dev/null +++ b/src/search/dto/get-search-main.dto.ts @@ -0,0 +1,8 @@ +// get-search-main.dto.ts + +import { CoverSignatureDto } from './cover-signature.dto'; + +export class GetSearchMainDto{ + hot: CoverSignatureDto[]; + new: CoverSignatureDto[]; +} \ No newline at end of file diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts new file mode 100644 index 0000000..0c786fc --- /dev/null +++ b/src/search/search.controller.ts @@ -0,0 +1,39 @@ +// search.controller.ts + +import { Body, Controller, Get } from '@nestjs/common'; +import { ResponseDto } from '../response/response.dto'; +import { GetSearchMainDto } from './dto/get-search-main.dto'; +import { TmpUserIdDto } from '../signature/dto/tmp-userId.dto'; +import { ResponseCode } from '../response/response-code.enum'; +import { CoverSignatureDto } from './dto/cover-signature.dto'; +import { SearchService } from './search.service'; + +@Controller('search') +export class SearchController{ + + constructor(private readonly searchService: SearchService) {} + + @Get('/') + async getSearchMain( + @Body() user_id: TmpUserIdDto + ): Promise>{ + + const getSearchMainDto:GetSearchMainDto = new GetSearchMainDto(); + + // [1] 인기 급상승 시그니처 가져오기 + const hotSignatures:CoverSignatureDto[] = await this.searchService.findHotSignatures(); + getSearchMainDto.hot = hotSignatures; + + // [2] 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 + const newSignatures:CoverSignatureDto[] = await this.searchService.findMatesNewSignatures(user_id.userId); + getSearchMainDto.new = newSignatures; + + return new ResponseDto( + ResponseCode.GET_SEARCH_MAIN_SUCCESS, + true, + "탐색탭 메인 화면 가져오기 성공", + getSearchMainDto + ); + } + +} diff --git a/src/search/search.module.ts b/src/search/search.module.ts new file mode 100644 index 0000000..b175202 --- /dev/null +++ b/src/search/search.module.ts @@ -0,0 +1,13 @@ +// search.module.ts + +import { Module } from '@nestjs/common'; +import { SearchService } from './search.service'; +import { SearchController } from './search.controller'; +import { UserService } from '../user/user.service'; +import { SignatureService } from '../signature/signature.service'; + +@Module({ + controllers: [SearchController], + providers: [SearchService, UserService, SignatureService], +}) +export class SearchModule {} \ No newline at end of file diff --git a/src/search/search.service.ts b/src/search/search.service.ts new file mode 100644 index 0000000..11f2690 --- /dev/null +++ b/src/search/search.service.ts @@ -0,0 +1,106 @@ +// search.service.ts + +import { Injectable } from '@nestjs/common'; +import { SignatureEntity } from '../signature/domain/signature.entity'; +import { CoverSignatureDto } from './dto/cover-signature.dto'; +import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; +import { UserService } from '../user/user.service'; +import { exit } from '@nestjs/cli/actions'; +import { SignatureService } from '../signature/signature.service'; + +@Injectable() +export class SearchService{ + + constructor( + private readonly userService: UserService, + private readonly signatureService: SignatureService + ) {} + + async findHotSignatures(): Promise { + try{ + /***************************************** + 인기 시그니처 알고리즘 로직: + [1] 최근 일주일 안에 올라온 시그니처 모두 가져오기 + [2] 그 중에서 좋아요 개수 상위 20개 리턴 + *****************************************/ + + // [1] 최근 일주일 안에 올라온 시그니처 가져오기 + const recentSignatures: SignatureEntity[] = await SignatureEntity.findRecentSignatures(); + + // [2] 최근 시그니처들 리스트 좋아요 순으로 정렬 + recentSignatures.sort((a,b) => a.liked - b.liked ); + console.log(recentSignatures); + + // [3] 그 중에서 20개만 리턴한다 + const hotSignatureCovers: CoverSignatureDto[] = await this.getSignatureCovers(recentSignatures); + + return hotSignatureCovers; + + }catch(error){ + console.log("Error on findHotSignatures: ", error); + throw error; + } + + } + + async findMatesNewSignatures(userId: number) { + try{ + /******************************************************** + 내 메이트 최신 시그니처 로직: + [1] 내가 팔로우하고 있는 메이트 목록 가져오기 + [2] 각 메이트가 작성한 시그니처 중 일주일 안으로 작성된 것만 가져오기 + [3] 최신순으로 정렬해서 20개만 리턴 + ********************************************************/ + + // [1] 내가 팔로우하고 있는 메이트 목록 가져오기 + const followingMates = await this.userService.findFollowingMates(userId); + + // [2] 각 메이트들이 작성한 시그니처 목록에 담기 + const totalNewSignatures: SignatureEntity[] = []; + for(const mate of followingMates){ + const mateNewSignatures:SignatureEntity[] = await SignatureEntity.findNewSignaturesByUser(mate.id); + + for(const newSignature of mateNewSignatures){ + totalNewSignatures.push(newSignature); + } + } + + // [3] 최신 순으로 정렬 + totalNewSignatures.sort((a,b)=>a.created.getDate()-b.created.getDate()); + + // [4] 20개만 리턴 + const newSignatureCovers: CoverSignatureDto[] = await this.getSignatureCovers(totalNewSignatures); + + return newSignatureCovers; + + }catch (error){ + console.log("Error on FindMatesNewSigs: "+error); + throw error; + } + } + + async getSignatureCovers(signatureEntities){ + + const signatureCovers: CoverSignatureDto[] = []; + + for (let i = 0; i < signatureEntities.length && i < 20; i++) { + const signature = signatureEntities[i]; + const signatureCoverDto = new CoverSignatureDto(); + + signatureCoverDto._id = signature.id; + signatureCoverDto.title = signature.title; + signatureCoverDto.liked = signature.liked; + signatureCoverDto.userName = signature.user.name; + + signatureCoverDto.date = await SignatureEntity.formatDateString(signature.created); + signatureCoverDto.image = await SignaturePageEntity.findThumbnail(signature.id); + + const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); + signatureCoverDto.userImage = userProfileImageEntity.imageKey; + + signatureCovers.push(signatureCoverDto); + } + + return signatureCovers; + } +} \ No newline at end of file diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index 2b3f8cf..66c1e7f 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -5,7 +5,7 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, EntitySubscriberInterface, EventSubscriber, InsertEvent, JoinColumn, ManyToOne, + Entity, EntitySubscriberInterface, EventSubscriber, InsertEvent, JoinColumn, ManyToOne, MoreThan, OneToMany, PrimaryGeneratedColumn, RemoveEvent, UpdateDateColumn, @@ -132,4 +132,34 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter return signature; } + + static async findRecentSignatures(): Promise { + + // [1] 기준이 되는 일주일 전 날짜 + const sevenDaysAgo: Date = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate()-7); + console.log(sevenDaysAgo); + + // [2] 오늘로부터 일주일 안으로 쓰여진 시그니처 가져오기 + const recentSignatures = await SignatureEntity.find({ + where:{ created: MoreThan(sevenDaysAgo) }, + relations: ['user'] // user 관계를 포함 + }); + + return recentSignatures; + } + + static async findNewSignaturesByUser(userId: number) { + // [1] 기준이 되는 일주일 전 날짜 + const sevenDaysAgo: Date = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate()-7); + console.log(sevenDaysAgo); + + // [2] 일주일 전에 쓰인 메이트의 최신 시그니처 가져오기 + const signatures = await SignatureEntity.find({ + where:{user:{id: userId}, created: MoreThan(sevenDaysAgo)}, + relations: ['user'] // user 관계를 포함 + }) + return signatures; + } } diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 1b98d9e..26450e6 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -61,12 +61,12 @@ export class SignaturePageEntity extends BaseEntity { return await signaturePage.save(); } - static async findThumbnail(id: number) { + static async findThumbnail(signatureId: number) { // 각 시그니처의 첫 번째 페이지의 이미지 가져오기 try { const firstPage = await SignaturePageEntity.findOne({ where: { - signature: { id: id }, + signature: { id: signatureId }, page: 1, }, }); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 6861035..81036ec 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -141,4 +141,19 @@ export class UserService { ); } } + + async findFollowingMates(userId: number) { + try{ + // userId에 해당하는 유저가 팔로우하고 있는 메이트 목록 리턴 + const followingMates = await UserEntity.find({ + where:{ + follower:{ user: { id: userId }} + } + }); + return followingMates; + }catch (error){ + console.log("Error on findFollowingMates: ", error); + throw error; + } + } } From da4a5e1790388c55e8a168e64be8f3ca57e7f2a3 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 6 Feb 2024 05:18:05 +0900 Subject: [PATCH 091/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=A9=A4=EB=B2=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rule.main.entity / rule.sub.entity / rule.invitation.entity 에 created, updated, deleted 속성 추가 (*코드 확인 다시 필요) --- src/member/member.controller.ts | 32 +++++++++++++++++++--- src/member/member.service.ts | 7 +++++ src/response/response-code.enum.ts | 4 +++ src/rule/domain/rule.invitation.entity.ts | 33 ++++++++++++++++++++++- src/rule/domain/rule.main.entity.ts | 28 ++++++++++++++++--- src/rule/domain/rule.sub.entity.ts | 20 +++++++++++++- src/rule/rule.service.ts | 26 +++++------------- 7 files changed, 121 insertions(+), 29 deletions(-) diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index 0b3d1f8..98934e9 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -3,6 +3,7 @@ import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { MemberService } from './member.service'; import { RuleService } from 'src/rule/rule.service'; +import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; // import { UserGuard } from 'src/user/user.guard'; // @UseGuards(UserGuard) @@ -46,9 +47,9 @@ export class MemberController { const userId = 2; // 이미 초대된 멤버인지 확인 - const ruleEntity = await this.ruleService.findRuleById(ruleId); + const ruleEntity = await RuleMainEntity.findRuleById(ruleId); console.log('--이미 참여하는 사용자인지 검증 시작--') - const check = this.ruleService.checkAlreadyMember(ruleEntity, invitedId); + const check = this.ruleService.checkMember(ruleEntity, invitedId); if (check) { return new ResponseDto( ResponseCode.IS_ALREADY_MEMBER, @@ -57,9 +58,9 @@ export class MemberController { null ); } + console.log('초대 가능한 사용자 입니다') console.log('--검증 완료--') - // 멤버 초대 try { await this.memberService.createInvitation(ruleId, userId, invitedId); @@ -78,4 +79,29 @@ export class MemberController { ); } } + + // [3] 여행 규칙 멤버 삭제 + @Delete('/:ruleId/:memberId') + async deleteMember(@Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 2; + + try { + await this.memberService.deleteMember(ruleId, memberId); + return new ResponseDto( + ResponseCode.DELETE_MEMBER_SUCCESS, + true, + "여행 규칙 멤버 삭제 성공", + null + ); + } catch (error) { + return new ResponseDto( + ResponseCode.DELETE_MEMBER_FAIL, + false, + "여행 규칙 멤버 삭제 실패", + null + ); + } + } } \ No newline at end of file diff --git a/src/member/member.service.ts b/src/member/member.service.ts index bb3e92c..c91dc89 100644 --- a/src/member/member.service.ts +++ b/src/member/member.service.ts @@ -2,11 +2,13 @@ import { Injectable, HttpException } from '@nestjs/common'; import { MemberListConverter } from './member.list.converter'; import { MemberDto } from './dto/member.dto'; import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; +import { RuleService } from 'src/rule/rule.service'; @Injectable() export class MemberService { constructor( private memberListConverter: MemberListConverter, + private ruleService: RuleService, ) {} // [1] 여행 규칙 멤버 리스트 조회 @@ -29,4 +31,9 @@ export class MemberService { } // [3] 여행 규칙 멤버 삭제 + async deleteMember(ruleId: number, memberId: number): Promise { + const invitation : RuleInvitationEntity = await RuleInvitationEntity.findInvitationByRuleId(ruleId, memberId); + + return invitation.softRemove(); + } } \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 5ddd6c9..7b15b32 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -15,6 +15,7 @@ export enum ResponseCode { GET_FOLLOWER_LIST_SUCCESS = 'OK', GET_FOLLOWING_LIST_SUCCESS = 'OK', GET_MEMBER_LIST_SUCCESS = 'OK', + DELETE_MEMBER_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ @@ -43,6 +44,9 @@ export enum ResponseCode { GET_MEMBER_LIST_FAIL = 'BAD_REQUEST', INVITATION_FAIL = 'BAD_REQUEST', IS_ALREADY_MEMBER = 'BAD_REQUEST', + IS_NOT_MEMBER = 'BAD_REQUEST', + DELETE_MEMBER_FAIL = 'BAD_REQUEST', + diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index 9b9a062..182b077 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -1,4 +1,12 @@ -import { BaseEntity, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; +import { BaseEntity, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, +} from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; import { RuleMainEntity } from './rule.main.entity' @@ -19,6 +27,15 @@ export class RuleInvitationEntity extends BaseEntity { @JoinColumn({name: 'invited_id'}) invited: UserEntity; + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; + static async findNameById(inviterId: number): Promise<{ memberId : number, name : string }> { const userEntity : UserEntity = await UserEntity.findOne({ where: { id: inviterId } @@ -28,4 +45,18 @@ export class RuleInvitationEntity extends BaseEntity { return { memberId, name }; } + + static async findInvitationByRuleId(ruleId: number, memberId: number): Promise { + try { + const invitation = await RuleInvitationEntity.findOne({ + where: [{ rule : { id : ruleId }}, + { invited : { id : memberId }}] + }); + console.log('invitation 조회 결과 : ', invitation); + return invitation; + } catch (error) { + console.log('Error on findInvitationByRuleId: ', error); + throw error; + } + } } \ No newline at end of file diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index 253b22b..a402d3b 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -1,4 +1,12 @@ -import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, OneToMany } from 'typeorm'; +import { BaseEntity, + Column, + Entity, + PrimaryGeneratedColumn, + OneToMany, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, +} from 'typeorm'; import { RuleSubEntity } from './rule.sub.entity'; import { RuleInvitationEntity } from './rule.invitation.entity' import { CommentEntity } from 'src/comment/domain/comment.entity'; @@ -11,13 +19,13 @@ export class RuleMainEntity extends BaseEntity { @Column({ type: 'varchar', length: 255 }) mainTitle: string; - @CreateDateColumn({ type: 'timestamp' }) + @CreateDateColumn() created: Date; - @UpdateDateColumn({ type: 'timestamp' }) + @UpdateDateColumn() updated: Date; - @Column({ type: 'timestamp', nullable: true }) + @DeleteDateColumn() deleted: Date; @OneToMany(() => RuleSubEntity, ruleSub => ruleSub.main) @@ -37,4 +45,16 @@ export class RuleMainEntity extends BaseEntity { return ruleMain; } + + static async findRuleById(ruleId: number): Promise { + try { + const rule: RuleMainEntity = await RuleMainEntity.findOne({ + where: { id: ruleId }, + }); + return rule; + } catch (error) { + console.log('Error on findRuleById: ', error); + throw error; + } + } } \ No newline at end of file diff --git a/src/rule/domain/rule.sub.entity.ts b/src/rule/domain/rule.sub.entity.ts index c45a646..d580fe4 100644 --- a/src/rule/domain/rule.sub.entity.ts +++ b/src/rule/domain/rule.sub.entity.ts @@ -1,4 +1,13 @@ -import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; +import { BaseEntity, + Column, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, +} from 'typeorm'; import { RuleMainEntity } from './rule.main.entity'; @Entity() @@ -15,4 +24,13 @@ export class RuleSubEntity extends BaseEntity { @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.rules) @JoinColumn({ name: 'rule_id'}) main: RuleMainEntity; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; } \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 0c979c2..0e8af82 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -42,21 +42,7 @@ export class RuleService { throw new HttpException('Internal Server Error', 500); } } - - // - - async findRuleById(ruleId: number): Promise { - try { - const rule: RuleMainEntity = await RuleMainEntity.findOne({ - where: { id: ruleId }, - }); - return rule; - } catch (error) { - console.log('Error on findRuleById: ', error); - throw error; - } - } - + // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { @@ -70,15 +56,15 @@ export class RuleService { } } - // [member] 이미 초대된 멤버인지 확인 - async checkAlreadyMember(rule: RuleMainEntity, targetUserId: number): Promise { + // [member] 멤버인지 확인 + async checkMember(rule: RuleMainEntity, targetUserId: number): Promise { const invitedArray = rule.invitations || []; - const isAlreadyMember = invitedArray.some( + const isMember = invitedArray.some( (invitations) => invitations.invited.id === targetUserId, ); - console.log('테스트 결과 : ', isAlreadyMember); - return isAlreadyMember; + console.log('테스트 결과 : ', isMember); + return isMember; } } From eee4b2ef64cc84b5df979e39b6333d84f7579af2 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 06:00:26 +0900 Subject: [PATCH 092/316] =?UTF-8?q?[Feat]=20=ED=83=90=EC=83=89:=20?= =?UTF-8?q?=ED=82=A4=EC=9B=8C=EB=93=9C=EB=A1=9C=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 3 +- src/search/search.controller.ts | 62 +++++++++++++++++----- src/search/search.service.ts | 67 +++++++++++++++++------- src/signature/domain/signature.entity.ts | 6 +-- 4 files changed, 101 insertions(+), 37 deletions(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 521bfd5..2f99722 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -14,7 +14,7 @@ export enum ResponseCode { PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', GET_SEARCH_MAIN_SUCCESS = 'OK', - + SEARCH_BY_KEYWORD_SUCCESS = 'OK', /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', @@ -33,6 +33,7 @@ export enum ResponseCode { SIGNATURE_PATCH_FAIL = 'BAD_REQUEST', GET_LIKE_SIGNATURE_PROFILES_FAIL = 'BAD_REQUEST', GET_SEARCH_MAIN_FAIL = 'BAD_REQUEST', + SEARCH_BY_KEYWORD_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ INVALID_AUTH_TOKEN = 'UNAUTHORIZED', diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index 0c786fc..435ed2b 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -1,6 +1,6 @@ // search.controller.ts -import { Body, Controller, Get } from '@nestjs/common'; +import { Body, Controller, Get, Query } from '@nestjs/common'; import { ResponseDto } from '../response/response.dto'; import { GetSearchMainDto } from './dto/get-search-main.dto'; import { TmpUserIdDto } from '../signature/dto/tmp-userId.dto'; @@ -17,23 +17,57 @@ export class SearchController{ async getSearchMain( @Body() user_id: TmpUserIdDto ): Promise>{ + try{ + const getSearchMainDto:GetSearchMainDto = new GetSearchMainDto(); - const getSearchMainDto:GetSearchMainDto = new GetSearchMainDto(); + // [1] 인기 급상승 시그니처 가져오기 + const hotSignatures:CoverSignatureDto[] = await this.searchService.findHotSignatures(); + getSearchMainDto.hot = hotSignatures; - // [1] 인기 급상승 시그니처 가져오기 - const hotSignatures:CoverSignatureDto[] = await this.searchService.findHotSignatures(); - getSearchMainDto.hot = hotSignatures; + // [2] 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 + const newSignatures:CoverSignatureDto[] = await this.searchService.findMatesNewSignatures(user_id.userId); + getSearchMainDto.new = newSignatures; - // [2] 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 - const newSignatures:CoverSignatureDto[] = await this.searchService.findMatesNewSignatures(user_id.userId); - getSearchMainDto.new = newSignatures; + return new ResponseDto( + ResponseCode.GET_SEARCH_MAIN_SUCCESS, + true, + "탐색탭 메인 화면 가져오기 성공", + getSearchMainDto + ); + }catch (error){ + console.log("탐색탭 메인 가져오기 실패: ", error); + return new ResponseDto( + ResponseCode.GET_SEARCH_MAIN_FAIL, + false, + "탐색탭 메인 화면 가져오기 실패", + null + ); + } + } + + @Get('/find') + async search(@Query('keyword') keyword: string): Promise> { + try{ + + const searchResult: CoverSignatureDto[] = await this.searchService.searchByKeyword(keyword); + + return new ResponseDto( + ResponseCode.SEARCH_BY_KEYWORD_SUCCESS, + true, + "키워드로 검색하기 성공", + searchResult + ); + + }catch(error){ + console.log("탑색- 키워드로 검색 실패: "+error); - return new ResponseDto( - ResponseCode.GET_SEARCH_MAIN_SUCCESS, - true, - "탐색탭 메인 화면 가져오기 성공", - getSearchMainDto - ); + return new ResponseDto( + ResponseCode.SEARCH_BY_KEYWORD_FAIL, + false, + "키워드로 검색하기 실패", + null + ); + } } } diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 11f2690..380dde2 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -7,14 +7,12 @@ import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; import { UserService } from '../user/user.service'; import { exit } from '@nestjs/cli/actions'; import { SignatureService } from '../signature/signature.service'; +import { Like } from 'typeorm'; @Injectable() export class SearchService{ - constructor( - private readonly userService: UserService, - private readonly signatureService: SignatureService - ) {} + constructor( private readonly userService: UserService ) {} async findHotSignatures(): Promise { try{ @@ -32,7 +30,7 @@ export class SearchService{ console.log(recentSignatures); // [3] 그 중에서 20개만 리턴한다 - const hotSignatureCovers: CoverSignatureDto[] = await this.getSignatureCovers(recentSignatures); + const hotSignatureCovers: CoverSignatureDto[] = await this.getSignatureCoversForSearchMain(recentSignatures); return hotSignatureCovers; @@ -69,7 +67,7 @@ export class SearchService{ totalNewSignatures.sort((a,b)=>a.created.getDate()-b.created.getDate()); // [4] 20개만 리턴 - const newSignatureCovers: CoverSignatureDto[] = await this.getSignatureCovers(totalNewSignatures); + const newSignatureCovers: CoverSignatureDto[] = await this.getSignatureCoversForSearchMain(totalNewSignatures); return newSignatureCovers; @@ -79,28 +77,59 @@ export class SearchService{ } } - async getSignatureCovers(signatureEntities){ + async getSignatureCoversForSearchMain(signatureEntities){ + // 탐색 메인화면에 출력될 시그니처 커버 20개 만들기 const signatureCovers: CoverSignatureDto[] = []; for (let i = 0; i < signatureEntities.length && i < 20; i++) { const signature = signatureEntities[i]; - const signatureCoverDto = new CoverSignatureDto(); + const signatureCover = await this.getSignatureCover(signature); - signatureCoverDto._id = signature.id; - signatureCoverDto.title = signature.title; - signatureCoverDto.liked = signature.liked; - signatureCoverDto.userName = signature.user.name; + signatureCovers.push(signatureCover); + } - signatureCoverDto.date = await SignatureEntity.formatDateString(signature.created); - signatureCoverDto.image = await SignaturePageEntity.findThumbnail(signature.id); + return signatureCovers; + } - const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); - signatureCoverDto.userImage = userProfileImageEntity.imageKey; + async searchByKeyword(keyword: string) { + try{ + // 키워드로 검색하기 + const resultSignatures = await SignatureEntity.find({ + where:{ title: Like(`%${keyword}%`) }, + relations: ['user'] // user 포함 + }); + + const resultCovers = []; + for(const signature of resultSignatures){ + const signatureCover = await this.getSignatureCover(signature); + resultCovers.push(signatureCover); + } + return resultCovers; - signatureCovers.push(signatureCoverDto); + }catch(error){ + console.log("검색 서비스 에러발생: "+error); + throw error; } + } - return signatureCovers; + + async getSignatureCover(signature:SignatureEntity):Promise{ + // 시그니처 커버 만들기 + const signatureCover = new CoverSignatureDto(); + + signatureCover._id = signature.id; + signatureCover.title = signature.title; + signatureCover.liked = signature.liked; + signatureCover.userName = signature.user.name; + + signatureCover.date = await SignatureEntity.formatDateString(signature.created); + signatureCover.image = await SignaturePageEntity.findThumbnail(signature.id); + + const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); + signatureCover.userImage = userProfileImageEntity.imageKey; + + return signatureCover; } -} \ No newline at end of file +} + diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index 66c1e7f..26526c3 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -127,7 +127,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter static async findSignatureById(signatureId: number): Promise { const signature:SignatureEntity = await SignatureEntity.findOne({ where: { id: signatureId }, - relations: ['user'] // user 관계를 포함 + relations: ['user'] // user 포함 }); return signature; @@ -143,7 +143,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter // [2] 오늘로부터 일주일 안으로 쓰여진 시그니처 가져오기 const recentSignatures = await SignatureEntity.find({ where:{ created: MoreThan(sevenDaysAgo) }, - relations: ['user'] // user 관계를 포함 + relations: ['user'] // user 포함 }); return recentSignatures; @@ -158,7 +158,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter // [2] 일주일 전에 쓰인 메이트의 최신 시그니처 가져오기 const signatures = await SignatureEntity.find({ where:{user:{id: userId}, created: MoreThan(sevenDaysAgo)}, - relations: ['user'] // user 관계를 포함 + relations: ['user'] // user 포함 }) return signatures; } From 3e1e4e8f4fe060a6539f5730296089dc8bfb1002 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 06:36:31 +0900 Subject: [PATCH 093/316] =?UTF-8?q?[Refactor]=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98,=20=ED=83=90=EC=83=89=20@UserGaurd=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.controller.ts | 10 +++--- src/signature/domain/signature.entity.ts | 4 +-- src/signature/domain/signature.page.entity.ts | 1 - src/signature/dto/author-signature.dto.ts | 2 +- src/signature/dto/tmp-userId.dto.ts | 4 --- src/signature/signature.controller.ts | 35 +++++++++++-------- src/signature/signature.service.ts | 10 ++---- 7 files changed, 32 insertions(+), 34 deletions(-) delete mode 100644 src/signature/dto/tmp-userId.dto.ts diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index 435ed2b..be93d20 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -1,12 +1,13 @@ // search.controller.ts -import { Body, Controller, Get, Query } from '@nestjs/common'; +import { Body, Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; import { ResponseDto } from '../response/response.dto'; import { GetSearchMainDto } from './dto/get-search-main.dto'; -import { TmpUserIdDto } from '../signature/dto/tmp-userId.dto'; import { ResponseCode } from '../response/response-code.enum'; import { CoverSignatureDto } from './dto/cover-signature.dto'; import { SearchService } from './search.service'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; @Controller('search') export class SearchController{ @@ -14,8 +15,9 @@ export class SearchController{ constructor(private readonly searchService: SearchService) {} @Get('/') + @UseGuards(UserGuard) async getSearchMain( - @Body() user_id: TmpUserIdDto + @Req() req: Request, ): Promise>{ try{ const getSearchMainDto:GetSearchMainDto = new GetSearchMainDto(); @@ -25,7 +27,7 @@ export class SearchController{ getSearchMainDto.hot = hotSignatures; // [2] 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 - const newSignatures:CoverSignatureDto[] = await this.searchService.findMatesNewSignatures(user_id.userId); + const newSignatures:CoverSignatureDto[] = await this.searchService.findMatesNewSignatures(req.user.id); getSearchMainDto.new = newSignatures; return new ResponseDto( diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index 26526c3..3a06303 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -78,7 +78,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter } static async createSignature( - createSignatureDto: CreateSignatureDto, + createSignatureDto: CreateSignatureDto, userId: number ): Promise { try { const signature: SignatureEntity = new SignatureEntity(); @@ -86,7 +86,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter // 현재 로그인한 사용자 아이디로 수정해야함 const user: UserEntity = await UserEntity.findOne({ - where: { id: 1 } + where: { id: userId} }); if(!user){ diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 26450e6..96adc57 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -13,7 +13,6 @@ import { } from 'typeorm'; import { SignatureEntity } from './signature.entity'; import { PageSignatureDto } from '../dto/page-signature.dto'; -import { errorContext } from 'rxjs/internal/util/errorContext'; @Entity() export class SignaturePageEntity extends BaseEntity { diff --git a/src/signature/dto/author-signature.dto.ts b/src/signature/dto/author-signature.dto.ts index 6490c54..0f1b4f0 100644 --- a/src/signature/dto/author-signature.dto.ts +++ b/src/signature/dto/author-signature.dto.ts @@ -2,7 +2,7 @@ export class AuthorSignatureDto { // 시그니처 작성자 정보 _id: number; // 메이트 아이디 - name: string; // 메이트 이름 + name: string; // 메이트 닉네임 image: string; // 메이트 프로필 이미지 is_followed: boolean; // 해당 메이트 팔로우 여부 } \ No newline at end of file diff --git a/src/signature/dto/tmp-userId.dto.ts b/src/signature/dto/tmp-userId.dto.ts deleted file mode 100644 index bee0c18..0000000 --- a/src/signature/dto/tmp-userId.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export class TmpUserIdDto { - userId: number -} \ No newline at end of file diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 35ffa58..348a8c3 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -1,18 +1,18 @@ // signature.controller.ts -import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Patch, Post, Req, UseGuards } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { CreateSignatureDto } from './dto/create-signature.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { HomeSignatureDto } from './dto/home-signature.dto'; import { DetailSignatureDto } from './dto/detail-signature.dto'; -import { TmpUserIdDto } from './dto/tmp-userId.dto'; import { SignatureEntity } from './domain/signature.entity'; import { SignatureLikeEntity } from './domain/signature.like.entity'; import { LikeSignatureDto } from './dto/like-signature.dto'; import { GetLikeListDto } from './dto/get-like-list.dto'; - +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; @Controller('signature') //@UseGuards(new AuthGuard()) @@ -20,10 +20,10 @@ export class SignatureController { constructor(private readonly signatureService: SignatureService) {} @Get('/') // 시그니처 탭 메인: 내 시그니처 목록 - async getMySignature(@Body() user_id: TmpUserIdDto): Promise> { + @UseGuards(UserGuard) + async getMySignature(@Req() req: Request): Promise> { - // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.homeSignature(user_id.userId); + const result = await this.signatureService.homeSignature(req.user.id); if(!result){ return new ResponseDto( @@ -44,10 +44,12 @@ export class SignatureController { } @Post('/new') + @UseGuards(UserGuard) async createNewSignature( // 시그니처 생성하기 @Body() newSignature: CreateSignatureDto, + @Req() req: Request ): Promise> { - const result = await this.signatureService.createSignature(newSignature); + const result = await this.signatureService.createSignature(newSignature, req.user.id); if(!result){ return new ResponseDto( @@ -67,14 +69,15 @@ export class SignatureController { } @Patch('/like/:signatureId') // 시그니처 좋아요 등록 or 취소 + @UseGuards(UserGuard) async addSignatureLike( - @Body() user_id: TmpUserIdDto, - @Param('signatureId') signatureId: number + @Param('signatureId') signatureId: number, + @Req() req: Request ): Promise> { try{ // [1] 이미 좋아요 했는지 확인 - const liked: SignatureLikeEntity= await this.signatureService.findIfAlreadyLiked(user_id.userId,signatureId); + const liked: SignatureLikeEntity= await this.signatureService.findIfAlreadyLiked(req.user.id,signatureId); let result = new SignatureEntity(); const likeSignatureDto= new LikeSignatureDto(); @@ -91,7 +94,7 @@ export class SignatureController { ); }else{ // 좋아요 한적 없으면 시그니처 좋아요 추가 - result = await this.signatureService.addLikeOnSignature(user_id.userId,signatureId); + result = await this.signatureService.addLikeOnSignature(req.user.id,signatureId); likeSignatureDto.liked = result.liked; likeSignatureDto.signatureId = result.id; @@ -110,14 +113,15 @@ export class SignatureController { } @Get('/:signatureId') // 시그니처 상세보기 + @UseGuards(UserGuard) async getSignatureDetail( - @Body() user_id: TmpUserIdDto, + @Req() req: Request, @Param('signatureId') signatureId: number ): Promise> { try{ // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.detailSignature(user_id.userId, signatureId); + const result = await this.signatureService.detailSignature(req.user.id, signatureId); if(result == null){ return new ResponseDto( @@ -220,12 +224,13 @@ export class SignatureController { } @Get('/like/:signatureId') // 시그니처에 좋아요한 사용자 목록 + @UseGuards(UserGuard) async getSignatureLikeList( - @Body() user_id: TmpUserIdDto, + @Req() req: Request, @Param('signatureId') signatureId: number ): Promise> { try{ - const getLikeListDto:GetLikeListDto = await this.signatureService.getSignatureLikeList(user_id.userId, signatureId); + const getLikeListDto:GetLikeListDto = await this.signatureService.getSignatureLikeList(req.user.id, signatureId); return new ResponseDto( ResponseCode.GET_LIKE_SIGNATURE_PROFILES_SUCCESS, diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 042e8c4..27ef1c4 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -1,11 +1,8 @@ // signature.service.ts import { BadRequestException, HttpException, Injectable, NotFoundException } from '@nestjs/common'; -import bcrypt from 'bcrypt'; -import jsonwebtoken from 'jsonwebtoken'; import { CreateSignatureDto } from './dto/create-signature.dto'; import { SignatureEntity } from './domain/signature.entity'; -import { InjectRepository } from '@nestjs/typeorm'; import { HomeSignatureDto } from './dto/home-signature.dto'; import { UserEntity } from 'src/user/user.entity'; import { SignaturePageEntity } from './domain/signature.page.entity'; @@ -17,17 +14,16 @@ import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; import { GetLikeListDto } from './dto/get-like-list.dto' import { LikeProfileDto } from './dto/like-profile.dto'; -import { errorContext } from 'rxjs/internal/util/errorContext'; @Injectable() export class SignatureService { constructor(private readonly userService: UserService) {} - async createSignature(createSignatureDto: CreateSignatureDto): Promise { + async createSignature(createSignatureDto: CreateSignatureDto, userId: number): Promise { // [1] 시그니처 저장 - const signature: SignatureEntity = await SignatureEntity.createSignature(createSignatureDto); + const signature: SignatureEntity = await SignatureEntity.createSignature(createSignatureDto, userId); if (!signature) throw new BadRequestException(); else{ // [2] 각 페이지 저장 @@ -87,7 +83,7 @@ export class SignatureService { // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 if(loginUser.id != signature.user.id) { authorDto._id = signature.user.id; - authorDto.name = signature.user.name; + authorDto.name = signature.user.nickname; const image = await this.userService.getProfileImage(signature.user.id); console.log("시그니처 작성자 프로필 이미지: ",image); From 9532a1e7807e8bd53a64a24e141887b6c5c1c84c Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 6 Feb 2024 13:04:28 +0900 Subject: [PATCH 094/316] =?UTF-8?q?feat=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/models/diary.image.entity.ts | 1 - src/journey/model/journey.entity.ts | 2 - src/schedule/schedule.controller.ts | 1 + src/schedule/schedule.entity.ts | 8 +- src/schedule/schedule.service.ts | 119 ++++++++++--------------- 5 files changed, 50 insertions(+), 81 deletions(-) diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index 200c2d4..781f1a5 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -19,7 +19,6 @@ export class DiaryImageEntity extends BaseEntity { @Column({ type: 'mediumtext' }) imageUrl: string; - @JoinColumn() @OneToOne(() => DiaryEntity, (diary) => diary.image) diary: DiaryEntity; diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index e3161d8..7d3c4ab 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -32,7 +32,6 @@ export class JourneyEntity extends BaseEntity { @Column({ nullable: true }) endDate: string; - @JoinColumn() @ManyToOne(() => UserEntity, (user) => user.journeys) user: UserEntity; @@ -77,7 +76,6 @@ export class JourneyEntity extends BaseEntity { where: { user: { id: userId }, }, - select: ['id', 'title', 'startDate', 'endDate'], }); return journeys; } diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index 9c4ea6e..dc9cc18 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -54,6 +54,7 @@ export class ScheduleController { year, month, }; + console.log('dto', findMonthlyScheduleDto); const result = await this.scheduleService.getMonthlyCalender( user.id, findMonthlyScheduleDto, diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 9d3676a..c91cd88 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -79,10 +79,10 @@ export class ScheduleEntity extends BaseEntity { } static async findExistScheduleByJourneyId(journey) { - const schedule = await ScheduleEntity.findOne({ - where: { journey: { id: journey.id } }, - select: ['id', 'title', 'date', 'location'], - relations: ['location'], + const schedule = await ScheduleEntity.find({ + where: { journey: journey }, + // select: ['id', 'title', 'date', 'location'], + relations: ['location', 'detailSchedules'], }); return schedule; } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index d73ae9a..691e41b 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -51,37 +51,9 @@ export class ScheduleService { const user = await UserEntity.findExistUser(userId); const journeys = await this.getMonthlyJourney(user.id, dates); const schedules = await this.getMonthlySchedule(journeys, dates); - const detailSchedules = await this.getMonthlyDetailSchedules(schedules); const diaries = await this.getMonthlyDiaries(schedules); - const monthlyCalender = await Promise.all( - journeys.map(async (journey) => { - return { - journeyId: journey.id, - journeyTitle: journey.title, - startDate: journey.startDate, - endDate: journey.endDate, - schedules: schedules.map((schedule) => ({ - scheduleId: schedule.id, - date: schedule.date, - title: schedule.title, - location: schedule.location - ? { - latitude: schedule.location.latitude, - longitude: schedule.location.longitude, - } - : null, - })), - detailSchedules: detailSchedules.map((detailSchedule) => ({ - detailScheduleId: detailSchedule.id, - content: detailSchedule.content, - isDone: detailSchedule.isDone, - })), - - diary: diaries.some((diaryExists) => diaryExists), - }; - }), - ); - return monthlyCalender; + const data = [journeys, schedules, diaries]; + return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, data); } async getMonthlyJourney(userId, dates: FindMonthlyScheduleDto) { @@ -89,56 +61,53 @@ export class ScheduleService { const monthlyJourneys = await Promise.all( journeys.map(async (journey) => { const startDate = await this.parseDate(journey.startDate); + const endDate = await this.parseDate(journey.endDate); if ( - startDate.year.toString() === dates.year.toString() && - startDate.month.toString() === dates.month.toString() + (startDate.year.toString() === dates.year.toString() && + startDate.month.toString() === dates.month.toString()) || + (endDate.year.toString() === dates.year.toString() && + endDate.month.toString() === dates.month.toString()) ) { return journey; } }), ); - return monthlyJourneys; - // const monthlyJourneys = []; - // for (const journey of journeys) { - // const startDate = await this.parseDate(journey.startDate); - // if ( - // startDate.year.toString() === dates.year.toString() && - // startDate.month.toString() === dates.month.toString() - // ) { - // monthlyJourneys.push(journey); - // } - // } - // return monthlyJourneys; + return monthlyJourneys.filter((journey) => journey !== null); } + // const monthlyJourneys = []; + // for (const journey of journeys) { + // const startDate = await this.parseDate(journey.startDate); + // if ( + // startDate.year.toString() === dates.year.toString() && + // startDate.month.toString() === dates.month.toString() + // ) { + // monthlyJourneys.push(journey); + // } + // } + // return monthlyJourneys; async getMonthlySchedule(journeys: JourneyEntity[], dates) { const monthlySchedule = await Promise.all( journeys.map(async (journey) => { - const schedule = await ScheduleEntity.findExistSchedule(journey); - const scheduleDate = await this.parseDate(schedule.date); - if ( - scheduleDate.year.toString() === dates.year.toString() && - scheduleDate.month.toString() === dates.month.toString() - ) { - return schedule; + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey, + ); + const monthlySchedules = []; + for (const schedule of schedules) { + const scheduleDate = await this.parseDate(schedule.date); + if ( + scheduleDate.year.toString() === dates.year.toString() && + scheduleDate.month.toString() === dates.month.toString() + ) { + monthlySchedules.push(schedule); + } } + console.log(monthlySchedules); + return monthlySchedules; }), ); return monthlySchedule; } - // const monthlySchedules = []; - // for (const journey of journeys) { - // const schedule = await ScheduleEntity.findExistSchedule(journey); - // const scheduleDate = await this.parseDate(schedule.date); - // if ( - // scheduleDate.year.toString() === dates.year.toString() && - // scheduleDate.month.toString() === dates.month.toString() - // ) { - // monthlySchedules.push(schedule); - // } - // } - // console.log(monthlySchedules); - // return monthlySchedules; async getMonthlyDetailSchedules(schedules: ScheduleEntity[]) { const monthlyDetailSchedules = []; @@ -147,19 +116,21 @@ export class ScheduleService { await DetailScheduleEntity.findExistDetailByScheduleId(schedule); monthlyDetailSchedules.push(detailSchedules); } + console.log('detail', monthlyDetailSchedules); return monthlyDetailSchedules; } - async getMonthlyDiaries(schedules: ScheduleEntity[]) { - const diaries = schedules.map(async (schedule) => { - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (diary.title === null) { - return false; - } - return true; - }); - const existDiaries = await Promise.all(diaries); - return existDiaries; + async getMonthlyDiaries(schedules) { + const monthlyDiaries = await Promise.all( + schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (diary.title === null) { + return false; + } + return true; + }), + ); + return monthlyDiaries; } async parseDate(Date) { From d55be30c06002d8d7e5986341e394343a4a0e2bc Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Tue, 6 Feb 2024 16:19:36 +0900 Subject: [PATCH 095/316] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B0=9C=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EC=84=A4=EC=A0=95=EC=9D=B4=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20user=20entity=EB=A5=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.entity.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index fc9db6d..a552ab7 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -38,6 +38,9 @@ export class UserEntity extends BaseEntity { @Column({ type: 'text' }) introduction: string; + @Column({ type: 'enum', enum: ['PUBLIC', 'PRIVATE', 'MATE'] }) + visibility: 'PUBLIC' | 'PRIVATE' | 'MATE'; + @Column() age: number; From f23d5c2131be96af56b09225d9a42f13435accec Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Tue, 6 Feb 2024 16:30:00 +0900 Subject: [PATCH 096/316] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B0=9C=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EC=84=A4=EC=A0=95=20API=EB=A5=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.providers.ts | 2 +- src/user/user.controller.ts | 9 ++++++ src/user/user.service.ts | 47 ++++++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index 08cb027..e7df9f8 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: false, + synchronize: process.env.DB_SYNC === 'true', }); return dataSource.initialize(); diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index ff4f408..bcb435a 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -18,4 +18,13 @@ export class UserController { UpdateProfile(@Body() body: Partial, @Req() req: Request) { return this.userService.updateUserProfile(req.user.id, body); } + + @Post('/profile/visibility') + @UseGuards(UserGuard) + UpdateUserVisibility( + @Body('visibility') visibility: 'PRIVATE' | 'PUBLIC' | 'MATE', + @Req() req: Request, + ) { + return this.userService.updateUserVisibility(req.user.id, visibility); + } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 3337bc0..84e4ad0 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -143,17 +143,52 @@ export class UserService { } } + async updateUserVisibility( + userId: number, + visibility: 'PUBLIC' | 'PRIVATE' | 'MATE', + ) { + try { + const user = await UserEntity.findOne({ + where: { + id: Number(userId), + }, + }); + + user.visibility = visibility; + await user.save(); + + return new ResponseDto( + ResponseCode.UPDATE_PROFILE_SUCCESS, + true, + '공개범위 설정 성공', + null, + ); + } catch (error) { + this.logger.error(error); + + if (error instanceof HttpException) { + throw error; + } + return new ResponseDto( + ResponseCode.INTERNAL_SERVEr_ERROR, + false, + '서버 내부 오류', + null, + ); + } + } + async findFollowingMates(userId: number) { - try{ + try { // userId에 해당하는 유저가 팔로우하고 있는 메이트 목록 리턴 const followingMates = await UserEntity.find({ - where:{ - follower:{ user: { id: userId }} - } + where: { + follower: { user: { id: userId } }, + }, }); return followingMates; - }catch (error){ - console.log("Error on findFollowingMates: ", error); + } catch (error) { + console.log('Error on findFollowingMates: ', error); throw error; } } From 081794715f3d4d272ceeca6de1d4cdfcc7ae1bd0 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Tue, 6 Feb 2024 16:42:58 +0900 Subject: [PATCH 097/316] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20API=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.controller.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index bcb435a..1a43d62 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -19,6 +19,12 @@ export class UserController { return this.userService.updateUserProfile(req.user.id, body); } + @Post('/profile/nickname') + @UseGuards(UserGuard) + UpdateNickname(@Body('nickname') nickname: string, @Req() req: Request) { + return this.userService.updateUserProfile(req.user.id, { nickname }); + } + @Post('/profile/visibility') @UseGuards(UserGuard) UpdateUserVisibility( From dc815bd6a067114cd537a585157e428b3298f048 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Tue, 6 Feb 2024 16:44:24 +0900 Subject: [PATCH 098/316] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=86=8C=EA=B0=9C=20=EC=88=98=EC=A0=95=20API=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.controller.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 1a43d62..63bd915 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -25,6 +25,12 @@ export class UserController { return this.userService.updateUserProfile(req.user.id, { nickname }); } + @Post('/profile/intro') + @UseGuards(UserGuard) + UpdateIntroduction(@Body('intro') introduction: string, @Req() req: Request) { + return this.userService.updateUserProfile(req.user.id, { introduction }); + } + @Post('/profile/visibility') @UseGuards(UserGuard) UpdateUserVisibility( From 4b5e112fc87a30005e4d0b8195e5997082c05702 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Tue, 6 Feb 2024 16:49:41 +0900 Subject: [PATCH 099/316] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20API=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 11 +++-------- src/user/user.controller.ts | 8 +++++++- src/user/user.service.ts | 31 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 2f99722..d27ad44 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -1,7 +1,6 @@ import { HttpStatus } from '@nestjs/common'; export enum ResponseCode { - /* 200 OK : 요청 성공 */ SIGNIN_SUCCESS = 'OK', SIGNOUT_SUCCESS = 'OK', @@ -15,14 +14,14 @@ export enum ResponseCode { GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', GET_SEARCH_MAIN_SUCCESS = 'OK', SEARCH_BY_KEYWORD_SUCCESS = 'OK', - + DELETE_ACCOUNT_SUCCESS = 'OK', + /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', SIGNATURE_CREATED = 'CREATED', RULE_CREATED = 'CREATED', LIKE_ON_SIGNATURE_CREATED = 'CREATED', - /* 400 BAD_REQUEST : 잘못된 요청 */ AUTH_NUMBER_INCORRECT = 'BAD_REQUEST', RESET_PASSWORD_FAIL_MATCH = 'BAD_REQUEST', @@ -40,25 +39,21 @@ export enum ResponseCode { INVALID_ACCOUNT = 'UNAUTHORIZED', UNKNOWN_AUTHENTICATION_ERROR = 'UNAUTHORIZED', - /* 403 FORBIDDEN : 권한이 없는 사용자 */ INVALID_REFRESH_TOKEN = 'FORBIDDEN', HOLDING_WITHDRAWAL = 'FORBIDDEN', SIGNOUT_FAIL_REFRESH_TOKEN = 'FORBIDDEN', - /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ ACCOUNT_NOT_FOUND = 'NOT_FOUND', REFRESH_TOKEN_NOT_FOUND = 'NOT_FOUND', SIGNATURE_NOT_FOUND = 'NOT_FOUND', - /* 409 CONFLICT : Resource 의 현재 상태와 충돌. 보통 중복된 데이터 존재 */ EMAIL_DUPLICATION = 'CONFLICT', USERNAME_DUPLICATION = 'CONFLICT', NICKNAME_DUPLICATION = 'CONFLICT', - /* 500 INTERNAL_SERVER_ERROR */ - INTERNAL_SERVEr_ERROR = 'INTERNAL_SERVER_ERROR' + INTERNAL_SERVEr_ERROR = 'INTERNAL_SERVER_ERROR', } diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 63bd915..54f2a36 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Post, Req, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; import { IUserProfile } from './user.dto'; import { UserGuard } from './user.guard'; @@ -39,4 +39,10 @@ export class UserController { ) { return this.userService.updateUserVisibility(req.user.id, visibility); } + + @Delete('/profile/delete') + @UseGuards(UserGuard) + DeleteAccount(@Req() req: Request) { + return this.userService.deleteAccount(req.user.id); + } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 84e4ad0..2b6bf9c 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -178,6 +178,37 @@ export class UserService { } } + async deleteAccount(userId: number) { + try { + const user = await UserEntity.findOne({ + where: { + id: Number(userId), + }, + }); + + await user.softRemove(); + + return new ResponseDto( + ResponseCode.DELETE_ACCOUNT_SUCCESS, + true, + '탈퇴 성공', + null, + ); + } catch (error) { + this.logger.error(error); + + if (error instanceof HttpException) { + throw error; + } + return new ResponseDto( + ResponseCode.INTERNAL_SERVEr_ERROR, + false, + '서버 내부 오류', + null, + ); + } + } + async findFollowingMates(userId: number) { try { // userId에 해당하는 유저가 팔로우하고 있는 메이트 목록 리턴 From 321fec4eb71892f21c85b7fc15126d866d361549 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:26:27 +0900 Subject: [PATCH 100/316] =?UTF-8?q?[Refactor]=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98,=20=ED=83=90=EC=83=89=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B0=8F=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.providers.ts | 2 +- src/search/search.module.ts | 3 +- src/search/search.service.ts | 25 ++-- src/signature/domain/signature.entity.ts | 18 --- src/signature/domain/signature.page.entity.ts | 30 +---- src/signature/signature.module.ts | 3 +- src/signature/signature.service.ts | 120 +++++++++++++++--- src/user/user.service.ts | 1 + 8 files changed, 125 insertions(+), 77 deletions(-) diff --git a/src/database/database.providers.ts b/src/database/database.providers.ts index 08cb027..e7df9f8 100644 --- a/src/database/database.providers.ts +++ b/src/database/database.providers.ts @@ -12,7 +12,7 @@ export const databaseProviders = [ password: process.env.DB_PASS, database: process.env.DB_NAME, entities: [__dirname + '/../**/*.entity{.ts,.js}'], - synchronize: false, + synchronize: process.env.DB_SYNC === 'true', }); return dataSource.initialize(); diff --git a/src/search/search.module.ts b/src/search/search.module.ts index b175202..c19bd01 100644 --- a/src/search/search.module.ts +++ b/src/search/search.module.ts @@ -5,9 +5,10 @@ import { SearchService } from './search.service'; import { SearchController } from './search.controller'; import { UserService } from '../user/user.service'; import { SignatureService } from '../signature/signature.service'; +import { S3UtilService } from '../utils/S3.service'; @Module({ controllers: [SearchController], - providers: [SearchService, UserService, SignatureService], + providers: [SearchService, UserService, SignatureService, S3UtilService], }) export class SearchModule {} \ No newline at end of file diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 380dde2..3e72590 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -8,11 +8,15 @@ import { UserService } from '../user/user.service'; import { exit } from '@nestjs/cli/actions'; import { SignatureService } from '../signature/signature.service'; import { Like } from 'typeorm'; +import { S3UtilService } from '../utils/S3.service'; @Injectable() export class SearchService{ - constructor( private readonly userService: UserService ) {} + constructor( + private readonly userService: UserService, + private readonly s3Service: S3UtilService, + ) {} async findHotSignatures(): Promise { try{ @@ -30,9 +34,7 @@ export class SearchService{ console.log(recentSignatures); // [3] 그 중에서 20개만 리턴한다 - const hotSignatureCovers: CoverSignatureDto[] = await this.getSignatureCoversForSearchMain(recentSignatures); - - return hotSignatureCovers; + return await this.getSignatureCoversForSearchMain(recentSignatures); }catch(error){ console.log("Error on findHotSignatures: ", error); @@ -67,9 +69,7 @@ export class SearchService{ totalNewSignatures.sort((a,b)=>a.created.getDate()-b.created.getDate()); // [4] 20개만 리턴 - const newSignatureCovers: CoverSignatureDto[] = await this.getSignatureCoversForSearchMain(totalNewSignatures); - - return newSignatureCovers; + return await this.getSignatureCoversForSearchMain(totalNewSignatures); }catch (error){ console.log("Error on FindMatesNewSigs: "+error); @@ -123,12 +123,19 @@ export class SearchService{ signatureCover.liked = signature.liked; signatureCover.userName = signature.user.name; + // 시그니처 썸네일 이미지 가져오기 signatureCover.date = await SignatureEntity.formatDateString(signature.created); - signatureCover.image = await SignaturePageEntity.findThumbnail(signature.id); + const signatureImageKey = await SignaturePageEntity.findThumbnail(signature.id); + signatureCover.image = await this.s3Service.getImageUrl(signatureImageKey); + // 시그니처 작성자 프로필 이미지 가져오기 const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); - signatureCover.userImage = userProfileImageEntity.imageKey; + if(userProfileImageEntity == null) signatureCover.userImage = null; + else{ + const userProfileImageKey = userProfileImageEntity.imageKey; + signatureCover.userImage = await this.s3Service.getImageUrl(userProfileImageKey); + } return signatureCover; } } diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index 3a06303..c041e0a 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -84,7 +84,6 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter const signature: SignatureEntity = new SignatureEntity(); signature.title = createSignatureDto.title; - // 현재 로그인한 사용자 아이디로 수정해야함 const user: UserEntity = await UserEntity.findOne({ where: { id: userId} }); @@ -105,24 +104,7 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter } } - static async findMySignature(user_id: number):Promise { - const mySignatureList:HomeSignatureDto[] = []; - const signatures = await SignatureEntity.find({ - where: { user: { id: user_id} }, - }); - - for(const signature of signatures){ - const homeSignature:HomeSignatureDto = new HomeSignatureDto(); - homeSignature._id = signature.id; - homeSignature.title = signature.title; - homeSignature.date = signature.created; - homeSignature.image = await SignaturePageEntity.findThumbnail(signature.id); - - mySignatureList.push(homeSignature); - } - return mySignatureList; - } static async findSignatureById(signatureId: number): Promise { const signature:SignatureEntity = await SignatureEntity.findOne({ diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 96adc57..e697c14 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -45,21 +45,6 @@ export class SignaturePageEntity extends BaseEntity { deleted: Date; - static async saveSignaturePage( - pageSignatureDto:PageSignatureDto, - signature:SignatureEntity):Promise { - - const signaturePage:SignaturePageEntity = new SignaturePageEntity(); - - signaturePage.signature = signature; - signaturePage.content = pageSignatureDto.content; - signaturePage.image = pageSignatureDto.image; // base64 이미지 서버에 올려야 - signaturePage.location = pageSignatureDto.location; - signaturePage.page = pageSignatureDto.page; - - return await signaturePage.save(); - } - static async findThumbnail(signatureId: number) { // 각 시그니처의 첫 번째 페이지의 이미지 가져오기 try { @@ -70,18 +55,9 @@ export class SignaturePageEntity extends BaseEntity { }, }); - if (firstPage && firstPage.signature) { - console.log( - '썸네일 아이디: ', - firstPage.id, - ' signatureId: ', - firstPage.signature.id, - ); - return firstPage.image; - } else { - console.log('썸네일을 찾을 수 없습니다.'); - return null; - } + console.log("썸네일 이미지: ",firstPage.image); + return firstPage.image; + } catch (error) { console.log('Error on findThumbnail: ', error); throw error; diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 0b71923..256fdd3 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -8,9 +8,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { EntityManager } from 'typeorm'; import { UserService } from '../user/user.service'; +import { S3UtilService } from '../utils/S3.service'; @Module({ controllers: [SignatureController], - providers: [SignatureService,UserService], + providers: [SignatureService,UserService, S3UtilService], }) export class SignatureModule {} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 27ef1c4..6cc7000 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -14,32 +14,72 @@ import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; import { GetLikeListDto } from './dto/get-like-list.dto' import { LikeProfileDto } from './dto/like-profile.dto'; +import { S3UtilService } from '../utils/S3.service'; @Injectable() export class SignatureService { - constructor(private readonly userService: UserService) {} + constructor( + private readonly userService: UserService, + private readonly s3Service: S3UtilService, + ) {} async createSignature(createSignatureDto: CreateSignatureDto, userId: number): Promise { + try{ + // [1] 시그니처 저장 + const signature: SignatureEntity = await SignatureEntity.createSignature(createSignatureDto, userId); + + if (!signature) throw new BadRequestException(); + else{ + // [2] 각 페이지 저장 + try{ + for(const pageSignatureDto of createSignatureDto.pages){ + await this.saveSignaturePage(pageSignatureDto,signature); + } + return signature.id; - // [1] 시그니처 저장 - const signature: SignatureEntity = await SignatureEntity.createSignature(createSignatureDto, userId); + }catch (e){ - if (!signature) throw new BadRequestException(); - else{ // [2] 각 페이지 저장 - for(const pageSignatureDto of createSignatureDto.pages){ - await SignaturePageEntity.saveSignaturePage(pageSignatureDto, signature); + // 만약 페이지 저장 중에 오류 발생해 저장이 중단되면 시그니처도 삭제하도록 + await this.deleteSignature(signature); + console.log("Error on createSignatruePage: ", e); + throw e; + } } } + catch(e){ + console.log("Error on createSignatrue: ", e); + throw e; + } + } + + async saveSignaturePage(pageSignatureDto:PageSignatureDto, signature:SignatureEntity){ + const signaturePage:SignaturePageEntity = new SignaturePageEntity(); + + signaturePage.signature = signature; + signaturePage.content = pageSignatureDto.content; + signaturePage.location = pageSignatureDto.location; + signaturePage.page = pageSignatureDto.page; + + // 랜덤 이미지 키 생성 + const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; - return signature.id; + // Base64 이미지 업로드 + const uploadedImage = await this.s3Service.putObjectFromBase64( + key, pageSignatureDto.image + ); + console.log(uploadedImage); + + signaturePage.image = key; + + await signaturePage.save(); } async homeSignature(userId: number): Promise { try { console.log("userId; ",userId); - const homeSignatureList: HomeSignatureDto[] = await SignatureEntity.findMySignature(userId); + const homeSignatureList: HomeSignatureDto[] = await this.findMySignature(userId); return homeSignatureList; } catch (error) { @@ -49,6 +89,28 @@ export class SignatureService { } } + async findMySignature(user_id: number):Promise { + const mySignatureList:HomeSignatureDto[] = []; + const signatures = await SignatureEntity.find({ + where: { user: { id: user_id} }, + }); + + for(const signature of signatures){ + const homeSignature:HomeSignatureDto = new HomeSignatureDto(); + + homeSignature._id = signature.id; + homeSignature.title = signature.title; + homeSignature.date = signature.created; + + // 이미지 가져오기 + const imageKey = await SignaturePageEntity.findThumbnail(signature.id); + homeSignature.image = await this.s3Service.getImageUrl(imageKey); + + mySignatureList.push(homeSignature); + } + return mySignatureList; + } + async checkIfLiked(user: UserEntity, signatureId: number): Promise { // user가 해당 시그니처에 좋아요 눌렀는지 확인 @@ -86,9 +148,10 @@ export class SignatureService { authorDto.name = signature.user.nickname; const image = await this.userService.getProfileImage(signature.user.id); - console.log("시그니처 작성자 프로필 이미지: ",image); - - authorDto.image = image.imageKey; + if(image == null) authorDto.image = null; + else{ + authorDto.image = await this.s3Service.getImageUrl(image.imageKey); + } // 해당 시그니처 작성자를 팔로우하고 있는지 확인 authorDto.is_followed = await this.userService.checkIfFollowing(loginUser,signature.user.id); @@ -123,9 +186,11 @@ export class SignatureService { pageDto._id = page.id; pageDto.page = page.page; pageDto.content = page.content; - pageDto.image = page.image; pageDto.location = page.location; + //이미지 가져오기 + pageDto.image = await this.s3Service.getImageUrl(page.image); + signaturePageDto.push(pageDto); } detailSignatureDto.pages = signaturePageDto; @@ -229,20 +294,29 @@ export class SignatureService { // [4] 기존 페이지 수정 및 새로운 페이지 추가하기 for(const patchedPage of patchSignatureDto.pages){ if(!patchedPage._id){ // id가 없으면 새로 추가할 페이지 - await SignaturePageEntity.saveSignaturePage(patchedPage,signature); + await this.saveSignaturePage(patchedPage, signature); } for( const originalPage of originalSignaturePages ){ if(patchedPage._id == originalPage.id){ originalPage.content = patchedPage.content; originalPage.location = patchedPage.location; - // 이미지 수정 필요 - originalPage.image = patchedPage.image; + // 랜덤 이미지 키 생성 + const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; + + // Base64 이미지 업로드 + const uploadedImage = await this.s3Service.putObjectFromBase64( + key, patchedPage.image + ); + + // 이미지 키 저장 + console.log(uploadedImage); + originalPage.image = key; + } await SignaturePageEntity.save(originalPage); } } - return signatureId; } @@ -251,7 +325,6 @@ export class SignatureService { async getSignatureLikeList(userId: number, signatureId: number): Promise { try{ - const signature = await SignatureEntity.findSignatureById(signatureId); if(!signature) { throw new NotFoundException(`Signature with ID ${signatureId} not found`); @@ -271,12 +344,19 @@ export class SignatureService { if (signatureLikeEntity.user) { - const image = await this.userService.getProfileImage(signatureLikeEntity.user.id); + likeProfileDto._id = signatureLikeEntity.user.id; - likeProfileDto.image = image.imageKey; likeProfileDto.introduction = signatureLikeEntity.user.introduction; likeProfileDto.nickname = signatureLikeEntity.user.nickname; + // 프로필 이미지 꺼내오기 + const image = await this.userService.getProfileImage(signatureLikeEntity.user.id); + if(image == null)likeProfileDto.image = null; + else{ + const userImageKey = image.imageKey; + likeProfileDto.image = await this.s3Service.getImageUrl(userImageKey); + } + // 만약 좋아요 누른 사용자가 본인이 아니라면 is_followed 값을 체크하고 본인이면 null로 보내준다. if(signatureLikeEntity.user.id != userId){ const loginUser= await this.userService.findUserById(userId); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 81036ec..2f63f1a 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -83,6 +83,7 @@ export class UserService { console.log('겟프로필이미지: ', profileImageEntity); return profileImageEntity; + } catch (error) { console.log('Error on getProfileImage: ' + error); } From d523dfc41cda9cc3e6df048c99776629ae62eb97 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 6 Feb 2024 22:17:36 +0900 Subject: [PATCH 101/316] =?UTF-8?q?fix=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20count=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=9D=BC=EC=A7=80API=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 22 ++++++++++++++++++++-- src/diary/diary.service.ts | 10 ++++++++-- src/diary/models/diary.entity.ts | 16 +++++++++++----- src/journey/journey.service.ts | 6 ++---- src/schedule/schedule.entity.ts | 3 ++- 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index fe889d6..83713c6 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -16,14 +16,32 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) - @Put('post/:diaryId') + @Post('create/:scheduleId') async postJourney( + @Param('scheduleId') scheduleId: number, + @Body() body: PostDiaryDto, + ) { + const result = await this.diaryService.createDiary(scheduleId, body); + return result; + } + + /*일지 수정하기 */ + @ApiOperation({ + summary: '일지 수정하기', + description: '일지를 작성 후 확인하기에서 바로 수정 가능', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Put('update/:diaryId') + async updateJourney( @Param('diaryId') diaryId: number, @Body() body: PostDiaryDto, ) { - const result = await this.diaryService.postDiary(diaryId, body); + const result = await this.diaryService.updateDiary(diaryId, body); return result; } + /*일지 사진 url 발급 */ /*일지 사진 업로드*/ @ApiOperation({ diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index d8779f6..90d94a9 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -12,12 +12,18 @@ export class DiaryService { constructor(private readonly s3UtilService: S3UtilService) {} /*일지 작성하기*/ - async postDiary(diaryId, diaryInfo: PostDiaryDto) { - const diary = await DiaryEntity.postDiary(diaryId, diaryInfo); + async createDiary(scheduleId, diaryInfo: PostDiaryDto) { + const diary = await DiaryEntity.createDiary(scheduleId, diaryInfo); console.log(diary); return response(BaseResponse.DIARY_CREATED); } + /*일지 수정하기*/ + async updateDiary(diaryId, diaryInfo: PostDiaryDto) { + const diary = await DiaryEntity.updateDiary(diaryId, diaryInfo); + return response(BaseResponse.DIARY_CREATED); + } + /*일지 사진 S3에 업로드 후 url 받기*/ async getDiaryImgUrl(diaryId, fileName: string) { const diary = await DiaryEntity.findExistDiary(diaryId); diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 4ab96c3..5aec4a6 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -53,7 +53,7 @@ export class DiaryEntity extends BaseEntity { @OneToOne(() => DiaryImageEntity, (image) => image.diary, {}) image: DiaryImageEntity; - @ManyToOne(() => ScheduleEntity, (schedule) => schedule.diary) + @OneToOne(() => ScheduleEntity, (schedule) => schedule.diary) schedule: ScheduleEntity; @CreateDateColumn() @@ -66,14 +66,20 @@ export class DiaryEntity extends BaseEntity { deleted: Date; /*일지 생성하기*/ - static async createDiary(schedule) { + static async createDiary(scheduleId, diaryInfo) { const diary = new DiaryEntity(); - diary.schedule = schedule.id; + diary.title = diaryInfo.title; + diary.place = diaryInfo.place; + diary.weather = diaryInfo.weather; + diary.mood = diaryInfo.mood; + diary.content = diaryInfo.content; + + diary.schedule = scheduleId; return await diary.save(); } - /*일지 작성하기*/ - static async postDiary(diaryId, diaryInfo: PostDiaryDto) { + /*일지 수정하기*/ + static async updateDiary(diaryId, diaryInfo: PostDiaryDto) { const diary = await this.findExistDiary(diaryId); diary.title = diaryInfo.title; diary.place = diaryInfo.place; diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 153b54e..fbc2de4 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -23,14 +23,12 @@ export class JourneyService { journey, currentDate, ); - //일지 생성하기 - const diary = await DiaryEntity.createDiary(schedule); currentDate = new Date(currentDate); currentDate.setDate(currentDate.getDate() + 1); + // //일지 생성하기 + // const diary = await DiaryEntity.createDiary(schedule); } return response(BaseResponse.JOURNEY_CREATED); } - - async getDateRange(startDate, endDate) {} } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index c91cd88..1be0777 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -8,6 +8,7 @@ import { ManyToOne, OneToMany, PrimaryGeneratedColumn, + OneToOne, } from 'typeorm'; import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; @@ -39,7 +40,7 @@ export class ScheduleEntity extends BaseEntity { ) detailSchedules: DetailScheduleEntity[]; - @OneToMany(() => DiaryEntity, (diary) => diary.schedule) + @OneToOne(() => DiaryEntity, (diary) => diary.schedule) diary: DiaryEntity[]; @CreateDateColumn() From 46e9e8fb133a2ecba08d963703f628496ba0b529 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:00:38 +0900 Subject: [PATCH 102/316] =?UTF-8?q?[FIX]=20=EA=B4=84=ED=98=B8=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 94291b9..487121b 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -165,8 +165,9 @@ export class UserService { }); return userFollowerEntity; } catch (error) { - console.log('Error on getFollowingList: ' + error); - + console.log('$Error on getFollowingList: ' + error); + } + } async updateUserVisibility( userId: number, visibility: 'PUBLIC' | 'PRIVATE' | 'MATE', From 4c381696da181e3c2031472f58f4bdec092ae56a Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:21:03 +0900 Subject: [PATCH 103/316] =?UTF-8?q?[FIX]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 16 ++++++++-------- src/user/user.service.ts | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 6cc7000..cc3d1fd 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -112,15 +112,15 @@ export class SignatureService { } async checkIfLiked(user: UserEntity, signatureId: number): Promise { - // user가 해당 시그니처에 좋아요 눌렀는지 확인 - - const likesArray = user.likes || []; - - const isLiked = likesArray.some( - (signatureLike) => signatureLike.id === signatureId - ); + const signatureLike = await SignatureLikeEntity.findOne({ + where:{ + user: { id: user.id }, + signature: {id: signatureId} + } + }); + if(signatureLike) return true; + else return false; - return isLiked; } async detailSignature(userId: number, signatureId: number):Promise { diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 94291b9..3d50eb4 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -166,7 +166,8 @@ export class UserService { return userFollowerEntity; } catch (error) { console.log('Error on getFollowingList: ' + error); - + } + } async updateUserVisibility( userId: number, visibility: 'PUBLIC' | 'PRIVATE' | 'MATE', From daa004e7034987e27fb43a7556f68c3554023f7c Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:30:23 +0900 Subject: [PATCH 104/316] =?UTF-8?q?[Fix]=20=ED=83=90=EC=83=89=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=99=94=EB=A9=B4:=20=EC=9D=B8=EA=B8=B0=20?= =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 3e72590..4490136 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -30,7 +30,7 @@ export class SearchService{ const recentSignatures: SignatureEntity[] = await SignatureEntity.findRecentSignatures(); // [2] 최근 시그니처들 리스트 좋아요 순으로 정렬 - recentSignatures.sort((a,b) => a.liked - b.liked ); + recentSignatures.sort((a,b) => b.liked - a.liked ); console.log(recentSignatures); // [3] 그 중에서 20개만 리턴한다 @@ -66,7 +66,8 @@ export class SearchService{ } // [3] 최신 순으로 정렬 - totalNewSignatures.sort((a,b)=>a.created.getDate()-b.created.getDate()); + totalNewSignatures.sort((a, b) => b.created.getTime() - a.created.getTime()); + // [4] 20개만 리턴 return await this.getSignatureCoversForSearchMain(totalNewSignatures); From 39454ef23c0b29ce7398f0837834498153cc2e91 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:52:34 +0900 Subject: [PATCH 105/316] =?UTF-8?q?[Refactor]=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?#73?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/dto/detail-signature.dto.ts | 3 ++- src/signature/dto/page-signature.dto.ts | 2 +- src/signature/dto/response-page-signature.dto.ts | 9 +++++++++ src/signature/signature.service.ts | 9 +++++---- 4 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 src/signature/dto/response-page-signature.dto.ts diff --git a/src/signature/dto/detail-signature.dto.ts b/src/signature/dto/detail-signature.dto.ts index fe5267a..f58240a 100644 --- a/src/signature/dto/detail-signature.dto.ts +++ b/src/signature/dto/detail-signature.dto.ts @@ -3,9 +3,10 @@ import { AuthorSignatureDto } from './author-signature.dto'; import { HeaderSignatureDto } from './header-signature.dto'; import { PageSignatureDto } from './page-signature.dto'; +import { ResponsePageSignatureDto } from './response-page-signature.dto'; export class DetailSignatureDto { // 시그니처 상세 보기 author: AuthorSignatureDto; // 시그니처 작성자 정보 header: HeaderSignatureDto; // 시그니처 제목 및 좋아요 정보 - pages: PageSignatureDto[]; // 시그니처 각 페이지 내용 + pages: ResponsePageSignatureDto[]; // 시그니처 각 페이지 내용 } \ No newline at end of file diff --git a/src/signature/dto/page-signature.dto.ts b/src/signature/dto/page-signature.dto.ts index 74bf120..817d299 100644 --- a/src/signature/dto/page-signature.dto.ts +++ b/src/signature/dto/page-signature.dto.ts @@ -5,5 +5,5 @@ export class PageSignatureDto { page: number; content: string; location: string; - image: string; + image: Buffer; } diff --git a/src/signature/dto/response-page-signature.dto.ts b/src/signature/dto/response-page-signature.dto.ts new file mode 100644 index 0000000..a9209dc --- /dev/null +++ b/src/signature/dto/response-page-signature.dto.ts @@ -0,0 +1,9 @@ +// response-page-signature.dto.ts + +export class ResponsePageSignatureDto { + _id: number; + page: number; + content: string; + location: string; + image: string; +} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index cc3d1fd..e2aa59b 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -15,6 +15,7 @@ import { SignatureLikeEntity } from './domain/signature.like.entity'; import { GetLikeListDto } from './dto/get-like-list.dto' import { LikeProfileDto } from './dto/like-profile.dto'; import { S3UtilService } from '../utils/S3.service'; +import { ResponsePageSignatureDto } from './dto/response-page-signature.dto'; @Injectable() export class SignatureService { @@ -65,7 +66,7 @@ export class SignatureService { const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; // Base64 이미지 업로드 - const uploadedImage = await this.s3Service.putObjectFromBase64( + const uploadedImage = await this.s3Service.putObject( key, pageSignatureDto.image ); console.log(uploadedImage); @@ -178,11 +179,11 @@ export class SignatureService { /****************************************/ // [4] 각 페이지 내용 가져오기 - const signaturePageDto: PageSignatureDto[] = []; + const signaturePageDto: ResponsePageSignatureDto[] = []; const pages: SignaturePageEntity[] = await SignaturePageEntity.findSignaturePages(signatureId); for(const page of pages){ - const pageDto:PageSignatureDto = new PageSignatureDto(); + const pageDto:ResponsePageSignatureDto = new ResponsePageSignatureDto(); pageDto._id = page.id; pageDto.page = page.page; pageDto.content = page.content; @@ -305,7 +306,7 @@ export class SignatureService { const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; // Base64 이미지 업로드 - const uploadedImage = await this.s3Service.putObjectFromBase64( + const uploadedImage = await this.s3Service.putObject( key, patchedPage.image ); From e2c94b201cc3efbe20ce190934e32775dd2fbf3a Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 02:44:47 +0900 Subject: [PATCH 106/316] =?UTF-8?q?=EC=9B=94=EB=B3=84=20=EC=97=AC=EC=A0=95?= =?UTF-8?q?=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/models/diary.entity.ts | 1 + src/journey/dtos/find-monthly-journey.dto.ts | 2 +- src/journey/journey.controller.ts | 38 ++++++- src/journey/journey.service.ts | 55 +++++++++- src/journey/model/journey.entity.ts | 40 ++++---- src/schedule/schedule.controller.ts | 29 ------ src/schedule/schedule.entity.ts | 22 ++-- src/schedule/schedule.service.ts | 102 ------------------- 8 files changed, 127 insertions(+), 162 deletions(-) diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 5aec4a6..e6eba41 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -53,6 +53,7 @@ export class DiaryEntity extends BaseEntity { @OneToOne(() => DiaryImageEntity, (image) => image.diary, {}) image: DiaryImageEntity; + @JoinColumn() @OneToOne(() => ScheduleEntity, (schedule) => schedule.diary) schedule: ScheduleEntity; diff --git a/src/journey/dtos/find-monthly-journey.dto.ts b/src/journey/dtos/find-monthly-journey.dto.ts index b0c63fd..6d45509 100644 --- a/src/journey/dtos/find-monthly-journey.dto.ts +++ b/src/journey/dtos/find-monthly-journey.dto.ts @@ -1,6 +1,6 @@ import { IsInt } from 'class-validator'; -export class FindMonthlyScheduleDto { +export class FindMonthlyJourneyDto { @IsInt() year: number; @IsInt() diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index a68dd27..f62b9c7 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,9 +1,18 @@ -import { Controller, Post, Body, Req, UseGuards } from '@nestjs/common'; +import { + Controller, + Param, + Body, + Req, + UseGuards, + Get, + Post, +} from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; import { UserGuard } from 'src/user/user.guard'; import { JourneyService } from './journey.service'; import { CreateJourneyDto } from './dtos/create-journey.dto'; +import { FindMonthlyJourneyDto } from './dtos/find-monthly-journey.dto'; @Controller('journey') export class JourneyController { @@ -28,4 +37,31 @@ export class JourneyController { ); return result; } + + /*월별 여정 불러오기*/ + @ApiOperation({ + summary: '월별 여정 불러오기', + description: '월별 여정과 일지 개수, 사진을 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Get('monthly/:year/:month') + async getMonthlyJourney( + @Param('year') year: number, + @Param('month') month: number, + @Req() req: Request, + ) { + const user = req.user; + const findMonthlyJourneyDto: FindMonthlyJourneyDto = { + year, + month, + }; + const result = await this.journeyService.getMonthlyJourneyMap( + user.id, + findMonthlyJourneyDto, + ); + return result; + } } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index fbc2de4..bf2171b 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,11 +1,13 @@ // journey.service.ts import { Injectable } from '@nestjs/common'; import { JourneyEntity } from './model/journey.entity'; -import { response } from 'src/response/response'; +import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; +import { UserEntity } from 'src/user/user.entity'; +import { DiaryEntity } from 'src/diary/models/diary.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; -import { DiaryEntity } from 'src/diary/models/diary.entity'; +import { FindMonthlyJourneyDto } from './dtos/find-monthly-journey.dto'; @Injectable() export class JourneyService { @@ -25,10 +27,55 @@ export class JourneyService { ); currentDate = new Date(currentDate); currentDate.setDate(currentDate.getDate() + 1); - // //일지 생성하기 - // const diary = await DiaryEntity.createDiary(schedule); } return response(BaseResponse.JOURNEY_CREATED); } + //지도에서 사용자의 월별 여정 불러오기 + async getMonthlyJourneyMap(userId: number, dates: FindMonthlyJourneyDto) { + const user = await UserEntity.findExistUser(userId); + const monthlyJourney = await this.getMonthlyJourney(user.id, dates); + if (monthlyJourney.length === 0) { + return errResponse(BaseResponse.JOURNEY_NOT_FOUND); + } + + const journeyList = await Promise.all( + monthlyJourney.map(async (journey) => { + const schedules = await this.getMonthlySchedule(journey.id, dates); + const diaryCount = await this.getDiaryCount(schedules); + return { + journeyId: journey.id, + title: journey.title, + startDate: journey.startDate, + endDate: journey.endDate, + diaryCount: diaryCount, + }; + }), + ); + return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, journeyList); + } + + //사용자의 월별 여정 가지고 오기 + async getMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { + const journeys = await JourneyEntity.findMonthlyJourney(userId, dates); + return journeys; + } + + //사용자의 월별 일정 가지고 오기 + async getMonthlySchedule(journeyId, dates: FindMonthlyJourneyDto) { + const schedules: ScheduleEntity[] = + await ScheduleEntity.findMonthlySchedule(journeyId, dates); + return schedules; + } + + async getDiaryCount(schedules) { + let diaryCount = 0; + for (const schedule of schedules) { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (diary) { + diaryCount += 1; + } + } + return diaryCount; + } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 7d3c4ab..fd6286c 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -12,11 +12,16 @@ import { ManyToOne, Between, JoinColumn, + MoreThanOrEqual, + LessThanOrEqual, } from 'typeorm'; import { CreateJourneyDto } from '../dtos/create-journey.dto'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; +import { FindMonthlyJourneyDto } from '../dtos/find-monthly-journey.dto'; +import { errResponse } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; @Entity() export class JourneyEntity extends BaseEntity { @@ -26,11 +31,11 @@ export class JourneyEntity extends BaseEntity { @Column() title: string; - @Column() - startDate: string; + @Column({ type: 'date' }) + startDate: Date; - @Column({ nullable: true }) - endDate: string; + @Column({ type: 'date' }) + endDate: Date; @ManyToOne(() => UserEntity, (user) => user.journeys) user: UserEntity; @@ -61,21 +66,20 @@ export class JourneyEntity extends BaseEntity { } } - // static async getMonthlyJourney(journeys: JourneyEntity[], dates) { - // const monthlyJourneys: JourneyEntity[] = journeys.filter((journey) => { - // return ( - // journey.startDate >= dates.startDate && journey.endDate <= dates.endDate - // ); - // }); - // console.log(monthlyJourneys); - // return monthlyJourneys; - // } - - static async findJourneysByuserId(userId) { + static async findMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { + const firstDate = new Date(`${dates.year}-${dates.month}-01`); + const lastDate = new Date(`${dates.year}-${dates.month}-31`); const journeys: JourneyEntity[] = await JourneyEntity.find({ - where: { - user: { id: userId }, - }, + where: [ + { + user: { id: userId }, + startDate: Between(firstDate, lastDate), + }, + { + user: { id: userId }, + endDate: Between(firstDate, lastDate), + }, + ], }); return journeys; } diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index dc9cc18..a5a4f61 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -33,33 +33,4 @@ export class ScheduleController { const result = await this.scheduleService.updateSchedule(scheduleId, body); return result; } - - @ApiOperation({ - summary: '홈 화면 - 캘린더', - description: '월별 일정을 불러옵니다.', - }) - @ApiOkResponse({ - description: '성공 ', - }) - @UseGuards(UserGuard) - @Get(':year/:month') - async getMonthlySchedule( - @Param('year') year: number, - @Param('month') month: number, - @Req() req: Request, - ) { - const user = req.user; - console.log(user.id); - const findMonthlyScheduleDto: FindMonthlyScheduleDto = { - year, - month, - }; - console.log('dto', findMonthlyScheduleDto); - const result = await this.scheduleService.getMonthlyCalender( - user.id, - findMonthlyScheduleDto, - ); - - return result; - } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 1be0777..3d5dd3f 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -9,6 +9,7 @@ import { OneToMany, PrimaryGeneratedColumn, OneToOne, + Between, } from 'typeorm'; import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; @@ -16,14 +17,15 @@ import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity' import { LocationEntity } from 'src/location/location.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; +import { FindMonthlyJourneyDto } from 'src/journey/dtos/find-monthly-journey.dto'; @Entity() export class ScheduleEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column({ nullable: true }) - date: string; + @Column({ type: 'date' }) + date: Date; @Column({ nullable: true }) title: string; @@ -41,7 +43,7 @@ export class ScheduleEntity extends BaseEntity { detailSchedules: DetailScheduleEntity[]; @OneToOne(() => DiaryEntity, (diary) => diary.schedule) - diary: DiaryEntity[]; + diary: DiaryEntity; @CreateDateColumn() created: Date; @@ -79,11 +81,17 @@ export class ScheduleEntity extends BaseEntity { return schedule; } - static async findExistScheduleByJourneyId(journey) { + static async findMonthlySchedule( + journeyId, + dates: FindMonthlyJourneyDto, + ): Promise { + const firstDate = new Date(`${dates.year}-${dates.month}-01`); + const lastDate = new Date(`${dates.year}-${dates.month}-31`); const schedule = await ScheduleEntity.find({ - where: { journey: journey }, - // select: ['id', 'title', 'date', 'location'], - relations: ['location', 'detailSchedules'], + where: { + journey: { id: journeyId }, + date: Between(firstDate, lastDate), + }, }); return schedule; } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 691e41b..19738c4 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -46,106 +46,4 @@ export class ScheduleService { console.log(location); } } - - async getMonthlyCalender(userId: number, dates: FindMonthlyScheduleDto) { - const user = await UserEntity.findExistUser(userId); - const journeys = await this.getMonthlyJourney(user.id, dates); - const schedules = await this.getMonthlySchedule(journeys, dates); - const diaries = await this.getMonthlyDiaries(schedules); - const data = [journeys, schedules, diaries]; - return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, data); - } - - async getMonthlyJourney(userId, dates: FindMonthlyScheduleDto) { - const journeys = await JourneyEntity.findJourneysByuserId(userId); - const monthlyJourneys = await Promise.all( - journeys.map(async (journey) => { - const startDate = await this.parseDate(journey.startDate); - const endDate = await this.parseDate(journey.endDate); - if ( - (startDate.year.toString() === dates.year.toString() && - startDate.month.toString() === dates.month.toString()) || - (endDate.year.toString() === dates.year.toString() && - endDate.month.toString() === dates.month.toString()) - ) { - return journey; - } - }), - ); - return monthlyJourneys.filter((journey) => journey !== null); - } - // const monthlyJourneys = []; - // for (const journey of journeys) { - // const startDate = await this.parseDate(journey.startDate); - // if ( - // startDate.year.toString() === dates.year.toString() && - // startDate.month.toString() === dates.month.toString() - // ) { - // monthlyJourneys.push(journey); - // } - // } - // return monthlyJourneys; - - async getMonthlySchedule(journeys: JourneyEntity[], dates) { - const monthlySchedule = await Promise.all( - journeys.map(async (journey) => { - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( - journey, - ); - const monthlySchedules = []; - for (const schedule of schedules) { - const scheduleDate = await this.parseDate(schedule.date); - if ( - scheduleDate.year.toString() === dates.year.toString() && - scheduleDate.month.toString() === dates.month.toString() - ) { - monthlySchedules.push(schedule); - } - } - console.log(monthlySchedules); - return monthlySchedules; - }), - ); - return monthlySchedule; - } - - async getMonthlyDetailSchedules(schedules: ScheduleEntity[]) { - const monthlyDetailSchedules = []; - for (const schedule of schedules) { - const detailSchedules: DetailScheduleEntity[] = - await DetailScheduleEntity.findExistDetailByScheduleId(schedule); - monthlyDetailSchedules.push(detailSchedules); - } - console.log('detail', monthlyDetailSchedules); - return monthlyDetailSchedules; - } - - async getMonthlyDiaries(schedules) { - const monthlyDiaries = await Promise.all( - schedules.map(async (schedule) => { - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (diary.title === null) { - return false; - } - return true; - }), - ); - return monthlyDiaries; - } - - async parseDate(Date) { - const [year, month] = Date.split('-').map(Number); - return { year, month }; - } } - -// async getDateRange(year, month) { -// const endDate = new Date(year, month, 0); -// const startDate = new Date(year, month - 1, 1); -// return { startDate, endDate }; -// } -// const dates = await this.getDateRange( -// findMonthlyScheduleDto.year, -// findMonthlyScheduleDto.month, -// ); -// console.log(dates); From 22323f2a3251d2803ee2fd2c3808f609f52df5e9 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 02:55:00 +0900 Subject: [PATCH 107/316] =?UTF-8?q?=20chore=20:=20=EC=A3=BC=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/models/diary.entity.ts | 6 ++++-- src/journey/journey.service.ts | 2 +- src/journey/model/journey.entity.ts | 1 + src/schedule/schedule.entity.ts | 6 +++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index e6eba41..f37beb2 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -66,7 +66,7 @@ export class DiaryEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - /*일지 생성하기*/ + //일지 생성하기 static async createDiary(scheduleId, diaryInfo) { const diary = new DiaryEntity(); diary.title = diaryInfo.title; @@ -79,7 +79,7 @@ export class DiaryEntity extends BaseEntity { return await diary.save(); } - /*일지 수정하기*/ + //일지 수정하기 static async updateDiary(diaryId, diaryInfo: PostDiaryDto) { const diary = await this.findExistDiary(diaryId); diary.title = diaryInfo.title; @@ -91,6 +91,7 @@ export class DiaryEntity extends BaseEntity { return await diary.save(); } + //일지 조회하기 static async findExistDiary(diaryId) { const diary = await DiaryEntity.findOne({ where: { id: diaryId }, @@ -101,6 +102,7 @@ export class DiaryEntity extends BaseEntity { return diary; } + //scheduleId로 일지 조회하기 static async findExistDiaryByScheduleId(schedule) { const diary = await DiaryEntity.findOne({ where: { schedule: { id: schedule.id } }, diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index bf2171b..87b874b 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -67,7 +67,7 @@ export class JourneyService { await ScheduleEntity.findMonthlySchedule(journeyId, dates); return schedules; } - + //여정에 작성한 일지 개수 가지고 오기 async getDiaryCount(schedules) { let diaryCount = 0; for (const schedule of schedules) { diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index fd6286c..9f38f1e 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -66,6 +66,7 @@ export class JourneyEntity extends BaseEntity { } } + //사용자의 월별 여정 조회 static async findMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { const firstDate = new Date(`${dates.year}-${dates.month}-01`); const lastDate = new Date(`${dates.year}-${dates.month}-31`); diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 3d5dd3f..7d2fc2a 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -54,6 +54,7 @@ export class ScheduleEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + //일정 작성하기 static async createSchedule(journey, currentDate) { const schedule = new ScheduleEntity(); schedule.date = currentDate.toISOString().split('T')[0]; @@ -61,16 +62,19 @@ export class ScheduleEntity extends BaseEntity { return await schedule.save(); } + //일정 작성하기 : title static async updateScheduleTitle(schedule, updateScheduleDto) { schedule.title = updateScheduleDto.title; return await schedule.save(); } + //일정 작성하기 : location static async updateScheduleLocation(schedule, location) { schedule.location = location.id; return await schedule.save(); } + //일정 조회하기 static async findExistSchedule(scheduleId) { const schedule = await ScheduleEntity.findOne({ where: { id: scheduleId }, @@ -80,7 +84,7 @@ export class ScheduleEntity extends BaseEntity { } return schedule; } - + // 월별 일정 조회하기 static async findMonthlySchedule( journeyId, dates: FindMonthlyJourneyDto, From 16238b7ec8b70f77b2f05bfbe24e0444249b8f54 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 05:13:36 +0900 Subject: [PATCH 108/316] =?UTF-8?q?feat=20:=20=EC=97=AC=EC=A0=95=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20-=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/models/diary.image.entity.ts | 9 +++++ src/journey/journey.controller.ts | 16 +++++++- src/journey/journey.service.ts | 55 ++++++++++++++++++++++++++ src/journey/model/journey.entity.ts | 10 +++++ src/location/location.entity.ts | 7 ++++ src/response/response.status.ts | 5 +++ src/schedule/schedule.entity.ts | 12 +++++- 7 files changed, 112 insertions(+), 2 deletions(-) diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index 781f1a5..da7319e 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -19,6 +19,7 @@ export class DiaryImageEntity extends BaseEntity { @Column({ type: 'mediumtext' }) imageUrl: string; + @JoinColumn() @OneToOne(() => DiaryEntity, (diary) => diary.image) diary: DiaryEntity; @@ -31,6 +32,7 @@ export class DiaryImageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + //사진 URL 저장 static async createDiaryImg(diary, ImgUrl: string) { const diaryImg = new DiaryImageEntity(); diaryImg.imageUrl = ImgUrl; @@ -38,4 +40,11 @@ export class DiaryImageEntity extends BaseEntity { console.log(diaryImg.diary); await diaryImg.save(); } + //사진 URL 불러오기 + static async findExistImgUrl(diary: DiaryEntity) { + const imgUrl = await DiaryImageEntity.findOne({ + where: { diary: { id: diary.id } }, + }); + return imgUrl; + } } diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index f62b9c7..25a7ad1 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -38,10 +38,24 @@ export class JourneyController { return result; } + /*여정 불러오기*/ + @ApiOperation({ + summary: '여정 불러오기', + description: '여정 제목, 날짜, 위치, 사진을 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Get(':journeyId') + async getJourneyPreview(@Param('journeyId') journeyId: number) { + const result = await this.journeyService.getJourneyPreview(journeyId); + return result; + } + /*월별 여정 불러오기*/ @ApiOperation({ summary: '월별 여정 불러오기', - description: '월별 여정과 일지 개수, 사진을 불러옵니다.', + description: '월별 여정 리스트 - 제목, 날짜, 일지 개수를 불러옵니다.', }) @ApiOkResponse({ description: '성공 ', diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 87b874b..57fead1 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -8,6 +8,8 @@ import { DiaryEntity } from 'src/diary/models/diary.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; import { FindMonthlyJourneyDto } from './dtos/find-monthly-journey.dto'; +import { LocationEntity } from 'src/location/location.entity'; +import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; @Injectable() export class JourneyService { @@ -55,6 +57,49 @@ export class JourneyService { return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, journeyList); } + //지도에서 여정 정보 보여주기 + async getJourneyPreview(journeyId) { + const journey = await this.getJourneyInfo(journeyId); + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journeyId, + ); + const locationList = await this.getJourneyList(schedules); + const journeyPreview = { journey, locationList }; + return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, journeyPreview); + } + + //journeylist + async getJourneyList(schedules: ScheduleEntity[]) { + const locationList = await Promise.all( + schedules.map(async (schedule) => { + const location = await LocationEntity.findExistLocationById( + schedule.location, + ); + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return errResponse(BaseResponse.DIARY_NOT_FOUND); + } + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + if (!diaryImg) { + return errResponse(BaseResponse.DIARY_NOT_FOUND); + } + return { + date: schedule.date, + location: { + id: location.id, + latitude: location.latitude, + longitude: location.longitude, + }, + diaryImage: { + id: diaryImg.id, + imageUrl: diaryImg.imageUrl, + }, + }; + }), + ); + return locationList; + } + //사용자의 월별 여정 가지고 오기 async getMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { const journeys = await JourneyEntity.findMonthlyJourney(userId, dates); @@ -78,4 +123,14 @@ export class JourneyService { } return diaryCount; } + + async getJourneyInfo(journeyId) { + const journey = await JourneyEntity.findExistJourney(journeyId); + return { + id: journey.id, + title: journey.title, + startDate: journey.startDate, + endDate: journey.endDate, + }; + } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 9f38f1e..40d7b72 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -66,6 +66,16 @@ export class JourneyEntity extends BaseEntity { } } + //여정 조회 + static async findExistJourney(journeyId: number) { + const journey: JourneyEntity = await JourneyEntity.findOne({ + where: { + id: journeyId, + }, + }); + return journey; + } + //사용자의 월별 여정 조회 static async findMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { const firstDate = new Date(`${dates.year}-${dates.month}-01`); diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 16b39d0..af308f4 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -70,4 +70,11 @@ export class LocationEntity extends BaseEntity { }); return location; } + + static async findExistLocationById(locationId) { + const location = await LocationEntity.findOne({ + where: { id: locationId }, + }); + return location; + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index ae342f5..b950f27 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -15,6 +15,11 @@ export const BaseResponse = { code: 200, message: '월별 여정을 불러오는데 성공했습니다.', }, + GET_JOURNEY_PREVIEW_SUCCESS: { + success: true, + code: 200, + message: '여정을 불러오는데 성공했습니다.', + }, /* 201 CREATED : 요청 성공, 자원 생성 */ DATEGROUP_CREATED: { diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 7d2fc2a..2b4ce02 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -75,7 +75,7 @@ export class ScheduleEntity extends BaseEntity { } //일정 조회하기 - static async findExistSchedule(scheduleId) { + static async findExistSchedule(scheduleId): Promise { const schedule = await ScheduleEntity.findOne({ where: { id: scheduleId }, }); @@ -84,6 +84,16 @@ export class ScheduleEntity extends BaseEntity { } return schedule; } + + //journeyId로 일정 조회하기 + static async findExistScheduleByJourneyId( + journeyId: number, + ): Promise { + const schedules = await ScheduleEntity.find({ + where: { journey: { id: journeyId } }, + }); + return schedules; + } // 월별 일정 조회하기 static async findMonthlySchedule( journeyId, From fcad34b60b7bab4d12e9e1448868bd63bd04e095 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 7 Feb 2024 05:37:13 +0900 Subject: [PATCH 109/316] =?UTF-8?q?feat=20:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20(=EB=A9=94=EC=9D=B4=ED=8A=B8=20=ED=83=90?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 메이트 탐색 페이지 -> 검색 --- src/follow/follow.controller.ts | 28 +++++++++++++++++++++++++++ src/response/response-code.enum.ts | 6 ++++++ src/user/user.search.dto.ts | 31 ++++++++++++++++++++++++++++++ src/user/user.service.ts | 30 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 src/user/user.search.dto.ts diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index eb0f426..cb0d956 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -4,6 +4,7 @@ import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserEntity } from 'src/user/user.entity'; import { UserService } from 'src/user/user.service'; +import { UserSearchDto} from "../user/user.search.dto"; // import { UserGuard } from 'src/user/user.guard'; // @UseGuards(UserGuard) @@ -136,4 +137,31 @@ export class FollowController { } } + // [5] 메이트 검색 + @Get('/:searchTerm') + async getSearchResult( + @Req() req: Request, + @Param('searchTerm') searchTerm: string): Promise> { + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 1; + + try { + const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(userId, searchTerm) + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + "검색 결과 리스트 불러오기 성공", + userSearchDto + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + "검색 결과 리스트 불러오기 실패", + null + ); + } + } + } \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 8e2025b..1da99de 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -15,6 +15,8 @@ export enum ResponseCode { GET_FOLLOWING_LIST_SUCCESS = 'OK', GET_MEMBER_LIST_SUCCESS = 'OK', DELETE_MEMBER_SUCCESS = 'OK', + RULE_EDIT_SUCCESS = 'OK', + GET_SEARCH_RESULT_SUCCESS = 'OK', @@ -36,6 +38,7 @@ export enum ResponseCode { FOLLOW_CREATED = 'CREATED', INVITATION_CREATED = 'CREATED', + /* 400 BAD_REQUEST : 잘못된 요청 */ @@ -56,6 +59,9 @@ export enum ResponseCode { IS_ALREADY_MEMBER = 'BAD_REQUEST', IS_NOT_MEMBER = 'BAD_REQUEST', DELETE_MEMBER_FAIL = 'BAD_REQUEST', + RULE_EDIT_FAIL = 'BAD_REQUEST', + GET_SEARCH_RESULT_FAIL = 'BAD_REQUEST', + diff --git a/src/user/user.search.dto.ts b/src/user/user.search.dto.ts new file mode 100644 index 0000000..4cdce99 --- /dev/null +++ b/src/user/user.search.dto.ts @@ -0,0 +1,31 @@ +import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; + +export class UserSearchDto { + @IsNotEmpty() + @IsNumber() + mateId: number; + + @IsNotEmpty() + @IsString() + nickName: string; + + @IsOptional() + @IsString() + introduction: string; + + @IsNotEmpty() + @IsString() + followerCnt: number; + + @IsOptional() + @IsString() + followingCnt: number; + + @IsOptional() + @IsString() + image: string; + + @IsOptional() + @IsBoolean() + isFollowing: boolean; +} \ No newline at end of file diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 3d50eb4..b239e54 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -7,6 +7,7 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; +import { UserSearchDto} from "./user.search.dto"; @Injectable() export class UserService { @@ -249,4 +250,33 @@ export class UserService { } } + + async getSearchResult(userId: number, searchTerm: string) : Promise { + // 현재 로그인한 유저 객체 + const user = await this.findUserById(userId); + console.log('현재 로그인한 유저 아이디 : ', user.id) + + // 검색 결과로 보여줄 유저 객체 리스트 + const mates = await UserEntity.find({ + where: { name: searchTerm, nickname: searchTerm }, + relations : { profileImage : true, following : true, follower : true}, + }); + console.log(mates); + + // dto 리스트 생성 + const results : UserSearchDto[] = await Promise.all(mates.map(async (mate) => { + const userSearchDto = new UserSearchDto(); + userSearchDto.mateId = mate.id; + userSearchDto.nickName = mate.nickname; + userSearchDto.introduction = mate.introduction; + userSearchDto.followerCnt = mate.follower.length; + userSearchDto.followingCnt = mate.following.length; + userSearchDto.image = mate.profileImage.imageKey; + userSearchDto.isFollowing = await this.checkIfFollowing(user, mate.id); + + return userSearchDto; + })); + + return results; + } } From 7c23f6e60055fba2fe7e9f1aeae037e62a5f4ff0 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 05:59:25 +0900 Subject: [PATCH 110/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20-=20=EC=A7=80=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 16 +++++++++++++++- src/diary/diary.service.ts | 34 ++++++++++++++++++++++++++++++++- src/response/response.status.ts | 5 +++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 83713c6..d3ef7f3 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -1,5 +1,5 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; -import { Controller, Put, Post, Body, Param } from '@nestjs/common'; +import { Controller, Put, Post, Get, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; @@ -59,4 +59,18 @@ export class DiaryController { const result = await this.diaryService.getDiaryImgUrl(diaryId, fileName); return result; } + + /*일지 불러오기 - 지도 */ + @ApiOperation({ + summary: '일지 불러오기 - 지도', + description: 'journeyId로 일지 불러오기', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Get('get/:journeyId') + async getDiaryList(@Param('journeyId') journeyId: number) { + const result = await this.diaryService.getDiaryList(journeyId); + return result; + } } diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 90d94a9..e7a7040 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -1,11 +1,13 @@ import { Injectable } from '@nestjs/common'; -import { response } from 'src/response/response'; +import { response, errResponse } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { DiaryEntity } from './models/diary.entity'; import { DiaryImageEntity } from './models/diary.image.entity'; import { PostDiaryDto } from './dtos/post-diary.dto'; import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; import { S3UtilService } from 'src/utils/S3.service'; +import { JourneyEntity } from 'src/journey/model/journey.entity'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; @Injectable() export class DiaryService { @@ -34,4 +36,34 @@ export class DiaryService { await DiaryImageEntity.createDiaryImg(diary, imageUrl); return response(BaseResponse.DIARY_IMG_URL_CREATED); } + + /*일지 불러오기 - 지도*/ + async getDiaryList(journeyId) { + const journey = await JourneyEntity.findExistJourney(journeyId); + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey.id, + ); + const diaryList = await Promise.all( + schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return errResponse(BaseResponse.DIARY_NOT_FOUND); + } + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + if (!diaryImg) { + return errResponse(BaseResponse.DIARY_NOT_FOUND); + } + return { + journeyId: journeyId, + date: schedule.date, + diary: diary, + diaryImage: { + id: diaryImg.id, + imageUrl: diaryImg.imageUrl, + }, + }; + }), + ); + return response(BaseResponse.GET_DIARY_SUCCESS, diaryList); + } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index b950f27..f182846 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -20,6 +20,11 @@ export const BaseResponse = { code: 200, message: '여정을 불러오는데 성공했습니다.', }, + GET_DIARY_SUCCESS: { + success: true, + code: 200, + message: '일지를 불러오는데 성공했습니다.', + }, /* 201 CREATED : 요청 성공, 자원 생성 */ DATEGROUP_CREATED: { From 596d743248d5a9f7627277d974b532ae3d55fce8 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 06:10:07 +0900 Subject: [PATCH 111/316] =?UTF-8?q?chore=20:=20=EB=B0=98=ED=99=98=EA=B0=92?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.service.ts | 4 ++-- src/journey/journey.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index e7a7040..72ea616 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -47,11 +47,11 @@ export class DiaryService { schedules.map(async (schedule) => { const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); if (!diary) { - return errResponse(BaseResponse.DIARY_NOT_FOUND); + return null; } const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); if (!diaryImg) { - return errResponse(BaseResponse.DIARY_NOT_FOUND); + return null; } return { journeyId: journeyId, diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 57fead1..938f25c 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -77,11 +77,11 @@ export class JourneyService { ); const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); if (!diary) { - return errResponse(BaseResponse.DIARY_NOT_FOUND); + return null; } const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); if (!diaryImg) { - return errResponse(BaseResponse.DIARY_NOT_FOUND); + return null; } return { date: schedule.date, From 56af56559b291b23b8307ae8c49f316e19b69243 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 06:30:23 +0900 Subject: [PATCH 112/316] =?UTF-8?q?refactor=20:=20map=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 ++ src/map/map.controller.ts | 0 src/map/map.module.ts | 0 src/map/map.service.ts | 0 4 files changed, 2 insertions(+) create mode 100644 src/map/map.controller.ts create mode 100644 src/map/map.module.ts create mode 100644 src/map/map.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 3e105f4..1ff5b85 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -18,6 +18,7 @@ import { S3Module } from './utils/S3.module'; import { FollowModule } from './follow/follow.module'; import { MemberModule } from './member/member.module'; import { SearchModule } from './search/search.module'; +import { MapModule } from './map/map.module'; @Module({ imports: [ @@ -39,6 +40,7 @@ import { SearchModule } from './search/search.module'; FollowModule, MemberModule, SearchModule, + MapModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/map/map.module.ts b/src/map/map.module.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/map/map.service.ts b/src/map/map.service.ts new file mode 100644 index 0000000..e69de29 From 2adeabdf15167441d8b5ded66a0519687f85bda0 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 7 Feb 2024 06:37:20 +0900 Subject: [PATCH 113/316] =?UTF-8?q?fix=20:=20getSearchResult=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/member/member.controller.ts | 30 ++++++++++++++++++++++++++++++ src/user/user.service.ts | 10 ++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index 98934e9..51fcbf2 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -4,6 +4,8 @@ import { ResponseDto } from '../response/response.dto'; import { MemberService } from './member.service'; import { RuleService } from 'src/rule/rule.service'; import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; +import {UserSearchDto} from "../user/user.search.dto"; +import { UserService} from "../user/user.service"; // import { UserGuard } from 'src/user/user.guard'; // @UseGuards(UserGuard) @@ -12,6 +14,7 @@ export class MemberController { constructor( private readonly memberService: MemberService, private readonly ruleService: RuleService, + private readonly userService: UserService, ) {} // [1] 여행 규칙 멤버 리스트 조회 @@ -104,4 +107,31 @@ export class MemberController { ); } } + + // [4] 초대할 여행 규칙 멤버 검색 + @Get('/search/:searchTerm') + async getSearchResult( + @Req() req: Request, + @Param('searchTerm') searchTerm: string): Promise> { + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 1; + + try { + const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(userId, searchTerm) + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + "검색 결과 리스트 불러오기 성공", + userSearchDto + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + "검색 결과 리스트 불러오기 실패", + null + ); + } + } } \ No newline at end of file diff --git a/src/user/user.service.ts b/src/user/user.service.ts index b239e54..99bde62 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -256,10 +256,14 @@ export class UserService { const user = await this.findUserById(userId); console.log('현재 로그인한 유저 아이디 : ', user.id) + console.log(searchTerm); + // 검색 결과로 보여줄 유저 객체 리스트 const mates = await UserEntity.find({ - where: { name: searchTerm, nickname: searchTerm }, - relations : { profileImage : true, following : true, follower : true}, + where: [ + {name: searchTerm}, {nickname: searchTerm}, + ], + relations : [ 'profileImage', 'following', 'follower' ], }); console.log(mates); @@ -277,6 +281,8 @@ export class UserService { return userSearchDto; })); + console.log('검색 결과 : ', results); + return results; } } From 63c735773c4fde3bf085aaf75ad7d6b553e31328 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Wed, 7 Feb 2024 07:00:04 +0900 Subject: [PATCH 114/316] =?UTF-8?q?refactor=20:=20map=20API=20=EB=94=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 14 -- src/diary/diary.service.ts | 30 ---- src/journey/journey.controller.ts | 52 +----- src/journey/journey.service.ts | 107 +------------ src/journey/model/journey.entity.ts | 9 +- src/map/map.controller.ts | 65 ++++++++ src/map/map.module.ts | 10 ++ src/map/map.service.ts | 150 ++++++++++++++++++ .../month-info.dto.ts} | 2 +- src/schedule/schedule.entity.ts | 4 +- src/schedule/schedule.service.ts | 4 - 11 files changed, 232 insertions(+), 215 deletions(-) rename src/{journey/dtos/find-monthly-journey.dto.ts => map/month-info.dto.ts} (72%) diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index d3ef7f3..86e2c14 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -59,18 +59,4 @@ export class DiaryController { const result = await this.diaryService.getDiaryImgUrl(diaryId, fileName); return result; } - - /*일지 불러오기 - 지도 */ - @ApiOperation({ - summary: '일지 불러오기 - 지도', - description: 'journeyId로 일지 불러오기', - }) - @ApiOkResponse({ - description: '성공 ', - }) - @Get('get/:journeyId') - async getDiaryList(@Param('journeyId') journeyId: number) { - const result = await this.diaryService.getDiaryList(journeyId); - return result; - } } diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 72ea616..00f5016 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -36,34 +36,4 @@ export class DiaryService { await DiaryImageEntity.createDiaryImg(diary, imageUrl); return response(BaseResponse.DIARY_IMG_URL_CREATED); } - - /*일지 불러오기 - 지도*/ - async getDiaryList(journeyId) { - const journey = await JourneyEntity.findExistJourney(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( - journey.id, - ); - const diaryList = await Promise.all( - schedules.map(async (schedule) => { - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (!diary) { - return null; - } - const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); - if (!diaryImg) { - return null; - } - return { - journeyId: journeyId, - date: schedule.date, - diary: diary, - diaryImage: { - id: diaryImg.id, - imageUrl: diaryImg.imageUrl, - }, - }; - }), - ); - return response(BaseResponse.GET_DIARY_SUCCESS, diaryList); - } } diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 25a7ad1..603269a 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,18 +1,9 @@ -import { - Controller, - Param, - Body, - Req, - UseGuards, - Get, - Post, -} from '@nestjs/common'; +import { Controller, Body, Req, UseGuards, Post } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; import { UserGuard } from 'src/user/user.guard'; import { JourneyService } from './journey.service'; import { CreateJourneyDto } from './dtos/create-journey.dto'; -import { FindMonthlyJourneyDto } from './dtos/find-monthly-journey.dto'; @Controller('journey') export class JourneyController { @@ -37,45 +28,4 @@ export class JourneyController { ); return result; } - - /*여정 불러오기*/ - @ApiOperation({ - summary: '여정 불러오기', - description: '여정 제목, 날짜, 위치, 사진을 불러옵니다.', - }) - @ApiOkResponse({ - description: '성공 ', - }) - @Get(':journeyId') - async getJourneyPreview(@Param('journeyId') journeyId: number) { - const result = await this.journeyService.getJourneyPreview(journeyId); - return result; - } - - /*월별 여정 불러오기*/ - @ApiOperation({ - summary: '월별 여정 불러오기', - description: '월별 여정 리스트 - 제목, 날짜, 일지 개수를 불러옵니다.', - }) - @ApiOkResponse({ - description: '성공 ', - }) - @UseGuards(UserGuard) - @Get('monthly/:year/:month') - async getMonthlyJourney( - @Param('year') year: number, - @Param('month') month: number, - @Req() req: Request, - ) { - const user = req.user; - const findMonthlyJourneyDto: FindMonthlyJourneyDto = { - year, - month, - }; - const result = await this.journeyService.getMonthlyJourneyMap( - user.id, - findMonthlyJourneyDto, - ); - return result; - } } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 938f25c..56f28f4 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -3,13 +3,8 @@ import { Injectable } from '@nestjs/common'; import { JourneyEntity } from './model/journey.entity'; import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; -import { UserEntity } from 'src/user/user.entity'; -import { DiaryEntity } from 'src/diary/models/diary.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; -import { FindMonthlyJourneyDto } from './dtos/find-monthly-journey.dto'; -import { LocationEntity } from 'src/location/location.entity'; -import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; @Injectable() export class JourneyService { @@ -31,106 +26,6 @@ export class JourneyService { currentDate.setDate(currentDate.getDate() + 1); } - return response(BaseResponse.JOURNEY_CREATED); - } - //지도에서 사용자의 월별 여정 불러오기 - async getMonthlyJourneyMap(userId: number, dates: FindMonthlyJourneyDto) { - const user = await UserEntity.findExistUser(userId); - const monthlyJourney = await this.getMonthlyJourney(user.id, dates); - if (monthlyJourney.length === 0) { - return errResponse(BaseResponse.JOURNEY_NOT_FOUND); - } - - const journeyList = await Promise.all( - monthlyJourney.map(async (journey) => { - const schedules = await this.getMonthlySchedule(journey.id, dates); - const diaryCount = await this.getDiaryCount(schedules); - return { - journeyId: journey.id, - title: journey.title, - startDate: journey.startDate, - endDate: journey.endDate, - diaryCount: diaryCount, - }; - }), - ); - return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, journeyList); - } - - //지도에서 여정 정보 보여주기 - async getJourneyPreview(journeyId) { - const journey = await this.getJourneyInfo(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( - journeyId, - ); - const locationList = await this.getJourneyList(schedules); - const journeyPreview = { journey, locationList }; - return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, journeyPreview); - } - - //journeylist - async getJourneyList(schedules: ScheduleEntity[]) { - const locationList = await Promise.all( - schedules.map(async (schedule) => { - const location = await LocationEntity.findExistLocationById( - schedule.location, - ); - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (!diary) { - return null; - } - const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); - if (!diaryImg) { - return null; - } - return { - date: schedule.date, - location: { - id: location.id, - latitude: location.latitude, - longitude: location.longitude, - }, - diaryImage: { - id: diaryImg.id, - imageUrl: diaryImg.imageUrl, - }, - }; - }), - ); - return locationList; - } - - //사용자의 월별 여정 가지고 오기 - async getMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { - const journeys = await JourneyEntity.findMonthlyJourney(userId, dates); - return journeys; - } - - //사용자의 월별 일정 가지고 오기 - async getMonthlySchedule(journeyId, dates: FindMonthlyJourneyDto) { - const schedules: ScheduleEntity[] = - await ScheduleEntity.findMonthlySchedule(journeyId, dates); - return schedules; - } - //여정에 작성한 일지 개수 가지고 오기 - async getDiaryCount(schedules) { - let diaryCount = 0; - for (const schedule of schedules) { - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (diary) { - diaryCount += 1; - } - } - return diaryCount; - } - - async getJourneyInfo(journeyId) { - const journey = await JourneyEntity.findExistJourney(journeyId); - return { - id: journey.id, - title: journey.title, - startDate: journey.startDate, - endDate: journey.endDate, - }; + return errResponse(BaseResponse.JOURNEY_CREATED); } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 40d7b72..b13ff35 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -11,17 +11,12 @@ import { OneToMany, ManyToOne, Between, - JoinColumn, - MoreThanOrEqual, - LessThanOrEqual, } from 'typeorm'; import { CreateJourneyDto } from '../dtos/create-journey.dto'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; -import { FindMonthlyJourneyDto } from '../dtos/find-monthly-journey.dto'; -import { errResponse } from 'src/response/response'; -import { BaseResponse } from 'src/response/response.status'; +import { MonthInfoDto } from 'src/map/month-info.dto'; @Entity() export class JourneyEntity extends BaseEntity { @@ -77,7 +72,7 @@ export class JourneyEntity extends BaseEntity { } //사용자의 월별 여정 조회 - static async findMonthlyJourney(userId, dates: FindMonthlyJourneyDto) { + static async findMonthlyJourney(userId, dates: MonthInfoDto) { const firstDate = new Date(`${dates.year}-${dates.month}-01`); const lastDate = new Date(`${dates.year}-${dates.month}-31`); const journeys: JourneyEntity[] = await JourneyEntity.find({ diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index e69de29..e93bfa7 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -0,0 +1,65 @@ +import { MapService } from './map.service'; +import { Controller, Param, Req, UseGuards, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +import { Request } from 'express'; +import { UserGuard } from 'src/user/user.guard'; +import { MonthInfoDto } from './month-info.dto'; +@Controller('map') +export class MapController { + constructor(private readonly mapService: MapService) {} + + /*여정 불러오기*/ + @ApiOperation({ + summary: '여정 불러오기', + description: '여정 제목, 날짜, 위치, 사진을 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Get('get-journey/:journeyId') + async getJourneyPreview(@Param('journeyId') journeyId: number) { + const result = await this.mapService.getJourneyPreview(journeyId); + return result; + } + + /*월별 여정 불러오기*/ + @ApiOperation({ + summary: '월별 여정 불러오기', + description: '월별 여정 리스트 - 제목, 날짜, 일지 개수를 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Get('get-monthly-journey/:year/:month') + async getMonthlyJourney( + @Param('year') year: number, + @Param('month') month: number, + @Req() req: Request, + ) { + const user = req.user; + const monthInfoDto: MonthInfoDto = { + year, + month, + }; + const result = await this.mapService.getMonthlyJourneyMap( + user.id, + monthInfoDto, + ); + return result; + } + + /*일지 불러오기 - 지도 */ + @ApiOperation({ + summary: '일지 불러오기 - 지도', + description: 'journeyId로 일지 불러오기', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Get('get-diaries/:journeyId') + async getDiaryList(@Param('journeyId') journeyId: number) { + const result = await this.mapService.getDiaryList(journeyId); + return result; + } +} diff --git a/src/map/map.module.ts b/src/map/map.module.ts index e69de29..c3a9b8b 100644 --- a/src/map/map.module.ts +++ b/src/map/map.module.ts @@ -0,0 +1,10 @@ +// Map.module.ts +import { Module } from '@nestjs/common'; +import { MapController } from './map.controller'; +import { MapService } from './map.service'; + +@Module({ + controllers: [MapController], + providers: [MapService], +}) +export class MapModule {} diff --git a/src/map/map.service.ts b/src/map/map.service.ts index e69de29..e6963da 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -0,0 +1,150 @@ +import { Injectable } from '@nestjs/common'; +import { JourneyEntity } from '../journey/model/journey.entity'; +import { errResponse, response } from 'src/response/response'; +import { BaseResponse } from 'src/response/response.status'; +import { UserEntity } from 'src/user/user.entity'; +import { DiaryEntity } from 'src/diary/models/diary.entity'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; +import { MonthInfoDto } from './month-info.dto'; +import { LocationEntity } from 'src/location/location.entity'; +import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; + +@Injectable() +export class MapService { + //지도에서 사용자의 월별 여정 불러오기 + async getMonthlyJourneyMap(userId: number, monthInfoDto: MonthInfoDto) { + const user = await UserEntity.findExistUser(userId); + const monthlyJourney = await this.getMonthlyJourney(user.id, monthInfoDto); + if (monthlyJourney.length === 0) { + return errResponse(BaseResponse.JOURNEY_NOT_FOUND); + } + + const journeyList = await Promise.all( + monthlyJourney.map(async (journey) => { + const schedules = await this.getMonthlySchedule( + journey.id, + monthInfoDto, + ); + const diaryCount = await this.getDiaryCount(schedules); + return { + journeyId: journey.id, + title: journey.title, + startDate: journey.startDate, + endDate: journey.endDate, + diaryCount: diaryCount, + }; + }), + ); + return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, journeyList); + } + + //지도에서 여정 정보 보여주기 + async getJourneyPreview(journeyId) { + const journey = await this.getJourneyInfo(journeyId); + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journeyId, + ); + const locationList = await this.getJourneyList(schedules); + const journeyPreview = { journey, locationList }; + return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, journeyPreview); + } + + //journeylist + async getJourneyList(schedules: ScheduleEntity[]) { + const locationList = await Promise.all( + schedules.map(async (schedule) => { + const location = await LocationEntity.findExistLocationById( + schedule.location, + ); + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return null; + } + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + if (!diaryImg) { + return null; + } + return { + date: schedule.date, + location: { + id: location.id, + latitude: location.latitude, + longitude: location.longitude, + }, + diaryImage: { + id: diaryImg.id, + imageUrl: diaryImg.imageUrl, + }, + }; + }), + ); + return locationList; + } + + //사용자의 월별 여정 가지고 오기 + async getMonthlyJourney(userId, monthInfoDto: MonthInfoDto) { + const journeys = await JourneyEntity.findMonthlyJourney( + userId, + monthInfoDto, + ); + return journeys; + } + + //사용자의 월별 일정 가지고 오기 + async getMonthlySchedule(journeyId, monthInfoDto: MonthInfoDto) { + const schedules: ScheduleEntity[] = + await ScheduleEntity.findMonthlySchedule(journeyId, monthInfoDto); + return schedules; + } + //여정에 작성한 일지 개수 가지고 오기 + async getDiaryCount(schedules) { + let diaryCount = 0; + for (const schedule of schedules) { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (diary) { + diaryCount += 1; + } + } + return diaryCount; + } + + async getJourneyInfo(journeyId) { + const journey = await JourneyEntity.findExistJourney(journeyId); + return { + id: journey.id, + title: journey.title, + startDate: journey.startDate, + endDate: journey.endDate, + }; + } + + /*일지 불러오기 - 지도*/ + async getDiaryList(journeyId) { + const journey = await JourneyEntity.findExistJourney(journeyId); + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey.id, + ); + const diaryList = await Promise.all( + schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return null; + } + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + if (!diaryImg) { + return null; + } + return { + journeyId: journeyId, + date: schedule.date, + diary: diary, + diaryImage: { + id: diaryImg.id, + imageUrl: diaryImg.imageUrl, + }, + }; + }), + ); + return response(BaseResponse.GET_DIARY_SUCCESS, diaryList); + } +} diff --git a/src/journey/dtos/find-monthly-journey.dto.ts b/src/map/month-info.dto.ts similarity index 72% rename from src/journey/dtos/find-monthly-journey.dto.ts rename to src/map/month-info.dto.ts index 6d45509..ae0b5a6 100644 --- a/src/journey/dtos/find-monthly-journey.dto.ts +++ b/src/map/month-info.dto.ts @@ -1,6 +1,6 @@ import { IsInt } from 'class-validator'; -export class FindMonthlyJourneyDto { +export class MonthInfoDto { @IsInt() year: number; @IsInt() diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 2b4ce02..189f7eb 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -17,7 +17,7 @@ import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity' import { LocationEntity } from 'src/location/location.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; -import { FindMonthlyJourneyDto } from 'src/journey/dtos/find-monthly-journey.dto'; +import { MonthInfoDto } from 'src/map/month-info.dto'; @Entity() export class ScheduleEntity extends BaseEntity { @@ -97,7 +97,7 @@ export class ScheduleEntity extends BaseEntity { // 월별 일정 조회하기 static async findMonthlySchedule( journeyId, - dates: FindMonthlyJourneyDto, + dates: MonthInfoDto, ): Promise { const firstDate = new Date(`${dates.year}-${dates.month}-01`); const lastDate = new Date(`${dates.year}-${dates.month}-31`); diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 19738c4..417cb91 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -4,11 +4,7 @@ import { BaseResponse } from 'src/response/response.status'; import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; import { UserEntity } from 'src/user/user.entity'; -import { JourneyEntity } from 'src/journey/model/journey.entity'; -import { DiaryEntity } from 'src/diary/models/diary.entity'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; -import { FindMonthlyScheduleDto } from './dtos/find-monthly-schedule.dto'; -import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; @Injectable() export class ScheduleService { From 8ef57c5d5a9a201d59ce853691800c6d4392bcd4 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 7 Feb 2024 07:03:00 +0900 Subject: [PATCH 115/316] =?UTF-8?q?fix=20:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 99bde62..54ae550 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -8,6 +8,7 @@ import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; import { UserSearchDto} from "./user.search.dto"; +import {Like} from "typeorm"; @Injectable() export class UserService { @@ -261,9 +262,14 @@ export class UserService { // 검색 결과로 보여줄 유저 객체 리스트 const mates = await UserEntity.find({ where: [ - {name: searchTerm}, {nickname: searchTerm}, + {name: Like(`%${searchTerm}%`)}, + {nickname: Like(`%${searchTerm}%`)}, ], - relations : [ 'profileImage', 'following', 'follower' ], + relations : { + profileImage: true, + following: true, + follower: true, + } }); console.log(mates); From fc3463460840c540508ef5da38af53f72ce95904 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 7 Feb 2024 08:58:24 +0900 Subject: [PATCH 116/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EB=82=98=EA=B0=80=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 초대 삭제 --- package.json | 2 +- src/response/response-code.enum.ts | 6 +++-- src/rule/domain/rule.invitation.entity.ts | 14 +++++++++++ src/rule/rule.controller.ts | 29 ++++++++++++++++++++++- src/rule/rule.service.ts | 14 ++++++++--- 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 15340d5..5a1931f 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", - "mysql2": "^3.9.0", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", + "mysql2": "^3.9.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.20", diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 1da99de..dcdb951 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -17,9 +17,10 @@ export enum ResponseCode { DELETE_MEMBER_SUCCESS = 'OK', RULE_EDIT_SUCCESS = 'OK', GET_SEARCH_RESULT_SUCCESS = 'OK', - - + DELETE_INVITATION_SUCCESS = 'OK', + + DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', @@ -61,6 +62,7 @@ export enum ResponseCode { DELETE_MEMBER_FAIL = 'BAD_REQUEST', RULE_EDIT_FAIL = 'BAD_REQUEST', GET_SEARCH_RESULT_FAIL = 'BAD_REQUEST', + DELETE_INVITATION_FAIL = 'BAD_REQUEST', diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index 182b077..ac8af1a 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -59,4 +59,18 @@ export class RuleInvitationEntity extends BaseEntity { throw error; } } + + static async findInvitationByRuleAndUser(ruleId: number, userId: number) : Promise { + try { + const invitation = await RuleInvitationEntity.findOne({ + where: [{rule: {id : ruleId}}, + {invited: {id : userId}}] + }); + console.log('invitation 조회 결과 : ', invitation); + return invitation; + } catch (error) { + console.log('Error on findInvitationByRuleId: ', error); + throw error; + } + } } \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 1fc3a31..8fd3af6 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Body, Get, Param } from '@nestjs/common'; +import {Controller, Post, Body, Get, Param, Delete} from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; @@ -56,4 +56,31 @@ export class RuleController { ); } } + + // 여행 규칙 나가기 + @Delete('/:ruleId') + async deleteInvitation(@Param('ruleId') ruleId: number){ + + // 현재 로그인한 사용자 ID + // const userId = req.user.id; + const userId = 2; + + try { + await this.ruleService.deleteInvitation(ruleId, userId); + return new ResponseDto( + ResponseCode.DELETE_INVITATION_SUCCESS, + true, + "여행 규칙 나가기 성공", + null + ); + } catch (error) { + return new ResponseDto( + ResponseCode.DELETE_INVITATION_FAIL, + false, + "여행 규칙 나가기 실패", + null + ); + } + + } } \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 0e8af82..d4647b3 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -34,15 +34,23 @@ export class RuleService { async getDetail(ruleId : number, metaToBackDto : MetaToBackDto): Promise { try{ const detailPageDto : DetailPageDto = new DetailPageDto(); - - return detailPageDto; + + return detailPageDto; } catch(error){ console.error('Error on GetDetail : ', error); throw new HttpException('Internal Server Error', 500); } } - + + // [3] 여행 규칙 나가기 + // -1) 초대 받은 팀원 -> 초대 삭제 + async deleteInvitation(ruleId: number, userId: number): Promise { + const invitation : RuleInvitationEntity = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, userId); + + return invitation.softRemove(); + } + // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { From 293f3b161a00af53188d8f434619adaa3db8ed06 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 7 Feb 2024 09:24:50 +0900 Subject: [PATCH 117/316] refactor : @UserGuard --- src/comment/comment.controller.ts | 9 ++++---- src/comment/comment.converter.ts | 4 ++-- src/comment/comment.service.ts | 4 ++-- src/follow/follow.controller.ts | 37 ++++++++++++++++++------------- src/member/member.controller.ts | 27 +++++++++++++--------- src/rule/rule.controller.ts | 13 +++++++---- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index 8268bdd..c2e4907 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -3,9 +3,9 @@ import { CommentService } from './comment.service'; import { CreateCommentDto } from './dto/create-comment.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -// import { UserGuard } from 'src/user/user.guard'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; -// @UseGuards(UserGuard) @Controller('mate/rule') export class CommentController { constructor( @@ -14,8 +14,9 @@ export class CommentController { // 여행 규칙 코멘트 생성 @Post('/:ruleId') - async createComment(@Body() createCommentDto: CreateCommentDto, @Param('ruleId') ruleId: number): Promise> { - const result = await this.commentService.createComment(createCommentDto, ruleId); + @UseGuards(UserGuard) + async createComment(@Body() createCommentDto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { + const result = await this.commentService.createComment(createCommentDto, ruleId, req.user.id); if(!result){ return new ResponseDto( diff --git a/src/comment/comment.converter.ts b/src/comment/comment.converter.ts index ca171f7..15353ca 100644 --- a/src/comment/comment.converter.ts +++ b/src/comment/comment.converter.ts @@ -7,7 +7,7 @@ import { CreateCommentDto } from './dto/create-comment.dto'; @Injectable() export class CommentConverter { - async toEntity(dto: CreateCommentDto, ruleId:number): Promise { + async toEntity(dto: CreateCommentDto, ruleId:number, userId: number): Promise { const comment = new CommentEntity(); comment.content = dto.content; @@ -15,7 +15,7 @@ export class CommentConverter { const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId } }); comment.rule = rule; console.log(comment.rule); - const user = await UserEntity.findOneOrFail({ where: { id: dto.userId } }); + const user = await UserEntity.findOneOrFail({ where: { id: userId } }); comment.user = user; return comment; diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 72c5c66..6592a24 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -9,8 +9,8 @@ export class CommentService { private commentConverter: CommentConverter ) {} - async createComment(createCommentDto: CreateCommentDto, ruleId: number): Promise { - const comment = await this.commentConverter.toEntity(createCommentDto, ruleId); + async createComment(createCommentDto: CreateCommentDto, ruleId: number, userId: number): Promise { + const comment = await this.commentConverter.toEntity(createCommentDto, ruleId, userId); const savedComment = await CommentEntity.save(comment); diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index cb0d956..d9b9548 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -5,9 +5,9 @@ import { ResponseDto } from '../response/response.dto'; import { UserEntity } from 'src/user/user.entity'; import { UserService } from 'src/user/user.service'; import { UserSearchDto} from "../user/user.search.dto"; -// import { UserGuard } from 'src/user/user.guard'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; -// @UseGuards(UserGuard) @Controller('mate/search') export class FollowController { constructor( @@ -17,13 +17,14 @@ export class FollowController { // [1] 팔로우 @Post('/:followingId') - async createFollow(@Param('followingId') followingId : number): Promise> { + @UseGuards(UserGuard) + async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { // 현재 사용자 ID // const userId = req.user.id; - const userId = 1; + // const userId = 1; // 이미 팔로우하는 사용자인지 검증 - const user : UserEntity = await this.userService.findUserById(userId); + const user : UserEntity = await this.userService.findUserById(req.user.id); const isAlreadyFollow = this.userService.checkIfFollowing(user, followingId); if (isAlreadyFollow) { @@ -36,7 +37,7 @@ export class FollowController { } try { - await this.followService.createFollow(userId, followingId); + await this.followService.createFollow(req.user.id, followingId); return new ResponseDto( ResponseCode.FOLLOW_CREATED, true, @@ -55,10 +56,11 @@ export class FollowController { // [2] 언팔로우 @Delete('/:followId') - async deleteFollow(@Param('followId') followId: number): Promise> { + @UseGuards(UserGuard) + async deleteFollow(@Req() req: Request, @Param('followId') followId: number): Promise> { // 현재 사용자 ID // const userId = req.user.id; - const userId = 1; + // const userId = 1; try { const result = await this.followService.deleteFollow(followId); @@ -89,13 +91,14 @@ export class FollowController { // [3] 팔로우 리스트 조회 @Get('/followList') - async getFollowList(): Promise> { + @UseGuards(UserGuard) + async getFollowList(@Req() req: Request): Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 1; + // const userId = 1; try { - const followList = await this.followService.getFollowList(userId); + const followList = await this.followService.getFollowList(req.user.id); return new ResponseDto( ResponseCode.GET_FOLLOWING_LIST_SUCCESS, true, @@ -114,13 +117,14 @@ export class FollowController { // [4] 팔로워 리스트 조회 @Get('/followerList') - async getFollowerList(): Promise> { + @UseGuards(UserGuard) + async getFollowerList(@Req() req: Request): Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 1; + // const userId = 1; try { - const followerList = await this.followService.getFollowerList(userId); + const followerList = await this.followService.getFollowerList(req.user.id); return new ResponseDto( ResponseCode.GET_FOLLOWER_LIST_SUCCESS, true, @@ -139,15 +143,16 @@ export class FollowController { // [5] 메이트 검색 @Get('/:searchTerm') + @UseGuards(UserGuard) async getSearchResult( @Req() req: Request, @Param('searchTerm') searchTerm: string): Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 1; + // const userId = 1; try { - const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(userId, searchTerm) + const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(req.user.id, searchTerm) return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index 51fcbf2..1a36063 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -6,7 +6,8 @@ import { RuleService } from 'src/rule/rule.service'; import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; import {UserSearchDto} from "../user/user.search.dto"; import { UserService} from "../user/user.service"; -// import { UserGuard } from 'src/user/user.guard'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; // @UseGuards(UserGuard) @Controller('mate/rule/member') @@ -19,13 +20,14 @@ export class MemberController { // [1] 여행 규칙 멤버 리스트 조회 @Get('/:ruleId') - async getMember(@Param('ruleId') ruleId : number) : Promise> { + @UseGuards(UserGuard) + async getMember(@Req() req: Request, @Param('ruleId') ruleId : number) : Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 2; + // const userId = 2; try { - const memberList = await this.memberService.getMemberList(userId, ruleId); + const memberList = await this.memberService.getMemberList(req.user.id, ruleId); return new ResponseDto( ResponseCode.GET_MEMBER_LIST_SUCCESS, true, @@ -44,10 +46,11 @@ export class MemberController { // [2] 여행 규칙 멤버 초대 @Post('/:ruleId/:invitedId') - async createInvitation(@Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { + @UseGuards(UserGuard) + async createInvitation(@Req() req: Request, @Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 2; + // const userId = 2; // 이미 초대된 멤버인지 확인 const ruleEntity = await RuleMainEntity.findRuleById(ruleId); @@ -66,7 +69,7 @@ export class MemberController { // 멤버 초대 try { - await this.memberService.createInvitation(ruleId, userId, invitedId); + await this.memberService.createInvitation(ruleId, req.user.id, invitedId); return new ResponseDto( ResponseCode.INVITATION_CREATED, true, @@ -85,10 +88,11 @@ export class MemberController { // [3] 여행 규칙 멤버 삭제 @Delete('/:ruleId/:memberId') - async deleteMember(@Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { + @UseGuards(UserGuard) + async deleteMember(@Req() req: Request, @Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 2; + // const userId = 2; try { await this.memberService.deleteMember(ruleId, memberId); @@ -110,15 +114,16 @@ export class MemberController { // [4] 초대할 여행 규칙 멤버 검색 @Get('/search/:searchTerm') + @UseGuards(UserGuard) async getSearchResult( @Req() req: Request, @Param('searchTerm') searchTerm: string): Promise> { // 현재 로그인한 사용자 ID // const userId = req.user.id; - const userId = 1; + // const userId = 1; try { - const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(userId, searchTerm) + const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(req.user.id, searchTerm) return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 8fd3af6..3dd6a18 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,9 +1,11 @@ -import {Controller, Post, Body, Get, Param, Delete} from '@nestjs/common'; +import {Controller, Post, Body, Get, Param, Delete, UseGuards, Req} from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { MetaToBackDto } from './dto/meta-to-back.dto'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; @Controller('mate/rule') export class RuleController { @@ -13,7 +15,8 @@ export class RuleController { // 여행 규칙 생성 @Post('/write') - async createRule(@Body() createRuleDto: CreateRuleDto): Promise> { + @UseGuards(UserGuard) + async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { const result = await this.ruleService.createRule(createRuleDto); if(!result){ @@ -35,7 +38,8 @@ export class RuleController { // 여행 규칙 및 댓글 조회 @Get('/detail/:ruleId') - async getDetail(@Param('ruleId') ruleId: number, @Body() metaToBackDto: MetaToBackDto): Promise> { + @UseGuards(UserGuard) + async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number, @Body() metaToBackDto: MetaToBackDto): Promise> { const result = await this.ruleService.getDetail(ruleId, metaToBackDto); @@ -59,7 +63,8 @@ export class RuleController { // 여행 규칙 나가기 @Delete('/:ruleId') - async deleteInvitation(@Param('ruleId') ruleId: number){ + @UseGuards(UserGuard) + async deleteInvitation(@Req() req: Request, @Param('ruleId') ruleId: number){ // 현재 로그인한 사용자 ID // const userId = req.user.id; From 0ff47f58c0021b2c75990df744c03c0cbe08b8a5 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 00:11:06 +0900 Subject: [PATCH 118/316] =?UTF-8?q?feat=20:=20=EC=97=AC=EC=A0=95=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.service.ts | 7 ++- src/diary/dtos/post-diary.dto.ts | 3 + src/diary/models/diary.image.entity.ts | 5 +- src/journey/model/journey.entity.ts | 2 +- src/location/location.entity.ts | 26 ++++----- src/map/map.service.ts | 77 +++++++++++++++++++++++--- src/schedule/schedule.entity.ts | 28 ++++++++-- src/schedule/schedule.service.ts | 2 +- 8 files changed, 116 insertions(+), 34 deletions(-) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 00f5016..453f60a 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -16,6 +16,7 @@ export class DiaryService { /*일지 작성하기*/ async createDiary(scheduleId, diaryInfo: PostDiaryDto) { const diary = await DiaryEntity.createDiary(scheduleId, diaryInfo); + const diaryImg = await this.getDiaryImgUrl(diary, diaryInfo.fileName); console.log(diary); return response(BaseResponse.DIARY_CREATED); } @@ -23,17 +24,17 @@ export class DiaryService { /*일지 수정하기*/ async updateDiary(diaryId, diaryInfo: PostDiaryDto) { const diary = await DiaryEntity.updateDiary(diaryId, diaryInfo); + const diaryImg = await this.getDiaryImgUrl(diary, diaryInfo.fileName); return response(BaseResponse.DIARY_CREATED); } /*일지 사진 S3에 업로드 후 url 받기*/ - async getDiaryImgUrl(diaryId, fileName: string) { - const diary = await DiaryEntity.findExistDiary(diaryId); + async getDiaryImgUrl(diary, fileName: string) { const imageKey = this.s3UtilService.generateRandomImageKey(fileName); // await this.s3UtilService.putObjectFromBase64(imageKey, fileName); const imageUrl = await this.s3UtilService.getImageUrl(imageKey); console.log('url', imageUrl); await DiaryImageEntity.createDiaryImg(diary, imageUrl); - return response(BaseResponse.DIARY_IMG_URL_CREATED); + return imageUrl; } } diff --git a/src/diary/dtos/post-diary.dto.ts b/src/diary/dtos/post-diary.dto.ts index 33bbec3..c6ba54f 100644 --- a/src/diary/dtos/post-diary.dto.ts +++ b/src/diary/dtos/post-diary.dto.ts @@ -15,4 +15,7 @@ export class PostDiaryDto { @IsString() content: string; + + @IsString() + fileName: string; } diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index da7319e..0053e9d 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -33,13 +33,14 @@ export class DiaryImageEntity extends BaseEntity { deleted: Date; //사진 URL 저장 - static async createDiaryImg(diary, ImgUrl: string) { + static async createDiaryImg(diary: DiaryEntity, ImgUrl: string) { const diaryImg = new DiaryImageEntity(); diaryImg.imageUrl = ImgUrl; - diaryImg.diary = diary.id; + diaryImg.diary = diary; console.log(diaryImg.diary); await diaryImg.save(); } + //사진 URL 불러오기 static async findExistImgUrl(diary: DiaryEntity) { const imgUrl = await DiaryImageEntity.findOne({ diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index b13ff35..5e3fc7a 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -53,7 +53,7 @@ export class JourneyEntity extends BaseEntity { journey.title = createJourneyDto.title; journey.startDate = createJourneyDto.startDate; journey.endDate = createJourneyDto.endDate; - journey.user = user.id; + journey.user = user; return await journey.save(); } catch (error) { diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index af308f4..d1e7f44 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -7,7 +7,7 @@ import { Entity, PrimaryGeneratedColumn, UpdateDateColumn, - OneToOne, + OneToMany, } from 'typeorm'; @Entity() @@ -21,8 +21,8 @@ export class LocationEntity extends BaseEntity { @Column({ type: 'decimal', precision: 10, scale: 6 }) longitude: number; - @OneToOne(() => ScheduleEntity, (schedule) => schedule.location) - schedule: ScheduleEntity; + @OneToMany(() => ScheduleEntity, (schedule) => schedule.location) + schedules: ScheduleEntity[]; @CreateDateColumn() created: Date; @@ -45,15 +45,15 @@ export class LocationEntity extends BaseEntity { } } - static async updateLocation(schedule, updateScheduleDto) { + static async updateLocation(location: LocationEntity, updateScheduleDto) { try { - const location = await LocationEntity.findOneOrFail({ - where: { id: schedule.locationId }, + const updateLocation = await LocationEntity.findOneOrFail({ + where: { id: location.id }, }); - location.latitude = updateScheduleDto.latitude; - location.longitude = updateScheduleDto.longitude; + updateLocation.latitude = updateScheduleDto.latitude; + updateLocation.longitude = updateScheduleDto.longitude; - return await location.save(); + return await updateLocation.save(); } catch (error) { throw new Error(error); } @@ -71,10 +71,10 @@ export class LocationEntity extends BaseEntity { return location; } - static async findExistLocationById(locationId) { - const location = await LocationEntity.findOne({ - where: { id: locationId }, + static async findExistLocationById(schedule) { + const existLocation = await LocationEntity.findOne({ + where: { schedules: {} }, }); - return location; + return existLocation; } } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index e6963da..fa5df6e 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -44,25 +44,86 @@ export class MapService { const schedules = await ScheduleEntity.findExistScheduleByJourneyId( journeyId, ); - const locationList = await this.getJourneyList(schedules); - const journeyPreview = { journey, locationList }; - return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, journeyPreview); + const locationList = await this.getLocationList(schedules); + const imageList = await this.getDiaryImageList(schedules); + const scheduleList = schedules.map((schedule, index) => { + return { + location: locationList[index], + diaryImage: imageList[index], + }; + }); + + return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, scheduleList); + } + + async getScheduleList(schedules: ScheduleEntity[]) { + const scheduleInfoList = await Promise.all( + schedules.map(async (schedule) => { + const scheduleInfo = await ScheduleEntity.findExistSchedule( + schedule.id, + ); + return { + scheduleId: scheduleInfo.id, + title: scheduleInfo.title, + date: scheduleInfo.date, + }; + }), + ); + return scheduleInfoList; + } + + //위치 불러오기 + + async getLocationList(schedules: ScheduleEntity[]) { + const locationList = await Promise.all( + schedules.map(async (schedule) => { + const location = await ScheduleEntity.findExistLocation(schedule.id); + console.log(location); + if (!location) { + return { location: null }; + } + return { + locationId: location.id, + latitude: location.latitude, + longitude: location.longitude, + }; + }), + ); + return locationList; + } + + async getDiaryImageList(schedules: ScheduleEntity[]) { + const diaryImageList = await Promise.all( + schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return null; + } + const diaryImage = await DiaryImageEntity.findExistImgUrl(diary); + return { + imageId: diaryImage.id, + imageUrl: diaryImage.imageUrl, + }; + }), + ); + return diaryImageList; } //journeylist async getJourneyList(schedules: ScheduleEntity[]) { const locationList = await Promise.all( schedules.map(async (schedule) => { - const location = await LocationEntity.findExistLocationById( - schedule.location, - ); + const location = await ScheduleEntity.findExistLocation(schedule.id); + if (!location) { + return { location: null }; + } const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); if (!diary) { - return null; + return { diary: null }; } const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); if (!diaryImg) { - return null; + return { diaryImg: null }; } return { date: schedule.date, diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 189f7eb..7d687a3 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -30,7 +30,7 @@ export class ScheduleEntity extends BaseEntity { @Column({ nullable: true }) title: string; - @ManyToOne(() => LocationEntity, (location) => location.schedule) + @ManyToOne(() => LocationEntity, (location) => location.schedules) location: LocationEntity; @ManyToOne(() => JourneyEntity, (journey) => journey.schedules) @@ -55,22 +55,28 @@ export class ScheduleEntity extends BaseEntity { deleted: Date; //일정 작성하기 - static async createSchedule(journey, currentDate) { + static async createSchedule(journey: JourneyEntity, currentDate) { const schedule = new ScheduleEntity(); schedule.date = currentDate.toISOString().split('T')[0]; - schedule.journey = journey.id; + schedule.journey = journey; return await schedule.save(); } //일정 작성하기 : title - static async updateScheduleTitle(schedule, updateScheduleDto) { + static async updateScheduleTitle( + schedule: ScheduleEntity, + updateScheduleDto, + ) { schedule.title = updateScheduleDto.title; return await schedule.save(); } //일정 작성하기 : location - static async updateScheduleLocation(schedule, location) { - schedule.location = location.id; + static async updateScheduleLocation( + schedule: ScheduleEntity, + location: LocationEntity, + ) { + schedule.location = location; return await schedule.save(); } @@ -78,6 +84,7 @@ export class ScheduleEntity extends BaseEntity { static async findExistSchedule(scheduleId): Promise { const schedule = await ScheduleEntity.findOne({ where: { id: scheduleId }, + relations: ['location'], }); if (!schedule) { throw new NotFoundException(BaseResponse.SCHEDULE_NOT_FOUND); @@ -85,12 +92,21 @@ export class ScheduleEntity extends BaseEntity { return schedule; } + static async findExistLocation(scheduleId): Promise { + const schedule = await ScheduleEntity.findOne({ + where: { id: scheduleId }, + relations: ['location'], + }); + return schedule.location; + } + //journeyId로 일정 조회하기 static async findExistScheduleByJourneyId( journeyId: number, ): Promise { const schedules = await ScheduleEntity.find({ where: { journey: { id: journeyId } }, + relations: ['location'], }); return schedules; } diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 417cb91..685313d 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -32,7 +32,7 @@ export class ScheduleService { await ScheduleEntity.updateScheduleLocation(schedule, existLocation); } else if (schedule.location) { const location = LocationEntity.updateLocation( - schedule, + schedule.location, updateScheduleDto, ); console.log(location); From 93719f030798ccf16b9d83ee5ffd3ddee53378c5 Mon Sep 17 00:00:00 2001 From: kaaang Date: Thu, 8 Feb 2024 00:19:07 +0900 Subject: [PATCH 119/316] =?UTF-8?q?fix:=20preSignedURL=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EA=B0=80=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/S3.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/S3.service.ts b/src/utils/S3.service.ts index 5438d0b..8632d0a 100644 --- a/src/utils/S3.service.ts +++ b/src/utils/S3.service.ts @@ -41,7 +41,7 @@ export class S3UtilService { } public async getPresignedUrl(key: string) { - return this.s3.getSignedUrlPromise('getObject', { + return this.s3.getSignedUrlPromise('putObject', { Bucket: process.env.S3_BUCKET_NAME, Key: key, Expires: 60, From 473963a6ce7b5ec4b4857397542891243fb6a23a Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 00:28:49 +0900 Subject: [PATCH 120/316] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=97=AC=EC=A0=95=20=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 45 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index fa5df6e..c85d1a4 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -53,7 +53,15 @@ export class MapService { }; }); - return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, scheduleList); + return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, { + journey: { + journeyId: journey.id, + title: journey.title, + startDate: journey.startDate, + endDate: journey.endDate, + }, + scheduleList, + }); } async getScheduleList(schedules: ScheduleEntity[]) { @@ -91,6 +99,7 @@ export class MapService { ); return locationList; } + //이미지 리스트 불러오기 async getDiaryImageList(schedules: ScheduleEntity[]) { const diaryImageList = await Promise.all( @@ -109,39 +118,6 @@ export class MapService { return diaryImageList; } - //journeylist - async getJourneyList(schedules: ScheduleEntity[]) { - const locationList = await Promise.all( - schedules.map(async (schedule) => { - const location = await ScheduleEntity.findExistLocation(schedule.id); - if (!location) { - return { location: null }; - } - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (!diary) { - return { diary: null }; - } - const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); - if (!diaryImg) { - return { diaryImg: null }; - } - return { - date: schedule.date, - location: { - id: location.id, - latitude: location.latitude, - longitude: location.longitude, - }, - diaryImage: { - id: diaryImg.id, - imageUrl: diaryImg.imageUrl, - }, - }; - }), - ); - return locationList; - } - //사용자의 월별 여정 가지고 오기 async getMonthlyJourney(userId, monthInfoDto: MonthInfoDto) { const journeys = await JourneyEntity.findMonthlyJourney( @@ -169,6 +145,7 @@ export class MapService { return diaryCount; } + //여정 정보 불러오기 async getJourneyInfo(journeyId) { const journey = await JourneyEntity.findExistJourney(journeyId); return { From 7ab12830c6f29d0c633c3eab5f235786a2a58c9d Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Thu, 8 Feb 2024 01:44:30 +0900 Subject: [PATCH 121/316] =?UTF-8?q?[Refactor]=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20base64=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20PresignedUrl=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/dto/page-signature.dto.ts | 3 ++- src/signature/signature.service.ts | 4 ++-- src/utils/S3.controller.ts | 22 ++++++++++++++++++++++ src/utils/S3.module.ts | 2 ++ src/utils/S3.presignedUrl.dto.ts | 6 ++++++ src/utils/S3.service.ts | 20 +++++++++++++++++++- 6 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/utils/S3.controller.ts create mode 100644 src/utils/S3.presignedUrl.dto.ts diff --git a/src/signature/dto/page-signature.dto.ts b/src/signature/dto/page-signature.dto.ts index 817d299..d1e1aec 100644 --- a/src/signature/dto/page-signature.dto.ts +++ b/src/signature/dto/page-signature.dto.ts @@ -5,5 +5,6 @@ export class PageSignatureDto { page: number; content: string; location: string; - image: Buffer; + //image: Buffer; // form-data 형식 + image: string // base-64 형식 } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index e2aa59b..e53183d 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -66,7 +66,7 @@ export class SignatureService { const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; // Base64 이미지 업로드 - const uploadedImage = await this.s3Service.putObject( + const uploadedImage = await this.s3Service.putObjectFromBase64( key, pageSignatureDto.image ); console.log(uploadedImage); @@ -306,7 +306,7 @@ export class SignatureService { const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; // Base64 이미지 업로드 - const uploadedImage = await this.s3Service.putObject( + const uploadedImage = await this.s3Service.putObjectFromBase64( key, patchedPage.image ); diff --git a/src/utils/S3.controller.ts b/src/utils/S3.controller.ts new file mode 100644 index 0000000..d7a2e91 --- /dev/null +++ b/src/utils/S3.controller.ts @@ -0,0 +1,22 @@ +// S3.controller.ts + + +import { Body, Controller, Get, Post, Put } from '@nestjs/common'; +import { S3UtilService } from './S3.service'; + +@Controller('image') +export class S3UtilController{ + constructor(private readonly s3Service:S3UtilService) {} + + @Get('/signature') + GetPresignedUrlForSignature() { // 시그니처 이미지 업로드 요청시 + return this.s3Service.GetPresignedUrlForSignature(); + } + + @Get('/test') + TestImageUrlWithKey( //presigned URL 잘 보내졌나 테스트용 + @Body('key') key: string + ){ + return this.s3Service.TestImageUrlWithKey(key); + } +} \ No newline at end of file diff --git a/src/utils/S3.module.ts b/src/utils/S3.module.ts index ff879cc..b2e5bb6 100644 --- a/src/utils/S3.module.ts +++ b/src/utils/S3.module.ts @@ -1,7 +1,9 @@ import { Module } from '@nestjs/common'; import { S3UtilService } from './S3.service'; +import { S3UtilController } from './S3.controller'; @Module({ + controllers: [S3UtilController], providers: [S3UtilService], exports: [S3UtilService], }) diff --git a/src/utils/S3.presignedUrl.dto.ts b/src/utils/S3.presignedUrl.dto.ts new file mode 100644 index 0000000..bdf93e4 --- /dev/null +++ b/src/utils/S3.presignedUrl.dto.ts @@ -0,0 +1,6 @@ +// S3.presignedUrl.dto.ts + +export class S3PresignedUrlDto{ + key: string; + url: string; +} \ No newline at end of file diff --git a/src/utils/S3.service.ts b/src/utils/S3.service.ts index 5438d0b..80887ce 100644 --- a/src/utils/S3.service.ts +++ b/src/utils/S3.service.ts @@ -1,6 +1,7 @@ import * as S3 from 'aws-sdk/clients/s3.js'; import { v4 as uuidv4 } from 'uuid'; import { Injectable } from '@nestjs/common'; +import { S3PresignedUrlDto } from './S3.presignedUrl.dto'; @Injectable() export class S3UtilService { @@ -41,7 +42,7 @@ export class S3UtilService { } public async getPresignedUrl(key: string) { - return this.s3.getSignedUrlPromise('getObject', { + return this.s3.getSignedUrlPromise('putObject', { Bucket: process.env.S3_BUCKET_NAME, Key: key, Expires: 60, @@ -59,4 +60,21 @@ export class S3UtilService { return `${uuid}.${ext}`; } + + public async GetPresignedUrlForSignature(): Promise { + + const s3PresignedUrlDto:S3PresignedUrlDto= new S3PresignedUrlDto(); + + // 이미지 키 생성: 프론트에서는 업로드 후 백엔드에 키값을 보내줘야함 + s3PresignedUrlDto.key = `signature/${this.generateRandomImageKey('signature.png')}`; + + // 프론트에서 이미지를 업로드할 presignedUrl + s3PresignedUrlDto.url = await this.getPresignedUrl(s3PresignedUrlDto.key); + + return s3PresignedUrlDto; + } + + async TestImageUrlWithKey(key: string) { + return await this.getImageUrl(key); + } } From 1e32c098bfcd908c28cf29646f4f4618a7a59f38 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 01:49:47 +0900 Subject: [PATCH 122/316] =?UTF-8?q?feat=20:=20=EC=A7=80=EB=8F=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=97=AC=EC=A0=95=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.controller.ts | 14 ++++ src/map/map.service.ts | 110 +++++++++++++++++++++----------- src/response/response.status.ts | 5 ++ 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index e93bfa7..6687bdf 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -62,4 +62,18 @@ export class MapController { const result = await this.mapService.getDiaryList(journeyId); return result; } + + /*세부 여정 불러오기 - 지도 */ + @ApiOperation({ + summary: '세부 여정 불러오기 - 지도', + description: 'journeyId로 일정 불러오기', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Get('get-schedules/:journeyId') + async getDetailJourneyList(@Param('journeyId') journeyId: number) { + const result = await this.mapService.getDetailJourneyList(journeyId); + return result; + } } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index c85d1a4..cb42b70 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { JourneyEntity } from '../journey/model/journey.entity'; import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; @@ -11,7 +11,7 @@ import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; @Injectable() export class MapService { - //지도에서 사용자의 월별 여정 불러오기 + /*지도에서 사용자의 월별 여정 불러오기*/ async getMonthlyJourneyMap(userId: number, monthInfoDto: MonthInfoDto) { const user = await UserEntity.findExistUser(userId); const monthlyJourney = await this.getMonthlyJourney(user.id, monthInfoDto); @@ -38,7 +38,7 @@ export class MapService { return response(BaseResponse.GET_MONTHLY_JOURNEY_SUCCESS, journeyList); } - //지도에서 여정 정보 보여주기 + /*지도에서 여정 정보 보여주기*/ async getJourneyPreview(journeyId) { const journey = await this.getJourneyInfo(journeyId); const schedules = await ScheduleEntity.findExistScheduleByJourneyId( @@ -64,6 +64,58 @@ export class MapService { }); } + /*작성한 일지 불러오기 - 지도*/ + async getDiaryList(journeyId) { + const journey = await JourneyEntity.findExistJourney(journeyId); + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey.id, + ); + const diaryList = await Promise.all( + schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return null; + } + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + if (!diaryImg) { + return null; + } + return { + journeyId: journeyId, + date: schedule.date, + diary: diary, + diaryImage: { + id: diaryImg.id, + imageUrl: diaryImg.imageUrl, + }, + }; + }), + ); + return response(BaseResponse.GET_DIARY_SUCCESS, diaryList); + } + + /* 지도에서 세부 여정 확인하기 */ + async getDetailJourneyList(journeyId) { + const journey = await this.getJourneyInfo(journeyId); + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey.id, + ); + const scheduleInfoList = await this.getScheduleList(schedules); + const locationList = await this.getLocationList(schedules); + const imageList = await this.getDiaryImageList(schedules); + const diaryStatus = await this.getDiaryStatus(schedules); + const detailJourneyList = schedules.map((schedule, index) => { + return { + schedule: scheduleInfoList[index], + location: locationList[index], + diaryImage: imageList[index], + diary: diaryStatus[index], + }; + }); + return response(BaseResponse.GET_SCHEDULE_SUCCESS, detailJourneyList); + } + + //일정 정보 불러오기 async getScheduleList(schedules: ScheduleEntity[]) { const scheduleInfoList = await Promise.all( schedules.map(async (schedule) => { @@ -80,8 +132,7 @@ export class MapService { return scheduleInfoList; } - //위치 불러오기 - + //위치 정보 불러오기 async getLocationList(schedules: ScheduleEntity[]) { const locationList = await Promise.all( schedules.map(async (schedule) => { @@ -99,8 +150,8 @@ export class MapService { ); return locationList; } - //이미지 리스트 불러오기 + //이미지 리스트 불러오기 async getDiaryImageList(schedules: ScheduleEntity[]) { const diaryImageList = await Promise.all( schedules.map(async (schedule) => { @@ -145,9 +196,26 @@ export class MapService { return diaryCount; } + //일지 작성 여부 가져오기 + async getDiaryStatus(schedules) { + const diaryStatusList = await Promise.all( + schedules.map(async (schedule) => { + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return false; + } + }), + ); + + return diaryStatusList; + } + //여정 정보 불러오기 async getJourneyInfo(journeyId) { const journey = await JourneyEntity.findExistJourney(journeyId); + if (!journey) { + throw new NotFoundException(BaseResponse.JOURNEY_NOT_FOUND); + } return { id: journey.id, title: journey.title, @@ -155,34 +223,4 @@ export class MapService { endDate: journey.endDate, }; } - - /*일지 불러오기 - 지도*/ - async getDiaryList(journeyId) { - const journey = await JourneyEntity.findExistJourney(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( - journey.id, - ); - const diaryList = await Promise.all( - schedules.map(async (schedule) => { - const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); - if (!diary) { - return null; - } - const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); - if (!diaryImg) { - return null; - } - return { - journeyId: journeyId, - date: schedule.date, - diary: diary, - diaryImage: { - id: diaryImg.id, - imageUrl: diaryImg.imageUrl, - }, - }; - }), - ); - return response(BaseResponse.GET_DIARY_SUCCESS, diaryList); - } } diff --git a/src/response/response.status.ts b/src/response/response.status.ts index f182846..24deeee 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -25,6 +25,11 @@ export const BaseResponse = { code: 200, message: '일지를 불러오는데 성공했습니다.', }, + GET_SCHEDULE_SUCCESS: { + success: true, + code: 200, + message: '일정를 불러오는데 성공했습니다.', + }, /* 201 CREATED : 요청 성공, 자원 생성 */ DATEGROUP_CREATED: { From dea0420199036ea5cfa0f3ec98b34b5857ff2e91 Mon Sep 17 00:00:00 2001 From: kaaang Date: Thu, 8 Feb 2024 02:02:17 +0900 Subject: [PATCH 123/316] =?UTF-8?q?chore:=20max=20body=20size=EB=A5=BC=205?= =?UTF-8?q?0MB=EB=A1=9C=20=EC=83=81=ED=96=A5=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.ts b/src/main.ts index 26e06ad..4cba48b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,14 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; +import { urlencoded, json } from 'express'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Todo: Enable CORS only for development, specify the origin in production app.enableCors(); app.setGlobalPrefix('api/v1'); + app.use(json({ limit: '50mb' })); + app.use(urlencoded({ extended: true, limit: '50mb' })); await app.listen(3000); } bootstrap(); From e79455428d371c4a5b7213f7e5634f903c7414f9 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 03:24:50 +0900 Subject: [PATCH 124/316] =?UTF-8?q?feat=20:=20=EC=82=AD=EC=A0=9C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/detail-schedule/detail-schedule.entity.ts | 2 +- src/diary/models/diary.entity.ts | 5 +++ src/journey/journey.service.ts | 40 ++++++++++++++++++- src/journey/model/journey.entity.ts | 6 +++ src/schedule/schedule.entity.ts | 9 ++++- 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index 7959414..a665d54 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -55,7 +55,7 @@ export class DetailScheduleEntity extends BaseEntity { } //세부 일정 삭제하기 static async deleteDetailSchedule(detailSchedule) { - return await DetailScheduleEntity.softRemove(detailSchedule); + return await DetailScheduleEntity.remove(detailSchedule); } static async findExistDetail(detailId: number) { diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index f37beb2..818d328 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -91,6 +91,11 @@ export class DiaryEntity extends BaseEntity { return await diary.save(); } + //세부 일정 삭제하기 + static async deleteDiary(diary) { + return await DiaryEntity.softRemove(diary); + } + //일지 조회하기 static async findExistDiary(diaryId) { const diary = await DiaryEntity.findOne({ diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 56f28f4..cb2c3cc 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,10 +1,12 @@ // journey.service.ts -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { JourneyEntity } from './model/journey.entity'; import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; +import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; +import { DiaryEntity } from 'src/diary/models/diary.entity'; @Injectable() export class JourneyService { @@ -28,4 +30,40 @@ export class JourneyService { return errResponse(BaseResponse.JOURNEY_CREATED); } + + //여정 삭제하기 - 일정, 일지, + + async deleteJourney(journeyId: number) { + const journey = await JourneyEntity.findExistJourney(journeyId); + if (!journey) { + throw new NotFoundException(BaseResponse.JOURNEY_NOT_FOUND); + } + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey.id, + ); + for (const schedule of schedules) { + await this.deleteScheduleRelations(schedule); + } + + const deleteJourney = await JourneyEntity.deleteJourney(journey); + return response(BaseResponse.DELETE_JOURNEY_SUCCESS); + } + async deleteScheduleRelations(schedule) { + const deleteSchedule = await ScheduleEntity.findExistSchedule(schedule.id); + + //세부 일정 지우기 + const detailSchedules = + await DetailScheduleEntity.findExistDetailByScheduleId(schedule); + for (const detailSchedule of detailSchedules) { + await DetailScheduleEntity.deleteDetailSchedule(detailSchedule); + } + + //일지 지우기 + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule.id); + await this.deleteDiaryRelations(diary); + + await ScheduleEntity.deleteSchedule(deleteSchedule); + } + + async deleteDiaryRelations(diary) {} } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 5e3fc7a..fac54ef 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -47,6 +47,7 @@ export class JourneyEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + //여정 생성하기 static async createJourney(user, createJourneyDto) { try { const journey: JourneyEntity = new JourneyEntity(); @@ -61,6 +62,11 @@ export class JourneyEntity extends BaseEntity { } } + //여정 삭제하기 + static async deleteJourney(journey) { + return await JourneyEntity.remove(journey); + } + //여정 조회 static async findExistJourney(journeyId: number) { const journey: JourneyEntity = await JourneyEntity.findOne({ diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 7d687a3..eb1f889 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -33,7 +33,9 @@ export class ScheduleEntity extends BaseEntity { @ManyToOne(() => LocationEntity, (location) => location.schedules) location: LocationEntity; - @ManyToOne(() => JourneyEntity, (journey) => journey.schedules) + @ManyToOne(() => JourneyEntity, (journey) => journey.schedules, { + onDelete: 'CASCADE', + }) journey: JourneyEntity; @OneToMany( @@ -80,6 +82,11 @@ export class ScheduleEntity extends BaseEntity { return await schedule.save(); } + //일정 삭제하기 + static async deleteSchedule(schedule) { + return await ScheduleEntity.remove(schedule); + } + //일정 조회하기 static async findExistSchedule(scheduleId): Promise { const schedule = await ScheduleEntity.findOne({ From 8b29f593b74e381cfab3328b23281e44f3949506 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 8 Feb 2024 03:34:26 +0900 Subject: [PATCH 125/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 2 +- src/rule/dto/create-rule.dto.ts | 15 +------------ src/rule/dto/update-rule.dto.ts | 23 ++++++++++++++++++++ src/rule/rule.controller.ts | 2 +- src/rule/rule.converter.ts | 22 +++++++++++-------- src/rule/rule.service.ts | 38 ++++++++++++++++++++++++++------- 6 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 src/rule/dto/update-rule.dto.ts diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 6592a24..74fc2da 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { BadRequestException, HttpException, Injectable, NotFoundException } from '@nestjs/common'; import { CreateCommentDto } from './dto/create-comment.dto'; import { CommentConverter } from './comment.converter'; import { CommentEntity } from './domain/comment.entity'; diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index 185bfab..50b6c81 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -1,15 +1,6 @@ import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; - -class RulePairDto { - @IsNotEmpty() - @IsString() - ruleTitle: string; - - @IsNotEmpty() - @IsString() - ruleDetail: string; -} +import { RulePairDto} from "./rule-pair.dto"; export class CreateRuleDto { @IsNotEmpty() @@ -21,10 +12,6 @@ export class CreateRuleDto { @Type(() => RulePairDto) rulePairs: RulePairDto[]; - @IsNotEmpty() - @IsNumber() - inviterId: number; - @IsNotEmpty() @IsArray() @IsNumber({}, { each: true }) diff --git a/src/rule/dto/update-rule.dto.ts b/src/rule/dto/update-rule.dto.ts new file mode 100644 index 0000000..953023e --- /dev/null +++ b/src/rule/dto/update-rule.dto.ts @@ -0,0 +1,23 @@ +import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; + +class RulePairDto { + @IsNotEmpty() + @IsString() + ruleTitle: string; + + @IsNotEmpty() + @IsString() + ruleDetail: string; +} + +export class CreateRuleDto { + @IsNotEmpty() + @IsString() + mainTitle: string; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => RulePairDto) + rulePairs: RulePairDto[]; +} \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 3dd6a18..ba77c75 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -17,7 +17,7 @@ export class RuleController { @Post('/write') @UseGuards(UserGuard) async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { - const result = await this.ruleService.createRule(createRuleDto); + const result = await this.ruleService.createRule(createRuleDto, req.user.id); if(!result){ return new ResponseDto( diff --git a/src/rule/rule.converter.ts b/src/rule/rule.converter.ts index 3039c64..85918b1 100644 --- a/src/rule/rule.converter.ts +++ b/src/rule/rule.converter.ts @@ -19,26 +19,30 @@ import { MetaToFrontDto } from './dto/meta-to-front.dto'; @Injectable() export class RuleConverter { - async toEntity(dto: CreateRuleDto): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleInvitationEntity[] }> { + async toEntity(dto: CreateRuleDto, userId:number): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleInvitationEntity[] }> { + // main 저장 const main = new RuleMainEntity(); main.mainTitle = dto.mainTitle; + //rule 저장 const rules = dto.rulePairs.map(pair => { const sub = new RuleSubEntity(); sub.ruleTitle = pair.ruleTitle; sub.ruleDetail = pair.ruleDetail; + sub.main = main; return sub; }); - const inviter = await UserEntity.findOneOrFail({ where: { id: dto.inviterId } }); - const rule = await RuleMainEntity.findOneOrFail({ where: { id: dto.inviterId } }); - const invitations = await Promise.all(dto.invitedId.map(async invitedId => { - const invited = await UserEntity.findOneOrFail({ where: { id: invitedId } }); - + // invitation 저장 + const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); + const invitations = await Promise.all(dto.invitedId.map(async invited => { const invitation = new RuleInvitationEntity(); - invitation.inviter = inviter; - invitation.rule = rule; - invitation.invited = invited; + + const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); + invitation.rule = main; + invitation.invited = invitedEntity; + invitation.inviter = inviterEntity; + return invitation; })); diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index d4647b3..3bb17cc 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,4 +1,4 @@ -import { Injectable, HttpException } from '@nestjs/common'; +import {Injectable, HttpException, BadRequestException} from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleConverter } from './rule.converter'; import { RuleMainEntity } from './domain/rule.main.entity'; @@ -9,7 +9,7 @@ import { DetailRuleDto } from './dto/detail-rule.dto'; import { DetailMemberDto } from './dto/detail-member.dto'; import { DetailCommentDto } from './dto/detail-comment.dto'; import { MetaToBackDto } from './dto/meta-to-back.dto'; - +import { UserEntity} from "../user/user.entity"; @Injectable() export class RuleService { @@ -18,16 +18,38 @@ export class RuleService { ) {} // [1] 여행 규칙 생성 - async createRule(createRuleDto: CreateRuleDto): Promise { - const { main, rules, invitations } = await this.ruleConverter.toEntity(createRuleDto); + async createRule(dto: CreateRuleDto, userId: number): Promise { + // main 저장 + const main = new RuleMainEntity(); + main.mainTitle = dto.mainTitle; + await main.save(); + + //rule 저장 + const subs = dto.rulePairs.map(async pair => { + const sub = new RuleSubEntity(); + sub.ruleTitle = pair.ruleTitle; + sub.ruleDetail = pair.ruleDetail; + sub.main = main; + + await sub.save(); + return sub; + }); - const savedMain = await RuleMainEntity.save(main); + // invitation 저장 + const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); + const invitations = await Promise.all(dto.invitedId.map(async invited => { + const invitation = new RuleInvitationEntity(); - const savedRules = await RuleSubEntity.save(rules); + const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); + invitation.rule = main; + invitation.invited = invitedEntity; + invitation.inviter = inviterEntity; - const savedInvitations = await RuleInvitationEntity.save(invitations); + await invitation.save(); + return invitation; + })); - return savedMain.id; + return main.id; } // [2] 여행 규칙 조회 From 67167b484a82cefdbc9dce36983bfb2ff9b81748 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 03:45:36 +0900 Subject: [PATCH 126/316] =?UTF-8?q?fix=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=97=AC=EC=A0=95=EC=97=90=20=EC=9C=84=EC=B9=98=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 4 ++++ src/response/response.status.ts | 15 +++++++++++++++ src/schedule/schedule.entity.ts | 4 +--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index cb42b70..e3bc501 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -25,12 +25,15 @@ export class MapService { journey.id, monthInfoDto, ); + const locations = await this.getLocationList(schedules); + const diaryCount = await this.getDiaryCount(schedules); return { journeyId: journey.id, title: journey.title, startDate: journey.startDate, endDate: journey.endDate, + location: locations, diaryCount: diaryCount, }; }), @@ -184,6 +187,7 @@ export class MapService { await ScheduleEntity.findMonthlySchedule(journeyId, monthInfoDto); return schedules; } + //여정에 작성한 일지 개수 가지고 오기 async getDiaryCount(schedules) { let diaryCount = 0; diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 24deeee..e0eb6e5 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -1,5 +1,20 @@ export const BaseResponse = { /* 200 OK : 요청 성공 */ + DELETE_JOURNEY_SUCCESS: { + success: true, + code: 200, + message: '여정을 삭제했습니다.', + }, + DELETE_SCHEDULE_SUCCESS: { + success: true, + code: 200, + message: '일정을 삭제했습니다.', + }, + DELETE_DIARY_SUCCESS: { + success: true, + code: 200, + message: '일지를 삭제했습니다.', + }, DELETE_DETAIL_SCHEDULE_SUCCESS: { success: true, code: 200, diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index eb1f889..cbee0cd 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -33,9 +33,7 @@ export class ScheduleEntity extends BaseEntity { @ManyToOne(() => LocationEntity, (location) => location.schedules) location: LocationEntity; - @ManyToOne(() => JourneyEntity, (journey) => journey.schedules, { - onDelete: 'CASCADE', - }) + @ManyToOne(() => JourneyEntity, (journey) => journey.schedules) journey: JourneyEntity; @OneToMany( From 807d0318de81050bc7ade686e89df297b0634b46 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 03:54:20 +0900 Subject: [PATCH 127/316] =?UTF-8?q?fix=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=97=AC=EC=A0=95=EC=97=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index e3bc501..f95d908 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -26,14 +26,20 @@ export class MapService { monthInfoDto, ); const locations = await this.getLocationList(schedules); - + const images = await this.getDiaryImageList(schedules); + const mapInfo = schedules.map((schedule, index) => { + return { + location: locations[index], + diaryImage: images[index], + }; + }); const diaryCount = await this.getDiaryCount(schedules); return { journeyId: journey.id, title: journey.title, startDate: journey.startDate, endDate: journey.endDate, - location: locations, + map: mapInfo, diaryCount: diaryCount, }; }), From 91e28ac52dafae33046854c04b25b7bf2ddc2fa2 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 8 Feb 2024 05:03:19 +0900 Subject: [PATCH 128/316] =?UTF-8?q?fix=20:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.controller.ts | 6 +++--- src/comment/comment.converter.ts | 23 --------------------- src/comment/comment.module.ts | 3 +-- src/comment/comment.service.ts | 29 ++++++++++++++++++--------- src/comment/dto/create-comment.dto.ts | 6 +----- 5 files changed, 25 insertions(+), 42 deletions(-) delete mode 100644 src/comment/comment.converter.ts diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index c2e4907..49fccce 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -15,16 +15,16 @@ export class CommentController { // 여행 규칙 코멘트 생성 @Post('/:ruleId') @UseGuards(UserGuard) - async createComment(@Body() createCommentDto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { - const result = await this.commentService.createComment(createCommentDto, ruleId, req.user.id); + async createComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { + const result = await this.commentService.createComment(dto, ruleId, req.user.id); + console.log('controller 진입') if(!result){ return new ResponseDto( ResponseCode.COMMENT_CREATION_FAIL, false, "여행 규칙 코멘트 생성 실패", null); - } else{ return new ResponseDto( diff --git a/src/comment/comment.converter.ts b/src/comment/comment.converter.ts deleted file mode 100644 index 15353ca..0000000 --- a/src/comment/comment.converter.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { CommentEntity } from './domain/comment.entity'; -import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; -import { UserEntity } from 'src/user/user.entity'; -import { CreateCommentDto } from './dto/create-comment.dto'; - -@Injectable() -export class CommentConverter { - - async toEntity(dto: CreateCommentDto, ruleId:number, userId: number): Promise { - const comment = new CommentEntity(); - - comment.content = dto.content; - console.log(comment.content); - const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId } }); - comment.rule = rule; - console.log(comment.rule); - const user = await UserEntity.findOneOrFail({ where: { id: userId } }); - comment.user = user; - - return comment; - } -} \ No newline at end of file diff --git a/src/comment/comment.module.ts b/src/comment/comment.module.ts index f078682..1520b96 100644 --- a/src/comment/comment.module.ts +++ b/src/comment/comment.module.ts @@ -1,10 +1,9 @@ import { Module } from '@nestjs/common'; import { CommentService } from './comment.service'; import { CommentController } from './comment.controller'; -import { CommentConverter } from './comment.converter'; @Module({ controllers: [CommentController], - providers: [CommentService, CommentConverter], + providers: [CommentService], }) export class CommentModule {} diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 74fc2da..4f4eb75 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -1,19 +1,30 @@ -import { BadRequestException, HttpException, Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { CreateCommentDto } from './dto/create-comment.dto'; -import { CommentConverter } from './comment.converter'; import { CommentEntity } from './domain/comment.entity'; +import {RuleMainEntity} from "../rule/domain/rule.main.entity"; +import {UserEntity} from "../user/user.entity"; @Injectable() export class CommentService { - constructor( - private commentConverter: CommentConverter - ) {} - async createComment(createCommentDto: CreateCommentDto, ruleId: number, userId: number): Promise { - const comment = await this.commentConverter.toEntity(createCommentDto, ruleId, userId); + async createComment(dto: CreateCommentDto, ruleId: number, userId: number): Promise { - const savedComment = await CommentEntity.save(comment); + const comment = new CommentEntity(); - return savedComment.id; + const user = await UserEntity.findOneOrFail({ where: { id: userId } }); + const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId } }); + + if(!user || !rule){ + throw new Error('Data not found'); + } + else{ + console.log("user name: "+ user.name); + comment.user = user; + console.log("rule id: "+ rule.id); + comment.rule = rule; + comment.content = dto.content; + await comment.save(); + } + return comment.id; } } diff --git a/src/comment/dto/create-comment.dto.ts b/src/comment/dto/create-comment.dto.ts index 182ca0f..8fd8ba2 100644 --- a/src/comment/dto/create-comment.dto.ts +++ b/src/comment/dto/create-comment.dto.ts @@ -1,10 +1,6 @@ -import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; export class CreateCommentDto { - @IsNotEmpty() - @IsNumber() - userId: number; - @IsNotEmpty() @IsString() content: string; From e00b39be38df2a7a264f1800a6ccd8f9c5d6889f Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 07:51:46 +0900 Subject: [PATCH 129/316] =?UTF-8?q?feat=20:=20=EC=97=AC=EC=A0=95=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/dtos/create-journey.dto.ts | 2 +- src/journey/journey.service.ts | 33 +++++++++++++++++++++++++- src/journey/model/journey.entity.ts | 13 ++++++++++ src/response/response.status.ts | 6 +++++ 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/journey/dtos/create-journey.dto.ts b/src/journey/dtos/create-journey.dto.ts index 73edc3f..5cd6b20 100644 --- a/src/journey/dtos/create-journey.dto.ts +++ b/src/journey/dtos/create-journey.dto.ts @@ -1,5 +1,5 @@ // create-journey.dto.ts -import { IsString, IsDateString, IsNumber } from 'class-validator'; +import { IsString, IsDateString } from 'class-validator'; export class CreateJourneyDto { @IsString() diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index cb2c3cc..0bfecc3 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,5 +1,9 @@ // journey.service.ts -import { Injectable, NotFoundException } from '@nestjs/common'; +import { + ConflictException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { JourneyEntity } from './model/journey.entity'; import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; @@ -12,6 +16,19 @@ import { DiaryEntity } from 'src/diary/models/diary.entity'; export class JourneyService { //여정 생성하기 - 일정, 일지 함께 생성 async createJourney(user, createJourneyDto: CreateJourneyDto) { + // const firstJourneyDate = await this.formatDateString( + // createJourneyDto.startDate, + // ); + // const lastJourneyDate = await this.formatDateString( + // createJourneyDto.endDate, + // ); + // console.log(lastJourneyDate); + const existJourney = await JourneyEntity.findExistJourneyByDate( + createJourneyDto, + ); + if (existJourney) { + return errResponse(BaseResponse.JOURNEY_DUPLICATION); + } //여정 제목, 날짜 저장하기 const journey = await JourneyEntity.createJourney(user, createJourneyDto); @@ -35,6 +52,7 @@ export class JourneyService { async deleteJourney(journeyId: number) { const journey = await JourneyEntity.findExistJourney(journeyId); + if (!journey) { throw new NotFoundException(BaseResponse.JOURNEY_NOT_FOUND); } @@ -66,4 +84,17 @@ export class JourneyService { } async deleteDiaryRelations(diary) {} + + // async formatDateString(dateString) { + // // 주어진 문자열을 파싱하여 Date 객체로 변환 + // const dateObject = new Date(dateString); + + // // Date 객체에서 년, 월, 일을 추출 + // const year = dateObject.getFullYear(); + // const month = String(dateObject.getMonth() + 1).padStart(2, '0'); + // const day = String(dateObject.getDate()).padStart(2, '0'); + + // // YYYY-MM-DD 형식의 문자열로 변환하여 반환 + // return new Date(`${year}-${month}-${day}`); + // } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index fac54ef..0521738 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -17,6 +17,8 @@ import { CreateJourneyDto } from '../dtos/create-journey.dto'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { MonthInfoDto } from 'src/map/month-info.dto'; +import { ConflictException } from '@nestjs/common'; +import { BaseResponse } from 'src/response/response.status'; @Entity() export class JourneyEntity extends BaseEntity { @@ -77,6 +79,17 @@ export class JourneyEntity extends BaseEntity { return journey; } + static async findExistJourneyByDate(createJourneyDto) { + const journey: JourneyEntity = await JourneyEntity.findOne({ + where: { + startDate: createJourneyDto.startDate, + endDate: createJourneyDto.endDate, + }, + }); + console.log(journey.startDate); + return journey; + } + //사용자의 월별 여정 조회 static async findMonthlyJourney(userId, dates: MonthInfoDto) { const firstDate = new Date(`${dates.year}-${dates.month}-01`); diff --git a/src/response/response.status.ts b/src/response/response.status.ts index e0eb6e5..8f39ee0 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -118,4 +118,10 @@ export const BaseResponse = { code: 404, message: '일지가 없습니다.', }, + /* 409 CONFLICT : Resource 의 현재 상태와 충돌. 보통 중복된 데이터 존재 */ + JOURNEY_DUPLICATION: { + success: false, + code: 409, + message: '이미 여정이 있습니다.', + }, }; From fae684e5f772503f87e5ebdc1616cfd3c2061b4a Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 09:12:27 +0900 Subject: [PATCH 130/316] =?UTF-8?q?feat=20:=20=EC=97=AC=EC=A0=95=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=ED=95=98=EB=A9=B4=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=EB=90=98=EC=96=B4=20=EC=82=AD=EC=A0=9C=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/detail-schedule/detail-schedule.entity.ts | 4 ++- src/diary/models/diary.entity.ts | 4 +-- src/diary/models/diary.image.entity.ts | 9 +++++- src/journey/journey.controller.ts | 29 +++++++++++++++++- src/journey/journey.service.ts | 30 ++++++++----------- src/journey/model/journey.entity.ts | 8 +++++ src/schedule/schedule.entity.ts | 4 ++- 7 files changed, 65 insertions(+), 23 deletions(-) diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index a665d54..87ec397 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -25,7 +25,9 @@ export class DetailScheduleEntity extends BaseEntity { @Column({ default: false }) isDone: boolean; - @ManyToOne(() => ScheduleEntity, (schedule) => schedule.detailSchedules) + @ManyToOne(() => ScheduleEntity, (schedule) => schedule.detailSchedules, { + onDelete: 'CASCADE', + }) schedule: ScheduleEntity; @CreateDateColumn() diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 818d328..73eddfa 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -91,9 +91,9 @@ export class DiaryEntity extends BaseEntity { return await diary.save(); } - //세부 일정 삭제하기 + // 일지 삭제하기 static async deleteDiary(diary) { - return await DiaryEntity.softRemove(diary); + return await DiaryEntity.remove(diary); } //일지 조회하기 diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index 0053e9d..cdc29d8 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -20,7 +20,9 @@ export class DiaryImageEntity extends BaseEntity { imageUrl: string; @JoinColumn() - @OneToOne(() => DiaryEntity, (diary) => diary.image) + @OneToOne(() => DiaryEntity, (diary) => diary.image, { + onDelete: 'CASCADE', + }) diary: DiaryEntity; @CreateDateColumn() @@ -48,4 +50,9 @@ export class DiaryImageEntity extends BaseEntity { }); return imgUrl; } + + //세부 일정 삭제하기 + static async deleteDiaryImg(diaryImg) { + return await DiaryImageEntity.remove(diaryImg); + } } diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 603269a..8170926 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -1,4 +1,12 @@ -import { Controller, Body, Req, UseGuards, Post } from '@nestjs/common'; +import { + Controller, + Body, + Req, + UseGuards, + Post, + Delete, + Param, +} from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; import { UserGuard } from 'src/user/user.guard'; @@ -28,4 +36,23 @@ export class JourneyController { ); return result; } + + /*여정 저장하기*/ + @ApiOperation({ + summary: '여정 삭제하기', + description: + '여정을 삭제할때 일정, 세부일정, 일지, 사진을 모두 삭제합니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Delete('delete/:journeyId') + async deleteJourney( + @Param('journeyId') journeyId: number, + @Req() req: Request, + ) { + const result = await this.journeyService.deleteJourney(journeyId); + return result; + } } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 0bfecc3..e09ba7e 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -11,6 +11,8 @@ import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; +import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; +import { UserEntity } from 'src/user/user.entity'; @Injectable() export class JourneyService { @@ -52,10 +54,6 @@ export class JourneyService { async deleteJourney(journeyId: number) { const journey = await JourneyEntity.findExistJourney(journeyId); - - if (!journey) { - throw new NotFoundException(BaseResponse.JOURNEY_NOT_FOUND); - } const schedules = await ScheduleEntity.findExistScheduleByJourneyId( journey.id, ); @@ -78,23 +76,21 @@ export class JourneyService { //일지 지우기 const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule.id); - await this.deleteDiaryRelations(diary); + if (diary) { + await DiaryEntity.deleteDiary(diary); + } await ScheduleEntity.deleteSchedule(deleteSchedule); } - async deleteDiaryRelations(diary) {} - - // async formatDateString(dateString) { - // // 주어진 문자열을 파싱하여 Date 객체로 변환 - // const dateObject = new Date(dateString); + async deleteDiaryRelations(diary) { + if (!diary) { + return; // 일지가 없으면 삭제할 필요 없음 + } + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); - // // Date 객체에서 년, 월, 일을 추출 - // const year = dateObject.getFullYear(); - // const month = String(dateObject.getMonth() + 1).padStart(2, '0'); - // const day = String(dateObject.getDate()).padStart(2, '0'); + await DiaryImageEntity.deleteDiaryImg(diaryImg); - // // YYYY-MM-DD 형식의 문자열로 변환하여 반환 - // return new Date(`${year}-${month}-${day}`); - // } + await DiaryEntity.deleteDiary(diary); + } } diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 0521738..8dcdac59 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -79,6 +79,14 @@ export class JourneyEntity extends BaseEntity { return journey; } + static async findExistJourneyByUserId(userId) { + const journeys: JourneyEntity[] = await JourneyEntity.find({ + where: { user: { id: userId } }, + }); + + return journeys; + } + static async findExistJourneyByDate(createJourneyDto) { const journey: JourneyEntity = await JourneyEntity.findOne({ where: { diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index cbee0cd..eb1f889 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -33,7 +33,9 @@ export class ScheduleEntity extends BaseEntity { @ManyToOne(() => LocationEntity, (location) => location.schedules) location: LocationEntity; - @ManyToOne(() => JourneyEntity, (journey) => journey.schedules) + @ManyToOne(() => JourneyEntity, (journey) => journey.schedules, { + onDelete: 'CASCADE', + }) journey: JourneyEntity; @OneToMany( From e69fdd3bab3402042475b8ee9a2613a47c6c4634 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 10:09:48 +0900 Subject: [PATCH 131/316] =?UTF-8?q?refactor=20:=20=EB=AF=B8=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20import=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 1 - src/diary/diary.service.ts | 3 --- src/diary/dtos/get-diary-img-url.dto.ts | 2 +- src/diary/dtos/post-diary.dto.ts | 2 +- src/diary/models/diary.entity.ts | 1 - src/journey/journey.service.ts | 11 ++--------- src/journey/model/journey.entity.ts | 3 --- src/location/dtos/location-info.dto.ts | 0 src/schedule/schedule.controller.ts | 3 --- 9 files changed, 4 insertions(+), 22 deletions(-) delete mode 100644 src/location/dtos/location-info.dto.ts diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 86e2c14..8f20c16 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -2,7 +2,6 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; import { Controller, Put, Post, Get, Body, Param } from '@nestjs/common'; import { DiaryService } from './diary.service'; import { PostDiaryDto } from './dtos/post-diary.dto'; -import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; @Controller('diary') export class DiaryController { diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 453f60a..2cd5be1 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -4,10 +4,7 @@ import { BaseResponse } from 'src/response/response.status'; import { DiaryEntity } from './models/diary.entity'; import { DiaryImageEntity } from './models/diary.image.entity'; import { PostDiaryDto } from './dtos/post-diary.dto'; -import { GetDiaryImgUrlDto } from './dtos/get-diary-img-url.dto'; import { S3UtilService } from 'src/utils/S3.service'; -import { JourneyEntity } from 'src/journey/model/journey.entity'; -import { ScheduleEntity } from 'src/schedule/schedule.entity'; @Injectable() export class DiaryService { diff --git a/src/diary/dtos/get-diary-img-url.dto.ts b/src/diary/dtos/get-diary-img-url.dto.ts index ebfdd5f..68ee7df 100644 --- a/src/diary/dtos/get-diary-img-url.dto.ts +++ b/src/diary/dtos/get-diary-img-url.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty, PickType } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString } from 'class-validator'; export class GetDiaryImgUrlDto { diff --git a/src/diary/dtos/post-diary.dto.ts b/src/diary/dtos/post-diary.dto.ts index c6ba54f..f6920af 100644 --- a/src/diary/dtos/post-diary.dto.ts +++ b/src/diary/dtos/post-diary.dto.ts @@ -1,4 +1,4 @@ -import { IsString, IsOptional, IsEnum } from 'class-validator'; +import { IsString, IsEnum } from 'class-validator'; export class PostDiaryDto { @IsString() diff --git a/src/diary/models/diary.entity.ts b/src/diary/models/diary.entity.ts index 73eddfa..af47e2b 100644 --- a/src/diary/models/diary.entity.ts +++ b/src/diary/models/diary.entity.ts @@ -14,7 +14,6 @@ import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from '../../user/user.entity'; -import { JourneyEntity } from 'src/journey/model/journey.entity'; import { DiaryImageEntity } from './diary.image.entity'; import { PostDiaryDto } from '../dtos/post-diary.dto'; diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index e09ba7e..53c7a95 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -12,19 +12,11 @@ import { CreateJourneyDto } from './dtos/create-journey.dto'; import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; -import { UserEntity } from 'src/user/user.entity'; @Injectable() export class JourneyService { //여정 생성하기 - 일정, 일지 함께 생성 async createJourney(user, createJourneyDto: CreateJourneyDto) { - // const firstJourneyDate = await this.formatDateString( - // createJourneyDto.startDate, - // ); - // const lastJourneyDate = await this.formatDateString( - // createJourneyDto.endDate, - // ); - // console.log(lastJourneyDate); const existJourney = await JourneyEntity.findExistJourneyByDate( createJourneyDto, ); @@ -74,7 +66,7 @@ export class JourneyService { await DetailScheduleEntity.deleteDetailSchedule(detailSchedule); } - //일지 지우기 + //일정 지우기 const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule.id); if (diary) { await DiaryEntity.deleteDiary(diary); @@ -83,6 +75,7 @@ export class JourneyService { await ScheduleEntity.deleteSchedule(deleteSchedule); } + //일지 지우기 async deleteDiaryRelations(diary) { if (!diary) { return; // 일지가 없으면 삭제할 필요 없음 diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 8dcdac59..0d62270 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -13,12 +13,9 @@ import { Between, } from 'typeorm'; -import { CreateJourneyDto } from '../dtos/create-journey.dto'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { MonthInfoDto } from 'src/map/month-info.dto'; -import { ConflictException } from '@nestjs/common'; -import { BaseResponse } from 'src/response/response.status'; @Entity() export class JourneyEntity extends BaseEntity { diff --git a/src/location/dtos/location-info.dto.ts b/src/location/dtos/location-info.dto.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index a5a4f61..e0e150a 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -8,11 +8,8 @@ import { UseGuards, } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; -import { Request } from 'express'; -import { UserGuard } from 'src/user/user.guard'; import { ScheduleService } from './schedule.service'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; -import { FindMonthlyScheduleDto } from './dtos/find-monthly-schedule.dto'; @Controller('schedule') export class ScheduleController { From 21bd74b13ac7eac0775dbd1d75609136b1f1fd56 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 16:22:05 +0900 Subject: [PATCH 132/316] =?UTF-8?q?chore=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20Base64=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.service.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 2cd5be1..6d358d0 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -27,11 +27,10 @@ export class DiaryService { /*일지 사진 S3에 업로드 후 url 받기*/ async getDiaryImgUrl(diary, fileName: string) { - const imageKey = this.s3UtilService.generateRandomImageKey(fileName); - // await this.s3UtilService.putObjectFromBase64(imageKey, fileName); - const imageUrl = await this.s3UtilService.getImageUrl(imageKey); - console.log('url', imageUrl); - await DiaryImageEntity.createDiaryImg(diary, imageUrl); - return imageUrl; + const imageKey = `diary/${this.s3UtilService.generateRandomImageKey( + fileName, + )}`; + await this.s3UtilService.putObjectFromBase64(imageKey, fileName); + await DiaryImageEntity.createDiaryImg(diary, imageKey); } } From e5bbf2797c0dc83436a51a0ad67eb8176fdf2899 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 18:06:25 +0900 Subject: [PATCH 133/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20-=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.controller.ts | 18 +++++++----------- src/diary/diary.service.ts | 24 ++++++++++++++++++++++++ src/diary/dtos/diary-info.dto.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 src/diary/dtos/diary-info.dto.ts diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 8f20c16..3d33a56 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -33,7 +33,7 @@ export class DiaryController { description: '성공 ', }) @Put('update/:diaryId') - async updateJourney( + async updateDiary( @Param('diaryId') diaryId: number, @Body() body: PostDiaryDto, ) { @@ -41,21 +41,17 @@ export class DiaryController { return result; } - /*일지 사진 url 발급 */ - /*일지 사진 업로드*/ + /*일지 불러오기-캘린더*/ @ApiOperation({ - summary: '일지 사진 업로드 위한 presigned Url 발급', - description: '일지를 작성하고 저장한 상태', + summary: '일지 불러오기 - 캘린더 ', + description: '일지가 존재하는 경우 id,date,내용', }) @ApiOkResponse({ description: '성공 ', }) - @Post('image-url/:diaryId') - async getDiaryImageUrl( - @Param('diaryId') diaryId: number, - @Body('fileName') fileName: string, - ) { - const result = await this.diaryService.getDiaryImgUrl(diaryId, fileName); + @Get('get/:scheduleId') + async getDiary(@Param('scheduleId') scheduleId: number) { + const result = await this.diaryService.getDiary(scheduleId); return result; } } diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index 6d358d0..b1f4107 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -4,7 +4,9 @@ import { BaseResponse } from 'src/response/response.status'; import { DiaryEntity } from './models/diary.entity'; import { DiaryImageEntity } from './models/diary.image.entity'; import { PostDiaryDto } from './dtos/post-diary.dto'; +import { DiaryInfoDto } from './dtos/diary-info.dto'; import { S3UtilService } from 'src/utils/S3.service'; +import { ScheduleEntity } from 'src/schedule/schedule.entity'; @Injectable() export class DiaryService { @@ -33,4 +35,26 @@ export class DiaryService { await this.s3UtilService.putObjectFromBase64(imageKey, fileName); await DiaryImageEntity.createDiaryImg(diary, imageKey); } + + /*캘린더에서 일지 불러오기*/ + async getDiary(scheduleId: number) { + const schedule = await ScheduleEntity.findExistSchedule(scheduleId); + const diary = await DiaryEntity.findExistDiaryByScheduleId(schedule); + if (!diary) { + return errResponse(BaseResponse.DIARY_NOT_FOUND); + } + const imageKey = await DiaryImageEntity.findExistImgUrl(diary); + const diaryImg = await this.s3UtilService.getImageUrl(imageKey.imageUrl); + const diaryInfo: DiaryInfoDto = new DiaryInfoDto(); + diaryInfo.id = diary.id; + diaryInfo.date = schedule.date; + diaryInfo.title = diary.title; + diaryInfo.place = diary.place; + diaryInfo.weather = diary.weather; + diaryInfo.mood = diary.mood; + diaryInfo.content = diary.content; + diaryInfo.imageId = diary.id; + diaryInfo.imageUrl = diaryImg; + return response(BaseResponse.GET_DIARY_SUCCESS, diaryInfo); + } } diff --git a/src/diary/dtos/diary-info.dto.ts b/src/diary/dtos/diary-info.dto.ts new file mode 100644 index 0000000..863c11f --- /dev/null +++ b/src/diary/dtos/diary-info.dto.ts @@ -0,0 +1,30 @@ +import { IsString, IsEnum, IsDate, IsInt } from 'class-validator'; + +export class DiaryInfoDto { + @IsInt() + id: number; + + @IsDate() + date: Date; + + @IsString() + title: string; + + @IsString() + place: string; + + @IsEnum(['CLOUDY', 'RAINY', 'SNOWY', 'PARTLY_CLOUDY', 'SUNNY']) + weather: 'CLOUDY' | 'RAINY' | 'SNOWY' | 'PARTLY_CLOUDY' | 'SUNNY'; + + @IsEnum(['ANGRY', 'SAD', 'SMILE', 'HAPPY', 'SHOCKED']) + mood: 'ANGRY' | 'SAD' | 'SMILE' | 'HAPPY' | 'SHOCKED'; + + @IsString() + content: string; + + @IsInt() + imageId: number; + + @IsString() + imageUrl: string; +} From 060ee568d4924e39b73b99953f7afb23ef17339d Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 8 Feb 2024 18:30:09 +0900 Subject: [PATCH 134/316] =?UTF-8?q?fix=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0,?= =?UTF-8?q?=20=EC=96=B8=ED=8C=94=EB=A1=9C=EC=9A=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 105 ++++++++++++-------------------- src/follow/follow.service.ts | 42 ++++++++++--- 2 files changed, 71 insertions(+), 76 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index d9b9548..bc540d5 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/common'; +import {Controller, Post, Req, UseGuards, Param, Delete, Get, Patch} from '@nestjs/common'; import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; @@ -7,6 +7,7 @@ import { UserService } from 'src/user/user.service'; import { UserSearchDto} from "../user/user.search.dto"; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; +import {UserFollowingEntity} from "../user/user.following.entity"; @Controller('mate/search') export class FollowController { @@ -16,80 +17,52 @@ export class FollowController { ) {} // [1] 팔로우 - @Post('/:followingId') + @Patch('/:followingId') @UseGuards(UserGuard) async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { - // 현재 사용자 ID - // const userId = req.user.id; - // const userId = 1; - // 이미 팔로우하는 사용자인지 검증 - const user : UserEntity = await this.userService.findUserById(req.user.id); - const isAlreadyFollow = this.userService.checkIfFollowing(user, followingId); + try { + const user: UserEntity = await this.userService.findUserById(req.user.id); + console.log('현재 로그인한 사용자 : ', user.id); + const following: UserEntity = await this.userService.findUserById(followingId); + console.log('팔로우 대상 사용자 : ', following.id); - if (isAlreadyFollow) { - return new ResponseDto( - ResponseCode.IS_ALREADY_FOLLOW, - false, - "이미 팔로우하는 사용자입니다", - null - ); - } - - try { - await this.followService.createFollow(req.user.id, followingId); - return new ResponseDto( - ResponseCode.FOLLOW_CREATED, - true, - "팔로우 성공", - null - ); - } catch (error) { - return new ResponseDto( - ResponseCode.FOLLOW_CREATION_FAIL, - false, - "팔로우 실패", - null - ); - } - } - - // [2] 언팔로우 - @Delete('/:followId') - @UseGuards(UserGuard) - async deleteFollow(@Req() req: Request, @Param('followId') followId: number): Promise> { - // 현재 사용자 ID - // const userId = req.user.id; - // const userId = 1; + // 팔로우 관계 확인 + const checkIfFollowing = this.userService.checkIfFollowing(user, followingId); + console.log('checkIfFollowing : ', checkIfFollowing); - try { - const result = await this.followService.deleteFollow(followId); - if (result) { + // 이미 팔로우 한 사이, 언팔로우 + if (!checkIfFollowing) { + console.log('언팔로우 service 호출'); + await this.followService.deleteFollow(req.user.id, followingId); + return new ResponseDto( + ResponseCode.UNFOLLOW_SUCCESS, + true, + "언팔로우 성공", + null + ); + } else { + // 팔로우 + console.log('팔로우 service 호출'); + await this.followService.createFollow(req.user.id, followingId); + return new ResponseDto( + ResponseCode.FOLLOW_CREATED, + true, + "팔로우 성공", + null + ); + } + } catch(error) { return new ResponseDto( - ResponseCode.UNFOLLOW_SUCCESS, - true, - "언팔로우 성공", - null - ); - } else { - return new ResponseDto( - ResponseCode.UNFOLLOW_FAIL, - false, - "언팔로우 실패", - null + ResponseCode.UNFOLLOW_FAIL, + false, + "처리 실패", + null ); - } - } catch (error) { - return new ResponseDto( - ResponseCode.UNFOLLOW_FAIL, - false, - "언팔로우 실패", - null - ); } - } + } - // [3] 팔로우 리스트 조회 + // [2] 팔로우 리스트 조회 @Get('/followList') @UseGuards(UserGuard) async getFollowList(@Req() req: Request): Promise> { diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 4c8ca28..54da07a 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -3,28 +3,50 @@ import { UserFollowingEntity } from 'src/user/user.following.entity'; import { FollowListConverter } from './follow.list.converter'; import { FollowerListConverter } from './follower.list.converter'; import { FollowDto } from './dto/follow.dto'; +import { UserEntity } from "../user/user.entity"; +import { UserService } from "../user/user.service"; @Injectable() export class FollowService { constructor( - private followListConverter: FollowListConverter, - private followerListConverter: FollowerListConverter, + private readonly followListConverter: FollowListConverter, + private readonly followerListConverter: FollowerListConverter, + private readonly userService: UserService, ) {} // [1] 팔로우 async createFollow(userId : number, followingId : number): Promise { - const follow = UserFollowingEntity.create({ - user: { id : userId }, - followUser: { id : followingId }, - }); - return follow.save(); + + const userEntity : UserEntity = await this.userService.findUserById(userId); + const followingUserEntity : UserEntity = await this.userService.findUserById(followingId); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUserEntity); + + try{ + const userFollowingEntity = new UserFollowingEntity(); + userFollowingEntity.user = userEntity; + userFollowingEntity.followUser = followingUserEntity; + + return userFollowingEntity.save(); + + }catch(error){ + console.error('Error on following: ', error); + throw new Error('Failed to following'); + } } // [2] 언팔로우 - async deleteFollow(followId:number): Promise { - const followEntity : UserFollowingEntity = await UserFollowingEntity.findOne({ where: { id : followId }}); + async deleteFollow(userId: number, followingId:number): Promise { + const userEntity = await this.userService.findUserById(userId); + const followingEntity = await this.userService.findUserById(followingId); + const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: { user : userEntity, followUser : followingEntity }}); - return followEntity.softRemove(); + try{ + return followEntity.softRemove(); + }catch(error){ + console.error('Error on unfollowing: ', error); + throw new Error('Failed to unfollowing'); + } } // [3] 팔로우 리스트 조회 From bb9a67af178aded057bda1d5648f76c26b402d77 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 8 Feb 2024 20:59:36 +0900 Subject: [PATCH 135/316] =?UTF-8?q?fix=20:=20=EC=96=B8=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95=20(?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 86 +++++++++++++++------------------ src/follow/follow.service.ts | 7 +-- src/user/user.service.ts | 16 ++++++ 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index bc540d5..aa346df 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -16,60 +16,51 @@ export class FollowController { private readonly userService: UserService, ) {} - // [1] 팔로우 - @Patch('/:followingId') - @UseGuards(UserGuard) - async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { - - try { - const user: UserEntity = await this.userService.findUserById(req.user.id); - console.log('현재 로그인한 사용자 : ', user.id); - const following: UserEntity = await this.userService.findUserById(followingId); - console.log('팔로우 대상 사용자 : ', following.id); + // [1] 팔로우 + @Patch('/follow/:followingId') + @UseGuards(UserGuard) + async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { - // 팔로우 관계 확인 - const checkIfFollowing = this.userService.checkIfFollowing(user, followingId); - console.log('checkIfFollowing : ', checkIfFollowing); + try { + // 팔로우 관계 확인 + const isAlreadyFollowing = await this.userService.isAlreadyFollowing(req.user.id, followingId); + console.log('Is already following? : ', isAlreadyFollowing); - // 이미 팔로우 한 사이, 언팔로우 - if (!checkIfFollowing) { - console.log('언팔로우 service 호출'); - await this.followService.deleteFollow(req.user.id, followingId); - return new ResponseDto( - ResponseCode.UNFOLLOW_SUCCESS, - true, - "언팔로우 성공", - null - ); - } else { - // 팔로우 - console.log('팔로우 service 호출'); - await this.followService.createFollow(req.user.id, followingId); - return new ResponseDto( - ResponseCode.FOLLOW_CREATED, - true, - "팔로우 성공", - null - ); - } - } catch(error) { - return new ResponseDto( - ResponseCode.UNFOLLOW_FAIL, - false, - "처리 실패", - null - ); - } - } + // -1) 이미 팔로우 한 사이, 팔로우 취소 + if (isAlreadyFollowing) { + console.log('언팔로우 service 호출'); + await this.followService.deleteFollow(req.user.id, followingId); + return new ResponseDto( + ResponseCode.UNFOLLOW_SUCCESS, + true, + "언팔로우 성공", + null + ); + } else { + // -2) 팔로우 + console.log('팔로우 service 호출'); + await this.followService.createFollow(req.user.id, followingId); + return new ResponseDto( + ResponseCode.FOLLOW_CREATED, + true, + "팔로우 성공", + null + ); + } + } catch(error) { + return new ResponseDto( + ResponseCode.UNFOLLOW_FAIL, + false, + "처리 실패", + null + ); + } + } // [2] 팔로우 리스트 조회 @Get('/followList') @UseGuards(UserGuard) async getFollowList(@Req() req: Request): Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 1; - try { const followList = await this.followService.getFollowList(req.user.id); return new ResponseDto( @@ -141,5 +132,4 @@ export class FollowController { ); } } - } \ No newline at end of file diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 54da07a..6967404 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -37,9 +37,10 @@ export class FollowService { // [2] 언팔로우 async deleteFollow(userId: number, followingId:number): Promise { - const userEntity = await this.userService.findUserById(userId); - const followingEntity = await this.userService.findUserById(followingId); - const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: { user : userEntity, followUser : followingEntity }}); + console.log('언팔로우 서비스 호출'); + const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: + { user : {id : userId}, followUser : {id : followingId}} + }); try{ return followEntity.softRemove(); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 54ae550..d6eb1ba 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -59,6 +59,7 @@ export class UserService { // user가 targetUser를 팔로우하고 있는지 확인 const followingArray = user.following || []; + console.log('사용자가 팔로우하는 유저', user.following); const isFollowing = followingArray.some( (following) => following.followUser.id === targetUserId, @@ -291,4 +292,19 @@ export class UserService { return results; } + + async isAlreadyFollowing(userId:number, followingId: number) { + const userEntity = await this.findUserById(userId); + const followingEntity = await this.findUserById(followingId); + console.log('현재 로그인한 사용자 : ', userEntity.id); + console.log('팔로우 대상 사용자 : ', followingEntity.id); + + const isFollowing = await UserFollowingEntity.findOne({ + where: { + user : {id : userId}, followUser : {id : followingId}, + }}); + + // 팔로우 관계 : true 반환 + return !!isFollowing; + } } From b04f1f1312220c8c20507241294480024a070f47 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Thu, 8 Feb 2024 21:12:07 +0900 Subject: [PATCH 136/316] =?UTF-8?q?fix=20:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.service.ts | 7 ++++--- src/diary/models/diary.image.entity.ts | 10 ++++++++++ src/journey/model/journey.entity.ts | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index b1f4107..d7afc63 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -16,7 +16,7 @@ export class DiaryService { async createDiary(scheduleId, diaryInfo: PostDiaryDto) { const diary = await DiaryEntity.createDiary(scheduleId, diaryInfo); const diaryImg = await this.getDiaryImgUrl(diary, diaryInfo.fileName); - console.log(diary); + await DiaryImageEntity.createDiaryImg(diary, diaryImg); return response(BaseResponse.DIARY_CREATED); } @@ -24,16 +24,17 @@ export class DiaryService { async updateDiary(diaryId, diaryInfo: PostDiaryDto) { const diary = await DiaryEntity.updateDiary(diaryId, diaryInfo); const diaryImg = await this.getDiaryImgUrl(diary, diaryInfo.fileName); + await DiaryImageEntity.updateDiaryImg(diary, diaryImg); return response(BaseResponse.DIARY_CREATED); } - /*일지 사진 S3에 업로드 후 url 받기*/ + /*일지 사진 S3에 업로드 후 url 받기- 생성 */ async getDiaryImgUrl(diary, fileName: string) { const imageKey = `diary/${this.s3UtilService.generateRandomImageKey( fileName, )}`; await this.s3UtilService.putObjectFromBase64(imageKey, fileName); - await DiaryImageEntity.createDiaryImg(diary, imageKey); + return imageKey; } /*캘린더에서 일지 불러오기*/ diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index cdc29d8..b39a49c 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -43,6 +43,16 @@ export class DiaryImageEntity extends BaseEntity { await diaryImg.save(); } + //사진 URL 수정 + static async updateDiaryImg(diary: DiaryEntity, ImgUrl: string) { + const diaryImg = await DiaryImageEntity.findOne({ + where: { diary: { id: diary.id } }, + }); + diaryImg.imageUrl = ImgUrl; + diaryImg.diary = diary; + await diaryImg.save(); + } + //사진 URL 불러오기 static async findExistImgUrl(diary: DiaryEntity) { const imgUrl = await DiaryImageEntity.findOne({ diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 0d62270..324ce54 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -91,7 +91,7 @@ export class JourneyEntity extends BaseEntity { endDate: createJourneyDto.endDate, }, }); - console.log(journey.startDate); + return journey; } From 5803ab62bd0a6c2808784fbf0dc1be447dd49b57 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 8 Feb 2024 22:59:32 +0900 Subject: [PATCH 137/316] =?UTF-8?q?fix=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0,?= =?UTF-8?q?=20=ED=8C=94=EB=A1=9C=EC=9B=8C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/dto/follow.dto.ts | 8 ++-- src/follow/follow.controller.ts | 12 +----- src/follow/follow.list.converter.ts | 41 -------------------- src/follow/follow.module.ts | 4 +- src/follow/follow.service.ts | 56 +++++++++++++++++++++++---- src/follow/follower.list.converter.ts | 40 ------------------- src/user/user.service.ts | 2 +- 7 files changed, 56 insertions(+), 107 deletions(-) delete mode 100644 src/follow/follow.list.converter.ts delete mode 100644 src/follow/follower.list.converter.ts diff --git a/src/follow/dto/follow.dto.ts b/src/follow/dto/follow.dto.ts index c1af144..9c154b6 100644 --- a/src/follow/dto/follow.dto.ts +++ b/src/follow/dto/follow.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; +import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; export class FollowDto { @IsNotEmpty() @@ -21,7 +21,7 @@ export class FollowDto { @IsString() introduction: string; - @IsOptional() - @IsNumber() - followId: number; + @IsNotEmpty() + @IsBoolean() + isFollowing: boolean; } \ No newline at end of file diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index aa346df..db05d27 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -79,14 +79,10 @@ export class FollowController { } } - // [4] 팔로워 리스트 조회 + // [3] 팔로워 리스트 조회 @Get('/followerList') @UseGuards(UserGuard) async getFollowerList(@Req() req: Request): Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 1; - try { const followerList = await this.followService.getFollowerList(req.user.id); return new ResponseDto( @@ -105,16 +101,12 @@ export class FollowController { } } - // [5] 메이트 검색 + // [4] 메이트 검색 @Get('/:searchTerm') @UseGuards(UserGuard) async getSearchResult( @Req() req: Request, @Param('searchTerm') searchTerm: string): Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 1; - try { const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(req.user.id, searchTerm) return new ResponseDto( diff --git a/src/follow/follow.list.converter.ts b/src/follow/follow.list.converter.ts deleted file mode 100644 index e0290ef..0000000 --- a/src/follow/follow.list.converter.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { UserEntity } from 'src/user/user.entity'; -import { UserFollowingEntity } from 'src/user/user.following.entity'; -import { FollowDto } from './dto/follow.dto'; -import { UserService } from 'src/user/user.service'; - - -@Injectable() -export class FollowListConverter { - - constructor(private readonly userService: UserService) {} - - async toDto(userId: number): Promise { - - // 현재 로그인한 사용자 - const user : UserEntity = await this.userService.findUserById(userId); - console.log('현재 로그인한 사용자 : ',user); - - // 로그인한 사용자의 팔로우 리스트 - const followings : UserFollowingEntity[] = await this.userService.getFollowingList(userId); - console.log('followings : ',followings); - - // 팔로우 사용자들 정보 리스트 - const informs = await Promise.all(followings.map(async (following) => { - const followDto : FollowDto = new FollowDto(); - const mateEntity : UserEntity = following.user; - - followDto.nickName = mateEntity.nickname; - followDto.mateId = mateEntity.id; - followDto.email = mateEntity.email; - followDto.introduction = mateEntity.introduction; - followDto.followId = following.id; // 팔로우 테이블 ID - const image = await this.userService.getProfileImage(mateEntity.id); - followDto.image = image.imageKey; - - return followDto; - })) - - return informs; - } -} \ No newline at end of file diff --git a/src/follow/follow.module.ts b/src/follow/follow.module.ts index 6a9f3a9..41a3dff 100644 --- a/src/follow/follow.module.ts +++ b/src/follow/follow.module.ts @@ -1,12 +1,10 @@ import { Module } from '@nestjs/common'; import { FollowService } from './follow.service'; import { FollowController } from './follow.controller'; -import { FollowListConverter } from './follow.list.converter'; -import { FollowerListConverter } from './follower.list.converter'; import { UserService } from 'src/user/user.service'; @Module({ controllers: [FollowController], - providers: [FollowService, FollowListConverter, FollowerListConverter, UserService], + providers: [FollowService, UserService], }) export class FollowModule {} diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 6967404..208a80e 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -1,7 +1,5 @@ import { Injectable, HttpException } from '@nestjs/common'; import { UserFollowingEntity } from 'src/user/user.following.entity'; -import { FollowListConverter } from './follow.list.converter'; -import { FollowerListConverter } from './follower.list.converter'; import { FollowDto } from './dto/follow.dto'; import { UserEntity } from "../user/user.entity"; import { UserService } from "../user/user.service"; @@ -9,8 +7,6 @@ import { UserService } from "../user/user.service"; @Injectable() export class FollowService { constructor( - private readonly followListConverter: FollowListConverter, - private readonly followerListConverter: FollowerListConverter, private readonly userService: UserService, ) {} @@ -52,15 +48,59 @@ export class FollowService { // [3] 팔로우 리스트 조회 async getFollowList(userId: number): Promise { - const followDto: FollowDto[] = await this.followListConverter.toDto(userId); + // 현재 로그인한 사용자 + const user : UserEntity = await this.userService.findUserById(userId); + console.log('현재 로그인한 사용자 : ',user.id); - return followDto; + // 로그인한 사용자 = 팔로우하는 user + const follows : UserFollowingEntity[] = await this.userService.getFollowingList(userId); + + // 팔로우 사용자들 정보 리스트 + const informs = await Promise.all(follows.map(async (follow) => { + const followDto : FollowDto = new FollowDto(); + const mateEntity : UserEntity = follow.followUser; + console.log('팔로우 사용자의 ID : ', mateEntity.id); + + followDto.nickName = mateEntity.nickname; + followDto.mateId = mateEntity.id; + followDto.email = mateEntity.email; + followDto.introduction = mateEntity.introduction; + followDto.isFollowing = !!follow.id; + const image = await this.userService.getProfileImage(mateEntity.id); + followDto.image = image.imageKey; + + return followDto; + })) + + return informs; } // [4] 팔로워 리스트 조회 async getFollowerList(userId: number): Promise { - const followerDto: FollowDto[] = await this.followerListConverter.toDto(userId); + // 현재 로그인한 사용자 + const user : UserEntity = await this.userService.findUserById(userId); + console.log('현재 로그인한 사용자 : ',user.id); + + // 로그인한 사용자 = 팔로워 + const follows : UserFollowingEntity[] = await this.userService.getFollowerList(userId); + + // 팔로워 사용자들 정보 리스트 + const informs = await Promise.all(follows.map(async (follow) => { + const followDto : FollowDto = new FollowDto(); + const mateEntity : UserEntity = follow.user; + console.log('팔로워 사용자 ID : ', mateEntity.id); + + followDto.nickName = mateEntity.nickname; + followDto.mateId = mateEntity.id; + followDto.email = mateEntity.email; + followDto.introduction = mateEntity.introduction; + followDto.isFollowing = !!follow.id; + const image = await this.userService.getProfileImage(mateEntity.id); + followDto.image = image.imageKey; + + return followDto; + })) - return followerDto; + return informs; } } diff --git a/src/follow/follower.list.converter.ts b/src/follow/follower.list.converter.ts deleted file mode 100644 index 2509fcc..0000000 --- a/src/follow/follower.list.converter.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { UserEntity } from 'src/user/user.entity'; -import { UserFollowingEntity } from 'src/user/user.following.entity'; -import { FollowDto } from './dto/follow.dto'; -import { UserService } from 'src/user/user.service'; - -@Injectable() -export class FollowerListConverter { - - constructor(private readonly userService: UserService) {} - - async toDto(userId: number): Promise { - - // 현재 로그인한 사용자 - const user : UserEntity = await this.userService.findUserById(userId); - console.log('현재 로그인한 사용자 : ', user); - - // 로그인한 사용자의 팔로워 리스트 - const followers : UserFollowingEntity[] = await this.userService.getFollowerList(userId); - console.log('followers : ', followers); - - // 팔로워 사용자들 정보 리스트 - const informs = await Promise.all(followers.map(async (follower) => { - const followerDto : FollowDto = new FollowDto(); - const mateEntity : UserEntity = follower.user; - - followerDto.nickName = mateEntity.nickname; - followerDto.mateId = mateEntity.id; - followerDto.email = mateEntity.email; - followerDto.introduction = mateEntity.introduction; - followerDto.followId = follower.id; // 팔로우 테이블 ID (null인 경우 팔로우 버튼 표시) - const image = await this.userService.getProfileImage(mateEntity.id); - followerDto.image = image.imageKey; - - return followerDto; - })) - - return informs; - } -} \ No newline at end of file diff --git a/src/user/user.service.ts b/src/user/user.service.ts index d6eb1ba..b495a77 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -152,7 +152,7 @@ export class UserService { try { const userFollowingEntity = await UserFollowingEntity.find({ where: { user: { id: userId } }, - relations: ['user'], + relations: ['followUser'], }); return userFollowingEntity; } catch (error) { From 090a43742c303f361808b3bfdfe55e4e848a5b25 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 00:30:15 +0900 Subject: [PATCH 138/316] =?UTF-8?q?fix=20:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20s3=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.module.ts | 3 ++- src/follow/follow.service.ts | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/follow/follow.module.ts b/src/follow/follow.module.ts index 41a3dff..dc4ba2d 100644 --- a/src/follow/follow.module.ts +++ b/src/follow/follow.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { FollowService } from './follow.service'; import { FollowController } from './follow.controller'; import { UserService } from 'src/user/user.service'; +import { S3UtilService } from '../utils/S3.service'; @Module({ controllers: [FollowController], - providers: [FollowService, UserService], + providers: [FollowService, UserService, S3UtilService], }) export class FollowModule {} diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 208a80e..04d3c9d 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -3,11 +3,13 @@ import { UserFollowingEntity } from 'src/user/user.following.entity'; import { FollowDto } from './dto/follow.dto'; import { UserEntity } from "../user/user.entity"; import { UserService } from "../user/user.service"; +import { S3UtilService } from "../utils/S3.service"; @Injectable() export class FollowService { constructor( private readonly userService: UserService, + private readonly s3Service: S3UtilService, ) {} // [1] 팔로우 @@ -66,8 +68,14 @@ export class FollowService { followDto.email = mateEntity.email; followDto.introduction = mateEntity.introduction; followDto.isFollowing = !!follow.id; + + // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); - followDto.image = image.imageKey; + if(image == null) followDto.image = null; + else{ + const userImageKey = image.imageKey; + followDto.image = await this.s3Service.getImageUrl(userImageKey); + } return followDto; })) @@ -95,8 +103,14 @@ export class FollowService { followDto.email = mateEntity.email; followDto.introduction = mateEntity.introduction; followDto.isFollowing = !!follow.id; + + // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); - followDto.image = image.imageKey; + if(image == null) followDto.image = null; + else{ + const userImageKey = image.imageKey; + followDto.image = await this.s3Service.getImageUrl(userImageKey); + } return followDto; })) From f67a22188d067bd71344d11452d88409a32ae96e Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 04:14:41 +0900 Subject: [PATCH 139/316] refactor : ruleInvitation -> ruleMember --- src/member/member.list.converter.ts | 6 ++--- src/member/member.service.ts | 10 +++---- src/rule/domain/rule.main.entity.ts | 6 ++--- ...tation.entity.ts => rule.member.entity.ts} | 26 ++++++++----------- src/rule/rule.converter.ts | 12 ++++----- src/rule/rule.service.ts | 16 ++++++------ src/user/user.entity.ts | 10 +++---- 7 files changed, 41 insertions(+), 45 deletions(-) rename src/rule/domain/{rule.invitation.entity.ts => rule.member.entity.ts} (67%) diff --git a/src/member/member.list.converter.ts b/src/member/member.list.converter.ts index f17986a..c55ee1a 100644 --- a/src/member/member.list.converter.ts +++ b/src/member/member.list.converter.ts @@ -3,7 +3,7 @@ import { UserEntity } from 'src/user/user.entity'; import { MemberDto } from './dto/member.dto'; import { UserService } from 'src/user/user.service'; import { RuleService } from 'src/rule/rule.service'; -import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; +import { RuleMemberEntity } from 'src/rule/domain/rule.member.entity'; @Injectable() export class MemberListConverter { @@ -18,13 +18,13 @@ export class MemberListConverter { async toDto(userId: number,ruleId: number): Promise { // 여행 초대 객체 생성 - const invitations : RuleInvitationEntity[] = await this.ruleService.getInvitationList(ruleId); + const invitations : RuleMemberEntity[] = await this.ruleService.getInvitationList(ruleId); // 여행 참여 멤버 정보 리스트 생성 // -1. 팀원 멤버 (invited) 정보 추가 const informs = await Promise.all(invitations.map(async (invitaiton) => { const memberDto : MemberDto = new MemberDto(); - const invited : UserEntity = invitaiton.invited; + const invited : UserEntity = invitaiton.member; memberDto.memberId = invited.id; memberDto.name = invited.name; diff --git a/src/member/member.service.ts b/src/member/member.service.ts index c91dc89..ff6f0d3 100644 --- a/src/member/member.service.ts +++ b/src/member/member.service.ts @@ -1,7 +1,7 @@ import { Injectable, HttpException } from '@nestjs/common'; import { MemberListConverter } from './member.list.converter'; import { MemberDto } from './dto/member.dto'; -import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; +import { RuleMemberEntity } from 'src/rule/domain/rule.member.entity'; import { RuleService } from 'src/rule/rule.service'; @Injectable() @@ -19,10 +19,10 @@ export class MemberService { } // [2] 여행 규칙 멤버 초대 - async createInvitation(ruleId: number, userId: number, invitedId: number): Promise { + async createInvitation(ruleId: number, userId: number, invitedId: number): Promise { // invitation 객체 생성 - const invitation = RuleInvitationEntity.create({ + const invitation = RuleMemberEntity.create({ rule: { id : ruleId }, inviter: { id : userId}, invited: { id : invitedId}, @@ -31,8 +31,8 @@ export class MemberService { } // [3] 여행 규칙 멤버 삭제 - async deleteMember(ruleId: number, memberId: number): Promise { - const invitation : RuleInvitationEntity = await RuleInvitationEntity.findInvitationByRuleId(ruleId, memberId); + async deleteMember(ruleId: number, memberId: number): Promise { + const invitation : RuleMemberEntity = await RuleMemberEntity.findInvitationByRuleId(ruleId, memberId); return invitation.softRemove(); } diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index a402d3b..89fcc87 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -8,7 +8,7 @@ import { BaseEntity, DeleteDateColumn, } from 'typeorm'; import { RuleSubEntity } from './rule.sub.entity'; -import { RuleInvitationEntity } from './rule.invitation.entity' +import { RuleMemberEntity } from './rule.member.entity' import { CommentEntity } from 'src/comment/domain/comment.entity'; @Entity() @@ -31,8 +31,8 @@ export class RuleMainEntity extends BaseEntity { @OneToMany(() => RuleSubEntity, ruleSub => ruleSub.main) rules: RuleSubEntity[]; - @OneToMany(() => RuleInvitationEntity, ruleInvitation => ruleInvitation.rule) - invitations: RuleInvitationEntity[]; + @OneToMany(() => RuleMemberEntity, ruleInvitation => ruleInvitation.rule) + members: RuleMemberEntity[]; @OneToMany(() => CommentEntity, comment => comment.rule) comments: CommentEntity[]; diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.member.entity.ts similarity index 67% rename from src/rule/domain/rule.invitation.entity.ts rename to src/rule/domain/rule.member.entity.ts index ac8af1a..2e409b9 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.member.entity.ts @@ -11,21 +11,17 @@ import { UserEntity } from 'src/user/user.entity'; import { RuleMainEntity } from './rule.main.entity' @Entity() -export class RuleInvitationEntity extends BaseEntity { +export class RuleMemberEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.invitations) + @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.members) @JoinColumn({name: 'rule_id'}) rule: RuleMainEntity; - @ManyToOne(() => UserEntity, user => user.invitationsSent) @JoinColumn({name: 'inviter_id'}) - @JoinColumn({name: 'inviter_id'}) - inviter: UserEntity; - - @ManyToOne(() => UserEntity, user => user.invitationsReceived) - @JoinColumn({name: 'invited_id'}) - invited: UserEntity; + @ManyToOne(() => UserEntity, user => user.ruleParticipate) + @JoinColumn({name: 'member_id'}) + member: UserEntity; @CreateDateColumn() created: Date; @@ -46,11 +42,11 @@ export class RuleInvitationEntity extends BaseEntity { return { memberId, name }; } - static async findInvitationByRuleId(ruleId: number, memberId: number): Promise { + static async findInvitationByRuleId(ruleId: number, memberId: number): Promise { try { - const invitation = await RuleInvitationEntity.findOne({ + const invitation = await RuleMemberEntity.findOne({ where: [{ rule : { id : ruleId }}, - { invited : { id : memberId }}] + { member : { id : memberId }}] }); console.log('invitation 조회 결과 : ', invitation); return invitation; @@ -60,11 +56,11 @@ export class RuleInvitationEntity extends BaseEntity { } } - static async findInvitationByRuleAndUser(ruleId: number, userId: number) : Promise { + static async findInvitationByRuleAndUser(ruleId: number, userId: number) : Promise { try { - const invitation = await RuleInvitationEntity.findOne({ + const invitation = await RuleMemberEntity.findOne({ where: [{rule: {id : ruleId}}, - {invited: {id : userId}}] + {member: {id : userId}}] }); console.log('invitation 조회 결과 : ', invitation); return invitation; diff --git a/src/rule/rule.converter.ts b/src/rule/rule.converter.ts index 85918b1..842aec7 100644 --- a/src/rule/rule.converter.ts +++ b/src/rule/rule.converter.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; -import { RuleInvitationEntity } from './domain/rule.invitation.entity'; +import { RuleMemberEntity } from './domain/rule.member.entity'; import { UserEntity } from 'src/user/user.entity'; import { CreateRuleDto } from './dto/create-rule.dto'; import { DetailPageDto } from './dto/detail-page.dto'; @@ -19,7 +19,7 @@ import { MetaToFrontDto } from './dto/meta-to-front.dto'; @Injectable() export class RuleConverter { - async toEntity(dto: CreateRuleDto, userId:number): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleInvitationEntity[] }> { + async toEntity(dto: CreateRuleDto, userId:number): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleMemberEntity[] }> { // main 저장 const main = new RuleMainEntity(); main.mainTitle = dto.mainTitle; @@ -36,11 +36,11 @@ export class RuleConverter { // invitation 저장 const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); const invitations = await Promise.all(dto.invitedId.map(async invited => { - const invitation = new RuleInvitationEntity(); + const invitation = new RuleMemberEntity(); const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); invitation.rule = main; - invitation.invited = invitedEntity; + invitation.member = invitedEntity; invitation.inviter = inviterEntity; return invitation; @@ -76,12 +76,12 @@ export class RuleConverter { }); // [2] detailMemberDto - const ruleInvitation : RuleInvitationEntity[] = ruleMain.invitations; + const ruleInvitation : RuleMemberEntity[] = ruleMain.members; detailMemberDto.memberPairs = await Promise.all(ruleInvitation.map(async (invitation) => { const memberPairDto: MemberPairDto = new MemberPairDto(); const id = invitation.id; - const { memberId, name} = await RuleInvitationEntity.findNameById(id); + const { memberId, name} = await RuleMemberEntity.findNameById(id); memberPairDto.memberId = memberId; memberPairDto.name = name; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 3bb17cc..9fddbbc 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -3,7 +3,7 @@ import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleConverter } from './rule.converter'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; -import { RuleInvitationEntity } from './domain/rule.invitation.entity'; +import { RuleMemberEntity } from './domain/rule.member.entity'; import { DetailPageDto } from './dto/detail-page.dto'; import { DetailRuleDto } from './dto/detail-rule.dto'; import { DetailMemberDto } from './dto/detail-member.dto'; @@ -38,11 +38,11 @@ export class RuleService { // invitation 저장 const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); const invitations = await Promise.all(dto.invitedId.map(async invited => { - const invitation = new RuleInvitationEntity(); + const invitation = new RuleMemberEntity(); const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); invitation.rule = main; - invitation.invited = invitedEntity; + invitation.member = invitedEntity; invitation.inviter = inviterEntity; await invitation.save(); @@ -67,8 +67,8 @@ export class RuleService { // [3] 여행 규칙 나가기 // -1) 초대 받은 팀원 -> 초대 삭제 - async deleteInvitation(ruleId: number, userId: number): Promise { - const invitation : RuleInvitationEntity = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, userId); + async deleteInvitation(ruleId: number, userId: number): Promise { + const invitation : RuleMemberEntity = await RuleMemberEntity.findInvitationByRuleAndUser(ruleId, userId); return invitation.softRemove(); } @@ -76,7 +76,7 @@ export class RuleService { // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { - const invitationEntity = await RuleInvitationEntity.find({ + const invitationEntity = await RuleMemberEntity.find({ where: { id : ruleId }, relations: ['invited'], }); @@ -88,10 +88,10 @@ export class RuleService { // [member] 멤버인지 확인 async checkMember(rule: RuleMainEntity, targetUserId: number): Promise { - const invitedArray = rule.invitations || []; + const invitedArray = rule.members || []; const isMember = invitedArray.some( - (invitations) => invitations.invited.id === targetUserId, + (invitations) => invitations.member.id === targetUserId, ); console.log('테스트 결과 : ', isMember); diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 534a508..a8f173f 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -13,7 +13,7 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { UserFollowingEntity } from './user.following.entity'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; -import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; +import { RuleMemberEntity } from '../rule/domain/rule.member.entity'; import { CommentEntity } from 'src/comment/domain/comment.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; import { NotFoundException } from '@nestjs/common'; @@ -72,11 +72,11 @@ export class UserEntity extends BaseEntity { ) likes: SignatureLikeEntity[]; - @OneToMany(() => RuleInvitationEntity, (invitation) => invitation.inviter) - invitationsSent: RuleInvitationEntity[]; + @OneToMany(() => RuleMemberEntity, (invitation) => invitation.inviter) + invitationsSent: RuleMemberEntity[]; - @OneToMany(() => RuleInvitationEntity, (invitation) => invitation.invited) - invitationsReceived: RuleInvitationEntity[]; + @OneToMany(() => RuleMemberEntity, (invitation) => invitation.member) + ruleParticipate: RuleMemberEntity[]; @OneToMany(() => CommentEntity, (comment) => comment.user) comments: CommentEntity[]; From 559d7c47e4331a49635ab5bbed15ddd47713b08a Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 04:32:44 +0900 Subject: [PATCH 140/316] =?UTF-8?q?refactor=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=83=9D=EC=84=B1=20(member=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/create-rule.dto.ts | 2 +- src/rule/rule.service.ts | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index 50b6c81..ad4e211 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -15,5 +15,5 @@ export class CreateRuleDto { @IsNotEmpty() @IsArray() @IsNumber({}, { each: true }) - invitedId: number[]; + membersId: number[]; } diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9fddbbc..e35f1c8 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -35,18 +35,17 @@ export class RuleService { return sub; }); - // invitation 저장 + // member 저장 const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); - const invitations = await Promise.all(dto.invitedId.map(async invited => { - const invitation = new RuleMemberEntity(); + let members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { + const ruleMemberEntity = new RuleMemberEntity(); - const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); - invitation.rule = main; - invitation.member = invitedEntity; - invitation.inviter = inviterEntity; + const userEntity = await UserEntity.findOneOrFail({ where: { id: memberId } }); + ruleMemberEntity.rule = main; + ruleMemberEntity.member = userEntity; - await invitation.save(); - return invitation; + await ruleMemberEntity.save(); + return ruleMemberEntity; })); return main.id; From f03dc93e82f724e001acb1ad52c60d0db9cee917 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 05:54:10 +0900 Subject: [PATCH 141/316] refactor : ruleMember -> ruleInvitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [적용한 기능] 여행 규칙 생성 / 여행 규칙 멤버 초대 / 여행 멤버 삭제 / 여행 멤버 조회 / 여행 멤버 초대 --- src/member/dto/member.dto.ts | 4 +- src/member/member.controller.ts | 25 ++----- src/member/member.list.converter.ts | 54 --------------- src/member/member.module.ts | 4 +- src/member/member.service.ts | 65 ++++++++++++++----- ...er.entity.ts => rule.invitation.entity.ts} | 12 ++-- src/rule/domain/rule.main.entity.ts | 6 +- src/rule/rule.converter.ts | 10 +-- src/rule/rule.service.ts | 22 +++---- src/user/user.entity.ts | 9 +-- 10 files changed, 84 insertions(+), 127 deletions(-) delete mode 100644 src/member/member.list.converter.ts rename src/rule/domain/{rule.member.entity.ts => rule.invitation.entity.ts} (83%) diff --git a/src/member/dto/member.dto.ts b/src/member/dto/member.dto.ts index 597f71e..4f2b247 100644 --- a/src/member/dto/member.dto.ts +++ b/src/member/dto/member.dto.ts @@ -3,7 +3,7 @@ import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; export class MemberDto { @IsNotEmpty() @IsNumber() - memberId: number; + id: number; @IsNotEmpty() @IsString() @@ -11,7 +11,7 @@ export class MemberDto { @IsNotEmpty() @IsString() - nickName: string; + email: string; @IsOptional() @IsString() diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index 1a36063..c015afc 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -20,14 +20,9 @@ export class MemberController { // [1] 여행 규칙 멤버 리스트 조회 @Get('/:ruleId') - @UseGuards(UserGuard) - async getMember(@Req() req: Request, @Param('ruleId') ruleId : number) : Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 2; - + async getMember(@Param('ruleId') ruleId : number) : Promise> { try { - const memberList = await this.memberService.getMemberList(req.user.id, ruleId); + const memberList = await this.memberService.getMemberList(ruleId); return new ResponseDto( ResponseCode.GET_MEMBER_LIST_SUCCESS, true, @@ -46,12 +41,7 @@ export class MemberController { // [2] 여행 규칙 멤버 초대 @Post('/:ruleId/:invitedId') - @UseGuards(UserGuard) - async createInvitation(@Req() req: Request, @Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 2; - + async createInvitation(@Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { // 이미 초대된 멤버인지 확인 const ruleEntity = await RuleMainEntity.findRuleById(ruleId); console.log('--이미 참여하는 사용자인지 검증 시작--') @@ -69,7 +59,7 @@ export class MemberController { // 멤버 초대 try { - await this.memberService.createInvitation(ruleId, req.user.id, invitedId); + await this.memberService.createInvitation(ruleId, invitedId); return new ResponseDto( ResponseCode.INVITATION_CREATED, true, @@ -88,12 +78,7 @@ export class MemberController { // [3] 여행 규칙 멤버 삭제 @Delete('/:ruleId/:memberId') - @UseGuards(UserGuard) - async deleteMember(@Req() req: Request, @Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 2; - + async deleteMember(@Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { try { await this.memberService.deleteMember(ruleId, memberId); return new ResponseDto( diff --git a/src/member/member.list.converter.ts b/src/member/member.list.converter.ts deleted file mode 100644 index c55ee1a..0000000 --- a/src/member/member.list.converter.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { UserEntity } from 'src/user/user.entity'; -import { MemberDto } from './dto/member.dto'; -import { UserService } from 'src/user/user.service'; -import { RuleService } from 'src/rule/rule.service'; -import { RuleMemberEntity } from 'src/rule/domain/rule.member.entity'; - -@Injectable() -export class MemberListConverter { - - constructor( - private readonly userService: UserService, - private readonly ruleService: RuleService, - - ) {} - - // [1] 여행 규칙 멤버 정보 리스트 DTO 생성 - async toDto(userId: number,ruleId: number): Promise { - - // 여행 초대 객체 생성 - const invitations : RuleMemberEntity[] = await this.ruleService.getInvitationList(ruleId); - - // 여행 참여 멤버 정보 리스트 생성 - // -1. 팀원 멤버 (invited) 정보 추가 - const informs = await Promise.all(invitations.map(async (invitaiton) => { - const memberDto : MemberDto = new MemberDto(); - const invited : UserEntity = invitaiton.member; - - memberDto.memberId = invited.id; - memberDto.name = invited.name; - memberDto.nickName = invited.nickname; - memberDto.introduction = invited.introduction; - const image = await this.userService.getProfileImage(invited.id); - memberDto.image = image.imageKey; - - return memberDto; - })) - - // -2. 팀장 멤버 (inviter) 정보 추가 - const inviterDto : MemberDto = new MemberDto(); - const inviter : UserEntity = await this.userService.findUserById(userId); - - inviterDto.memberId = inviter.id; - inviterDto.name = inviter.name; - inviterDto.nickName = inviter.nickname; - inviterDto.introduction = inviter.introduction; - const image = await this.userService.getProfileImage(inviter.id); - inviterDto.image = image.imageKey; - - informs.push(inviterDto); - - return informs; - } -} \ No newline at end of file diff --git a/src/member/member.module.ts b/src/member/member.module.ts index b653da1..908742a 100644 --- a/src/member/member.module.ts +++ b/src/member/member.module.ts @@ -2,12 +2,12 @@ import { Module } from '@nestjs/common'; import { MemberService } from './member.service'; import { RuleService } from 'src/rule/rule.service'; import { MemberController } from './member.controller'; -import { MemberListConverter } from './member.list.converter'; import { UserService } from 'src/user/user.service'; import { RuleConverter } from 'src/rule/rule.converter'; +import { S3UtilService } from '../utils/S3.service'; @Module({ controllers: [MemberController], - providers: [MemberService, MemberListConverter, RuleService, UserService, RuleConverter], + providers: [MemberService, RuleService, UserService, RuleConverter, S3UtilService], }) export class MemberModule {} diff --git a/src/member/member.service.ts b/src/member/member.service.ts index ff6f0d3..018eedd 100644 --- a/src/member/member.service.ts +++ b/src/member/member.service.ts @@ -1,38 +1,67 @@ import { Injectable, HttpException } from '@nestjs/common'; -import { MemberListConverter } from './member.list.converter'; import { MemberDto } from './dto/member.dto'; -import { RuleMemberEntity } from 'src/rule/domain/rule.member.entity'; -import { RuleService } from 'src/rule/rule.service'; +import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; +import { UserEntity } from "../user/user.entity"; +import { UserService} from "../user/user.service"; +import {RuleMainEntity} from "../rule/domain/rule.main.entity"; +import { S3UtilService } from "../utils/S3.service"; @Injectable() export class MemberService { constructor( - private memberListConverter: MemberListConverter, - private ruleService: RuleService, + private userService: UserService, + private readonly s3Service: S3UtilService, ) {} // [1] 여행 규칙 멤버 리스트 조회 - async getMemberList(userId: number, ruleId: number): Promise { - const memberDto: MemberDto[] = await this.memberListConverter.toDto(userId, ruleId); + async getMemberList(ruleId: number): Promise { + const ruleEntity : RuleMainEntity = await RuleMainEntity.findOneOrFail({ + where: {id : ruleId}, + relations: ['invitations'] + }); - return memberDto; + const members : MemberDto[] = await Promise.all(ruleEntity.invitations.map(async (invitation) : Promise => { + const memberEntity : UserEntity = invitation.member; + const memberDto : MemberDto = new MemberDto(); + + memberDto.id = memberEntity.id; + memberDto.name = memberEntity.name; + memberDto.email = memberEntity.email; + memberDto.introduction = memberEntity.introduction; + + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage(memberEntity.id); + memberDto.image = image.imageKey; + if(image == null) memberDto.image = null; + else { + const userImageKey = image.imageKey; + memberDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return memberDto; + })) + + return members; } // [2] 여행 규칙 멤버 초대 - async createInvitation(ruleId: number, userId: number, invitedId: number): Promise { + async createInvitation(ruleId: number, invitedId: number): Promise { - // invitation 객체 생성 - const invitation = RuleMemberEntity.create({ - rule: { id : ruleId }, - inviter: { id : userId}, - invited: { id : invitedId}, - }) - return invitation.save(); + const invitationEntity : RuleInvitationEntity = new RuleInvitationEntity(); + const ruleEntity : RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); + const invitedUserEntity : UserEntity = await this.userService.findUserById(invitedId); + + invitationEntity.rule = ruleEntity; + invitationEntity.member = invitedUserEntity; + + return invitationEntity.save(); } // [3] 여행 규칙 멤버 삭제 - async deleteMember(ruleId: number, memberId: number): Promise { - const invitation : RuleMemberEntity = await RuleMemberEntity.findInvitationByRuleId(ruleId, memberId); + async deleteMember(ruleId: number, memberId: number): Promise { + const ruleEntity : RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); + const invitation = await RuleInvitationEntity.findOne({ + where: {rule :{id : ruleId}, member : {id : memberId}} + }) return invitation.softRemove(); } diff --git a/src/rule/domain/rule.member.entity.ts b/src/rule/domain/rule.invitation.entity.ts similarity index 83% rename from src/rule/domain/rule.member.entity.ts rename to src/rule/domain/rule.invitation.entity.ts index 2e409b9..309b498 100644 --- a/src/rule/domain/rule.member.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -11,11 +11,11 @@ import { UserEntity } from 'src/user/user.entity'; import { RuleMainEntity } from './rule.main.entity' @Entity() -export class RuleMemberEntity extends BaseEntity { +export class RuleInvitationEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.members) + @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.invitations) @JoinColumn({name: 'rule_id'}) rule: RuleMainEntity; @@ -42,9 +42,9 @@ export class RuleMemberEntity extends BaseEntity { return { memberId, name }; } - static async findInvitationByRuleId(ruleId: number, memberId: number): Promise { + static async findInvitationByRuleId(ruleId: number, memberId: number): Promise { try { - const invitation = await RuleMemberEntity.findOne({ + const invitation = await RuleInvitationEntity.findOne({ where: [{ rule : { id : ruleId }}, { member : { id : memberId }}] }); @@ -56,9 +56,9 @@ export class RuleMemberEntity extends BaseEntity { } } - static async findInvitationByRuleAndUser(ruleId: number, userId: number) : Promise { + static async findInvitationByRuleAndUser(ruleId: number, userId: number) : Promise { try { - const invitation = await RuleMemberEntity.findOne({ + const invitation = await RuleInvitationEntity.findOne({ where: [{rule: {id : ruleId}}, {member: {id : userId}}] }); diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index 89fcc87..a402d3b 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -8,7 +8,7 @@ import { BaseEntity, DeleteDateColumn, } from 'typeorm'; import { RuleSubEntity } from './rule.sub.entity'; -import { RuleMemberEntity } from './rule.member.entity' +import { RuleInvitationEntity } from './rule.invitation.entity' import { CommentEntity } from 'src/comment/domain/comment.entity'; @Entity() @@ -31,8 +31,8 @@ export class RuleMainEntity extends BaseEntity { @OneToMany(() => RuleSubEntity, ruleSub => ruleSub.main) rules: RuleSubEntity[]; - @OneToMany(() => RuleMemberEntity, ruleInvitation => ruleInvitation.rule) - members: RuleMemberEntity[]; + @OneToMany(() => RuleInvitationEntity, ruleInvitation => ruleInvitation.rule) + invitations: RuleInvitationEntity[]; @OneToMany(() => CommentEntity, comment => comment.rule) comments: CommentEntity[]; diff --git a/src/rule/rule.converter.ts b/src/rule/rule.converter.ts index 842aec7..cd95cc8 100644 --- a/src/rule/rule.converter.ts +++ b/src/rule/rule.converter.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; -import { RuleMemberEntity } from './domain/rule.member.entity'; +import { RuleInvitationEntity } from './domain/rule.invitation.entity'; import { UserEntity } from 'src/user/user.entity'; import { CreateRuleDto } from './dto/create-rule.dto'; import { DetailPageDto } from './dto/detail-page.dto'; @@ -19,7 +19,7 @@ import { MetaToFrontDto } from './dto/meta-to-front.dto'; @Injectable() export class RuleConverter { - async toEntity(dto: CreateRuleDto, userId:number): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleMemberEntity[] }> { + async toEntity(dto: CreateRuleDto, userId:number): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleInvitationEntity[] }> { // main 저장 const main = new RuleMainEntity(); main.mainTitle = dto.mainTitle; @@ -36,7 +36,7 @@ export class RuleConverter { // invitation 저장 const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); const invitations = await Promise.all(dto.invitedId.map(async invited => { - const invitation = new RuleMemberEntity(); + const invitation = new RuleInvitationEntity(); const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); invitation.rule = main; @@ -76,12 +76,12 @@ export class RuleConverter { }); // [2] detailMemberDto - const ruleInvitation : RuleMemberEntity[] = ruleMain.members; + const ruleInvitation : RuleInvitationEntity[] = ruleMain.members; detailMemberDto.memberPairs = await Promise.all(ruleInvitation.map(async (invitation) => { const memberPairDto: MemberPairDto = new MemberPairDto(); const id = invitation.id; - const { memberId, name} = await RuleMemberEntity.findNameById(id); + const { memberId, name} = await RuleInvitationEntity.findNameById(id); memberPairDto.memberId = memberId; memberPairDto.name = name; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index e35f1c8..51b34f7 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -3,7 +3,7 @@ import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleConverter } from './rule.converter'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; -import { RuleMemberEntity } from './domain/rule.member.entity'; +import { RuleInvitationEntity } from './domain/rule.invitation.entity'; import { DetailPageDto } from './dto/detail-page.dto'; import { DetailRuleDto } from './dto/detail-rule.dto'; import { DetailMemberDto } from './dto/detail-member.dto'; @@ -35,17 +35,17 @@ export class RuleService { return sub; }); - // member 저장 + // invitation 저장 const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); - let members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { - const ruleMemberEntity = new RuleMemberEntity(); + let members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { + const ruleInvitationEntity = new RuleInvitationEntity(); const userEntity = await UserEntity.findOneOrFail({ where: { id: memberId } }); - ruleMemberEntity.rule = main; - ruleMemberEntity.member = userEntity; + ruleInvitationEntity.rule = main; + ruleInvitationEntity.member = userEntity; - await ruleMemberEntity.save(); - return ruleMemberEntity; + await ruleInvitationEntity.save(); + return ruleInvitationEntity; })); return main.id; @@ -66,8 +66,8 @@ export class RuleService { // [3] 여행 규칙 나가기 // -1) 초대 받은 팀원 -> 초대 삭제 - async deleteInvitation(ruleId: number, userId: number): Promise { - const invitation : RuleMemberEntity = await RuleMemberEntity.findInvitationByRuleAndUser(ruleId, userId); + async deleteInvitation(ruleId: number, userId: number): Promise { + const invitation : RuleInvitationEntity = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, userId); return invitation.softRemove(); } @@ -75,7 +75,7 @@ export class RuleService { // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { - const invitationEntity = await RuleMemberEntity.find({ + const invitationEntity = await RuleInvitationEntity.find({ where: { id : ruleId }, relations: ['invited'], }); diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index a8f173f..3fc3fe0 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -13,7 +13,7 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { UserFollowingEntity } from './user.following.entity'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureLikeEntity } from '../signature/domain/signature.like.entity'; -import { RuleMemberEntity } from '../rule/domain/rule.member.entity'; +import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import { CommentEntity } from 'src/comment/domain/comment.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; import { NotFoundException } from '@nestjs/common'; @@ -72,11 +72,8 @@ export class UserEntity extends BaseEntity { ) likes: SignatureLikeEntity[]; - @OneToMany(() => RuleMemberEntity, (invitation) => invitation.inviter) - invitationsSent: RuleMemberEntity[]; - - @OneToMany(() => RuleMemberEntity, (invitation) => invitation.member) - ruleParticipate: RuleMemberEntity[]; + @OneToMany(() => RuleInvitationEntity, (invitation) => invitation.member) + ruleParticipate: RuleInvitationEntity[]; @OneToMany(() => CommentEntity, (comment) => comment.user) comments: CommentEntity[]; From 93a4ac774bf15f3b987799f4fc6437347b0b1d8e Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 16:33:44 +0900 Subject: [PATCH 142/316] =?UTF-8?q?refactor=20:=20ruleInvitationEntity=20i?= =?UTF-8?q?nviter=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [기능 구현 완료 확인] 여행 규칙 생성 여행 규칙 멤버 초대 여행 멤버 삭제 --- src/member/member.controller.ts | 105 ++++++------------- src/member/member.module.ts | 3 +- src/member/member.service.ts | 90 ++++++++++++++--- src/response/response-code.enum.ts | 2 + src/rule/domain/rule.invitation.entity.ts | 11 ++ src/rule/rule.converter.ts | 117 ---------------------- src/rule/rule.module.ts | 3 +- src/rule/rule.service.ts | 27 ++--- 8 files changed, 136 insertions(+), 222 deletions(-) delete mode 100644 src/rule/rule.converter.ts diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index c015afc..72b3b0a 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -1,100 +1,36 @@ import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/common'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -import { MemberService } from './member.service'; -import { RuleService } from 'src/rule/rule.service'; -import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; +import { MemberService } from './member.service'; import {UserSearchDto} from "../user/user.search.dto"; import { UserService} from "../user/user.service"; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; +import {RuleInvitationEntity} from "../rule/domain/rule.invitation.entity"; +import {UserEntity} from "../user/user.entity"; // @UseGuards(UserGuard) @Controller('mate/rule/member') export class MemberController { constructor( private readonly memberService: MemberService, - private readonly ruleService: RuleService, private readonly userService: UserService, ) {} - // [1] 여행 규칙 멤버 리스트 조회 - @Get('/:ruleId') - async getMember(@Param('ruleId') ruleId : number) : Promise> { - try { - const memberList = await this.memberService.getMemberList(ruleId); - return new ResponseDto( - ResponseCode.GET_MEMBER_LIST_SUCCESS, - true, - "여행 규칙 멤버 리스트 불러오기 성공", - memberList - ); - } catch (error) { - return new ResponseDto( - ResponseCode.GET_MEMBER_LIST_FAIL, - false, - "여행 규칙 멤버 리스트 불러오기 실패", - null - ); - } - } - // [2] 여행 규칙 멤버 초대 - @Post('/:ruleId/:invitedId') + @Post('/invite/:ruleId/:invitedId') async createInvitation(@Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { - // 이미 초대된 멤버인지 확인 - const ruleEntity = await RuleMainEntity.findRuleById(ruleId); - console.log('--이미 참여하는 사용자인지 검증 시작--') - const check = this.ruleService.checkMember(ruleEntity, invitedId); - if (check) { - return new ResponseDto( - ResponseCode.IS_ALREADY_MEMBER, - false, - "이미 초대된 사용자입니다", - null - ); - } - console.log('초대 가능한 사용자 입니다') - console.log('--검증 완료--') + const result = await this.memberService.createInvitation(ruleId, invitedId); - // 멤버 초대 - try { - await this.memberService.createInvitation(ruleId, invitedId); - return new ResponseDto( - ResponseCode.INVITATION_CREATED, - true, - "여행 규칙 멤버 초대 성공", - null - ); - } catch (error) { - return new ResponseDto( - ResponseCode.INVITATION_FAIL, - false, - "여행 규칙 멤버 초대 실패", - null - ); - } + return result; } // [3] 여행 규칙 멤버 삭제 - @Delete('/:ruleId/:memberId') + @Delete('/delete/:ruleId/:memberId') async deleteMember(@Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { - try { - await this.memberService.deleteMember(ruleId, memberId); - return new ResponseDto( - ResponseCode.DELETE_MEMBER_SUCCESS, - true, - "여행 규칙 멤버 삭제 성공", - null - ); - } catch (error) { - return new ResponseDto( - ResponseCode.DELETE_MEMBER_FAIL, - false, - "여행 규칙 멤버 삭제 실패", - null - ); - } + const result = await this.memberService.deleteMember(ruleId, memberId); + + return result; } // [4] 초대할 여행 규칙 멤버 검색 @@ -124,4 +60,25 @@ export class MemberController { ); } } + + // [1] 여행 규칙 멤버 리스트 조회 + @Get('/:ruleId') + async getMember(@Param('ruleId') ruleId : number) : Promise> { + try { + const memberList = await this.memberService.getMemberList(ruleId); + return new ResponseDto( + ResponseCode.GET_MEMBER_LIST_SUCCESS, + true, + "여행 규칙 멤버 리스트 불러오기 성공", + memberList + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_MEMBER_LIST_FAIL, + false, + "여행 규칙 멤버 리스트 불러오기 실패", + null + ); + } + } } \ No newline at end of file diff --git a/src/member/member.module.ts b/src/member/member.module.ts index 908742a..14bf82c 100644 --- a/src/member/member.module.ts +++ b/src/member/member.module.ts @@ -3,11 +3,10 @@ import { MemberService } from './member.service'; import { RuleService } from 'src/rule/rule.service'; import { MemberController } from './member.controller'; import { UserService } from 'src/user/user.service'; -import { RuleConverter } from 'src/rule/rule.converter'; import { S3UtilService } from '../utils/S3.service'; @Module({ controllers: [MemberController], - providers: [MemberService, RuleService, UserService, RuleConverter, S3UtilService], + providers: [MemberService, RuleService, UserService, S3UtilService], }) export class MemberModule {} diff --git a/src/member/member.service.ts b/src/member/member.service.ts index 018eedd..eda244f 100644 --- a/src/member/member.service.ts +++ b/src/member/member.service.ts @@ -1,10 +1,12 @@ -import { Injectable, HttpException } from '@nestjs/common'; +import {Injectable, NotFoundException} from '@nestjs/common'; import { MemberDto } from './dto/member.dto'; import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; import { UserEntity } from "../user/user.entity"; import { UserService} from "../user/user.service"; import {RuleMainEntity} from "../rule/domain/rule.main.entity"; import { S3UtilService } from "../utils/S3.service"; +import {ResponseDto} from "../response/response.dto"; +import {ResponseCode} from "../response/response-code.enum"; @Injectable() export class MemberService { @@ -44,25 +46,83 @@ export class MemberService { } // [2] 여행 규칙 멤버 초대 - async createInvitation(ruleId: number, invitedId: number): Promise { - - const invitationEntity : RuleInvitationEntity = new RuleInvitationEntity(); - const ruleEntity : RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); - const invitedUserEntity : UserEntity = await this.userService.findUserById(invitedId); + async createInvitation(ruleId: number, invitedId: number): Promise> { + // (1) 존재하는 회원인지 확인 + const invitedEntity = await this.userService.findUserById(invitedId); + if(!invitedEntity) { + return new ResponseDto( + ResponseCode.USER_NOT_FOUND, + false, + "존재하지 않는 회원 입니다", + null + ); + } - invitationEntity.rule = ruleEntity; - invitationEntity.member = invitedUserEntity; + // (2) 이미 초대된 멤버인지 확인 + const isAlreadyMember = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, invitedId); + console.log('isAlreadyMember', isAlreadyMember); - return invitationEntity.save(); + if (!isAlreadyMember) { + const invitationEntity : RuleInvitationEntity = new RuleInvitationEntity(); + const ruleEntity : RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); + const invitedUserEntity : UserEntity = await this.userService.findUserById(invitedId); + + invitationEntity.rule = ruleEntity; + invitationEntity.member = invitedUserEntity; + + await invitationEntity.save(); + return new ResponseDto( + ResponseCode.INVITATION_CREATED, + true, + "여행 규칙 멤버 초대 성공", + null + ); + } else { + return new ResponseDto( + ResponseCode.IS_ALREADY_MEMBER, + false, + "이미 초대된 멤버 입니다", + null + ); + } } // [3] 여행 규칙 멤버 삭제 - async deleteMember(ruleId: number, memberId: number): Promise { - const ruleEntity : RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); - const invitation = await RuleInvitationEntity.findOne({ - where: {rule :{id : ruleId}, member : {id : memberId}} - }) + async deleteMember(ruleId: number, memberId: number) :Promise> { + // (1) 존재하는 회원인지 확인 + const invitedEntity = await this.userService.findUserById(memberId); + if(!invitedEntity) { + return new ResponseDto( + ResponseCode.USER_NOT_FOUND, + false, + "존재하지 않는 회원 입니다", + null + ); + } + + // (2) 이미 초대된 멤버인지 확인 + const isAlreadyMember = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, memberId); + console.log('isAlreadyMember', isAlreadyMember); + + if (!!isAlreadyMember) { + const invitation = await RuleInvitationEntity.findOne({ + where: { rule: { id: ruleId }, member: { id: memberId } } + }); + await invitation.softRemove(); - return invitation.softRemove(); + return new ResponseDto( + ResponseCode.DELETE_MEMBER_SUCCESS, + true, + "여행 규칙 멤버 삭제 성공", + null + ); + } else { + return new ResponseDto( + ResponseCode.IS_NOT_MEMBER, + false, + "초대된 회원이 아닙니다", + null + ); + } } } \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index dcdb951..9a123e2 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -88,7 +88,9 @@ export enum ResponseCode { /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */ ACCOUNT_NOT_FOUND = 'NOT_FOUND', REFRESH_TOKEN_NOT_FOUND = 'NOT_FOUND', + USER_NOT_FOUND = 'NOT_FOUND', SIGNATURE_NOT_FOUND = 'NOT_FOUND', + INVITATION_NOT_FOUND = 'NOT_FOUND', /* 409 CONFLICT : Resource 의 현재 상태와 충돌. 보통 중복된 데이터 존재 */ EMAIL_DUPLICATION = 'CONFLICT', diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index 309b498..b962dce 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -69,4 +69,15 @@ export class RuleInvitationEntity extends BaseEntity { throw error; } } + + // [member] 멤버인지 확인 + static async isAlreadyMember(ruleId: number, targetUserId: number) :Promise { + const isAlreadyMember = await RuleInvitationEntity.findOne({ + where : {member: {id : targetUserId}, rule: {id : ruleId}} + }) + console.log(isAlreadyMember); + + if (!!isAlreadyMember) return true; + else return false; + } } \ No newline at end of file diff --git a/src/rule/rule.converter.ts b/src/rule/rule.converter.ts deleted file mode 100644 index cd95cc8..0000000 --- a/src/rule/rule.converter.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { RuleMainEntity } from './domain/rule.main.entity'; -import { RuleSubEntity } from './domain/rule.sub.entity'; -import { RuleInvitationEntity } from './domain/rule.invitation.entity'; -import { UserEntity } from 'src/user/user.entity'; -import { CreateRuleDto } from './dto/create-rule.dto'; -import { DetailPageDto } from './dto/detail-page.dto'; -import { DetailRuleDto } from './dto/detail-rule.dto'; -import { DetailMemberDto } from './dto/detail-member.dto'; -import { DetailCommentDto } from './dto/detail-comment.dto'; -import { MetaToBackDto } from './dto/meta-to-back.dto'; -import { RulePairDto } from './dto/rule-pair.dto'; -import { MemberPairDto } from './dto/member-pair.dto'; -import { UserProfileImageEntity } from 'src/user/user.profile.image.entity'; -import { CommentEntity } from 'src/comment/domain/comment.entity'; -import { CommentPairDto } from './dto/comment-pair.dto'; -import { MetaToFrontDto } from './dto/meta-to-front.dto'; - -@Injectable() -export class RuleConverter { - - async toEntity(dto: CreateRuleDto, userId:number): Promise<{ main: RuleMainEntity, rules: RuleSubEntity[], invitations: RuleInvitationEntity[] }> { - // main 저장 - const main = new RuleMainEntity(); - main.mainTitle = dto.mainTitle; - - //rule 저장 - const rules = dto.rulePairs.map(pair => { - const sub = new RuleSubEntity(); - sub.ruleTitle = pair.ruleTitle; - sub.ruleDetail = pair.ruleDetail; - sub.main = main; - return sub; - }); - - // invitation 저장 - const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); - const invitations = await Promise.all(dto.invitedId.map(async invited => { - const invitation = new RuleInvitationEntity(); - - const invitedEntity = await UserEntity.findOneOrFail({ where: { id: invited } }); - invitation.rule = main; - invitation.member = invitedEntity; - invitation.inviter = inviterEntity; - - return invitation; - })); - - return { main, rules, invitations }; - } - - async toDto(ruleId : number, metaToBackDto : MetaToBackDto): Promise { - - const detailPageDto : DetailPageDto = new DetailPageDto(); - const detailRuleDto : DetailRuleDto = new DetailRuleDto(); - const detailMemberDto : DetailMemberDto = new DetailMemberDto(); - const detailCommentDto : DetailCommentDto = new DetailCommentDto(); - // const metaToFrontDto : MetaToFrontDto = new MetaToFrontDto(); - - - // DetailPageDto 에 넣어야 하는 정보 - // [1] DetailRuleDto - const ruleMain : RuleMainEntity = await RuleMainEntity.findMainById(ruleId); - const ruleSub : RuleSubEntity[] = ruleMain.rules; - const comments : CommentEntity[] = ruleMain.comments; - - - detailRuleDto.id = ruleMain.id; - detailRuleDto.mainTitle = ruleMain.mainTitle; - - detailRuleDto.rulePairs = ruleSub.map(sub => { - const rulePairDto: RulePairDto = new RulePairDto(); - rulePairDto.ruleTitle = sub.ruleTitle; - rulePairDto.ruleDetail = sub.ruleDetail; - return rulePairDto; - }); - - // [2] detailMemberDto - const ruleInvitation : RuleInvitationEntity[] = ruleMain.members; - detailMemberDto.memberPairs = await Promise.all(ruleInvitation.map(async (invitation) => { - const memberPairDto: MemberPairDto = new MemberPairDto(); - const id = invitation.id; - - const { memberId, name} = await RuleInvitationEntity.findNameById(id); - memberPairDto.memberId = memberId; - memberPairDto.name = name; - - const userEntity : UserEntity = await UserEntity.findOne({ where: { id : memberId } }); - const imageKey = await UserProfileImageEntity.findImageKey(userEntity); - memberPairDto.image = imageKey; - - return memberPairDto; - - // (수정) 멤버 3명 이상인 경우, 3명까지만 정보 넘기기 - })); - - // [3] detailCommentDto - detailCommentDto.commentPairs = await Promise.all(comments.map(async (comment) => { - const commentPairDto: CommentPairDto = new CommentPairDto(); - - commentPairDto.id = comment.id; - commentPairDto.text = comment.content; - commentPairDto.created = comment.created; - - const userEntity : UserEntity = await UserEntity.findOne({ where: { id : comment.id } }); - const imageKey = await UserProfileImageEntity.findImageKey(userEntity); - commentPairDto.image = imageKey; - - return commentPairDto; - })); - - // [4] MetaBackDto - - - return detailPageDto; - } -} diff --git a/src/rule/rule.module.ts b/src/rule/rule.module.ts index 2bc25de..7f125fa 100644 --- a/src/rule/rule.module.ts +++ b/src/rule/rule.module.ts @@ -1,10 +1,9 @@ import { Module } from '@nestjs/common'; import { RuleService } from './rule.service'; import { RuleController } from './rule.controller'; -import { RuleConverter } from './rule.converter'; @Module({ controllers: [RuleController], - providers: [RuleService, RuleConverter], + providers: [RuleService], }) export class RuleModule {} diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 51b34f7..f05a33d 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,21 +1,14 @@ import {Injectable, HttpException, BadRequestException} from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; -import { RuleConverter } from './rule.converter'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; import { RuleInvitationEntity } from './domain/rule.invitation.entity'; import { DetailPageDto } from './dto/detail-page.dto'; -import { DetailRuleDto } from './dto/detail-rule.dto'; -import { DetailMemberDto } from './dto/detail-member.dto'; -import { DetailCommentDto } from './dto/detail-comment.dto'; import { MetaToBackDto } from './dto/meta-to-back.dto'; import { UserEntity} from "../user/user.entity"; @Injectable() export class RuleService { - constructor( - private ruleConverter: RuleConverter, - ) {} // [1] 여행 규칙 생성 async createRule(dto: CreateRuleDto, userId: number): Promise { @@ -23,9 +16,10 @@ export class RuleService { const main = new RuleMainEntity(); main.mainTitle = dto.mainTitle; await main.save(); + console.log(main); //rule 저장 - const subs = dto.rulePairs.map(async pair => { + const subs = dto.rulePairs.map(async (pair) => { const sub = new RuleSubEntity(); sub.ruleTitle = pair.ruleTitle; sub.ruleDetail = pair.ruleDetail; @@ -34,10 +28,11 @@ export class RuleService { await sub.save(); return sub; }); + console.log(subs); + // invitation 저장 - const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); - let members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { + const members = dto.membersId.map(async (memberId) : Promise => { const ruleInvitationEntity = new RuleInvitationEntity(); const userEntity = await UserEntity.findOneOrFail({ where: { id: memberId } }); @@ -46,7 +41,15 @@ export class RuleService { await ruleInvitationEntity.save(); return ruleInvitationEntity; - })); + }); + + // 여행 규칙 글 작성자 정보 저장 + const writerEntity = new RuleInvitationEntity(); + + const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); + writerEntity.member = inviterEntity; + writerEntity.rule = main; + await writerEntity.save(); return main.id; } @@ -87,7 +90,7 @@ export class RuleService { // [member] 멤버인지 확인 async checkMember(rule: RuleMainEntity, targetUserId: number): Promise { - const invitedArray = rule.members || []; + const invitedArray = rule.invitations || []; const isMember = invitedArray.some( (invitations) => invitations.member.id === targetUserId, From 8f72df8f59ae6740b50bd8f52a43e6795138f80a Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 21:26:59 +0900 Subject: [PATCH 143/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=83=9D=EC=84=B1,=20=EC=97=AC=ED=96=89?= =?UTF-8?q?=20=EA=B7=9C=EC=B9=99=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit S3 적용, ruleInvitationEntity 변경 사항 적용 --- src/rule/domain/rule.invitation.entity.ts | 8 +-- src/rule/domain/rule.sub.entity.ts | 12 ++++ src/rule/dto/create-rule.dto.ts | 10 ++- src/rule/dto/detail-member.dto.ts | 10 --- src/rule/dto/detail-page.dto.ts | 12 ---- src/rule/dto/detail-rule.dto.ts | 18 ----- src/rule/dto/detail.rule.dto.ts | 47 +++++++++++++ src/rule/dto/meta-to-back.dto.ts | 16 ----- src/rule/dto/rule-pair.dto.ts | 11 --- src/rule/rule.controller.ts | 7 +- src/rule/rule.module.ts | 3 +- src/rule/rule.service.ts | 83 ++++++++++++++--------- 12 files changed, 128 insertions(+), 109 deletions(-) delete mode 100644 src/rule/dto/detail-member.dto.ts delete mode 100644 src/rule/dto/detail-page.dto.ts delete mode 100644 src/rule/dto/detail-rule.dto.ts create mode 100644 src/rule/dto/detail.rule.dto.ts delete mode 100644 src/rule/dto/meta-to-back.dto.ts delete mode 100644 src/rule/dto/rule-pair.dto.ts diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index b962dce..fec931b 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -42,11 +42,11 @@ export class RuleInvitationEntity extends BaseEntity { return { memberId, name }; } - static async findInvitationByRuleId(ruleId: number, memberId: number): Promise { + static async findInvitationByRuleId(ruleId: number): Promise { try { - const invitation = await RuleInvitationEntity.findOne({ - where: [{ rule : { id : ruleId }}, - { member : { id : memberId }}] + const invitation = await RuleInvitationEntity.find({ + where: {rule: {id : ruleId}}, + relations: {member:true}, }); console.log('invitation 조회 결과 : ', invitation); return invitation; diff --git a/src/rule/domain/rule.sub.entity.ts b/src/rule/domain/rule.sub.entity.ts index d580fe4..f0bf1ef 100644 --- a/src/rule/domain/rule.sub.entity.ts +++ b/src/rule/domain/rule.sub.entity.ts @@ -33,4 +33,16 @@ export class RuleSubEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + + static async findSubById(ruleId: number): Promise { + try { + const rule: RuleSubEntity[] = await RuleSubEntity.find({ + where: {main: {id : ruleId}}, + }); + return rule; + } catch (error) { + console.log('Error on findRuleById: ', error); + throw error; + } + } } \ No newline at end of file diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index ad4e211..b6d0422 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -1,6 +1,14 @@ import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -import { RulePairDto} from "./rule-pair.dto"; +class RulePairDto { + @IsNotEmpty() + @IsString() + ruleTitle: string; + + @IsNotEmpty() + @IsString() + ruleDetail: string; +} export class CreateRuleDto { @IsNotEmpty() diff --git a/src/rule/dto/detail-member.dto.ts b/src/rule/dto/detail-member.dto.ts deleted file mode 100644 index 0eccabb..0000000 --- a/src/rule/dto/detail-member.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IsArray, ValidateNested } from 'class-validator'; -import { Type } from 'class-transformer'; -import { MemberPairDto } from './member-pair.dto'; - -export class DetailMemberDto { - @IsArray() - @ValidateNested({ each: true }) - @Type(() => MemberPairDto) - memberPairs: MemberPairDto[]; -} \ No newline at end of file diff --git a/src/rule/dto/detail-page.dto.ts b/src/rule/dto/detail-page.dto.ts deleted file mode 100644 index 4e0006d..0000000 --- a/src/rule/dto/detail-page.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DetailRuleDto } from './detail-rule.dto'; -import { DetailMemberDto } from './detail-member.dto'; -import { DetailCommentDto } from './detail-comment.dto'; -import { MetaToBackDto } from './meta-to-back.dto'; - - -export class DetailPageDto { - rule: DetailRuleDto; - member: DetailMemberDto; - comment: DetailCommentDto[]; - // metaBack: MetaToBackDto; -} \ No newline at end of file diff --git a/src/rule/dto/detail-rule.dto.ts b/src/rule/dto/detail-rule.dto.ts deleted file mode 100644 index 1a8bcb1..0000000 --- a/src/rule/dto/detail-rule.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; -import { Type } from 'class-transformer'; -import { RulePairDto } from './rule-pair.dto'; - -export class DetailRuleDto { - @IsNotEmpty() - @IsNumber() - id: number; - - @IsNotEmpty() - @IsString() - mainTitle: string; - - @IsArray() - @ValidateNested({ each: true }) - @Type(() => RulePairDto) - rulePairs: RulePairDto[]; -} \ No newline at end of file diff --git a/src/rule/dto/detail.rule.dto.ts b/src/rule/dto/detail.rule.dto.ts new file mode 100644 index 0000000..4300934 --- /dev/null +++ b/src/rule/dto/detail.rule.dto.ts @@ -0,0 +1,47 @@ +import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; + +export class RulePairDto { + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + ruleTitle: string; + + @IsNotEmpty() + @IsString() + ruleDetail: string; +} + +export class DetailMemberDto { + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + image: string; +} + +export class DetailRuleDto { + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + mainTitle: string; + + @IsArray() + @ValidateNested({each: true}) + @Type(() => RulePairDto) + rulePairs: RulePairDto[]; + + @IsArray() + @ValidateNested({each: true}) + @Type(() => DetailMemberDto) + detailMembers: DetailMemberDto[]; +} \ No newline at end of file diff --git a/src/rule/dto/meta-to-back.dto.ts b/src/rule/dto/meta-to-back.dto.ts deleted file mode 100644 index dfca19f..0000000 --- a/src/rule/dto/meta-to-back.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IsNotEmpty, IsNumber } from 'class-validator'; - -// [meta] Front -> Back -export class MetaToBackDto { - - // 페이지네이션을 위한 커서 ID - @IsNotEmpty() - @IsNumber() - take: number; - - // 페이지당 불러올 limit 값 - @IsNotEmpty() - @IsNumber() - cursor: number; - -} \ No newline at end of file diff --git a/src/rule/dto/rule-pair.dto.ts b/src/rule/dto/rule-pair.dto.ts deleted file mode 100644 index a0ccc5f..0000000 --- a/src/rule/dto/rule-pair.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class RulePairDto { - @IsNotEmpty() - @IsString() - ruleTitle: string; - - @IsNotEmpty() - @IsString() - ruleDetail: string; -} \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index ba77c75..985146f 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -3,7 +3,6 @@ import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -import { MetaToBackDto } from './dto/meta-to-back.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; @@ -36,12 +35,12 @@ export class RuleController { } } - // 여행 규칙 및 댓글 조회 + // 여행 규칙 조회 @Get('/detail/:ruleId') @UseGuards(UserGuard) - async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number, @Body() metaToBackDto: MetaToBackDto): Promise> { + async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { - const result = await this.ruleService.getDetail(ruleId, metaToBackDto); + const result = await this.ruleService.getDetail(ruleId); if(!result){ return new ResponseDto( diff --git a/src/rule/rule.module.ts b/src/rule/rule.module.ts index 7f125fa..e389807 100644 --- a/src/rule/rule.module.ts +++ b/src/rule/rule.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; import { RuleService } from './rule.service'; import { RuleController } from './rule.controller'; +import { S3UtilService } from "../utils/S3.service"; @Module({ controllers: [RuleController], - providers: [RuleService], + providers: [RuleService, S3UtilService], }) export class RuleModule {} diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f05a33d..08883a5 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -3,23 +3,27 @@ import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; import { RuleInvitationEntity } from './domain/rule.invitation.entity'; -import { DetailPageDto } from './dto/detail-page.dto'; -import { MetaToBackDto } from './dto/meta-to-back.dto'; import { UserEntity} from "../user/user.entity"; +import {DetailMemberDto, DetailRuleDto, RulePairDto} from "./dto/detail.rule.dto"; +import { S3UtilService} from "../utils/S3.service"; +import {UserService} from "../user/user.service"; @Injectable() export class RuleService { + constructor( + private readonly s3Service: S3UtilService, + ) {} // [1] 여행 규칙 생성 async createRule(dto: CreateRuleDto, userId: number): Promise { - // main 저장 + // -1) main 저장 const main = new RuleMainEntity(); main.mainTitle = dto.mainTitle; await main.save(); console.log(main); - //rule 저장 - const subs = dto.rulePairs.map(async (pair) => { + // -2) rule 저장 + const subs = await Promise.all(dto.rulePairs.map(async (pair) => { const sub = new RuleSubEntity(); sub.ruleTitle = pair.ruleTitle; sub.ruleDetail = pair.ruleDetail; @@ -27,12 +31,12 @@ export class RuleService { await sub.save(); return sub; - }); + })); console.log(subs); - // invitation 저장 - const members = dto.membersId.map(async (memberId) : Promise => { + // -3) invitation 저장 + const members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { const ruleInvitationEntity = new RuleInvitationEntity(); const userEntity = await UserEntity.findOneOrFail({ where: { id: memberId } }); @@ -41,9 +45,9 @@ export class RuleService { await ruleInvitationEntity.save(); return ruleInvitationEntity; - }); + })); - // 여행 규칙 글 작성자 정보 저장 + // -4) 여행 규칙 글 작성자 정보 저장 const writerEntity = new RuleInvitationEntity(); const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); @@ -55,16 +59,43 @@ export class RuleService { } // [2] 여행 규칙 조회 - async getDetail(ruleId : number, metaToBackDto : MetaToBackDto): Promise { - try{ - const detailPageDto : DetailPageDto = new DetailPageDto(); - - return detailPageDto; - } - catch(error){ - console.error('Error on GetDetail : ', error); - throw new HttpException('Internal Server Error', 500); - } + async getDetail(ruleId : number): Promise { + const dto = new DetailRuleDto(); + const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); + const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); + const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.findInvitationByRuleId(ruleId); + + // -1) 제목 + dto.id = ruleId; + dto.mainTitle = main.mainTitle; + + // -2) 규칙 + dto.rulePairs = await Promise.all(subs.map(async(sub):Promise => { + const rulePair = new RulePairDto(); + rulePair.id = sub.id; + rulePair.ruleTitle = sub.ruleTitle; + rulePair.ruleDetail = sub.ruleDetail; + + return rulePair; + })) + + // -3) 멤버 정보 + dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { + const detailMember = new DetailMemberDto; + const memberEntity = invitation.member; + detailMember.id = memberEntity.id; + detailMember.name = memberEntity.name; + + // 사용자 프로필 이미지 + const image = memberEntity.profileImage; + if(image == null) detailMember.image = null; + else{ + const userImageKey = image.imageKey; + detailMember.image = await this.s3Service.getImageUrl(userImageKey); + } + return detailMember; + })) + return dto; } // [3] 여행 규칙 나가기 @@ -87,16 +118,4 @@ export class RuleService { console.log('Error on getInvitationList: ' + error); } } - - // [member] 멤버인지 확인 - async checkMember(rule: RuleMainEntity, targetUserId: number): Promise { - const invitedArray = rule.invitations || []; - - const isMember = invitedArray.some( - (invitations) => invitations.member.id === targetUserId, - ); - - console.log('테스트 결과 : ', isMember); - return isMember; - } } From 53dd2ba10e0ddbdcee23e586da83d7e5b2a7f997 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:32:07 +0900 Subject: [PATCH 144/316] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=A4=20=EC=B6=94=EC=B2=9C=20=EB=AC=B4=ED=95=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cursor-page/cursor-page-option.dto.ts | 21 +++++++++++++++++++ ...cursor-page-options-parameter.interface.ts | 8 +++++++ .../cursor-page/cursor-page-order.enum.ts | 5 +++++ src/mate/cursor-page/cursor-page.dto.ts | 15 +++++++++++++ src/mate/cursor-page/cursor-page.meta.dto.ts | 18 ++++++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 src/mate/cursor-page/cursor-page-option.dto.ts create mode 100644 src/mate/cursor-page/cursor-page-options-parameter.interface.ts create mode 100644 src/mate/cursor-page/cursor-page-order.enum.ts create mode 100644 src/mate/cursor-page/cursor-page.dto.ts create mode 100644 src/mate/cursor-page/cursor-page.meta.dto.ts diff --git a/src/mate/cursor-page/cursor-page-option.dto.ts b/src/mate/cursor-page/cursor-page-option.dto.ts new file mode 100644 index 0000000..a8c9208 --- /dev/null +++ b/src/mate/cursor-page/cursor-page-option.dto.ts @@ -0,0 +1,21 @@ +// cursor-page.options.ts + +import { Type } from "class-transformer"; +import { IsEnum, IsOptional } from "class-validator"; +import { Order } from './cursor-page-order.enum'; + +export class CursorPageOptionsDto { + + @Type(() => String) + @IsEnum(Order) + @IsOptional() + readonly sort?: Order = Order.DESC; + + @Type(() => Number) + @IsOptional() + readonly take?: number = 5; + + @Type(() => String) + @IsOptional() + readonly cursorId?: number = "" as any; +} \ No newline at end of file diff --git a/src/mate/cursor-page/cursor-page-options-parameter.interface.ts b/src/mate/cursor-page/cursor-page-options-parameter.interface.ts new file mode 100644 index 0000000..3baa0fd --- /dev/null +++ b/src/mate/cursor-page/cursor-page-options-parameter.interface.ts @@ -0,0 +1,8 @@ +import { CursorPageOptionsDto } from './cursor-page-option.dto'; + +export interface CursorPageMetaDtoParameters { + cursorPageOptionsDto: CursorPageOptionsDto; + total: number; + hasNextData: boolean; + cursor: number; +} \ No newline at end of file diff --git a/src/mate/cursor-page/cursor-page-order.enum.ts b/src/mate/cursor-page/cursor-page-order.enum.ts new file mode 100644 index 0000000..2ff68ab --- /dev/null +++ b/src/mate/cursor-page/cursor-page-order.enum.ts @@ -0,0 +1,5 @@ +// cursor-page-order.enum.ts +export enum Order { + ASC = "asc", + DESC = "desc" +} \ No newline at end of file diff --git a/src/mate/cursor-page/cursor-page.dto.ts b/src/mate/cursor-page/cursor-page.dto.ts new file mode 100644 index 0000000..33f1dc3 --- /dev/null +++ b/src/mate/cursor-page/cursor-page.dto.ts @@ -0,0 +1,15 @@ +import { IsArray } from "class-validator"; +import { CursorPageMetaDto } from './cursor-page.meta.dto'; + +export class CursorPageDto { + + @IsArray() + readonly data: T[]; + + readonly meta: CursorPageMetaDto; + + constructor(data: T[], meta: CursorPageMetaDto) { + this.data = data; + this.meta = meta; + } +} \ No newline at end of file diff --git a/src/mate/cursor-page/cursor-page.meta.dto.ts b/src/mate/cursor-page/cursor-page.meta.dto.ts new file mode 100644 index 0000000..041c915 --- /dev/null +++ b/src/mate/cursor-page/cursor-page.meta.dto.ts @@ -0,0 +1,18 @@ +// cursor-page.meta.dto.ts + +import { CursorPageMetaDtoParameters } from './cursor-page-options-parameter.interface'; + +export class CursorPageMetaDto { + + readonly total: number; + readonly take: number; + readonly hasNextData: boolean; + readonly cursor: number; + + constructor({cursorPageOptionsDto, total, hasNextData, cursor}: CursorPageMetaDtoParameters) { + this.take = cursorPageOptionsDto.take; + this.total = total; + this.hasNextData = hasNextData; + this.cursor = cursor; + } +} \ No newline at end of file From d2bd763d5fafdf8bb9cada4036c9b6f2bfc8e91b Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:32:40 +0900 Subject: [PATCH 145/316] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=83=90=EC=83=89=20-=20=EC=9C=84=EC=B9=98=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98,=20=EB=9E=9C=EB=8D=A4=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 + src/mate/dto/mate-recommend-profile.dto.ts | 12 + src/mate/dto/mate-signature-cover.dto.ts | 7 + .../mate-with-common-location-response.dto.ts | 8 + src/mate/mate.controller.ts | 81 +++++++ src/mate/mate.module.ts | 12 + src/mate/mate.service.ts | 212 ++++++++++++++++++ src/response/response-code.enum.ts | 7 +- src/signature/signature.controller.ts | 3 +- src/signature/signature.service.ts | 12 + src/user/user.service.ts | 1 + 11 files changed, 352 insertions(+), 5 deletions(-) create mode 100644 src/mate/dto/mate-recommend-profile.dto.ts create mode 100644 src/mate/dto/mate-signature-cover.dto.ts create mode 100644 src/mate/dto/mate-with-common-location-response.dto.ts create mode 100644 src/mate/mate.controller.ts create mode 100644 src/mate/mate.module.ts create mode 100644 src/mate/mate.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 1ff5b85..4e9c740 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -19,6 +19,7 @@ import { FollowModule } from './follow/follow.module'; import { MemberModule } from './member/member.module'; import { SearchModule } from './search/search.module'; import { MapModule } from './map/map.module'; +import { MateModule } from './mate/mate.module'; @Module({ imports: [ @@ -41,6 +42,7 @@ import { MapModule } from './map/map.module'; MemberModule, SearchModule, MapModule, + MateModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/mate/dto/mate-recommend-profile.dto.ts b/src/mate/dto/mate-recommend-profile.dto.ts new file mode 100644 index 0000000..a25ad66 --- /dev/null +++ b/src/mate/dto/mate-recommend-profile.dto.ts @@ -0,0 +1,12 @@ +// mate-recommend-profile.dto.ts + +import { MateSignatureCoverDto } from './mate-signature-cover.dto'; + +export class MateRecommendProfileDto{ + _id: number; + userImage: string; // 유저 사진 + userName: string; // 유저 별명 + is_followed: boolean; // 팔로우 여부 + introduction: string; // 한 줄 소개 + signatures: MateSignatureCoverDto[]; +} \ No newline at end of file diff --git a/src/mate/dto/mate-signature-cover.dto.ts b/src/mate/dto/mate-signature-cover.dto.ts new file mode 100644 index 0000000..e54859d --- /dev/null +++ b/src/mate/dto/mate-signature-cover.dto.ts @@ -0,0 +1,7 @@ +// mate-sginature-cover.dto.ts + +export class MateSignatureCoverDto{ + _id: number; + image: string + title: string +} \ No newline at end of file diff --git a/src/mate/dto/mate-with-common-location-response.dto.ts b/src/mate/dto/mate-with-common-location-response.dto.ts new file mode 100644 index 0000000..44073c4 --- /dev/null +++ b/src/mate/dto/mate-with-common-location-response.dto.ts @@ -0,0 +1,8 @@ +// mate-with-common-location.dto.ts + +import { MateRecommendProfileDto } from './mate-recommend-profile.dto'; + +export class MateWithCommonLocationResponseDto{ + location: string; + mateProfiles: MateRecommendProfileDto[]; +} \ No newline at end of file diff --git a/src/mate/mate.controller.ts b/src/mate/mate.controller.ts new file mode 100644 index 0000000..db3c0fa --- /dev/null +++ b/src/mate/mate.controller.ts @@ -0,0 +1,81 @@ +//mate.controller.ts + +import { Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; +import { CursorPageOptionsDto } from './cursor-page/cursor-page-option.dto'; +import { MateService } from './mate.service'; +import { ResponseDto } from '../response/response.dto'; +import { MateRecommendProfileDto } from './dto/mate-recommend-profile.dto'; +import { ResponseCode } from '../response/response-code.enum'; +import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; + +@Controller('/mate') +export class MateController{ + + constructor(private readonly mateService:MateService) {} + + @Get('/location') + @UseGuards(UserGuard) + async getMateProfileWithMyFirstLocation( // 메이트 탐색 첫 줄: 나와 공통 장소를 사용한 메이트 추천 + @Req() req: Request, + ): Promise> { + + try{ + const result = await this.mateService.getMateProfileWithMyFirstLocation(req.user.id); + + if(!result){ + return new ResponseDto( + ResponseCode.GET_MATE_WITH_COMMON_LOCATION_SUCCESS, + true, + "공통 메이트가 없거나 내 시그니처가 없습니다.", + null + ) + } + return new ResponseDto( + ResponseCode.GET_MATE_WITH_COMMON_LOCATION_SUCCESS, + true, + "장소 기반 메이트 추천 리스트 가져오기 성공", + result + ); + + } + catch (e){ + console.log(e); + return new ResponseDto( + ResponseCode.GET_MATE_WITH_COMMON_LOCATION_FAIL, + false, + "장소 기반 메이트 추천 실패", + null + ); + } + + } + + @Get('/random') + @UseGuards(UserGuard) + async getRandomMateProfileWithInfiniteCursor( // 메이트 탐색 둘째 줄: 랜덤으로 메이트 추천 + @Req() req: Request, + @Query() cursorPageOptionDto: CursorPageOptionsDto + ){ + try{ + const result = await this.mateService.recommendRandomMateWithInfiniteScroll(cursorPageOptionDto, req.user.id); + + return new ResponseDto( + ResponseCode.GET_RANDOM_MATE_PROFILE_SUCCESS, + true, + "랜덤 메이트 추천 데이터 생성 성공", + result + ); + } + catch(e){ + console.log(e); + return new ResponseDto( + ResponseCode.GET_RANDOM_MATE_PROFILE_FAIL, + false, + "랜덤 메이트 추천 데이터 생성 실패", + null + ); + } + } +} \ No newline at end of file diff --git a/src/mate/mate.module.ts b/src/mate/mate.module.ts new file mode 100644 index 0000000..be8ad90 --- /dev/null +++ b/src/mate/mate.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { MateController } from './mate.controller'; +import { MateService } from './mate.service'; +import { UserService } from '../user/user.service'; +import { S3UtilService } from '../utils/S3.service'; +import { SignatureService } from '../signature/signature.service'; + +@Module({ + controllers: [MateController], + providers: [MateService, UserService, S3UtilService, SignatureService] +}) +export class MateModule{} diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts new file mode 100644 index 0000000..df34084 --- /dev/null +++ b/src/mate/mate.service.ts @@ -0,0 +1,212 @@ +//mate.service.ts + +import { Injectable } from '@nestjs/common'; +import { UserService } from '../user/user.service'; +import { S3UtilService } from '../utils/S3.service'; +import { CursorPageOptionsDto } from './cursor-page/cursor-page-option.dto'; +import { CursorPageDto } from './cursor-page/cursor-page.dto'; +import { SignatureEntity } from '../signature/domain/signature.entity'; +import { SignatureService } from '../signature/signature.service'; +import { LessThan, Like, MoreThan } from 'typeorm'; +import { CursorPageMetaDto } from './cursor-page/cursor-page.meta.dto'; +import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; +import { UserEntity } from '../user/user.entity'; +import { MateRecommendProfileDto } from './dto/mate-recommend-profile.dto'; +import { MateController } from './mate.controller'; +import { MateSignatureCoverDto } from './dto/mate-signature-cover.dto'; +import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; + +@Injectable() +export class MateService{ + constructor( + private readonly userService: UserService, + private readonly s3Service: S3UtilService, + private readonly signatureService: SignatureService, + ) {} + + async getMateProfileWithMyFirstLocation(userId: number): Promise { + try{ + + const mateWithCommonLocationResponseDto = new MateWithCommonLocationResponseDto(); + + // 1. 메이트 탐색의 기준이 될 장소 가져오기 = 사용자의 가장 최신 시그니처의 첫 번째 페이지 장소 + const mySignaturePageEntity = await SignaturePageEntity.findOne({ + where: { + signature:{ user:{ id: userId } }, + page: 1 + }, + order: { + created: 'DESC' // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 + }, + }); + + if(!mySignaturePageEntity){ // 사용자가 아직 한번도 시그니처를 작성한 적이 없을 경우 + return null; + } + + const longLocation = mySignaturePageEntity.location; + console.log("*longLocation: ",longLocation); + + + // 2. 쉼표로 구분된 현재 'longLocation'에서 핵심 부분인 마지막 부분을 가져오기 + const locationBlocks = longLocation.split(','); + const myLocation = locationBlocks[locationBlocks.length-1].trim(); + console.log("*firstLocation: ", myLocation); + + mateWithCommonLocationResponseDto.location = myLocation; + + // 3. 이제 내 기준 로케이션이 사용된 모든 페이지 가져오기 + const commonLocationSignaturePages = await SignaturePageEntity.find({ + where: { location: Like(`%${myLocation}%`) }, + relations: ['signature'] + }); + + // 4. 3번에서 찾아온 페이지의 시그니처 가져오기 + const commonLocationSignatures = []; + for(const page of commonLocationSignaturePages){ + const signature = await SignatureEntity.findOne({ + where: { id: page.signature.id}, + relations: ['user'], + }); + commonLocationSignatures.push(signature); + } + + // 5. 시그니처 작성자 기준으로 분류: 중복된 작성자를 또 찾아오지 않기 위해 + const signatureGroups = {}; + for(const signature of commonLocationSignatures){ + if(!signatureGroups[signature.user.id]){ + signatureGroups[signature.user.id] = []; + } + signatureGroups[signature.user.id].push(signature); + } + + + // 6. 유저 아이디 순회하면서 한명씩 찾아서 메이트 프로필 생성하기 + const mateProfiles : MateRecommendProfileDto[] = []; + + for(const authorUserId of Object.keys(signatureGroups)){ + const mate = await this.userService.findUserById(Number(authorUserId)); + console.log(mate); + + if(userId == Number(authorUserId)) continue; // 본인은 제외 + const mateProfile: MateRecommendProfileDto = await this.generateMateProfile(mate, userId); + mateProfiles.push(mateProfile); + + } + + mateWithCommonLocationResponseDto.mateProfiles = mateProfiles; + + return mateWithCommonLocationResponseDto; + + } + catch(error){ + console.log("Err: ", error); + throw error; + } + } + + async recommendRandomMateWithInfiniteScroll(cursorPageOptionsDto: CursorPageOptionsDto, userId: number) { + + let cursorId: number = 0; + + // [0] 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 + if(cursorPageOptionsDto.cursorId == 0){ + const newUser = await UserEntity.find({ + order: { + id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 가입한 유저 가져오기 + }, + take: 1 + }); + const max = newUser[0].id; // 랜덤 숫자 생성의 max 값 + console.log('max id: ',max); + + const min = 5; // 랜덤 숫자 생성의 min 값 + // TODO 사용자 늘어나면 min 값 늘리기 + cursorId = Math.floor(Math.random() * (max - min + 1)) + min; + console.log('random cursor: ', cursorId); + + } + else { + cursorId = cursorPageOptionsDto.cursorId; + } + + + // [1] 무한 스크롤: take만큼 cursorId보다 id값이 작은 유저들 불러오기 + const [mates, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: cursorId ? { + id: LessThan(cursorId), + }: null, + order: { + id: "DESC" as any, + }, + }); + + console.log("mates: ", mates); + + // [2] 가져온 메이트들 프로필 커버 만들기 + const mateProfiles:MateRecommendProfileDto[] = []; + + for(const mate of mates){ + const mateProfile = await this.generateMateProfile(mate, userId); + mateProfiles.push(mateProfile); + } + + // [3] 스크롤 설정 + let hasNextData = true; + let cursor: number; + + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = mates[mates.length - 1]; + + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } + + const cursorPageMetaDto = new CursorPageMetaDto( + { cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto(mateProfiles, cursorPageMetaDto); + + } + + async generateMateProfile(mate:UserEntity, userId:number){ + const mateProfile = new MateRecommendProfileDto(); + mateProfile._id = mate.id; + mateProfile.userName = mate.nickname; + mateProfile.introduction = mate.introduction; + + const myEntity = await this.userService.findUserById(userId); + mateProfile.is_followed = await this.userService.checkIfFollowing(mate, mate.id); + + let userProfileImage = await this.userService.getProfileImage(mate.id); + if(!userProfileImage) mateProfile.userImage = null; + else{ + let userImageKey = userProfileImage.imageKey; + mateProfile.userImage = await this.s3Service.getImageUrl(userImageKey); + } + + const recentSignatures = await this.signatureService.getMyRecentSignatures(mate.id); + + const signatureCovers = []; + //TODO 작성자가 작성한 시그니처가 하나일 경우에는 리스트에 하나만 담겨있음 프론트에 알리기 + for(const signature of recentSignatures) { + const signatureCover: MateSignatureCoverDto = new MateSignatureCoverDto(); + + signatureCover._id = signature.id; + signatureCover.title = signature.title; + let thumbnailImageKey = await SignaturePageEntity.findThumbnail(signature.id); + signatureCover.image = await this.s3Service.getImageUrl(thumbnailImageKey); + + signatureCovers.push(signatureCover); + } + mateProfile.signatures = signatureCovers; + return mateProfile; + + } + +} \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index dcdb951..c106e86 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -20,13 +20,14 @@ export enum ResponseCode { DELETE_INVITATION_SUCCESS = 'OK', - DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', GET_SEARCH_MAIN_SUCCESS = 'OK', SEARCH_BY_KEYWORD_SUCCESS = 'OK', DELETE_ACCOUNT_SUCCESS = 'OK', + GET_MATE_WITH_COMMON_LOCATION_SUCCESS = 'OK', + GET_RANDOM_MATE_PROFILE_SUCCESS = 'OK', @@ -63,8 +64,8 @@ export enum ResponseCode { RULE_EDIT_FAIL = 'BAD_REQUEST', GET_SEARCH_RESULT_FAIL = 'BAD_REQUEST', DELETE_INVITATION_FAIL = 'BAD_REQUEST', - - + GET_MATE_WITH_COMMON_LOCATION_FAIL = 'OK', + GET_RANDOM_MATE_PROFILE_FAIL = 'OK', SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 348a8c3..fa5cffd 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -15,7 +15,6 @@ import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; @Controller('signature') -//@UseGuards(new AuthGuard()) export class SignatureController { constructor(private readonly signatureService: SignatureService) {} @@ -70,7 +69,7 @@ export class SignatureController { @Patch('/like/:signatureId') // 시그니처 좋아요 등록 or 취소 @UseGuards(UserGuard) - async addSignatureLike( + async patchSignatureLike( @Param('signatureId') signatureId: number, @Req() req: Request ): Promise> { diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index e53183d..50264f8 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -377,4 +377,16 @@ export class SignatureService { } } + async getMyRecentSignatures(userId: number) { // 가장 최신 시그니처 두 개 반환 + // 1. 메이트 탐색의 기준이 될 장소 가져오기 = 사용자의 가장 최신 시그니처의 첫 번째 페이지 장소 + return await SignatureEntity.find({ + where: { + user:{ id: userId }, + }, + order: { + created: 'DESC' // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 + }, + take: 2, // 최신 시그니처 두 개 가져오기 + }); + } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 54ae550..2ff7b8e 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -71,6 +71,7 @@ export class UserService { try { const user: UserEntity = await UserEntity.findOne({ where: { id: userId }, + relations: [ 'profileImage' ], }); return user; } catch (error) { From faeba82cd51cb434537e458649017883ed3976d5 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 9 Feb 2024 22:30:58 +0900 Subject: [PATCH 146/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit S3 적용, member 디렉토리 삭제 --- src/member/member.controller.ts | 21 ------ src/member/member.service.ts | 31 -------- .../dto/get-member-list.dto.ts} | 2 +- src/rule/rule.controller.ts | 74 ++++++++++++------- src/rule/rule.module.ts | 3 +- src/rule/rule.service.ts | 33 +++++++++ 6 files changed, 83 insertions(+), 81 deletions(-) rename src/{member/dto/member.dto.ts => rule/dto/get-member-list.dto.ts} (91%) diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index 72b3b0a..1d10df2 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -60,25 +60,4 @@ export class MemberController { ); } } - - // [1] 여행 규칙 멤버 리스트 조회 - @Get('/:ruleId') - async getMember(@Param('ruleId') ruleId : number) : Promise> { - try { - const memberList = await this.memberService.getMemberList(ruleId); - return new ResponseDto( - ResponseCode.GET_MEMBER_LIST_SUCCESS, - true, - "여행 규칙 멤버 리스트 불러오기 성공", - memberList - ); - } catch (error) { - return new ResponseDto( - ResponseCode.GET_MEMBER_LIST_FAIL, - false, - "여행 규칙 멤버 리스트 불러오기 실패", - null - ); - } - } } \ No newline at end of file diff --git a/src/member/member.service.ts b/src/member/member.service.ts index eda244f..1a3fc48 100644 --- a/src/member/member.service.ts +++ b/src/member/member.service.ts @@ -1,5 +1,4 @@ import {Injectable, NotFoundException} from '@nestjs/common'; -import { MemberDto } from './dto/member.dto'; import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; import { UserEntity } from "../user/user.entity"; import { UserService} from "../user/user.service"; @@ -15,36 +14,6 @@ export class MemberService { private readonly s3Service: S3UtilService, ) {} - // [1] 여행 규칙 멤버 리스트 조회 - async getMemberList(ruleId: number): Promise { - const ruleEntity : RuleMainEntity = await RuleMainEntity.findOneOrFail({ - where: {id : ruleId}, - relations: ['invitations'] - }); - - const members : MemberDto[] = await Promise.all(ruleEntity.invitations.map(async (invitation) : Promise => { - const memberEntity : UserEntity = invitation.member; - const memberDto : MemberDto = new MemberDto(); - - memberDto.id = memberEntity.id; - memberDto.name = memberEntity.name; - memberDto.email = memberEntity.email; - memberDto.introduction = memberEntity.introduction; - - // 사용자 프로필 이미지 - const image = await this.userService.getProfileImage(memberEntity.id); - memberDto.image = image.imageKey; - if(image == null) memberDto.image = null; - else { - const userImageKey = image.imageKey; - memberDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return memberDto; - })) - - return members; - } - // [2] 여행 규칙 멤버 초대 async createInvitation(ruleId: number, invitedId: number): Promise> { // (1) 존재하는 회원인지 확인 diff --git a/src/member/dto/member.dto.ts b/src/rule/dto/get-member-list.dto.ts similarity index 91% rename from src/member/dto/member.dto.ts rename to src/rule/dto/get-member-list.dto.ts index 4f2b247..964960c 100644 --- a/src/member/dto/member.dto.ts +++ b/src/rule/dto/get-member-list.dto.ts @@ -1,6 +1,6 @@ import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; -export class MemberDto { +export class GetMemberListDto { @IsNotEmpty() @IsNumber() id: number; diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 985146f..3ccb176 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -12,51 +12,72 @@ export class RuleController { private readonly ruleService: RuleService, ) {} - // 여행 규칙 생성 - @Post('/write') + // [1] 여행 규칙 멤버 리스트 조회 + @Get('member/:ruleId') + async getMemberList(@Param('ruleId') ruleId : number) : Promise> { + try { + const memberList = await this.ruleService.getMemberList(ruleId); + return new ResponseDto( + ResponseCode.GET_MEMBER_LIST_SUCCESS, + true, + "여행 규칙 멤버 리스트 불러오기 성공", + memberList + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_MEMBER_LIST_FAIL, + false, + "여행 규칙 멤버 리스트 불러오기 실패", + null + ); + } + } + + // [2] 여행 규칙 조회 + @Get('/detail/:ruleId') @UseGuards(UserGuard) - async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { - const result = await this.ruleService.createRule(createRuleDto, req.user.id); + async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { + + const result = await this.ruleService.getDetail(ruleId); if(!result){ return new ResponseDto( - ResponseCode.RULE_CREATION_FAIL, - false, - "여행 규칙 생성 실패", - null); - + ResponseCode.GET_RULE_DETAIL_FAIL, + false, + "여행 규칙 및 댓글 조회 실패", + null + ); } else{ return new ResponseDto( - ResponseCode.RULE_CREATED, - true, - "여행 규칙 생성 성공", - result); + ResponseCode.GET_RULE_DETAIL_SUCCESS, + true, + "여행 규칙 및 댓글 조회 성공", + result + ); } } - // 여행 규칙 조회 - @Get('/detail/:ruleId') + // [3] 여행 규칙 생성 + @Post('/write') @UseGuards(UserGuard) - async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { - - const result = await this.ruleService.getDetail(ruleId); + async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { + const result = await this.ruleService.createRule(createRuleDto, req.user.id); if(!result){ return new ResponseDto( - ResponseCode.GET_RULE_DETAIL_FAIL, + ResponseCode.RULE_CREATION_FAIL, false, - "여행 규칙 및 댓글 조회 실패", - null - ); + "여행 규칙 생성 실패", + null); + } else{ return new ResponseDto( - ResponseCode.GET_RULE_DETAIL_SUCCESS, + ResponseCode.RULE_CREATED, true, - "여행 규칙 및 댓글 조회 성공", - result - ); + "여행 규칙 생성 성공", + result); } } @@ -85,6 +106,5 @@ export class RuleController { null ); } - } } \ No newline at end of file diff --git a/src/rule/rule.module.ts b/src/rule/rule.module.ts index e389807..b30a405 100644 --- a/src/rule/rule.module.ts +++ b/src/rule/rule.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { RuleService } from './rule.service'; import { RuleController } from './rule.controller'; import { S3UtilService } from "../utils/S3.service"; +import { UserService } from "../user/user.service"; @Module({ controllers: [RuleController], - providers: [RuleService, S3UtilService], + providers: [RuleService, S3UtilService, UserService], }) export class RuleModule {} diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 08883a5..43801db 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -6,12 +6,14 @@ import { RuleInvitationEntity } from './domain/rule.invitation.entity'; import { UserEntity} from "../user/user.entity"; import {DetailMemberDto, DetailRuleDto, RulePairDto} from "./dto/detail.rule.dto"; import { S3UtilService} from "../utils/S3.service"; +import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; @Injectable() export class RuleService { constructor( private readonly s3Service: S3UtilService, + private readonly userService: UserService, ) {} // [1] 여행 규칙 생성 @@ -106,6 +108,37 @@ export class RuleService { return invitation.softRemove(); } + // [4] 여행 규칙 멤버 리스트 조회 + async getMemberList(ruleId: number): Promise { + const invitationsList : RuleInvitationEntity[] = await RuleInvitationEntity.find({ + where : {rule : {id: ruleId}}, + relations : {member : true} + }) + + const membersList : GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation) : Promise => { + const memberEntity : UserEntity = invitation.member; + const memberDto : GetMemberListDto = new GetMemberListDto(); + + console.log('memberEntity : ', memberEntity); + memberDto.id = memberEntity.id; + memberDto.name = memberEntity.name; + memberDto.email = memberEntity.email; + memberDto.introduction = memberEntity.introduction; + + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage(memberEntity.id); + memberDto.image = image.imageKey; + if(image == null) memberDto.image = null; + else { + const userImageKey = image.imageKey; + memberDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return memberDto; + })) + const sortedList = membersList.sort((a, b) => a.id - b.id); + return sortedList; + } + // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { From 4a1d3df7f021369ff3715b84ec193a82f2bd5ab4 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:49:33 +0900 Subject: [PATCH 147/316] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=83=90=EC=83=89=20-=20=ED=98=84=EC=9E=AC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=ED=95=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-recommend-profile.dto.ts | 4 ++-- src/mate/dto/mate-with-common-location-response.dto.ts | 1 + src/mate/mate.service.ts | 8 +++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mate/dto/mate-recommend-profile.dto.ts b/src/mate/dto/mate-recommend-profile.dto.ts index a25ad66..905b116 100644 --- a/src/mate/dto/mate-recommend-profile.dto.ts +++ b/src/mate/dto/mate-recommend-profile.dto.ts @@ -4,8 +4,8 @@ import { MateSignatureCoverDto } from './mate-signature-cover.dto'; export class MateRecommendProfileDto{ _id: number; - userImage: string; // 유저 사진 - userName: string; // 유저 별명 + mateImage: string; // 유저 사진 + mateName: string; // 유저 별명 is_followed: boolean; // 팔로우 여부 introduction: string; // 한 줄 소개 signatures: MateSignatureCoverDto[]; diff --git a/src/mate/dto/mate-with-common-location-response.dto.ts b/src/mate/dto/mate-with-common-location-response.dto.ts index 44073c4..f7f772a 100644 --- a/src/mate/dto/mate-with-common-location-response.dto.ts +++ b/src/mate/dto/mate-with-common-location-response.dto.ts @@ -4,5 +4,6 @@ import { MateRecommendProfileDto } from './mate-recommend-profile.dto'; export class MateWithCommonLocationResponseDto{ location: string; + userName: string; // #112 로그인한 사용자 닉네임 추가 mateProfiles: MateRecommendProfileDto[]; } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index df34084..01db56a 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -53,7 +53,9 @@ export class MateService{ const myLocation = locationBlocks[locationBlocks.length-1].trim(); console.log("*firstLocation: ", myLocation); + const loginUser = await this.userService.findUserById(userId); mateWithCommonLocationResponseDto.location = myLocation; + mateWithCommonLocationResponseDto.userName = loginUser.nickname; // 3. 이제 내 기준 로케이션이 사용된 모든 페이지 가져오기 const commonLocationSignaturePages = await SignaturePageEntity.find({ @@ -177,17 +179,17 @@ export class MateService{ async generateMateProfile(mate:UserEntity, userId:number){ const mateProfile = new MateRecommendProfileDto(); mateProfile._id = mate.id; - mateProfile.userName = mate.nickname; + mateProfile.mateName = mate.nickname; mateProfile.introduction = mate.introduction; const myEntity = await this.userService.findUserById(userId); mateProfile.is_followed = await this.userService.checkIfFollowing(mate, mate.id); let userProfileImage = await this.userService.getProfileImage(mate.id); - if(!userProfileImage) mateProfile.userImage = null; + if(!userProfileImage) mateProfile.mateImage = null; else{ let userImageKey = userProfileImage.imageKey; - mateProfile.userImage = await this.s3Service.getImageUrl(userImageKey); + mateProfile.mateImage = await this.s3Service.getImageUrl(userImageKey); } const recentSignatures = await this.signatureService.getMyRecentSignatures(mate.id); From d9fee721cc479cdea4bac44d8c017c598481c7bc Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sat, 10 Feb 2024 14:04:41 +0900 Subject: [PATCH 148/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=A0=84=EC=B2=B4=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/domain/comment.entity.ts | 18 ++++++-- src/follow/follow.controller.ts | 11 ++--- src/follow/follow.service.ts | 12 ++++++ src/response/response-code.enum.ts | 2 + src/rule/dto/get-rule-list.dto.ts | 38 +++++++++++++++++ src/rule/dto/member-pair.dto.ts | 15 ------- src/rule/rule.controller.ts | 23 +++++++++++ src/rule/rule.service.ts | 61 +++++++++++++++++++++++++++- 8 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 src/rule/dto/get-rule-list.dto.ts delete mode 100644 src/rule/dto/member-pair.dto.ts diff --git a/src/comment/domain/comment.entity.ts b/src/comment/domain/comment.entity.ts index 64a0aae..9bf2c3e 100644 --- a/src/comment/domain/comment.entity.ts +++ b/src/comment/domain/comment.entity.ts @@ -1,4 +1,14 @@ -import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn +} from 'typeorm'; import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; import { UserEntity } from 'src/user/user.entity'; @@ -18,12 +28,12 @@ export class CommentEntity extends BaseEntity { @JoinColumn({ name: 'user_id'}) user: UserEntity; - @CreateDateColumn({ type: 'timestamp' }) + @CreateDateColumn() created: Date; - @UpdateDateColumn({ type: 'timestamp' }) + @UpdateDateColumn() updated: Date; - @Column({ type: 'timestamp', nullable: true }) + @DeleteDateColumn() deleted: Date; } \ No newline at end of file diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index db05d27..14c462e 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -1,4 +1,4 @@ -import {Controller, Post, Req, UseGuards, Param, Delete, Get, Patch} from '@nestjs/common'; +import {Controller, Post, Req, UseGuards, Param, Delete, Get, Patch, Query} from '@nestjs/common'; import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; @@ -6,8 +6,9 @@ import { UserEntity } from 'src/user/user.entity'; import { UserService } from 'src/user/user.service'; import { UserSearchDto} from "../user/user.search.dto"; import { UserGuard } from '../user/user.guard'; -import { Request } from 'express'; +import {query, Request} from 'express'; import {UserFollowingEntity} from "../user/user.following.entity"; +import * as querystring from "querystring"; @Controller('mate/search') export class FollowController { @@ -102,11 +103,11 @@ export class FollowController { } // [4] 메이트 검색 - @Get('/:searchTerm') + @Get('/search') @UseGuards(UserGuard) async getSearchResult( - @Req() req: Request, - @Param('searchTerm') searchTerm: string): Promise> { + @Query('searchTerm')searchTerm : string, + @Req() req: Request): Promise> { try { const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(req.user.id, searchTerm) return new ResponseDto( diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 04d3c9d..47580e2 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -117,4 +117,16 @@ export class FollowService { return informs; } + + // [5] 메이트 검색 + async getSearchResult(userId: number, searchTerm: string) { + // 검색 결과에 해당하는 값 찾기 + + // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + + // 찾은 사용자의 정보를 검색 결과 담아줄 dto 에 넣기 + + // 결과 return + } + } diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 1900cea..4f50109 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -18,6 +18,7 @@ export enum ResponseCode { RULE_EDIT_SUCCESS = 'OK', GET_SEARCH_RESULT_SUCCESS = 'OK', DELETE_INVITATION_SUCCESS = 'OK', + GET_RULE_LIST_SUCCESS = 'OK', DELETE_SIGNATURE_SUCCESS = 'OK', @@ -66,6 +67,7 @@ export enum ResponseCode { DELETE_INVITATION_FAIL = 'BAD_REQUEST', GET_MATE_WITH_COMMON_LOCATION_FAIL = 'OK', GET_RANDOM_MATE_PROFILE_FAIL = 'OK', + GET_RULE_LIST_FAIL = 'OK', SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', diff --git a/src/rule/dto/get-rule-list.dto.ts b/src/rule/dto/get-rule-list.dto.ts new file mode 100644 index 0000000..37c6dc7 --- /dev/null +++ b/src/rule/dto/get-rule-list.dto.ts @@ -0,0 +1,38 @@ +import {IsArray, IsDate, IsNotEmpty, IsNumber, IsOptional, IsString, ValidateNested} from 'class-validator'; +import {Type} from "class-transformer"; +export class MemberPairDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + name: string; + + @IsOptional() + @IsString() + image: string; +} + +export class GetRuleListDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + title: string; + + @IsNotEmpty() + @IsDate() + updated: Date; + + @IsNotEmpty() + @IsNumber() + memberCnt: number; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => MemberPairDto) + memberPairs: MemberPairDto[]; +} \ No newline at end of file diff --git a/src/rule/dto/member-pair.dto.ts b/src/rule/dto/member-pair.dto.ts deleted file mode 100644 index 99e1306..0000000 --- a/src/rule/dto/member-pair.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; - -export class MemberPairDto { - @IsNotEmpty() - @IsNumber() - memberId: number; - - @IsNotEmpty() - @IsString() - image: string; - - @IsNotEmpty() - @IsString() - name: string; -} \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 3ccb176..8171058 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -58,6 +58,29 @@ export class RuleController { } } + // [3] 여행 규칙 전체 리스트 조회 + @Get('list') + @UseGuards(UserGuard) + async getRuleList(@Req() req: Request): Promise> { + const result = await this.ruleService.getRuleList(req.user.id); + + if(!result){ + return new ResponseDto( + ResponseCode.GET_RULE_LIST_FAIL, + false, + "여행 규칙 전체 리스트 조회 실패", + null); + + } + else{ + return new ResponseDto( + ResponseCode.GET_RULE_LIST_SUCCESS, + true, + "여행 규칙 전체 리스트 조회 성공", + result); + } + } + // [3] 여행 규칙 생성 @Post('/write') @UseGuards(UserGuard) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 43801db..f464d84 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -8,6 +8,7 @@ import {DetailMemberDto, DetailRuleDto, RulePairDto} from "./dto/detail.rule.dto import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; +import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; @Injectable() export class RuleService { @@ -134,11 +135,69 @@ export class RuleService { memberDto.image = await this.s3Service.getImageUrl(userImageKey); } return memberDto; - })) + })); const sortedList = membersList.sort((a, b) => a.id - b.id); return sortedList; } + // [5] 여행 규칙 전체 리스트 조회 + async getRuleList(userId: number) :Promise { + const userEntity = await UserEntity.findOne({ + where: {id: userId}, + relations: ['ruleParticipate', 'profileImage', 'ruleParticipate'] + // relations: {ruleParticipate: true, profileImage: true} + }); + + try { + const invitationEntities = userEntity.ruleParticipate; + + if (!!invitationEntities) { + const ruleMains = await Promise.all(invitationEntities.map(async (invitation) : Promise => { + console.log(invitation); + const ruleMain = invitation.rule; + const ruleListDto = new GetRuleListDto; + + console.log('ruleMain.id : '); + + ruleListDto.id = ruleMain.id; + ruleListDto.title = ruleMain.mainTitle; + ruleListDto.updated = ruleMain.updated; + ruleListDto.memberCnt = ruleMain.invitations.length; + ruleListDto.memberPairs = await this.getMemberPairs(ruleListDto, ruleMain); + + return ruleListDto; + })); + return ruleMains; + } + } catch (e) { + console.log('참여하는 여행 규칙이 없습니다'); + } + } + + async getMemberPairs(ruleListDto: GetRuleListDto, ruleMain: RuleMainEntity) : Promise { + const invitations: RuleInvitationEntity[] = ruleMain.invitations; + + const result : MemberPairDto[] = await Promise.all(invitations.map(async (invitation) : Promise => { + const memberPair = new MemberPairDto; + const user: UserEntity = invitation.member; + + memberPair.id = user.id; + memberPair.name = user.name; + + // 사용자 프로필 이미지 + const image = user.profileImage; + memberPair.image = image.imageKey; + if(image == null) memberPair.image = null; + else { + const userImageKey = image.imageKey; + memberPair.image = await this.s3Service.getImageUrl(userImageKey); + } + return memberPair; + })); + return result; + } + + // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { try { From 512447f6c6c123cf419e8d44f95dcf073e19c45b Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 14:06:25 +0900 Subject: [PATCH 149/316] =?UTF-8?q?fix=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 21 ++++++++++++++++++++- src/schedule/schedule.entity.ts | 13 +++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index f95d908..de3b3e9 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -25,10 +25,12 @@ export class MapService { journey.id, monthInfoDto, ); + console.log(schedules); const locations = await this.getLocationList(schedules); const images = await this.getDiaryImageList(schedules); const mapInfo = schedules.map((schedule, index) => { return { + date: schedules[index].date, location: locations[index], diaryImage: images[index], }; @@ -188,10 +190,27 @@ export class MapService { } //사용자의 월별 일정 가지고 오기 - async getMonthlySchedule(journeyId, monthInfoDto: MonthInfoDto) { + async getMonthlySchedule( + journeyId, + monthInfoDto: MonthInfoDto, + ): Promise { const schedules: ScheduleEntity[] = await ScheduleEntity.findMonthlySchedule(journeyId, monthInfoDto); return schedules; + // const schedules: ScheduleEntity[] = + // await ScheduleEntity.findExistScheduleByJourneyId(journeyId); + // const monthlySchedules: ScheduleEntity[] = await Promise.all( + // schedules.map(async (schedule) => { + // const monthlySchedule = await ScheduleEntity.findMonthlySchedule( + // schedule, + // monthInfoDto, + // ); + // console.log('4월 일정', monthlySchedule); + // return monthlySchedule; + // }), + // ); + + // return monthlySchedules; } //여정에 작성한 일지 개수 가지고 오기 diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index eb1f889..df5c4ad 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -122,14 +122,19 @@ export class ScheduleEntity extends BaseEntity { journeyId, dates: MonthInfoDto, ): Promise { - const firstDate = new Date(`${dates.year}-${dates.month}-01`); - const lastDate = new Date(`${dates.year}-${dates.month}-31`); - const schedule = await ScheduleEntity.find({ + const firstDate = new Date(dates.year, dates.month - 1, 1); + const lastDate = new Date(dates.year, dates.month, 0); + console.log('startDate : ', firstDate, 'lastDate', lastDate); + const monthlySchedule = await ScheduleEntity.find({ where: { journey: { id: journeyId }, date: Between(firstDate, lastDate), }, + relations: ['location'], }); - return schedule; + for (const schedule of monthlySchedule) { + console.log(schedule.date); + } + return monthlySchedule; } } From 359dfc498e3ef7c9a88422647dccf904d9c99b0e Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sat, 10 Feb 2024 15:09:46 +0900 Subject: [PATCH 150/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=A0=84=EC=B2=B4=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로컬 테스트 확인 --- src/rule/rule.service.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f464d84..f0b6294 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -144,26 +144,25 @@ export class RuleService { async getRuleList(userId: number) :Promise { const userEntity = await UserEntity.findOne({ where: {id: userId}, - relations: ['ruleParticipate', 'profileImage', 'ruleParticipate'] - // relations: {ruleParticipate: true, profileImage: true} + relations: ['ruleParticipate', 'ruleParticipate.rule', 'ruleParticipate.rule.invitations', 'ruleParticipate.rule.invitations.member' , 'ruleParticipate.rule.invitations.member.profileImage'] }); try { const invitationEntities = userEntity.ruleParticipate; if (!!invitationEntities) { - const ruleMains = await Promise.all(invitationEntities.map(async (invitation) : Promise => { + const ruleMains = await Promise.all(invitationEntities.map(async (invitation : RuleInvitationEntity) : Promise => { console.log(invitation); - const ruleMain = invitation.rule; - const ruleListDto = new GetRuleListDto; + const ruleMain : RuleMainEntity = invitation.rule as RuleMainEntity; + const ruleListDto : GetRuleListDto = new GetRuleListDto; - console.log('ruleMain.id : '); + console.log('ruleMain.id : ', ruleMain.id); ruleListDto.id = ruleMain.id; ruleListDto.title = ruleMain.mainTitle; ruleListDto.updated = ruleMain.updated; ruleListDto.memberCnt = ruleMain.invitations.length; - ruleListDto.memberPairs = await this.getMemberPairs(ruleListDto, ruleMain); + ruleListDto.memberPairs = await this.getMemberPairs(ruleMain); return ruleListDto; })); @@ -174,13 +173,14 @@ export class RuleService { } } - async getMemberPairs(ruleListDto: GetRuleListDto, ruleMain: RuleMainEntity) : Promise { + async getMemberPairs(ruleMain: RuleMainEntity) : Promise { const invitations: RuleInvitationEntity[] = ruleMain.invitations; const result : MemberPairDto[] = await Promise.all(invitations.map(async (invitation) : Promise => { const memberPair = new MemberPairDto; const user: UserEntity = invitation.member; + console.log('user.id : ', user.id); memberPair.id = user.id; memberPair.name = user.name; From 7720f05f6c4f29ed35bb81cc58c22c58fe62f3e9 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 16:16:44 +0900 Subject: [PATCH 151/316] =?UTF-8?q?feat=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B0=80?= =?UTF-8?q?=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 2 +- src/journey/model/journey.entity.ts | 16 ++++++++-- src/map/map.controller.ts | 44 ++++++++++++++++++--------- src/map/map.service.ts | 46 ++++++++++++++++++++--------- 4 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 53c7a95..783a599 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -17,7 +17,7 @@ import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; export class JourneyService { //여정 생성하기 - 일정, 일지 함께 생성 async createJourney(user, createJourneyDto: CreateJourneyDto) { - const existJourney = await JourneyEntity.findExistJourneyByDate( + const existJourney = await JourneyEntity.findExistJourneyByPeriod( createJourneyDto, ); if (existJourney) { diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 324ce54..629074f 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -16,6 +16,7 @@ import { import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { MonthInfoDto } from 'src/map/month-info.dto'; +import { CreateJourneyDto } from '../dtos/create-journey.dto'; @Entity() export class JourneyEntity extends BaseEntity { @@ -84,7 +85,7 @@ export class JourneyEntity extends BaseEntity { return journeys; } - static async findExistJourneyByDate(createJourneyDto) { + static async findExistJourneyByPeriod(createJourneyDto) { const journey: JourneyEntity = await JourneyEntity.findOne({ where: { startDate: createJourneyDto.startDate, @@ -95,8 +96,19 @@ export class JourneyEntity extends BaseEntity { return journey; } + static async findExistJourneyByDate(userId: number, date) { + const journeys: JourneyEntity[] = await JourneyEntity.find({ + where: { + id: userId, + startDate: Between(new Date(0), date), + endDate: Between(date, new Date('9999-12-31')), + }, + }); + return journeys; + } + //사용자의 월별 여정 조회 - static async findMonthlyJourney(userId, dates: MonthInfoDto) { + static async findMonthlyJourney(userId: number, dates: MonthInfoDto) { const firstDate = new Date(`${dates.year}-${dates.month}-01`); const lastDate = new Date(`${dates.year}-${dates.month}-31`); const journeys: JourneyEntity[] = await JourneyEntity.find({ diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index 6687bdf..f9e5fb9 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -8,20 +8,6 @@ import { MonthInfoDto } from './month-info.dto'; export class MapController { constructor(private readonly mapService: MapService) {} - /*여정 불러오기*/ - @ApiOperation({ - summary: '여정 불러오기', - description: '여정 제목, 날짜, 위치, 사진을 불러옵니다.', - }) - @ApiOkResponse({ - description: '성공 ', - }) - @Get('get-journey/:journeyId') - async getJourneyPreview(@Param('journeyId') journeyId: number) { - const result = await this.mapService.getJourneyPreview(journeyId); - return result; - } - /*월별 여정 불러오기*/ @ApiOperation({ summary: '월별 여정 불러오기', @@ -48,6 +34,36 @@ export class MapController { ); return result; } + /*월별 일정 불러오기 -캘린더 */ + @ApiOperation({ + summary: '월별 일정 불러오기', + description: + '여정에 포함되는 일정, 위치, 세부 일정, 다이어리 유무를 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Get('get-monthly-schedule') + async getMonthlySchedule(@Param('date') date: Date, @Req() req: Request) { + const user = req.user; + const result = await this.mapService.getMonthlySchedules(user.id, date); + return result; + } + + /*여정 불러오기*/ + @ApiOperation({ + summary: '여정 불러오기', + description: '여정 제목, 날짜, 위치, 사진을 불러옵니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @Get('get-journey/:journeyId') + async getJourneyPreview(@Param('journeyId') journeyId: number) { + const result = await this.mapService.getJourneyPreview(journeyId); + return result; + } /*일지 불러오기 - 지도 */ @ApiOperation({ diff --git a/src/map/map.service.ts b/src/map/map.service.ts index de3b3e9..a31ef92 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -8,9 +8,29 @@ import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { MonthInfoDto } from './month-info.dto'; import { LocationEntity } from 'src/location/location.entity'; import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; +import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; @Injectable() export class MapService { + /*캘린더에서 사용자의 월별 일정 불러오기*/ + async getMonthlySchedules(userId: number, date: Date) { + const user = await UserEntity.findExistUser(userId); + const journeys = await JourneyEntity.findExistJourneyByDate(userId, date); + const scheduleList = await Promise.all( + journeys.map(async (journey) => { + const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + journey.id, + ); + if (!schedules) { + return errResponse(BaseResponse.SCHEDULE_NOT_FOUND); + } + const locations = await this.getLocationList(schedules); + const detailSchedules = await this.getDetailScheduleList(schedules); + const diary = await this.getDiaryStatus(schedules); + }), + ); + } + /*지도에서 사용자의 월별 여정 불러오기*/ async getMonthlyJourneyMap(userId: number, monthInfoDto: MonthInfoDto) { const user = await UserEntity.findExistUser(userId); @@ -197,20 +217,18 @@ export class MapService { const schedules: ScheduleEntity[] = await ScheduleEntity.findMonthlySchedule(journeyId, monthInfoDto); return schedules; - // const schedules: ScheduleEntity[] = - // await ScheduleEntity.findExistScheduleByJourneyId(journeyId); - // const monthlySchedules: ScheduleEntity[] = await Promise.all( - // schedules.map(async (schedule) => { - // const monthlySchedule = await ScheduleEntity.findMonthlySchedule( - // schedule, - // monthInfoDto, - // ); - // console.log('4월 일정', monthlySchedule); - // return monthlySchedule; - // }), - // ); - - // return monthlySchedules; + } + + // 사용자의 세부 일정 가지고 오기 + async getDetailScheduleList(schedules: ScheduleEntity[]) { + const detailScheduleList = await Promise.all( + schedules.map(async (schedule) => { + const detailSchedules = + await DetailScheduleEntity.findExistDetailByScheduleId(schedule); + return detailSchedules; + }), + ); + return detailScheduleList; } //여정에 작성한 일지 개수 가지고 오기 From 7e85a40320c5c024566209849a95532c45a84f9b Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 17:37:53 +0900 Subject: [PATCH 152/316] =?UTF-8?q?=EC=9B=94=EB=B3=84=20=EC=9D=BC=EC=A0=95?= =?UTF-8?q?=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/model/journey.entity.ts | 6 ++-- src/map/map.controller.ts | 2 +- src/map/map.service.ts | 53 ++++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 629074f..4925400 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -11,6 +11,8 @@ import { OneToMany, ManyToOne, Between, + LessThanOrEqual, + MoreThanOrEqual, } from 'typeorm'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; @@ -96,10 +98,10 @@ export class JourneyEntity extends BaseEntity { return journey; } - static async findExistJourneyByDate(userId: number, date) { + static async findExistJourneyByDate(userId: number, date: Date) { const journeys: JourneyEntity[] = await JourneyEntity.find({ where: { - id: userId, + user: { id: userId }, startDate: Between(new Date(0), date), endDate: Between(date, new Date('9999-12-31')), }, diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index f9e5fb9..00a0095 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -44,7 +44,7 @@ export class MapController { description: '성공 ', }) @UseGuards(UserGuard) - @Get('get-monthly-schedule') + @Get('get-monthly-schedule/:date') async getMonthlySchedule(@Param('date') date: Date, @Req() req: Request) { const user = req.user; const result = await this.mapService.getMonthlySchedules(user.id, date); diff --git a/src/map/map.service.ts b/src/map/map.service.ts index a31ef92..5cd39d8 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -15,20 +15,42 @@ export class MapService { /*캘린더에서 사용자의 월별 일정 불러오기*/ async getMonthlySchedules(userId: number, date: Date) { const user = await UserEntity.findExistUser(userId); - const journeys = await JourneyEntity.findExistJourneyByDate(userId, date); - const scheduleList = await Promise.all( + const journeys = await JourneyEntity.findExistJourneyByDate(user.id, date); + console.log('포함 여정 : ', journeys); + const result = await Promise.all( journeys.map(async (journey) => { const schedules = await ScheduleEntity.findExistScheduleByJourneyId( journey.id, ); - if (!schedules) { - return errResponse(BaseResponse.SCHEDULE_NOT_FOUND); - } - const locations = await this.getLocationList(schedules); - const detailSchedules = await this.getDetailScheduleList(schedules); - const diary = await this.getDiaryStatus(schedules); + const scheduleList = await Promise.all( + schedules.map(async (schedule) => { + const locations = await this.getLocationList([schedule]); // getLocationList에 schedule 배열을 전달 + const detailSchedules = await this.getDetailScheduleList([ + schedule, + ]); // getDetailScheduleList에 schedule 배열을 전달 + const diary = await this.getDiaryStatus([schedule]); // getDiaryStatus에 schedule 배열을 전달 + + return { + scheduleId: schedule.id, + title: schedule.title, + date: schedule.date, + location: locations, + detailSchedules: detailSchedules, + diary: diary, + }; + }), + ); + + return { + journeyId: journey.id, + startDate: journey.startDate, + endDate: journey.endDate, + scheduleList: scheduleList, + }; }), ); + + return response(BaseResponse.GET_SCHEDULE_SUCCESS, result); } /*지도에서 사용자의 월별 여정 불러오기*/ @@ -251,6 +273,7 @@ export class MapService { if (!diary) { return false; } + return true; }), ); @@ -271,3 +294,17 @@ export class MapService { }; } } + +// const scheduleList = await Promise.all( +// journeys.map(async (journey) => { +// const schedules = await ScheduleEntity.findExistScheduleByJourneyId( +// journey.id, +// ); +// if (!schedules) { +// return errResponse(BaseResponse.SCHEDULE_NOT_FOUND); +// } +// const locations = await this.getLocationList(schedules); +// const detailSchedules = await this.getDetailScheduleList(schedules); +// const diary = await this.getDiaryStatus(schedules); +// }), +// ); From 7b6f5c6212055609fe464459fbfe12708a6cabee Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:35:56 +0900 Subject: [PATCH 153/316] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=83=90=EC=83=89=20=EB=B2=84=EA=B7=B8=20=EB=B0=8F=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0=20#115?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/mate.service.ts | 70 +++++++++++++++++++++++------- src/signature/signature.service.ts | 42 +++++++++++------- src/user/user.service.ts | 14 +++--- 3 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 01db56a..1a9d105 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -7,7 +7,7 @@ import { CursorPageOptionsDto } from './cursor-page/cursor-page-option.dto'; import { CursorPageDto } from './cursor-page/cursor-page.dto'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureService } from '../signature/signature.service'; -import { LessThan, Like, MoreThan } from 'typeorm'; +import { LessThan, Like, MoreThan, Not } from 'typeorm'; import { CursorPageMetaDto } from './cursor-page/cursor-page.meta.dto'; import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; import { UserEntity } from '../user/user.entity'; @@ -40,7 +40,7 @@ export class MateService{ }, }); - if(!mySignaturePageEntity){ // 사용자가 아직 한번도 시그니처를 작성한 적이 없을 경우 + if(!mySignaturePageEntity){ // 로그인한 사용자가 아직 한번도 시그니처를 작성한 적이 없을 경우 return null; } @@ -76,10 +76,10 @@ export class MateService{ // 5. 시그니처 작성자 기준으로 분류: 중복된 작성자를 또 찾아오지 않기 위해 const signatureGroups = {}; for(const signature of commonLocationSignatures){ - if(!signatureGroups[signature.user.id]){ + if(!signatureGroups[signature.user.id]){ // 새로운 유저일 경우 새 리스트 생성, 시그니처 삽입 signatureGroups[signature.user.id] = []; + signatureGroups[signature.user.id].push(signature); } - signatureGroups[signature.user.id].push(signature); } @@ -87,11 +87,12 @@ export class MateService{ const mateProfiles : MateRecommendProfileDto[] = []; for(const authorUserId of Object.keys(signatureGroups)){ - const mate = await this.userService.findUserById(Number(authorUserId)); - console.log(mate); + const authorId:number = Number(authorUserId); + const mate = await this.userService.findUserById(authorId); - if(userId == Number(authorUserId)) continue; // 본인은 제외 - const mateProfile: MateRecommendProfileDto = await this.generateMateProfile(mate, userId); + if(userId == authorId) continue; // 본인은 제외 + const locationSignature:SignatureEntity = signatureGroups[authorId][0]; + const mateProfile: MateRecommendProfileDto = await this.generateMateProfile(mate, userId, locationSignature); mateProfiles.push(mateProfile); } @@ -119,7 +120,7 @@ export class MateService{ }, take: 1 }); - const max = newUser[0].id; // 랜덤 숫자 생성의 max 값 + const max = newUser[0].id + 1; // 랜덤 숫자 생성의 max 값 console.log('max id: ',max); const min = 5; // 랜덤 숫자 생성의 min 값 @@ -137,7 +138,7 @@ export class MateService{ const [mates, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: cursorId ? { - id: LessThan(cursorId), + id: LessThan(cursorId) }: null, order: { id: "DESC" as any, @@ -150,7 +151,8 @@ export class MateService{ const mateProfiles:MateRecommendProfileDto[] = []; for(const mate of mates){ - const mateProfile = await this.generateMateProfile(mate, userId); + if(userId == mate.id) continue; // 본인은 제외 + const mateProfile = await this.generateMateProfile(mate, userId, null); mateProfiles.push(mateProfile); } @@ -176,15 +178,19 @@ export class MateService{ } - async generateMateProfile(mate:UserEntity, userId:number){ + async generateMateProfile(mate:UserEntity, userId:number, locationSignature:SignatureEntity){ const mateProfile = new MateRecommendProfileDto(); + + // 1. 메이트의 기본 정보 담기 mateProfile._id = mate.id; mateProfile.mateName = mate.nickname; mateProfile.introduction = mate.introduction; + // 2. 로그인한 유저가 메이트를 팔로우하는지 팔로우 여부 체크 const myEntity = await this.userService.findUserById(userId); - mateProfile.is_followed = await this.userService.checkIfFollowing(mate, mate.id); + mateProfile.is_followed = await this.userService.checkIfFollowing(myEntity, mate.id); + // 3. 메이트 프로필 사진 가져오기 let userProfileImage = await this.userService.getProfileImage(mate.id); if(!userProfileImage) mateProfile.mateImage = null; else{ @@ -192,18 +198,50 @@ export class MateService{ mateProfile.mateImage = await this.s3Service.getImageUrl(userImageKey); } - const recentSignatures = await this.signatureService.getMyRecentSignatures(mate.id); + /**************************************************** + 4. 메이트 대표 시그니처 두 개 구하기 + [1] 랜덤 메이트 추천: 가장 최신 시그니처 두 개 + [2] 장소 기반 추천: 장소 관련 시그니처 1개, 최신 시그니처 1개 + ****************************************************/ + console.log("locationSig: ", locationSignature); + + let recommendSignatures = []; + + if(locationSignature == null){ // [1] 랜덤 추천이면 가장 최신 시그니처 두 개 가져오기 + recommendSignatures = await this.signatureService.getMyRecentSignatures(mate.id, 2); + } + else{ // [2] 장소 기반 추천이면 장소 관련 하나, 최신 시그니처 하나 + recommendSignatures.push(locationSignature); + console.log("recommendSignatures: ",recommendSignatures); + + // ㄱ. 삽입할 최신 시그니처 후보 두 개 가져오기 (두 개 중에 이미 삽입된 시그니처와 다른 것을 추가할 것임) + const recentSignatures = await this.signatureService.getMyRecentSignatures(mate.id, 2); + + // ㄴ. 이미 들어있는 시그니처와 id 비교해서 다르면 삽입 + for(const recentSignature of recentSignatures){ + console.log("recentSignature.id: ",recentSignature.id); + console.log("locationSignature.id: ",locationSignature.id); + + if(recentSignature.id != locationSignature.id) { // 이미 들어있는 시그니처와 다른 경우에만 push + console.log("push! : ", recentSignature.id) + recommendSignatures.push(recentSignature); + break; + } + } + } const signatureCovers = []; - //TODO 작성자가 작성한 시그니처가 하나일 경우에는 리스트에 하나만 담겨있음 프론트에 알리기 - for(const signature of recentSignatures) { + //TODO 작성자가 작성한 시그니처가 하나일 경우에는 리스트에 하나만 담겨있음 프론트에 알리기 -> 완료 + for(const signature of recommendSignatures) { const signatureCover: MateSignatureCoverDto = new MateSignatureCoverDto(); + console.log("signature.id: ",signature.id); signatureCover._id = signature.id; signatureCover.title = signature.title; let thumbnailImageKey = await SignaturePageEntity.findThumbnail(signature.id); signatureCover.image = await this.s3Service.getImageUrl(thumbnailImageKey); + console.log("signatureCover: ", signatureCover); signatureCovers.push(signatureCover); } mateProfile.signatures = signatureCovers; diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 50264f8..7f8a769 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -140,25 +140,35 @@ export class SignatureService { // [2] 시그니처 작성자 정보 가져오기 const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); + if(!authorDto){ + if(loginUser.id != signature.user.id) { + authorDto._id = signature.user.id; + authorDto.name = signature.user.nickname; - console.log("시그니처 작성자 id: ",signature.user.id); - console.log("로그인한 유저 id: ",loginUser.id); - // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 - if(loginUser.id != signature.user.id) { - authorDto._id = signature.user.id; - authorDto.name = signature.user.nickname; - - const image = await this.userService.getProfileImage(signature.user.id); - if(image == null) authorDto.image = null; - else{ - authorDto.image = await this.s3Service.getImageUrl(image.imageKey); - } + const image = await this.userService.getProfileImage(signature.user.id); + if(image == null) authorDto.image = null; + else{ + authorDto.image = await this.s3Service.getImageUrl(image.imageKey); + } - // 해당 시그니처 작성자를 팔로우하고 있는지 확인 - authorDto.is_followed = await this.userService.checkIfFollowing(loginUser,signature.user.id); + // 해당 시그니처 작성자를 팔로우하고 있는지 확인 + authorDto.is_followed = await this.userService.checkIfFollowing(loginUser,signature.user.id); + detailSignatureDto.author = authorDto; + } + } + else{ // 해당 시그니처를 작성한 유저가 존재하지 않는 경우(탈퇴한 경우) + console.log("유저가 존재하지 않습니다."); + authorDto._id = null; + authorDto.name = null; + authorDto.image = null; + authorDto.is_followed = null; detailSignatureDto.author = authorDto; } + console.log("시그니처 작성자 id: ",signature.user.id); + console.log("로그인한 유저 id: ",loginUser.id); + // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 + /****************************************/ // [3] 시그니처 헤더 정보 담기 @@ -377,7 +387,7 @@ export class SignatureService { } } - async getMyRecentSignatures(userId: number) { // 가장 최신 시그니처 두 개 반환 + async getMyRecentSignatures(userId: number, take:number) { // 가장 최신 시그니처 반환 // 1. 메이트 탐색의 기준이 될 장소 가져오기 = 사용자의 가장 최신 시그니처의 첫 번째 페이지 장소 return await SignatureEntity.find({ where: { @@ -386,7 +396,7 @@ export class SignatureService { order: { created: 'DESC' // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 }, - take: 2, // 최신 시그니처 두 개 가져오기 + take: take, // 최신 시그니처 가져오기 }); } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index a2d692b..db6c1d3 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -58,14 +58,16 @@ export class UserService { ): Promise { // user가 targetUser를 팔로우하고 있는지 확인 - const followingArray = user.following || []; - console.log('사용자가 팔로우하는 유저', user.following); + const isFollowing = await UserFollowingEntity.findOne({ + where: { + user: { id: targetUserId }, + followUser: { id: user.id } + } + }); - const isFollowing = followingArray.some( - (following) => following.followUser.id === targetUserId, - ); + if(isFollowing) return true; + else return false; - return isFollowing; } async findUserById(userId: number): Promise { From a79db7b2527fce75c09bf5f0d2d382c7c376a468 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 19:17:22 +0900 Subject: [PATCH 154/316] =?UTF-8?q?feat=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EB=AC=B4=ED=95=9C=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=201=EC=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.controller.ts | 9 ++++++++- src/map/map.service.ts | 23 ++++++++++++++++++++--- src/response/response.status.ts | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index 00a0095..cb5dccd 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -47,7 +47,14 @@ export class MapController { @Get('get-monthly-schedule/:date') async getMonthlySchedule(@Param('date') date: Date, @Req() req: Request) { const user = req.user; - const result = await this.mapService.getMonthlySchedules(user.id, date); + const cursor = 0; + const pageSize = 3; + const result = await this.mapService.getMonthlySchedules( + user.id, + date, + cursor, + pageSize, + ); return result; } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 5cd39d8..4a74cc0 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -13,12 +13,24 @@ import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity @Injectable() export class MapService { /*캘린더에서 사용자의 월별 일정 불러오기*/ - async getMonthlySchedules(userId: number, date: Date) { + async getMonthlySchedules( + userId: number, + date: Date, + cursor: number, + pageSize: number, + ) { const user = await UserEntity.findExistUser(userId); const journeys = await JourneyEntity.findExistJourneyByDate(user.id, date); console.log('포함 여정 : ', journeys); + // 커서 값에 해당하는 배너들을 가져옴 + const paginatedJourneys = journeys.slice(cursor, cursor + pageSize); + if (paginatedJourneys.length === 0) { + return { + data: response(BaseResponse.SCHEDULE_NOT_FOUND, { nextCursor: null }), + }; + } const result = await Promise.all( - journeys.map(async (journey) => { + paginatedJourneys.map(async (journey) => { const schedules = await ScheduleEntity.findExistScheduleByJourneyId( journey.id, ); @@ -49,8 +61,13 @@ export class MapService { }; }), ); + // 다음 페이지를 위한 커서 값 계산 + const nextCursor = cursor + pageSize; - return response(BaseResponse.GET_SCHEDULE_SUCCESS, result); + return { + data: response(BaseResponse.GET_SCHEDULE_SUCCESS, result), + nextCursor: nextCursor, + }; } /*지도에서 사용자의 월별 여정 불러오기*/ diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 8f39ee0..71a49e2 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -106,7 +106,7 @@ export const BaseResponse = { SCHEDULE_NOT_FOUND: { success: false, code: 404, - message: '스케줄이 없습니다.', + message: '일정이 없습니다.', }, DETAIL_SCHEDULE_NOT_FOUND: { success: false, From f18025a660bb0b21b998d9e2c79975c88374f386 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 19:43:59 +0900 Subject: [PATCH 155/316] =?UTF-8?q?feat=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cursor-based-pagination-request.dto.ts.ts | 25 +++++++++++++++++++ src/map/map.controller.ts | 15 ++++++----- src/map/map.service.ts | 13 ++++++---- 3 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 src/map/cursor-based-pagination-request.dto.ts.ts diff --git a/src/map/cursor-based-pagination-request.dto.ts.ts b/src/map/cursor-based-pagination-request.dto.ts.ts new file mode 100644 index 0000000..879f8b2 --- /dev/null +++ b/src/map/cursor-based-pagination-request.dto.ts.ts @@ -0,0 +1,25 @@ +// PaginationDto.ts +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt, IsOptional, Min } from 'class-validator'; + +export class CursorBasedPaginationRequestDto { + @ApiProperty({ + description: '커서 값', + required: false, + default: 3, + }) + @IsInt() + @IsOptional() + @Min(0) + cursor?: number = 0; + + @ApiProperty({ + description: '페이지 크기', + required: false, + default: 3, + }) + @IsInt() + @IsOptional() + @Min(1) + pageSize?: number = 3; +} diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index cb5dccd..b540ddc 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -1,9 +1,11 @@ import { MapService } from './map.service'; -import { Controller, Param, Req, UseGuards, Get } from '@nestjs/common'; +import { Controller, Param, Req, UseGuards, Get, Query } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; import { UserGuard } from 'src/user/user.guard'; import { MonthInfoDto } from './month-info.dto'; +import { CursorBasedPaginationRequestDto } from './cursor-based-pagination-request.dto.ts'; + @Controller('map') export class MapController { constructor(private readonly mapService: MapService) {} @@ -45,15 +47,16 @@ export class MapController { }) @UseGuards(UserGuard) @Get('get-monthly-schedule/:date') - async getMonthlySchedule(@Param('date') date: Date, @Req() req: Request) { + async getMonthlySchedule( + @Param('date') date: Date, + @Query() options: CursorBasedPaginationRequestDto, + @Req() req: Request, + ) { const user = req.user; - const cursor = 0; - const pageSize = 3; const result = await this.mapService.getMonthlySchedules( user.id, date, - cursor, - pageSize, + options, ); return result; } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 4a74cc0..62465c7 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -9,6 +9,7 @@ import { MonthInfoDto } from './month-info.dto'; import { LocationEntity } from 'src/location/location.entity'; import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; +import { CursorBasedPaginationRequestDto } from './cursor-based-pagination-request.dto.ts'; @Injectable() export class MapService { @@ -16,14 +17,16 @@ export class MapService { async getMonthlySchedules( userId: number, date: Date, - cursor: number, - pageSize: number, + options: CursorBasedPaginationRequestDto, ) { const user = await UserEntity.findExistUser(userId); const journeys = await JourneyEntity.findExistJourneyByDate(user.id, date); - console.log('포함 여정 : ', journeys); + // 커서 값에 해당하는 배너들을 가져옴 - const paginatedJourneys = journeys.slice(cursor, cursor + pageSize); + const paginatedJourneys = journeys.slice( + options.cursor, + options.cursor + options.pageSize, + ); if (paginatedJourneys.length === 0) { return { data: response(BaseResponse.SCHEDULE_NOT_FOUND, { nextCursor: null }), @@ -62,7 +65,7 @@ export class MapService { }), ); // 다음 페이지를 위한 커서 값 계산 - const nextCursor = cursor + pageSize; + const nextCursor = Number(options.cursor) + Number(options.pageSize); return { data: response(BaseResponse.GET_SCHEDULE_SUCCESS, result), From 5d09265919637f3822a00fe6c6988ffa41595400 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 10 Feb 2024 20:34:27 +0900 Subject: [PATCH 156/316] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=83=90=EC=83=89=20-=20=EC=9E=91=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.controller.ts | 34 ++++++++++++++++++++++----- src/signature/signature.service.ts | 10 ++++---- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index fa5cffd..6511ff0 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -130,12 +130,34 @@ export class SignatureController { result ); } - return new ResponseDto( - ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, - true, - "시그니처 상세보기 성공", - result - ); + + if(result.author){ // 작성자가 본인이 아닌 경우 + if(result.author._id == null){ // 작성자가 탈퇴한 경우 + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공: 작성자 탈퇴", + result + ); + } + else{ + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공: 메이트의 시그니처", + result + ); + + } + } + else{ // 작성자가 본인인 경우 author 없음 + return new ResponseDto( + ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, + true, + "시그니처 상세보기 성공: 내 시그니처", + result + ); + } } catch(error){ console.log('Error on signatureId: ',error); diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 7f8a769..9a6b06f 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -140,8 +140,9 @@ export class SignatureService { // [2] 시그니처 작성자 정보 가져오기 const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); - if(!authorDto){ - if(loginUser.id != signature.user.id) { + + if(signature.user){ + if(loginUser.id != signature.user.id) { // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 authorDto._id = signature.user.id; authorDto.name = signature.user.nickname; @@ -157,7 +158,7 @@ export class SignatureService { } } else{ // 해당 시그니처를 작성한 유저가 존재하지 않는 경우(탈퇴한 경우) - console.log("유저가 존재하지 않습니다."); + console.log("작성자 유저가 존재하지 않습니다."); authorDto._id = null; authorDto.name = null; authorDto.image = null; @@ -165,9 +166,6 @@ export class SignatureService { detailSignatureDto.author = authorDto; } - console.log("시그니처 작성자 id: ",signature.user.id); - console.log("로그인한 유저 id: ",loginUser.id); - // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 /****************************************/ From e08338fca899be4d0c70503f8619ee72ddd9288f Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 21:11:48 +0900 Subject: [PATCH 157/316] =?UTF-8?q?=20=EC=9B=94=EB=B3=84=20=EC=97=AC?= =?UTF-8?q?=EC=A0=95=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20:=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=A1=B0=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 1 + src/journey/model/journey.entity.ts | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 783a599..2b6bfed 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -18,6 +18,7 @@ export class JourneyService { //여정 생성하기 - 일정, 일지 함께 생성 async createJourney(user, createJourneyDto: CreateJourneyDto) { const existJourney = await JourneyEntity.findExistJourneyByPeriod( + user.id, createJourneyDto, ); if (existJourney) { diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 4925400..eb40fa6 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -87,11 +87,12 @@ export class JourneyEntity extends BaseEntity { return journeys; } - static async findExistJourneyByPeriod(createJourneyDto) { + static async findExistJourneyByPeriod(userId, createJourneyDto) { const journey: JourneyEntity = await JourneyEntity.findOne({ where: { - startDate: createJourneyDto.startDate, - endDate: createJourneyDto.endDate, + user: { id: userId }, + startDate: LessThanOrEqual(createJourneyDto.endDate), + endDate: MoreThanOrEqual(createJourneyDto.startDate), }, }); From abca1a0e538d996d9f3087f70e9b642d06f483b6 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:55:05 +0900 Subject: [PATCH 158/316] =?UTF-8?q?[Refactor]=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98=20=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=EC=9E=90=20=EC=A0=95=EB=B3=B4=20Responese?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.controller.ts | 7 +++---- src/signature/signature.service.ts | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 6511ff0..3df4315 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -42,9 +42,9 @@ export class SignatureController { } } - @Post('/new') + @Post('/new') // 시그니처 생성하기 @UseGuards(UserGuard) - async createNewSignature( // 시그니처 생성하기 + async createNewSignature( @Body() newSignature: CreateSignatureDto, @Req() req: Request ): Promise> { @@ -131,7 +131,7 @@ export class SignatureController { ); } - if(result.author){ // 작성자가 본인이 아닌 경우 + if(result.author.is_followed){ // 작성자가 본인이 아닌 경우 if(result.author._id == null){ // 작성자가 탈퇴한 경우 return new ResponseDto( ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, @@ -147,7 +147,6 @@ export class SignatureController { "시그니처 상세보기 성공: 메이트의 시그니처", result ); - } } else{ // 작성자가 본인인 경우 author 없음 diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 9a6b06f..ae5db25 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -142,20 +142,21 @@ export class SignatureService { const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); if(signature.user){ - if(loginUser.id != signature.user.id) { // 본인의 시그니처면 빈 객체를, 다르면 작성자의 프로필 정보를 담는다 - authorDto._id = signature.user.id; - authorDto.name = signature.user.nickname; + authorDto._id = signature.user.id; + authorDto.name = signature.user.nickname; - const image = await this.userService.getProfileImage(signature.user.id); - if(image == null) authorDto.image = null; - else{ - authorDto.image = await this.s3Service.getImageUrl(image.imageKey); - } + const image = await this.userService.getProfileImage(signature.user.id); + if(image == null) authorDto.image = null; + else authorDto.image = await this.s3Service.getImageUrl(image.imageKey); + if(loginUser.id == signature.user.id) { // 시그니처 작성자가 본인이면 is_followed == null + authorDto.is_followed = null; + } + else{ // 해당 시그니처 작성자를 팔로우하고 있는지 확인 authorDto.is_followed = await this.userService.checkIfFollowing(loginUser,signature.user.id); - detailSignatureDto.author = authorDto; } + detailSignatureDto.author = authorDto; } else{ // 해당 시그니처를 작성한 유저가 존재하지 않는 경우(탈퇴한 경우) console.log("작성자 유저가 존재하지 않습니다."); @@ -166,7 +167,6 @@ export class SignatureService { detailSignatureDto.author = authorDto; } - /****************************************/ // [3] 시그니처 헤더 정보 담기 From 912c5ea3eac22685626f1614ebf8533e4c62488b Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:55:30 +0900 Subject: [PATCH 159/316] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/mate.controller.ts | 59 +++++++++++++++++---------------- src/search/search.controller.ts | 4 +-- src/search/search.service.ts | 7 ++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/mate/mate.controller.ts b/src/mate/mate.controller.ts index db3c0fa..2b05e6c 100644 --- a/src/mate/mate.controller.ts +++ b/src/mate/mate.controller.ts @@ -15,9 +15,37 @@ export class MateController{ constructor(private readonly mateService:MateService) {} - @Get('/location') + + @Get('/random') // 메이트 탐색 첫째 줄: 랜덤으로 메이트 추천 + @UseGuards(UserGuard) + async getRandomMateProfileWithInfiniteCursor( + @Req() req: Request, + @Query() cursorPageOptionDto: CursorPageOptionsDto + ){ + try{ + const result = await this.mateService.recommendRandomMateWithInfiniteScroll(cursorPageOptionDto, req.user.id); + + return new ResponseDto( + ResponseCode.GET_RANDOM_MATE_PROFILE_SUCCESS, + true, + "랜덤 메이트 추천 데이터 생성 성공", + result + ); + } + catch(e){ + console.log(e); + return new ResponseDto( + ResponseCode.GET_RANDOM_MATE_PROFILE_FAIL, + false, + "랜덤 메이트 추천 데이터 생성 실패", + null + ); + } + } + + @Get('/location') // 메이트 탐색 둘째 줄: 나와 공통 장소를 사용한 메이트 추천 @UseGuards(UserGuard) - async getMateProfileWithMyFirstLocation( // 메이트 탐색 첫 줄: 나와 공통 장소를 사용한 메이트 추천 + async getMateProfileWithMyFirstLocation( @Req() req: Request, ): Promise> { @@ -49,33 +77,6 @@ export class MateController{ null ); } - } - @Get('/random') - @UseGuards(UserGuard) - async getRandomMateProfileWithInfiniteCursor( // 메이트 탐색 둘째 줄: 랜덤으로 메이트 추천 - @Req() req: Request, - @Query() cursorPageOptionDto: CursorPageOptionsDto - ){ - try{ - const result = await this.mateService.recommendRandomMateWithInfiniteScroll(cursorPageOptionDto, req.user.id); - - return new ResponseDto( - ResponseCode.GET_RANDOM_MATE_PROFILE_SUCCESS, - true, - "랜덤 메이트 추천 데이터 생성 성공", - result - ); - } - catch(e){ - console.log(e); - return new ResponseDto( - ResponseCode.GET_RANDOM_MATE_PROFILE_FAIL, - false, - "랜덤 메이트 추천 데이터 생성 실패", - null - ); - } - } } \ No newline at end of file diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index be93d20..0577c75 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -14,7 +14,7 @@ export class SearchController{ constructor(private readonly searchService: SearchService) {} - @Get('/') + @Get('/') // 팀색탭 메인: 인기 급상승, 메이트의 최신 시그니처 @UseGuards(UserGuard) async getSearchMain( @Req() req: Request, @@ -47,7 +47,7 @@ export class SearchController{ } } - @Get('/find') + @Get('/find') // 탑색탭 검색: 키워드로 시그니처 검색하기 async search(@Query('keyword') keyword: string): Promise> { try{ diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 4490136..b24b5c1 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -93,9 +93,8 @@ export class SearchService{ return signatureCovers; } - async searchByKeyword(keyword: string) { + async searchByKeyword(keyword: string) { // 키워드로 검색하기 try{ - // 키워드로 검색하기 const resultSignatures = await SignatureEntity.find({ where:{ title: Like(`%${keyword}%`) }, relations: ['user'] // user 포함 @@ -115,8 +114,8 @@ export class SearchService{ } - async getSignatureCover(signature:SignatureEntity):Promise{ - // 시그니처 커버 만들기 + async getSignatureCover(signature:SignatureEntity) // 시그니처 커버 만들기 + :Promise{ const signatureCover = new CoverSignatureDto(); signatureCover._id = signature.id; From faea98f270defe549e2574d2f7a963867b3453b8 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 21:57:45 +0900 Subject: [PATCH 160/316] =?UTF-8?q?=EC=97=AC=EC=A0=95=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.controller.ts | 26 +++++++++++++++++++++++++- src/journey/journey.service.ts | 12 ++++++++++++ src/journey/model/journey.entity.ts | 18 +++++++++++++++++- src/response/response.status.ts | 5 +++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 8170926..80aea7e 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -6,6 +6,7 @@ import { Post, Delete, Param, + Put, } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; @@ -36,8 +37,31 @@ export class JourneyController { ); return result; } + /*여정 수정하기*/ + @ApiOperation({ + summary: '여정 수정하기', + description: '제목을 수정합니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Put('update/:journeyId') + async updateJourney( + @Body('title') title: string, + @Param('journeyId') journeyId: number, + @Req() req: Request, + ) { + const user = req.user; + const result = await this.journeyService.updateJourney( + user, + journeyId, + title, + ); + return result; + } - /*여정 저장하기*/ + /*여정 삭제하기*/ @ApiOperation({ summary: '여정 삭제하기', description: diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 2b6bfed..ea34267 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -11,6 +11,7 @@ import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { CreateJourneyDto } from './dtos/create-journey.dto'; import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; +import { UserEntity } from 'src/user/user.entity'; import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; @Injectable() @@ -43,6 +44,17 @@ export class JourneyService { return errResponse(BaseResponse.JOURNEY_CREATED); } + //여정 수정하기 + async updateJourney(user, journeyId: number, title: string) { + const existUser = await UserEntity.findExistUser(user.id); + const journey = await JourneyEntity.findExistJourneyByOptions( + existUser.id, + journeyId, + ); + const updateJourney = await JourneyEntity.updateJourney(journey, title); + return response(BaseResponse.UPDATE_JOURNEY_TITLE_SUCCESS); + } + //여정 삭제하기 - 일정, 일지, async deleteJourney(journeyId: number) { diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index eb40fa6..32d773e 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -64,6 +64,12 @@ export class JourneyEntity extends BaseEntity { } } + //여정 수정하기 + static async updateJourney(journey: JourneyEntity, title: string) { + journey.title = title; + return await journey.save(); + } + //여정 삭제하기 static async deleteJourney(journey) { return await JourneyEntity.remove(journey); @@ -79,7 +85,7 @@ export class JourneyEntity extends BaseEntity { return journey; } - static async findExistJourneyByUserId(userId) { + static async findExistJourneysByUserId(userId) { const journeys: JourneyEntity[] = await JourneyEntity.find({ where: { user: { id: userId } }, }); @@ -87,6 +93,16 @@ export class JourneyEntity extends BaseEntity { return journeys; } + static async findExistJourneyByOptions(userId, journeyId) { + const journey: JourneyEntity = await JourneyEntity.findOne({ + where: { + id: journeyId, + user: { id: userId }, + }, + }); + return journey; + } + static async findExistJourneyByPeriod(userId, createJourneyDto) { const journey: JourneyEntity = await JourneyEntity.findOne({ where: { diff --git a/src/response/response.status.ts b/src/response/response.status.ts index 71a49e2..4610971 100644 --- a/src/response/response.status.ts +++ b/src/response/response.status.ts @@ -20,6 +20,11 @@ export const BaseResponse = { code: 200, message: '세부 일정을 삭제했습니다.', }, + UPDATE_JOURNEY_TITLE_SUCCESS: { + success: true, + code: 200, + message: '여정 제목을 수정했습니다', + }, UPDATE_DETAIL_SCHEDULE_STATUS_SUCCESS: { success: true, code: 200, From 0b78b110e672def800e9bcfad1cc3f35af671788 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sat, 10 Feb 2024 22:11:10 +0900 Subject: [PATCH 161/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A0=95=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0-title=EC=9D=B4=20null?= =?UTF-8?q?=EC=9D=BC=EB=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index ea34267..db213ad 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -51,6 +51,13 @@ export class JourneyService { existUser.id, journeyId, ); + if (!journey) { + return errResponse(BaseResponse.JOURNEY_NOT_FOUND); + } + if (title === null) { + const updateJourney = await JourneyEntity.updateJourney(journey, ''); + return response(BaseResponse.UPDATE_JOURNEY_TITLE_SUCCESS); + } const updateJourney = await JourneyEntity.updateJourney(journey, title); return response(BaseResponse.UPDATE_JOURNEY_TITLE_SUCCESS); } From 74702e31f8e6188a8024f4918929d8513d1dfdb8 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 11 Feb 2024 00:22:13 +0900 Subject: [PATCH 162/316] =?UTF-8?q?chore:=20relation=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=ED=98=95=ED=83=9C=EB=A5=BC=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f0b6294..740b7a1 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -143,8 +143,25 @@ export class RuleService { // [5] 여행 규칙 전체 리스트 조회 async getRuleList(userId: number) :Promise { const userEntity = await UserEntity.findOne({ - where: {id: userId}, - relations: ['ruleParticipate', 'ruleParticipate.rule', 'ruleParticipate.rule.invitations', 'ruleParticipate.rule.invitations.member' , 'ruleParticipate.rule.invitations.member.profileImage'] + where: { + id: userId, + }, + relations: { + ruleParticipate: { + rule: { + invitations: { + member: { + profileImage: true, + }, + }, + }, + }, + // 'ruleParticipate', + // 'ruleParticipate.rule', + // 'ruleParticipate.rule.invitations', + // 'ruleParticipate.rule.invitations.member', + // 'ruleParticipate.rule.invitations.member.profileImage' + }, }); try { From 441bedbeb33147ca85434838bc89d537766a9ad5 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 11 Feb 2024 00:23:57 +0900 Subject: [PATCH 163/316] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 740b7a1..427d960 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -186,6 +186,7 @@ export class RuleService { return ruleMains; } } catch (e) { + console.error(e); console.log('참여하는 여행 규칙이 없습니다'); } } From 62746ce3334ad15776bd912c7b2326631e690a4f Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 11 Feb 2024 00:25:56 +0900 Subject: [PATCH 164/316] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EA=B0=80=20=EC=97=86=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=EC=9D=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 427d960..90fa8eb 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -204,7 +204,6 @@ export class RuleService { // 사용자 프로필 이미지 const image = user.profileImage; - memberPair.image = image.imageKey; if(image == null) memberPair.image = null; else { const userImageKey = image.imageKey; From 38e30ec34408023ef1d3b335cc2b627ab52c3864 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 11 Feb 2024 00:32:26 +0900 Subject: [PATCH 165/316] =?UTF-8?q?feat=20:=20=EC=9B=94=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EB=B0=8F=20=EC=9C=84=EC=B9=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 2 +- src/location/location.entity.ts | 12 +++++++++ src/map/map.service.ts | 9 ++++--- src/schedule/dtos/update-schedule-dto.ts | 4 +++ src/schedule/schedule.controller.ts | 32 ++++++++++++++++-------- src/schedule/schedule.entity.ts | 31 ++++++++++++++++++++--- src/schedule/schedule.service.ts | 24 ++++++++++++++++++ 7 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index db213ad..0581f93 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -66,7 +66,7 @@ export class JourneyService { async deleteJourney(journeyId: number) { const journey = await JourneyEntity.findExistJourney(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, ); for (const schedule of schedules) { diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index d1e7f44..b381c6d 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -15,6 +15,9 @@ export class LocationEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; + @Column() + name: string; + @Column({ type: 'decimal', precision: 10, scale: 6 }) latitude: number; @@ -33,9 +36,11 @@ export class LocationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; + //위치 생성하기 static async createLocation(updateScheduleDto) { try { const location: LocationEntity = new LocationEntity(); + location.name = updateScheduleDto.location; location.latitude = updateScheduleDto.latitude; location.longitude = updateScheduleDto.longitude; @@ -45,11 +50,13 @@ export class LocationEntity extends BaseEntity { } } + //위치 수정하기 static async updateLocation(location: LocationEntity, updateScheduleDto) { try { const updateLocation = await LocationEntity.findOneOrFail({ where: { id: location.id }, }); + updateLocation.name = updateScheduleDto.location; updateLocation.latitude = updateScheduleDto.latitude; updateLocation.longitude = updateScheduleDto.longitude; @@ -59,6 +66,11 @@ export class LocationEntity extends BaseEntity { } } + //위치 삭제하기 + static async deleteLocation(location) { + return await LocationEntity.remove(location); + } + static async findExistLocation(updateScheduleDto) { { } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 62465c7..35f0524 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -34,7 +34,7 @@ export class MapService { } const result = await Promise.all( paginatedJourneys.map(async (journey) => { - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, ); const scheduleList = await Promise.all( @@ -114,7 +114,7 @@ export class MapService { /*지도에서 여정 정보 보여주기*/ async getJourneyPreview(journeyId) { const journey = await this.getJourneyInfo(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journeyId, ); const locationList = await this.getLocationList(schedules); @@ -140,7 +140,7 @@ export class MapService { /*작성한 일지 불러오기 - 지도*/ async getDiaryList(journeyId) { const journey = await JourneyEntity.findExistJourney(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, ); const diaryList = await Promise.all( @@ -170,7 +170,7 @@ export class MapService { /* 지도에서 세부 여정 확인하기 */ async getDetailJourneyList(journeyId) { const journey = await this.getJourneyInfo(journeyId); - const schedules = await ScheduleEntity.findExistScheduleByJourneyId( + const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, ); const scheduleInfoList = await this.getScheduleList(schedules); @@ -216,6 +216,7 @@ export class MapService { } return { locationId: location.id, + name: location.name, latitude: location.latitude, longitude: location.longitude, }; diff --git a/src/schedule/dtos/update-schedule-dto.ts b/src/schedule/dtos/update-schedule-dto.ts index dfaa492..8bd60b7 100644 --- a/src/schedule/dtos/update-schedule-dto.ts +++ b/src/schedule/dtos/update-schedule-dto.ts @@ -6,6 +6,10 @@ export class UpdateScheduleDto { @IsOptional() title: string; + @IsString() + @IsOptional() + location: string; + @IsOptional() @IsNumber() latitude: number; diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index e0e150a..19a1952 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -1,13 +1,7 @@ -import { - Body, - Controller, - Put, - Get, - Param, - Req, - UseGuards, -} from '@nestjs/common'; +import { Body, Controller, Put, Param, Req, UseGuards } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +import { Request } from 'express'; +import { UserGuard } from 'src/user/user.guard'; import { ScheduleService } from './schedule.service'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; @@ -16,7 +10,7 @@ export class ScheduleController { constructor(private readonly scheduleService: ScheduleService) {} @ApiOperation({ - summary: '여정 작성하기', + summary: '일정 작성하기', description: '제목과 위치를 작성합니다.', }) @ApiOkResponse({ @@ -30,4 +24,22 @@ export class ScheduleController { const result = await this.scheduleService.updateSchedule(scheduleId, body); return result; } + + @ApiOperation({ + summary: '일정 삭제하기', + description: '제목과 위치를 삭제합니다.', + }) + @ApiOkResponse({ + description: '성공 ', + }) + @UseGuards(UserGuard) + @Put('reset/:scheduleId') + async deleteSchedule( + @Param('scheduleId') scheduleId: number, + @Req() req: Request, + ) { + const user = req.user; + const result = await this.scheduleService.resetSchedule(user, scheduleId); + return result; + } } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index df5c4ad..47d0a37 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -30,8 +30,10 @@ export class ScheduleEntity extends BaseEntity { @Column({ nullable: true }) title: string; - @ManyToOne(() => LocationEntity, (location) => location.schedules) - location: LocationEntity; + @ManyToOne(() => LocationEntity, (location) => location.schedules, { + nullable: true, + }) + location: LocationEntity | null; @ManyToOne(() => JourneyEntity, (journey) => journey.schedules, { onDelete: 'CASCADE', @@ -82,11 +84,18 @@ export class ScheduleEntity extends BaseEntity { return await schedule.save(); } - //일정 삭제하기 + //일정 삭제하기 - 여정 삭제했을때 static async deleteSchedule(schedule) { return await ScheduleEntity.remove(schedule); } + //일정 리셋하기 - 제목, 위치 + static async resetSchedule(schedule: ScheduleEntity) { + schedule.title = ''; + schedule.location = null; + await schedule.save(); + } + //일정 조회하기 static async findExistSchedule(scheduleId): Promise { const schedule = await ScheduleEntity.findOne({ @@ -107,8 +116,18 @@ export class ScheduleEntity extends BaseEntity { return schedule.location; } + static async findExistLocations(location: LocationEntity) { + const existLocation = await ScheduleEntity.find({ + where: { + location: location, + }, + relations: ['location'], + }); + return existLocation; + } + //journeyId로 일정 조회하기 - static async findExistScheduleByJourneyId( + static async findExistSchedulesByJourneyId( journeyId: number, ): Promise { const schedules = await ScheduleEntity.find({ @@ -117,6 +136,10 @@ export class ScheduleEntity extends BaseEntity { }); return schedules; } + + static async findExistScheduleByOptions(journeyId, scheduleId) { + const schedule = await ScheduleEntity.find({}); + } // 월별 일정 조회하기 static async findMonthlySchedule( journeyId, diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index 685313d..e078384 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -5,6 +5,7 @@ import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; +import { JourneyEntity } from 'src/journey/model/journey.entity'; @Injectable() export class ScheduleService { @@ -42,4 +43,27 @@ export class ScheduleService { console.log(location); } } + + async resetSchedule(user, scheduleId) { + const existUser = await UserEntity.findExistUser(user.id); + const schedule = await ScheduleEntity.findExistSchedule(scheduleId); + + // 스케줄이 위치를 가지고 있는지 확인 + if (schedule.location) { + // 해당 위치가 다른 스케줄에서 사용되고 있는지 확인 + const existLocation = await ScheduleEntity.findExistLocations( + schedule.location, + ); + console.log(existLocation); + if (!existLocation) { + // 다른 스케줄에서 사용되고 있지 않으면 해당 위치 삭제 + console.log('삭제할', schedule.location); + await LocationEntity.deleteLocation(schedule.location); + } + } + + // 스케줄 초기화 + await ScheduleEntity.resetSchedule(schedule); + return response(BaseResponse.DELETE_SCHEDULE_SUCCESS); + } } From b70ceb214ba2050600dfb6344cccdf6b07d8dc00 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 11 Feb 2024 00:34:27 +0900 Subject: [PATCH 166/316] =?UTF-8?q?fix=20:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EC=97=90=20=EC=9D=B4=EB=A6=84=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/location/location.entity.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index b381c6d..300f9ed 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -76,6 +76,7 @@ export class LocationEntity extends BaseEntity { } const location = await LocationEntity.findOne({ where: { + name: updateScheduleDto.location, latitude: updateScheduleDto.latitude, longitude: updateScheduleDto.longitude, }, From 643bce2b8812ce792596a0ee286e05d74738c1f7 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 11 Feb 2024 00:54:57 +0900 Subject: [PATCH 167/316] =?UTF-8?q?chore=20:=20UserGuard=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-schedule.controller.ts | 24 ++++++++++++++++--- src/diary/diary.controller.ts | 20 ++++++++++++++-- src/map/map.controller.ts | 18 +++++++++++--- src/schedule/schedule.controller.ts | 2 ++ 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index 0ec7490..5bb982d 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -6,10 +6,14 @@ import { Delete, Param, Body, + UseGuards, + Req, } from '@nestjs/common'; import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +import { Request } from 'express'; import { DetailScheduleService } from './detail-schedule.service'; import { DetailContentDto } from './detail-schedule-info.dto'; +import { UserGuard } from 'src/user/user.guard'; @Controller('detail-schedule') export class DetailScheduleController { @@ -23,8 +27,12 @@ export class DetailScheduleController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Post('create/:scheduleId') - async createDetailSchedule(@Param('scheduleId') scheduleId: number) { + async createDetailSchedule( + @Req() req: Request, + @Param('scheduleId') scheduleId: number, + ) { const result = await this.detailScheduleService.createDetailSchedule( scheduleId, ); @@ -39,8 +47,10 @@ export class DetailScheduleController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Put('update/:detailId') async updateDetailSchedule( + @Req() req: Request, @Param('detailId') detailId: number, @Body() detailContentDto: DetailContentDto, ) { @@ -59,8 +69,12 @@ export class DetailScheduleController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Patch('update-status/:detailId') - async updateDetailStatus(@Param('detailId') detailId: number) { + async updateDetailStatus( + @Req() req: Request, + @Param('detailId') detailId: number, + ) { const result = await this.detailScheduleService.updateDetailStatus( detailId, ); @@ -76,8 +90,12 @@ export class DetailScheduleController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Delete('delete/:detailId') - async deleteDetailSchedule(@Param('detailId') detailId: number) { + async deleteDetailSchedule( + @Req() req: Request, + @Param('detailId') detailId: number, + ) { const result = await this.detailScheduleService.deleteDetailSchedule( detailId, ); diff --git a/src/diary/diary.controller.ts b/src/diary/diary.controller.ts index 3d33a56..b0e832d 100644 --- a/src/diary/diary.controller.ts +++ b/src/diary/diary.controller.ts @@ -1,5 +1,16 @@ import { ApiOperation, ApiOkResponse } from '@nestjs/swagger'; -import { Controller, Put, Post, Get, Body, Param } from '@nestjs/common'; +import { + Controller, + Req, + UseGuards, + Put, + Post, + Get, + Body, + Param, +} from '@nestjs/common'; +import { Request } from 'express'; +import { UserGuard } from 'src/user/user.guard'; import { DiaryService } from './diary.service'; import { PostDiaryDto } from './dtos/post-diary.dto'; @@ -15,8 +26,10 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Post('create/:scheduleId') async postJourney( + @Req() req: Request, @Param('scheduleId') scheduleId: number, @Body() body: PostDiaryDto, ) { @@ -32,8 +45,10 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Put('update/:diaryId') async updateDiary( + @Req() req: Request, @Param('diaryId') diaryId: number, @Body() body: PostDiaryDto, ) { @@ -49,8 +64,9 @@ export class DiaryController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Get('get/:scheduleId') - async getDiary(@Param('scheduleId') scheduleId: number) { + async getDiary(@Req() req: Request, @Param('scheduleId') scheduleId: number) { const result = await this.diaryService.getDiary(scheduleId); return result; } diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index b540ddc..986b104 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -69,8 +69,12 @@ export class MapController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Get('get-journey/:journeyId') - async getJourneyPreview(@Param('journeyId') journeyId: number) { + async getJourneyPreview( + @Req() req: Request, + @Param('journeyId') journeyId: number, + ) { const result = await this.mapService.getJourneyPreview(journeyId); return result; } @@ -83,8 +87,12 @@ export class MapController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Get('get-diaries/:journeyId') - async getDiaryList(@Param('journeyId') journeyId: number) { + async getDiaryList( + @Req() req: Request, + @Param('journeyId') journeyId: number, + ) { const result = await this.mapService.getDiaryList(journeyId); return result; } @@ -97,8 +105,12 @@ export class MapController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Get('get-schedules/:journeyId') - async getDetailJourneyList(@Param('journeyId') journeyId: number) { + async getDetailJourneyList( + @Req() req: Request, + @Param('journeyId') journeyId: number, + ) { const result = await this.mapService.getDetailJourneyList(journeyId); return result; } diff --git a/src/schedule/schedule.controller.ts b/src/schedule/schedule.controller.ts index 19a1952..16dec62 100644 --- a/src/schedule/schedule.controller.ts +++ b/src/schedule/schedule.controller.ts @@ -16,8 +16,10 @@ export class ScheduleController { @ApiOkResponse({ description: '성공 ', }) + @UseGuards(UserGuard) @Put('update/:scheduleId') async updateSchedule( + @Req() req: Request, @Param('scheduleId') scheduleId: number, @Body() body: UpdateScheduleDto, ) { From c89178ebddf98e2395ebf21a407527abe9ea0e36 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 01:28:34 +0900 Subject: [PATCH 168/316] =?UTF-8?q?fix=20:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=86=EB=8A=94=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.controller.ts | 26 ++++++++++- src/comment/comment.service.ts | 6 +++ .../dto/follow.search.dto.ts} | 4 +- src/follow/follow.controller.ts | 13 ++---- src/follow/follow.service.ts | 46 +++++++++++++++---- src/member/member.controller.ts | 4 +- src/rule/rule.service.ts | 1 + src/user/user.service.ts | 5 +- 8 files changed, 84 insertions(+), 21 deletions(-) rename src/{user/user.search.dto.ts => follow/dto/follow.search.dto.ts} (90%) diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index 49fccce..37edc74 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Body, Req, UseGuards, Param } from '@nestjs/common'; +import {Controller, Post, Body, Req, UseGuards, Param, Patch} from '@nestjs/common'; import { CommentService } from './comment.service'; import { CreateCommentDto } from './dto/create-comment.dto'; import { ResponseCode } from '../response/response-code.enum'; @@ -34,4 +34,28 @@ export class CommentController { result); } } + + // 여행 규칙 코멘트 수정 + /* + @Patch('/:ruleId') + @UseGuards(UserGuard) + async updateComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { + const result = await this.commentService.updateComment(dto, ruleId, req.user.id); + + if(!result){ + return new ResponseDto( + ResponseCode.COMMENT_UPDATE_FAIL, + false, + "여행 규칙 코멘트 수정 실패", + null); + } + else{ + return new ResponseDto( + ResponseCode.COMMENT_UPDATE_SUCCESS, + true, + "여행 규칙 코멘트 수정 성공", + result); + } + } + */ } \ No newline at end of file diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 4f4eb75..f953dad 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -27,4 +27,10 @@ export class CommentService { } return comment.id; } + + /* + async updateComment(dto: CreateCommentDto, ruleId: number, userId: number) : Promise { + + } + */ } diff --git a/src/user/user.search.dto.ts b/src/follow/dto/follow.search.dto.ts similarity index 90% rename from src/user/user.search.dto.ts rename to src/follow/dto/follow.search.dto.ts index 4cdce99..06cbe00 100644 --- a/src/user/user.search.dto.ts +++ b/src/follow/dto/follow.search.dto.ts @@ -1,9 +1,9 @@ import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; -export class UserSearchDto { +export class FollowSearchDto { @IsNotEmpty() @IsNumber() - mateId: number; + id: number; @IsNotEmpty() @IsString() diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 14c462e..bb0ecc6 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -2,15 +2,12 @@ import {Controller, Post, Req, UseGuards, Param, Delete, Get, Patch, Query} from import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -import { UserEntity } from 'src/user/user.entity'; import { UserService } from 'src/user/user.service'; -import { UserSearchDto} from "../user/user.search.dto"; import { UserGuard } from '../user/user.guard'; import {query, Request} from 'express'; -import {UserFollowingEntity} from "../user/user.following.entity"; -import * as querystring from "querystring"; +import { FollowSearchDto } from "./dto/follow.search.dto"; -@Controller('mate/search') +@Controller('mate') export class FollowController { constructor( private readonly followService: FollowService, @@ -18,7 +15,7 @@ export class FollowController { ) {} // [1] 팔로우 - @Patch('/follow/:followingId') + @Patch('/search/follow/:followingId') @UseGuards(UserGuard) async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { @@ -109,12 +106,12 @@ export class FollowController { @Query('searchTerm')searchTerm : string, @Req() req: Request): Promise> { try { - const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(req.user.id, searchTerm) + const followSearchDto : FollowSearchDto[] = await this.followService.getSearchResult(req.user.id, searchTerm) return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, "검색 결과 리스트 불러오기 성공", - userSearchDto + followSearchDto ); } catch (error) { return new ResponseDto( diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 47580e2..432caea 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -4,6 +4,9 @@ import { FollowDto } from './dto/follow.dto'; import { UserEntity } from "../user/user.entity"; import { UserService } from "../user/user.service"; import { S3UtilService } from "../utils/S3.service"; +import {SignatureEntity} from "../signature/domain/signature.entity"; +import {Like} from "typeorm"; +import {FollowSearchDto} from "./dto/follow.search.dto"; @Injectable() export class FollowService { @@ -71,6 +74,7 @@ export class FollowService { // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); + followDto.image = image.imageKey; if(image == null) followDto.image = null; else{ const userImageKey = image.imageKey; @@ -78,8 +82,7 @@ export class FollowService { } return followDto; - })) - + })); return informs; } @@ -106,14 +109,14 @@ export class FollowService { // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); + followDto.image = image.imageKey; if(image == null) followDto.image = null; else{ const userImageKey = image.imageKey; followDto.image = await this.s3Service.getImageUrl(userImageKey); } - return followDto; - })) + })); return informs; } @@ -121,12 +124,39 @@ export class FollowService { // [5] 메이트 검색 async getSearchResult(userId: number, searchTerm: string) { // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); + const resultUsers = await UserEntity.find({ + where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], + relations: ['profileImage', 'following'] + }); - // 찾은 사용자의 정보를 검색 결과 담아줄 dto 에 넣기 + const userEntity = await UserEntity.findExistUser(userId); - // 결과 return - } + const searchResult = await Promise.all(resultUsers.map(async (user) => { + const followSearchDto = new FollowSearchDto(); + + console.log('현재의 유저 : ', user.id); + followSearchDto.id = user.id; + followSearchDto.nickName = user.nickname; + followSearchDto.introduction = user.introduction; + followSearchDto.followerCnt = user.follower.length; + followSearchDto.followingCnt = user.following.length; + + // 팔로우 여부 + followSearchDto.isFollowing = await this.userService.checkIfFollowing(userEntity, followSearchDto.id); + + // 사용자 프로필 이미지 + const image = user.profileImage; + followSearchDto.image = image.imageKey; + if(image == null) followSearchDto.image = null; + else{ + const userImageKey = image.imageKey; + followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return followSearchDto; + })) + return searchResult; + } } diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts index 1d10df2..f6499d7 100644 --- a/src/member/member.controller.ts +++ b/src/member/member.controller.ts @@ -2,7 +2,7 @@ import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/co import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { MemberService } from './member.service'; -import {UserSearchDto} from "../user/user.search.dto"; +// import {UserSearchDto} from "../follow/dto/follow.search.dto"; import { UserService} from "../user/user.service"; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; @@ -34,6 +34,7 @@ export class MemberController { } // [4] 초대할 여행 규칙 멤버 검색 + /* @Get('/search/:searchTerm') @UseGuards(UserGuard) async getSearchResult( @@ -60,4 +61,5 @@ export class MemberController { ); } } + */ } \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f0b6294..abcc747 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -91,6 +91,7 @@ export class RuleService { // 사용자 프로필 이미지 const image = memberEntity.profileImage; + detailMember.image = image.imageKey; if(image == null) detailMember.image = null; else{ const userImageKey = image.imageKey; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index db6c1d3..e7c41de 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -7,7 +7,6 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; -import { UserSearchDto} from "./user.search.dto"; import {Like} from "typeorm"; @Injectable() @@ -256,6 +255,7 @@ export class UserService { } } + /* async getSearchResult(userId: number, searchTerm: string) : Promise { // 현재 로그인한 유저 객체 const user = await this.findUserById(userId); @@ -277,6 +277,7 @@ export class UserService { }); console.log(mates); + // dto 리스트 생성 const results : UserSearchDto[] = await Promise.all(mates.map(async (mate) => { const userSearchDto = new UserSearchDto(); @@ -296,6 +297,8 @@ export class UserService { return results; } + */ + async isAlreadyFollowing(userId:number, followingId: number) { const userEntity = await this.findUserById(userId); const followingEntity = await this.findUserById(followingId); From 2abd4161f4dee9eb52115f44ecd87de1ca0eb456 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 11 Feb 2024 02:38:19 +0900 Subject: [PATCH 169/316] =?UTF-8?q?fix=20:=20=EC=84=B8=EB=B6=80=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-schedule.controller.ts | 7 +++++- src/detail-schedule/detail-schedule.entity.ts | 10 ++++++--- .../detail-schedule.service.ts | 4 +++- src/map/map.controller.ts | 12 +++++++--- src/map/map.service.ts | 22 ++++++++++++++----- 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index 5bb982d..024606b 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -12,7 +12,10 @@ import { import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; import { DetailScheduleService } from './detail-schedule.service'; -import { DetailContentDto } from './detail-schedule-info.dto'; +import { + DetailContentDto, + DetailScheduleInfoDto, +} from './detail-schedule-info.dto'; import { UserGuard } from 'src/user/user.guard'; @Controller('detail-schedule') @@ -32,9 +35,11 @@ export class DetailScheduleController { async createDetailSchedule( @Req() req: Request, @Param('scheduleId') scheduleId: number, + @Body() body: DetailContentDto, ) { const result = await this.detailScheduleService.createDetailSchedule( scheduleId, + body, ); return result; } diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index 87ec397..ccccfeb 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -12,7 +12,10 @@ import { import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from '../schedule/schedule.entity'; -import { DetailScheduleInfoDto } from './detail-schedule-info.dto'; +import { + DetailContentDto, + DetailScheduleInfoDto, +} from './detail-schedule-info.dto'; @Entity() export class DetailScheduleEntity extends BaseEntity { @@ -40,9 +43,10 @@ export class DetailScheduleEntity extends BaseEntity { deleted: Date; //세부 일정 추가하기 - static async createDetailSchedule(scheduleId) { + static async createDetailSchedule(schedule, content: DetailContentDto) { const detailSchedule = new DetailScheduleEntity(); - detailSchedule.schedule = scheduleId; + detailSchedule.schedule = schedule; + detailSchedule.content = content.content; return await detailSchedule.save(); } //세부 일정 작성하기 diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index 496997a..c37316a 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -3,15 +3,17 @@ import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { DetailScheduleEntity } from './detail-schedule.entity'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; +import { DetailContentDto } from './detail-schedule-info.dto'; @Injectable() export class DetailScheduleService { //세부일정 추가하기 - async createDetailSchedule(scheduleId: number) { + async createDetailSchedule(scheduleId: number, content: DetailContentDto) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); console.log(schedule.id); const detailSchedule = await DetailScheduleEntity.createDetailSchedule( schedule.id, + content, ); console.log(detailSchedule); return response(BaseResponse.DETAIL_SCHEDULE_CREATED); diff --git a/src/map/map.controller.ts b/src/map/map.controller.ts index 986b104..5af3c5e 100644 --- a/src/map/map.controller.ts +++ b/src/map/map.controller.ts @@ -75,7 +75,8 @@ export class MapController { @Req() req: Request, @Param('journeyId') journeyId: number, ) { - const result = await this.mapService.getJourneyPreview(journeyId); + const user = req.user; + const result = await this.mapService.getJourneyPreview(user.id, journeyId); return result; } @@ -93,7 +94,8 @@ export class MapController { @Req() req: Request, @Param('journeyId') journeyId: number, ) { - const result = await this.mapService.getDiaryList(journeyId); + const user = req.user; + const result = await this.mapService.getDiaryList(user.id, journeyId); return result; } @@ -111,7 +113,11 @@ export class MapController { @Req() req: Request, @Param('journeyId') journeyId: number, ) { - const result = await this.mapService.getDetailJourneyList(journeyId); + const user = req.user; + const result = await this.mapService.getDetailJourneyList( + user.id, + journeyId, + ); return result; } } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 35f0524..6346be7 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -57,6 +57,7 @@ export class MapService { ); return { + userId: user.id, journeyId: journey.id, startDate: journey.startDate, endDate: journey.endDate, @@ -99,6 +100,7 @@ export class MapService { }); const diaryCount = await this.getDiaryCount(schedules); return { + userId: user.id, journeyId: journey.id, title: journey.title, startDate: journey.startDate, @@ -112,7 +114,8 @@ export class MapService { } /*지도에서 여정 정보 보여주기*/ - async getJourneyPreview(journeyId) { + async getJourneyPreview(userId, journeyId) { + const user = await UserEntity.findExistUser(userId); const journey = await this.getJourneyInfo(journeyId); const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journeyId, @@ -127,6 +130,7 @@ export class MapService { }); return response(BaseResponse.GET_JOURNEY_PREVIEW_SUCCESS, { + userId: user.id, journey: { journeyId: journey.id, title: journey.title, @@ -138,7 +142,8 @@ export class MapService { } /*작성한 일지 불러오기 - 지도*/ - async getDiaryList(journeyId) { + async getDiaryList(userId, journeyId) { + const user = await UserEntity.findExistUser(userId); const journey = await JourneyEntity.findExistJourney(journeyId); const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, @@ -164,11 +169,15 @@ export class MapService { }; }), ); - return response(BaseResponse.GET_DIARY_SUCCESS, diaryList); + return response(BaseResponse.GET_DIARY_SUCCESS, { + userId: user.id, + diaryList, + }); } /* 지도에서 세부 여정 확인하기 */ - async getDetailJourneyList(journeyId) { + async getDetailJourneyList(userId, journeyId) { + const user = await UserEntity.findExistUser(userId); const journey = await this.getJourneyInfo(journeyId); const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, @@ -185,7 +194,10 @@ export class MapService { diary: diaryStatus[index], }; }); - return response(BaseResponse.GET_SCHEDULE_SUCCESS, detailJourneyList); + return response(BaseResponse.GET_SCHEDULE_SUCCESS, { + userId: user.id, + detailJourneyList, + }); } //일정 정보 불러오기 From 7b4b629aaab511e766a744c4c104df734b515cae Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 02:56:22 +0900 Subject: [PATCH 170/316] =?UTF-8?q?feat=20:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로컬 테스트 완료 --- src/comment/comment.controller.ts | 42 +++++++++++++++++------- src/comment/comment.service.ts | 51 ++++++++++++++++++++++++++++-- src/response/response-code.enum.ts | 7 +++- 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index 37edc74..076fefb 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -1,10 +1,12 @@ -import {Controller, Post, Body, Req, UseGuards, Param, Patch} from '@nestjs/common'; +import {Controller, Post, Body, Req, UseGuards, Param, Patch, Delete} from '@nestjs/common'; import { CommentService } from './comment.service'; import { CreateCommentDto } from './dto/create-comment.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; +import {UserEntity} from "../user/user.entity"; +import {RuleMainEntity} from "../rule/domain/rule.main.entity"; @Controller('mate/rule') export class CommentController { @@ -36,26 +38,42 @@ export class CommentController { } // 여행 규칙 코멘트 수정 - /* - @Patch('/:ruleId') + @Patch('/:ruleId/:commentId') @UseGuards(UserGuard) - async updateComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { - const result = await this.commentService.updateComment(dto, ruleId, req.user.id); - - if(!result){ + async updateComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Param('commentId') commentId: number,@Req() req: Request): Promise> { + try { + const result = await this.commentService.updateComment(dto, ruleId, req.user.id, commentId); + return new ResponseDto( + ResponseCode.COMMENT_UPDATE_SUCCESS, + true, + "여행 규칙 코멘트 수정 성공", + result); + } catch (e) { return new ResponseDto( ResponseCode.COMMENT_UPDATE_FAIL, false, - "여행 규칙 코멘트 수정 실패", + e.message, null); } - else{ + } + + // 여행 규칙 코멘트 삭제 + @Delete('/:ruleId/:commentId') + @UseGuards(UserGuard) + async deleteComment(@Param('ruleId') ruleId: number, @Param('commentId') commentId: number,@Req() req: Request): Promise> { + try { + const result = await this.commentService.deleteComment(ruleId, req.user.id, commentId); return new ResponseDto( - ResponseCode.COMMENT_UPDATE_SUCCESS, + ResponseCode.COMMENT_DELETE_SUCCESS, true, - "여행 규칙 코멘트 수정 성공", + "여행 규칙 코멘트 삭제 성공", result); + } catch (e) { + return new ResponseDto( + ResponseCode.COMMENT_DELETE_FAIL, + false, + e.message, + null); } } - */ } \ No newline at end of file diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index f953dad..2540601 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -28,9 +28,54 @@ export class CommentService { return comment.id; } - /* - async updateComment(dto: CreateCommentDto, ruleId: number, userId: number) : Promise { + async updateComment(dto: CreateCommentDto, ruleId: number, userId: number, commentId: number) : Promise { + try { + // 사용자, 규칙, 댓글 검증 + const user = await UserEntity.findExistUser(userId); + if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); + const rule = await RuleMainEntity.findRuleById(ruleId); + if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); + const comment = await CommentEntity.findOne({ + where: {user: {id: userId}, rule: {id: ruleId}} + }) + if(!comment) throw new NotFoundException("데이터를 찾을 수 없습니다"); + + if(comment.id != commentId) throw new NotFoundException('해당 댓글 수정 권한이 없는 사용자입니다'); + + if (comment.id == commentId) { + comment.content = dto.content; + await CommentEntity.save(comment); + return comment.id; + } + } catch (e) { + console.log('해당 댓글 수정 권한이 없는 사용자 입니다'); + throw new Error(e.message); + } + } + + async deleteComment(ruleId: number, userId: number, commentId: number) : Promise { + try { + // 사용자, 규칙, 댓글 검증 + const user = await UserEntity.findExistUser(userId); + if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); + const rule = await RuleMainEntity.findRuleById(ruleId); + if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); + + const comment = await CommentEntity.findOne({ + where: {user: {id: userId}, rule: {id: ruleId}} + }) + if (!comment) throw new NotFoundException("데이터를 찾을 수 없습니다"); + + if (comment.id != commentId) throw new NotFoundException('해당 댓글을 작성한 사용자가 아닙니다'); + + if (comment.id == commentId) { + await comment.softRemove(); + return comment.id; + } + } catch (e) { + console.log('해당 댓글 삭제 권한이 없는 사용자 입니다'); + throw new Error(e.message); + } } - */ } diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 4f50109..7997e3d 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -19,7 +19,9 @@ export enum ResponseCode { GET_SEARCH_RESULT_SUCCESS = 'OK', DELETE_INVITATION_SUCCESS = 'OK', GET_RULE_LIST_SUCCESS = 'OK', - + COMMENT_UPDATE_SUCCESS = 'OK', + COMMENT_DELETE_SUCCESS = 'OK', + DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', @@ -68,6 +70,9 @@ export enum ResponseCode { GET_MATE_WITH_COMMON_LOCATION_FAIL = 'OK', GET_RANDOM_MATE_PROFILE_FAIL = 'OK', GET_RULE_LIST_FAIL = 'OK', + COMMENT_UPDATE_FAIL = 'OK', + COMMENT_DELETE_FAIL = 'OK', + SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', From 130f9617b17692fc67e3a27eaec57a6d5ecad84d Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:57:39 +0900 Subject: [PATCH 171/316] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=A0=95=EB=B3=B4=20=EB=B6=88?= =?UTF-8?q?=EB=9F=AC=EC=98=A4=EA=B8=B0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-profile-response.dto.ts | 9 ++++++ src/mate/dto/mate-profile-signature.dto.ts | 6 ++++ src/mate/mate.controller.ts | 36 +++++++++++++++++++++- src/mate/mate.service.ts | 36 ++++++++++++++++++++++ src/search/dto/get-search-main.dto.ts | 6 ++-- src/search/search.controller.ts | 10 +++--- src/search/search.service.ts | 11 +++---- src/signature/signature.controller.ts | 1 - 8 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 src/mate/dto/mate-profile-response.dto.ts create mode 100644 src/mate/dto/mate-profile-signature.dto.ts diff --git a/src/mate/dto/mate-profile-response.dto.ts b/src/mate/dto/mate-profile-response.dto.ts new file mode 100644 index 0000000..cf9e81c --- /dev/null +++ b/src/mate/dto/mate-profile-response.dto.ts @@ -0,0 +1,9 @@ +// mate-profile-response.dto.ts + +export class MateProfileResponseDto { + _id : number; + image: string; + nickname: string; + introduction: string; + is_followed: boolean; +} \ No newline at end of file diff --git a/src/mate/dto/mate-profile-signature.dto.ts b/src/mate/dto/mate-profile-signature.dto.ts new file mode 100644 index 0000000..36fc858 --- /dev/null +++ b/src/mate/dto/mate-profile-signature.dto.ts @@ -0,0 +1,6 @@ +// mate-profile-signature.dto.ts + +export class MateProfileSignatureDto{ + _id: number; + image: string; +} \ No newline at end of file diff --git a/src/mate/mate.controller.ts b/src/mate/mate.controller.ts index 2b05e6c..6d4a03e 100644 --- a/src/mate/mate.controller.ts +++ b/src/mate/mate.controller.ts @@ -1,6 +1,6 @@ //mate.controller.ts -import { Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; +import { Controller, Get, Param, Query, Req, UseGuards } from '@nestjs/common'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; import { CursorPageOptionsDto } from './cursor-page/cursor-page-option.dto'; @@ -9,6 +9,7 @@ import { ResponseDto } from '../response/response.dto'; import { MateRecommendProfileDto } from './dto/mate-recommend-profile.dto'; import { ResponseCode } from '../response/response-code.enum'; import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; +import { MateProfileResponseDto } from './dto/mate-profile-response.dto'; @Controller('/mate') export class MateController{ @@ -79,4 +80,37 @@ export class MateController{ } } + @Get(':userId') + @UseGuards(UserGuard) + async getUserProfile( + @Req() req: Request, + @Param('userId') userId: number + ): Promise>{ + try{ + const result = await this.mateService.findProfileWithUserId(req.user.id, userId); + + if(!result){ + return new ResponseDto( + ResponseCode.GET_USER_PROFILE_FAIL, + false, + "유저 프로필 정보 가져오기 실패", + null); + } + else{ + return new ResponseDto( + ResponseCode.GET_USER_PROFILE_SUCCESS, + true, + "유저 프로필 정보 가져오기 성공", + result); + } + + }catch (error) { + console.log("Error at GetUserProfile: ", error); + return new ResponseDto( + ResponseCode.GET_USER_PROFILE_FAIL, + false, + "유저 프로필 정보 가져오기 실패", + null); + } + } } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 1a9d105..56f4876 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -15,6 +15,7 @@ import { MateRecommendProfileDto } from './dto/mate-recommend-profile.dto'; import { MateController } from './mate.controller'; import { MateSignatureCoverDto } from './dto/mate-signature-cover.dto'; import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; +import { MateProfileResponseDto } from './dto/mate-profile-response.dto'; @Injectable() export class MateService{ @@ -249,4 +250,39 @@ export class MateService{ } + async findProfileWithUserId(loginUserId: number, targetUserId) { // 유저 정보 가져오기 + try{ + const targetUserEntity = await this.userService.findUserById(targetUserId); + console.log(targetUserEntity); + + const mateProfileResponseDto:MateProfileResponseDto = new MateProfileResponseDto(); + mateProfileResponseDto._id = targetUserEntity.id; + mateProfileResponseDto.nickname = targetUserEntity.nickname; + mateProfileResponseDto.introduction = targetUserEntity.introduction; + + // 타겟 유저 프로필 이미지 가져오기 + const userProfileImageEntity = await this.userService.getProfileImage(targetUserId); + if(userProfileImageEntity == null) mateProfileResponseDto.image = null; + else{ + const userProfileImageKey = userProfileImageEntity.imageKey; + mateProfileResponseDto.image = await this.s3Service.getImageUrl(userProfileImageKey); + } + + // 현재 로그인한 유저가 타켓 유저를 팔로우하는지 여부 가져오기 + if(loginUserId == targetUserId){ // 현재 로그인 유저와 타겟 유저가 같다면 is_followed = null + mateProfileResponseDto.is_followed = null; + + }else{ + const loginUserEntity = await this.userService.findUserById(loginUserId); + mateProfileResponseDto.is_followed = await this.userService.checkIfFollowing(loginUserEntity,targetUserId); + + } + return mateProfileResponseDto; + + } + catch(error){ + console.log("Err on findProfileWithId Service: ",error); + throw error; + } + } } \ No newline at end of file diff --git a/src/search/dto/get-search-main.dto.ts b/src/search/dto/get-search-main.dto.ts index a0c1635..51e1d84 100644 --- a/src/search/dto/get-search-main.dto.ts +++ b/src/search/dto/get-search-main.dto.ts @@ -1,8 +1,8 @@ // get-search-main.dto.ts -import { CoverSignatureDto } from './cover-signature.dto'; +import { SignatureCoverDto } from './signature-cover.dto'; export class GetSearchMainDto{ - hot: CoverSignatureDto[]; - new: CoverSignatureDto[]; + hot: SignatureCoverDto[]; + new: SignatureCoverDto[]; } \ No newline at end of file diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index 0577c75..18229fd 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -4,7 +4,7 @@ import { Body, Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; import { ResponseDto } from '../response/response.dto'; import { GetSearchMainDto } from './dto/get-search-main.dto'; import { ResponseCode } from '../response/response-code.enum'; -import { CoverSignatureDto } from './dto/cover-signature.dto'; +import { SignatureCoverDto } from './dto/signature-cover.dto'; import { SearchService } from './search.service'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; @@ -23,11 +23,11 @@ export class SearchController{ const getSearchMainDto:GetSearchMainDto = new GetSearchMainDto(); // [1] 인기 급상승 시그니처 가져오기 - const hotSignatures:CoverSignatureDto[] = await this.searchService.findHotSignatures(); + const hotSignatures:SignatureCoverDto[] = await this.searchService.findHotSignatures(); getSearchMainDto.hot = hotSignatures; // [2] 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 - const newSignatures:CoverSignatureDto[] = await this.searchService.findMatesNewSignatures(req.user.id); + const newSignatures:SignatureCoverDto[] = await this.searchService.findMatesNewSignatures(req.user.id); getSearchMainDto.new = newSignatures; return new ResponseDto( @@ -48,10 +48,10 @@ export class SearchController{ } @Get('/find') // 탑색탭 검색: 키워드로 시그니처 검색하기 - async search(@Query('keyword') keyword: string): Promise> { + async search(@Query('keyword') keyword: string): Promise> { try{ - const searchResult: CoverSignatureDto[] = await this.searchService.searchByKeyword(keyword); + const searchResult: SignatureCoverDto[] = await this.searchService.searchByKeyword(keyword); return new ResponseDto( ResponseCode.SEARCH_BY_KEYWORD_SUCCESS, diff --git a/src/search/search.service.ts b/src/search/search.service.ts index b24b5c1..b0dbcc7 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { SignatureEntity } from '../signature/domain/signature.entity'; -import { CoverSignatureDto } from './dto/cover-signature.dto'; +import { SignatureCoverDto } from './dto/signature-cover.dto'; import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; import { UserService } from '../user/user.service'; import { exit } from '@nestjs/cli/actions'; @@ -18,7 +18,7 @@ export class SearchService{ private readonly s3Service: S3UtilService, ) {} - async findHotSignatures(): Promise { + async findHotSignatures(): Promise { try{ /***************************************** 인기 시그니처 알고리즘 로직: @@ -81,7 +81,7 @@ export class SearchService{ async getSignatureCoversForSearchMain(signatureEntities){ // 탐색 메인화면에 출력될 시그니처 커버 20개 만들기 - const signatureCovers: CoverSignatureDto[] = []; + const signatureCovers: SignatureCoverDto[] = []; for (let i = 0; i < signatureEntities.length && i < 20; i++) { const signature = signatureEntities[i]; @@ -115,8 +115,8 @@ export class SearchService{ async getSignatureCover(signature:SignatureEntity) // 시그니처 커버 만들기 - :Promise{ - const signatureCover = new CoverSignatureDto(); + :Promise{ + const signatureCover = new SignatureCoverDto(); signatureCover._id = signature.id; signatureCover.title = signature.title; @@ -134,7 +134,6 @@ export class SearchService{ else{ const userProfileImageKey = userProfileImageEntity.imageKey; signatureCover.userImage = await this.s3Service.getImageUrl(userProfileImageKey); - } return signatureCover; } diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index 3df4315..d971567 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -56,7 +56,6 @@ export class SignatureController { false, "시그니처 생성에 실패했습니다", null); - } else{ return new ResponseDto( From 2d21d12e6838eb39fc7664b2f4ec225b097e3fae Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:58:34 +0900 Subject: [PATCH 172/316] =?UTF-8?q?Refactor:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20dto=20=EB=B0=8F=20response=20code=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 8 +++++--- .../{cover-signature.dto.ts => signature-cover.dto.ts} | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) rename src/search/dto/{cover-signature.dto.ts => signature-cover.dto.ts} (79%) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 4f50109..7ad7b91 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -19,6 +19,7 @@ export enum ResponseCode { GET_SEARCH_RESULT_SUCCESS = 'OK', DELETE_INVITATION_SUCCESS = 'OK', GET_RULE_LIST_SUCCESS = 'OK', + GET_USER_PROFILE_SUCCESS = 'OK', DELETE_SIGNATURE_SUCCESS = 'OK', @@ -65,9 +66,10 @@ export enum ResponseCode { RULE_EDIT_FAIL = 'BAD_REQUEST', GET_SEARCH_RESULT_FAIL = 'BAD_REQUEST', DELETE_INVITATION_FAIL = 'BAD_REQUEST', - GET_MATE_WITH_COMMON_LOCATION_FAIL = 'OK', - GET_RANDOM_MATE_PROFILE_FAIL = 'OK', - GET_RULE_LIST_FAIL = 'OK', + GET_MATE_WITH_COMMON_LOCATION_FAIL = 'BAD_REQUEST', + GET_RANDOM_MATE_PROFILE_FAIL = 'BAD_REQUEST', + GET_RULE_LIST_FAIL = 'BAD_REQUEST', + GET_USER_PROFILE_FAIL = 'BAD_REQUEST', SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', diff --git a/src/search/dto/cover-signature.dto.ts b/src/search/dto/signature-cover.dto.ts similarity index 79% rename from src/search/dto/cover-signature.dto.ts rename to src/search/dto/signature-cover.dto.ts index eb1253a..769e2d4 100644 --- a/src/search/dto/cover-signature.dto.ts +++ b/src/search/dto/signature-cover.dto.ts @@ -1,6 +1,6 @@ -// cover-signature.dto.ts +// signature-cover.dto.ts -export class CoverSignatureDto { +export class SignatureCoverDto { _id: number; title: string; image: string; // 시그니처 첫 번째 페이지 사진 From 590af72c78726f00f8862817169210e785698f08 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 03:30:07 +0900 Subject: [PATCH 173/316] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=EC=97=90=20=ED=8C=94=EB=A1=9C=EC=9B=8C/?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9E=89=20=EA=B0=9C=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-profile-response.dto.ts | 3 +++ src/mate/mate.service.ts | 10 +++++++++- src/user/user.service.ts | 8 ++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/mate/dto/mate-profile-response.dto.ts b/src/mate/dto/mate-profile-response.dto.ts index cf9e81c..fbace97 100644 --- a/src/mate/dto/mate-profile-response.dto.ts +++ b/src/mate/dto/mate-profile-response.dto.ts @@ -6,4 +6,7 @@ export class MateProfileResponseDto { nickname: string; introduction: string; is_followed: boolean; + + followerCnt: number; + followingCnt: number; } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 56f4876..555fc7a 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -17,6 +17,7 @@ import { MateSignatureCoverDto } from './dto/mate-signature-cover.dto'; import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; import { MateProfileResponseDto } from './dto/mate-profile-response.dto'; + @Injectable() export class MateService{ constructor( @@ -250,7 +251,7 @@ export class MateService{ } - async findProfileWithUserId(loginUserId: number, targetUserId) { // 유저 정보 가져오기 + async findProfileWithUserId(loginUserId: number, targetUserId): Promise { // 유저 정보 가져오기 try{ const targetUserEntity = await this.userService.findUserById(targetUserId); console.log(targetUserEntity); @@ -277,6 +278,13 @@ export class MateService{ mateProfileResponseDto.is_followed = await this.userService.checkIfFollowing(loginUserEntity,targetUserId); } + + const followingList = await this.userService.getFollowingList(targetUserId); + mateProfileResponseDto.followingCnt = followingList.length; + + const followerList = await this.userService.getFollowerList(targetUserId); + mateProfileResponseDto.followerCnt = followerList.length; + return mateProfileResponseDto; } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index e7c41de..684ce73 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -152,11 +152,11 @@ export class UserService { async getFollowingList(userId: number) { try { - const userFollowingEntity = await UserFollowingEntity.find({ + return await UserFollowingEntity.find({ where: { user: { id: userId } }, relations: ['followUser'], }); - return userFollowingEntity; + } catch (error) { console.log('Error on getFollowingList: ' + error); } @@ -164,11 +164,11 @@ export class UserService { async getFollowerList(userId: number) { try { - const userFollowerEntity = await UserFollowingEntity.find({ + return await UserFollowingEntity.find({ where: { followUser: { id: userId } }, relations: ['user'], }); - return userFollowerEntity; + } catch (error) { console.log('Error on getFollowingList: ' + error); } From 5219b393d3f40f8e9bb49e9f8407e21e36d294b7 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 03:31:57 +0900 Subject: [PATCH 174/316] =?UTF-8?q?chore:=20=ED=8C=94=EB=A1=9C=EC=9E=89/?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9B=8C=20=EB=AA=85=EC=B9=AD=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-profile-response.dto.ts | 4 ++-- src/mate/mate.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mate/dto/mate-profile-response.dto.ts b/src/mate/dto/mate-profile-response.dto.ts index fbace97..fc4861f 100644 --- a/src/mate/dto/mate-profile-response.dto.ts +++ b/src/mate/dto/mate-profile-response.dto.ts @@ -7,6 +7,6 @@ export class MateProfileResponseDto { introduction: string; is_followed: boolean; - followerCnt: number; - followingCnt: number; + follower: number; + following: number; } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 555fc7a..b13b6d4 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -280,10 +280,10 @@ export class MateService{ } const followingList = await this.userService.getFollowingList(targetUserId); - mateProfileResponseDto.followingCnt = followingList.length; + mateProfileResponseDto.following = followingList.length; const followerList = await this.userService.getFollowerList(targetUserId); - mateProfileResponseDto.followerCnt = followerList.length; + mateProfileResponseDto.follower = followerList.length; return mateProfileResponseDto; From 58305ca162355b84336565271c2deb782e1b330f Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 03:52:58 +0900 Subject: [PATCH 175/316] =?UTF-8?q?feat=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=20=ED=95=A0=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20=EA=B2=80=EC=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로컬 테스트 완료 --- src/follow/follow.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 432caea..3c19f30 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -128,7 +128,7 @@ export class FollowService { console.log('검색 값: ', searchTerm); const resultUsers = await UserEntity.find({ where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], - relations: ['profileImage', 'following'] + relations: {profileImage : true, follower : true, following : true} }); const userEntity = await UserEntity.findExistUser(userId); From 7bded26621a7ad9d84b53f222dacbedec8031794 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 11 Feb 2024 04:14:30 +0900 Subject: [PATCH 176/316] =?UTF-8?q?fix=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/detail-schedule/detail-schedule.service.ts | 3 +-- src/diary/diary.service.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index c37316a..50a3eae 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -12,10 +12,9 @@ export class DetailScheduleService { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); console.log(schedule.id); const detailSchedule = await DetailScheduleEntity.createDetailSchedule( - schedule.id, + schedule, content, ); - console.log(detailSchedule); return response(BaseResponse.DETAIL_SCHEDULE_CREATED); } diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index d7afc63..a9b2149 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -31,7 +31,7 @@ export class DiaryService { /*일지 사진 S3에 업로드 후 url 받기- 생성 */ async getDiaryImgUrl(diary, fileName: string) { const imageKey = `diary/${this.s3UtilService.generateRandomImageKey( - fileName, + 'diary.png', )}`; await this.s3UtilService.putObjectFromBase64(imageKey, fileName); return imageKey; From ff3d1760e7a6750e1b87158a7a67764e8c8648b3 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 04:47:16 +0900 Subject: [PATCH 177/316] =?UTF-8?q?fix=20:=20=EB=B3=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9A=B0=20=EB=A7=89=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 팔로우 예외 상황 처리 추가 2. 팔로우 관련 기능 구현 완료 - 팔로우 (언팔로우) - 팔로우 목록, 팔로잉 목록 - 팔로우할 멤버 검색 --- src/follow/follow.controller.ts | 48 +++++++------------- src/follow/follow.service.ts | 71 ++++++++++++++++++++++-------- src/response/response-code.enum.ts | 4 +- 3 files changed, 70 insertions(+), 53 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index bb0ecc6..33729b2 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -18,38 +18,19 @@ export class FollowController { @Patch('/search/follow/:followingId') @UseGuards(UserGuard) async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { - try { - // 팔로우 관계 확인 - const isAlreadyFollowing = await this.userService.isAlreadyFollowing(req.user.id, followingId); - console.log('Is already following? : ', isAlreadyFollowing); - - // -1) 이미 팔로우 한 사이, 팔로우 취소 - if (isAlreadyFollowing) { - console.log('언팔로우 service 호출'); - await this.followService.deleteFollow(req.user.id, followingId); - return new ResponseDto( - ResponseCode.UNFOLLOW_SUCCESS, - true, - "언팔로우 성공", - null - ); - } else { - // -2) 팔로우 - console.log('팔로우 service 호출'); - await this.followService.createFollow(req.user.id, followingId); - return new ResponseDto( - ResponseCode.FOLLOW_CREATED, - true, - "팔로우 성공", - null - ); - } - } catch(error) { + const result = await this.followService.checkFollow(req.user.id, followingId); return new ResponseDto( - ResponseCode.UNFOLLOW_FAIL, + ResponseCode.FOLLOW_SUCCESS, + true, + "팔로우 / 언팔로우 성공", + result + ); + } catch (e) { + return new ResponseDto( + ResponseCode.FOLLOW_FAIL, false, - "처리 실패", + e.message, null ); } @@ -59,6 +40,7 @@ export class FollowController { @Get('/followList') @UseGuards(UserGuard) async getFollowList(@Req() req: Request): Promise> { + console.log('controller'); try { const followList = await this.followService.getFollowList(req.user.id); return new ResponseDto( @@ -67,11 +49,11 @@ export class FollowController { "팔로우 리스트 불러오기 성공", followList ); - } catch (error) { + } catch (e) { return new ResponseDto( ResponseCode.GET_FOLLOWING_LIST_FAIL, false, - "팔로우 리스트 불러오기 실패", + e.message, null ); } @@ -89,11 +71,11 @@ export class FollowController { "팔로워 리스트 불러오기 성공", followerList ); - } catch (error) { + } catch (e) { return new ResponseDto( ResponseCode.GET_FOLLOWER_LIST_FAIL, false, - "팔로워 리스트 불러오기 실패", + e.message, null ); } diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 3c19f30..3e1ce75 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -1,4 +1,4 @@ -import { Injectable, HttpException } from '@nestjs/common'; +import {Injectable, HttpException, NotFoundException, BadRequestException} from '@nestjs/common'; import { UserFollowingEntity } from 'src/user/user.following.entity'; import { FollowDto } from './dto/follow.dto'; import { UserEntity } from "../user/user.entity"; @@ -15,39 +15,74 @@ export class FollowService { private readonly s3Service: S3UtilService, ) {} + // 팔로우 가능한 사이인지 검증 + async checkFollow(userId : number, followingId : number): Promise { + try { + // case1) 유효한 유저인지 검증 + const userEntity : UserEntity = await this.userService.findUserById(userId); + const followingUser = await UserEntity.findExistUser(followingId); + if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUser); + + // case2) 본인을 팔로우한 경우 + if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); + + // case3) 팔로우 관계 확인 + const isAlreadyFollowing = await this.userService.isAlreadyFollowing(userId, followingId); + console.log('Is already following? : ', isAlreadyFollowing); + + // [2] 이미 팔로우 한 사이, 팔로우 취소 + if (isAlreadyFollowing) { + console.log('언팔로우 service 호출'); + return this.deleteFollow(userId, followingId); + } else { + // [1] 팔로우 + console.log('팔로우 service 호출'); + return this.createFollow(userId, followingId); + } + } catch (e) { + console.log('팔로우 요청에 실패하였습니다'); + throw new Error(e.message); + } + } + // [1] 팔로우 - async createFollow(userId : number, followingId : number): Promise { + async createFollow(userId : number, followingId : number): Promise { - const userEntity : UserEntity = await this.userService.findUserById(userId); - const followingUserEntity : UserEntity = await this.userService.findUserById(followingId); - console.log('현재 로그인한 유저 : ', userEntity); - console.log('팔로우 대상 유저 : ', followingUserEntity); + try { + const userEntity : UserEntity = await this.userService.findUserById(userId); + const followingUser = await UserEntity.findExistUser(followingId); + if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUser); + if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); - try{ const userFollowingEntity = new UserFollowingEntity(); userFollowingEntity.user = userEntity; - userFollowingEntity.followUser = followingUserEntity; - - return userFollowingEntity.save(); + userFollowingEntity.followUser = followingUser; - }catch(error){ - console.error('Error on following: ', error); - throw new Error('Failed to following'); + await userFollowingEntity.save(); + return userFollowingEntity.id; + } catch (e) { + console.log('팔로우 요청에 실패하였습니다'); + throw new Error(e.message); } } // [2] 언팔로우 - async deleteFollow(userId: number, followingId:number): Promise { + async deleteFollow(userId: number, followingId:number): Promise { console.log('언팔로우 서비스 호출'); const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: { user : {id : userId}, followUser : {id : followingId}} }); try{ - return followEntity.softRemove(); - }catch(error){ - console.error('Error on unfollowing: ', error); - throw new Error('Failed to unfollowing'); + await followEntity.softRemove(); + return followEntity.id; + }catch(e){ + console.error('언팔로우 요청에 실패하였습니다: '); + throw new Error(e.message); } } diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index d5de974..04eff4e 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -23,6 +23,7 @@ export enum ResponseCode { GET_USER_PROFILE_SUCCESS = 'OK', COMMENT_UPDATE_SUCCESS = 'OK', COMMENT_DELETE_SUCCESS = 'OK', + FOLLOW_SUCCESS = 'OK', DELETE_SIGNATURE_SUCCESS = 'OK', @@ -42,7 +43,6 @@ export enum ResponseCode { RULE_CREATED = 'CREATED', LIKE_ON_SIGNATURE_CREATED = 'CREATED', COMMENT_CREATED = 'CREATED', - FOLLOW_CREATED = 'CREATED', INVITATION_CREATED = 'CREATED', @@ -56,7 +56,7 @@ export enum ResponseCode { GET_MY_SIGNATURE_FAIL = 'BAD_REQUEST', COMMENT_CREATION_FAIL = 'BAD_REQUEST', GET_RULE_DETAIL_FAIL = 'BAD_REQUEST', - FOLLOW_CREATION_FAIL = 'BAD_REQUEST', + FOLLOW_FAIL = 'BAD_REQUEST', UNFOLLOW_FAIL = 'BAD_REQUEST', GET_FOLLOWER_LIST_FAIL = 'BAD_REQUEST', GET_FOLLOWING_LIST_FAIL = 'BAD_REQUEST', From 3991843ebc83e1d39d05bfe868444d39ada6959d Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:07:03 +0900 Subject: [PATCH 178/316] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EA=B8=B0=20api=20=EA=B5=AC=ED=98=84=20#134?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-signature-cover.dto.ts | 3 +- src/mate/mate.controller.ts | 32 +++++++++- src/mate/mate.service.ts | 75 ++++++++++++++++++++++++ src/response/response-code.enum.ts | 5 +- 4 files changed, 110 insertions(+), 5 deletions(-) diff --git a/src/mate/dto/mate-signature-cover.dto.ts b/src/mate/dto/mate-signature-cover.dto.ts index e54859d..8e0db99 100644 --- a/src/mate/dto/mate-signature-cover.dto.ts +++ b/src/mate/dto/mate-signature-cover.dto.ts @@ -1,7 +1,8 @@ -// mate-sginature-cover.dto.ts +// mate-signature-cover.dto.ts export class MateSignatureCoverDto{ _id: number; image: string title: string + liked: number; } \ No newline at end of file diff --git a/src/mate/mate.controller.ts b/src/mate/mate.controller.ts index 6d4a03e..26d7f10 100644 --- a/src/mate/mate.controller.ts +++ b/src/mate/mate.controller.ts @@ -80,14 +80,14 @@ export class MateController{ } } - @Get(':userId') + @Get(':mateId') @UseGuards(UserGuard) async getUserProfile( @Req() req: Request, - @Param('userId') userId: number + @Param('mateId') mateId: number ): Promise>{ try{ - const result = await this.mateService.findProfileWithUserId(req.user.id, userId); + const result = await this.mateService.findProfileWithUserId(req.user.id, mateId); if(!result){ return new ResponseDto( @@ -113,4 +113,30 @@ export class MateController{ null); } } + + @Get('/signature/:mateId') + async getSignaturesWithInfiniteCursor( + @Param('mateId') mateId: number, + @Query() cursorPageOptionDto: CursorPageOptionsDto + ){ + try{ + const result = await this.mateService.getSignaturesWithInfiniteCursor(cursorPageOptionDto, mateId); + + return new ResponseDto( + ResponseCode.GET_USER_SIGNATURES_SUCCESS, + true, + "메이트의 시그니처 가져오기 성공", + result + ); + + }catch(error){ + console.log("Err on getUserSignatures: ", error); + return new ResponseDto( + ResponseCode.GET_USER_SIGNATURES_FAIL, + false, + "메이트의 시그니처 가져오기 실패", + null + ); + } + } } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index b13b6d4..716f547 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -293,4 +293,79 @@ export class MateService{ throw error; } } + + async getSignaturesWithInfiniteCursor(cursorPageOptionsDto: CursorPageOptionsDto, mateId: number) { + try{ + + let cursorId: number = 0; + + // 1. 맨 처음 요청일 경우 해당 유저의 시그니처 중 가장 최근 시그니처 id 가져오기 + if(cursorPageOptionsDto.cursorId == 0) { + const recentSignature = await SignatureEntity.findOne({ + where: { user: {id: mateId} }, + order: { + id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 작성한 시그니처 + } + }); + + cursorId = recentSignature.id; + + } + else cursorId = cursorPageOptionsDto.cursorId; + + + // 2. 무한 스크롤: take만큼 cursorId보다 id값이 작은 시그니처들 불러오기 + const [ signatureEntities, total] = await SignatureEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + id: cursorId ? LessThan(cursorId) : null, + user: { id: mateId } + }, + order: { + id: "DESC" as any, + }, + }); + + + // 3. 가져온 시그니처들로 커버 만들기 + const signatureCoverDtos: MateSignatureCoverDto[] = []; + + for( const signatureEntity of signatureEntities){ + const signatureCover: MateSignatureCoverDto = new MateSignatureCoverDto(); + + signatureCover._id = signatureEntity.id; + signatureCover.title = signatureEntity.title; + signatureCover.liked = signatureEntity.liked; + + // 시그니처 썸네일 가져오기 + const imageKey = await SignaturePageEntity.findThumbnail(signatureEntity.id); + signatureCover.image = await this.s3Service.getImageUrl(imageKey); + + signatureCoverDtos.push(signatureCover); + } + + // 4. 스크롤 설정 + let hasNextData = true; + let cursor: number; + + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = signatureEntities[signatureEntities.length - 1]; + + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } + + const cursorPageMetaDto = new CursorPageMetaDto( + { cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto(signatureCoverDtos, cursorPageMetaDto); + + }catch(error){ + throw error; + } + } } \ No newline at end of file diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index d5de974..f3aa42a 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -21,10 +21,13 @@ export enum ResponseCode { GET_RULE_LIST_SUCCESS = 'OK', GET_USER_PROFILE_SUCCESS = 'OK', + GET_USER_SIGNATURES_SUCCESS = 'OK', + COMMENT_UPDATE_SUCCESS = 'OK', COMMENT_DELETE_SUCCESS = 'OK', + DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', @@ -84,7 +87,7 @@ export enum ResponseCode { GET_LIKE_SIGNATURE_PROFILES_FAIL = 'BAD_REQUEST', GET_SEARCH_MAIN_FAIL = 'BAD_REQUEST', SEARCH_BY_KEYWORD_FAIL = 'BAD_REQUEST', - + GET_USER_SIGNATURES_FAIL = 'BAD_REQUEST', /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ From 9c014f0a8fd0bc5a3781671f18c8f53592487040 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:23:00 +0900 Subject: [PATCH 179/316] =?UTF-8?q?hotfix:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20=EC=BB=A4?= =?UTF-8?q?=EC=84=9C=20=EC=B4=88=EA=B8=B0=EA=B0=92=20=EC=9E=AC=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/mate.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 716f547..d6e0ac9 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -308,7 +308,7 @@ export class MateService{ } }); - cursorId = recentSignature.id; + cursorId = recentSignature.id + 1; } else cursorId = cursorPageOptionsDto.cursorId; From 8d0bac9effa4fd3745a21942981a77140608f2ec Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 15:27:58 +0900 Subject: [PATCH 180/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=B0=B8=EC=97=AC=EC=97=90=20=EC=B4=88?= =?UTF-8?q?=EB=8C=80=ED=95=A0=20=EB=A9=A4=EB=B2=84=20=EA=B2=80=EC=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/get.search.member.dto.ts | 27 ++++++++++++++++++ src/rule/rule.controller.ts | 32 ++++++++++++++++++++- src/rule/rule.service.ts | 41 +++++++++++++++++++++++++++ src/user/user.service.ts | 10 +++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/rule/dto/get.search.member.dto.ts diff --git a/src/rule/dto/get.search.member.dto.ts b/src/rule/dto/get.search.member.dto.ts new file mode 100644 index 0000000..a6fd99b --- /dev/null +++ b/src/rule/dto/get.search.member.dto.ts @@ -0,0 +1,27 @@ +import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; + +export class GetSearchMemberDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + email: string; + + @IsOptional() + @IsString() + introduction: string; + + @IsOptional() + @IsString() + image: string; + + @IsOptional() + @IsBoolean() + isInvited: boolean; +} \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 8171058..990b1f1 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,10 +1,12 @@ -import {Controller, Post, Body, Get, Param, Delete, UseGuards, Req} from '@nestjs/common'; +import {Controller, Post, Body, Get, Param, Delete, UseGuards, Req, Query} from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; +import {FollowSearchDto} from "../follow/dto/follow.search.dto"; +import {GetSearchMemberDto} from "./dto/get.search.member.dto"; @Controller('mate/rule') export class RuleController { @@ -104,7 +106,34 @@ export class RuleController { } } + // [4] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 + @Get('/write/search/:ruleId') + @UseGuards(UserGuard) + async getSearchMember( + @Query('searchTerm')searchTerm : string, + @Param('ruleId') ruleId: number, + @Req() req: Request): Promise> { + try { + const getSearchMemberDto : GetSearchMemberDto[] = await this.ruleService.getSearchMember(req.user.id, ruleId, searchTerm) + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + "초대할 메이트 검색 결과 리스트 불러오기 성공", + getSearchMemberDto + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + "초대할 메이트 검색 결과 리스트 불러오기 실패", + null + ); + } + } + + // 여행 규칙 나가기 + /* @Delete('/:ruleId') @UseGuards(UserGuard) async deleteInvitation(@Req() req: Request, @Param('ruleId') ruleId: number){ @@ -130,4 +159,5 @@ export class RuleController { ); } } + */ } \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 6f56089..5fdc5a1 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -9,6 +9,8 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; +import {Like} from "typeorm"; +import {GetSearchMemberDto} from "./dto/get.search.member.dto"; @Injectable() export class RuleService { @@ -215,6 +217,45 @@ export class RuleService { return result; } + // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 + async getSearchMember(userId: number, ruleId: number, searchTerm: string): Promise { + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); + const resultUsers = await UserEntity.find({ + where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], + relations: {profileImage : true, ruleParticipate: true} + }); + + const result = await Promise.all(resultUsers.map(async (user) => { + const dto: GetSearchMemberDto = new GetSearchMemberDto(); + + dto.id = user.id; + dto.name = user.name; + dto.email = user.email; + dto.introduction = user.introduction; + + // 이미 여행 규칙에 참여하는 멤버인지 여부 + dto.isInvited = await this.userService.checkAlreadyMember(user, ruleId); + + dto.image + // 사용자 프로필 이미지 + const image = user.profileImage; + dto.image = image.imageKey; + if(image == null) dto.image = null; + else{ + const userImageKey = image.imageKey; + dto.image = await this.s3Service.getImageUrl(userImageKey); + } + return dto; + })) + return result; + } + + + + + // [member] 초대 받은 멤버 리스트 생성 async getInvitationList(ruleId: number) { diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 684ce73..2d860c1 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -8,6 +8,7 @@ import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; import {Like} from "typeorm"; +import {RuleInvitationEntity} from "../rule/domain/rule.invitation.entity"; @Injectable() export class UserService { @@ -313,4 +314,13 @@ export class UserService { // 팔로우 관계 : true 반환 return !!isFollowing; } + + async checkAlreadyMember(user: UserEntity, ruleID: number) { + const rule = await RuleInvitationEntity.findOne({ + where: {member: {id: user.id}, rule: {id: ruleID}} + }); + // 이미 규칙 멤버인 경우 : true 반환 + console.log('rule : ', rule); + return !!rule; + } } From 8eb5500417d6bc6fc8bb9547bf407793210e9c74 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 17:07:37 +0900 Subject: [PATCH 181/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 4 +++ src/rule/dto/update-rule.dto.ts | 15 +++++++--- src/rule/rule.controller.ts | 38 +++++++++++++++++++++--- src/rule/rule.service.ts | 47 +++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 04eff4e..bccf50c 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -19,6 +19,8 @@ export enum ResponseCode { GET_SEARCH_RESULT_SUCCESS = 'OK', DELETE_INVITATION_SUCCESS = 'OK', GET_RULE_LIST_SUCCESS = 'OK', + PATCH_RULE_SUCCESS = 'OK', + GET_USER_PROFILE_SUCCESS = 'OK', COMMENT_UPDATE_SUCCESS = 'OK', @@ -69,6 +71,8 @@ export enum ResponseCode { RULE_EDIT_FAIL = 'BAD_REQUEST', GET_SEARCH_RESULT_FAIL = 'BAD_REQUEST', DELETE_INVITATION_FAIL = 'BAD_REQUEST', + PATCH_RULE_FAIL = 'BAD_REQUEST', + GET_MATE_WITH_COMMON_LOCATION_FAIL = 'BAD_REQUEST', GET_RANDOM_MATE_PROFILE_FAIL = 'BAD_REQUEST', diff --git a/src/rule/dto/update-rule.dto.ts b/src/rule/dto/update-rule.dto.ts index 953023e..e0ab74e 100644 --- a/src/rule/dto/update-rule.dto.ts +++ b/src/rule/dto/update-rule.dto.ts @@ -1,7 +1,10 @@ import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -class RulePairDto { +export class UpdateRulePairDto { + @IsNumber() + id: number; + @IsNotEmpty() @IsString() ruleTitle: string; @@ -11,13 +14,17 @@ class RulePairDto { ruleDetail: string; } -export class CreateRuleDto { +export class UpdateRuleDto { @IsNotEmpty() @IsString() mainTitle: string; @IsArray() @ValidateNested({ each: true }) - @Type(() => RulePairDto) - rulePairs: RulePairDto[]; + @Type(() => UpdateRulePairDto) + rulePairs: UpdateRulePairDto[]; + + @IsArray() + @IsNumber({}, { each: true }) + membersId: number[]; } \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 990b1f1..4d96d1c 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,4 +1,4 @@ -import {Controller, Post, Body, Get, Param, Delete, UseGuards, Req, Query} from '@nestjs/common'; +import {Controller, Post, Body, Get, Param, Delete, UseGuards, Req, Query, Patch} from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; @@ -7,6 +7,7 @@ import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; import {FollowSearchDto} from "../follow/dto/follow.search.dto"; import {GetSearchMemberDto} from "./dto/get.search.member.dto"; +import { UpdateRuleDto } from "./dto/update-rule.dto"; @Controller('mate/rule') export class RuleController { @@ -35,7 +36,7 @@ export class RuleController { } } - // [2] 여행 규칙 조회 + // [2] 여행 규칙 상세 페이지 조회 (게시글) @Get('/detail/:ruleId') @UseGuards(UserGuard) async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { @@ -46,7 +47,7 @@ export class RuleController { return new ResponseDto( ResponseCode.GET_RULE_DETAIL_FAIL, false, - "여행 규칙 및 댓글 조회 실패", + "여행 규칙 상세 페이지 (게시글) 조회 실패", null ); } @@ -54,12 +55,41 @@ export class RuleController { return new ResponseDto( ResponseCode.GET_RULE_DETAIL_SUCCESS, true, - "여행 규칙 및 댓글 조회 성공", + "여행 규칙 상세 페이지 (게시글) 조회 성공", result ); } } + // [2] 여행 규칙 수정 + /* + @Patch('/detail/:ruleId') + @UseGuards(UserGuard) + async updateRule(@Body() updateRuleDto: UpdateRuleDto, @Req() req: Request, @Param('ruleId') ruleId: number): Promise> { + + const result = await this.ruleService.updateRule(updateRuleDto, req.user.id, ruleId); + + if(!result){ + return new ResponseDto( + ResponseCode.PATCH_RULE_FAIL, + false, + "여행 규칙 수정 실패", + null + ); + } + else{ + return new ResponseDto( + ResponseCode.PATCH_RULE_SUCCESS, + true, + "여행 규칙 수정 성공", + result + ); + } + } + + */ + + // [3] 여행 규칙 전체 리스트 조회 @Get('list') @UseGuards(UserGuard) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 5fdc5a1..d19a38f 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -11,6 +11,8 @@ import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; import {Like} from "typeorm"; import {GetSearchMemberDto} from "./dto/get.search.member.dto"; +import {UpdateRuleDto} from "./dto/update-rule.dto"; +import {FollowSearchDto} from "../follow/dto/follow.search.dto"; @Injectable() export class RuleService { @@ -63,7 +65,7 @@ export class RuleService { return main.id; } - // [2] 여행 규칙 조회 + // [2] 여행 규칙 상세 페이지 조회 (게시글) async getDetail(ruleId : number): Promise { const dto = new DetailRuleDto(); const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); @@ -252,6 +254,49 @@ export class RuleService { return result; } + // [7] 여행 규칙 수정 + /* + async updateRule(updateRuleDto: UpdateRuleDto, userId: number, ruleId: number) { + const rule = await RuleMainEntity.findOne({ + where: {id : ruleId }, + relations: { rules: true, invitations: {member : true} } + }) + + rule.mainTitle = updateRuleDto.mainTitle + + // 상세 규칙 수정 + // 기존 세부 규칙 정보 + const subs = rule.rules; + + // 새로운 세부 규칙 리스트 + const updateSubsList = updateRuleDto.rulePairs; + + // 수정 및 규칙 추가 + const result = await Promise.all(updateSubsList.map(async (updateSub) => { + // case1) 새로운 규칙 + if (!updateSub.id) { + const newSub = new RuleSubEntity(); + newSub.ruleTitle = updateSub.ruleTitle; + newSub.ruleDetail = updateSub.ruleDetail; + + return new + } + // case2) 수정 규칙 + if (!!updateSub.id && !!updateSub.ruleTitle && !!updateSub.ruleDetail) { + const oldSub = await RuleSubEntity.findOne({ + where: {id: updateSub.id} + }) + oldSub.ruleTitle = updateSub.ruleTitle; + oldSub.ruleDetail = updateSub.ruleDetail; + + } + // case3) 삭제 규칙 + if (!!updateSub.id && !updateSub.ruleTitle && !updateSub.ruleDetail) { + + } + }} + + */ From 908506395ee87ec9f33d8bc9431a60283968c426 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 17:39:36 +0900 Subject: [PATCH 182/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=A1=B0=ED=9A=8C=20relations=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 6f56089..382881b 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -66,7 +66,10 @@ export class RuleService { const dto = new DetailRuleDto(); const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); - const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.findInvitationByRuleId(ruleId); + const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ + where: {rule: {id: ruleId}}, + relations: {member: {profileImage : true}} + }) // -1) 제목 dto.id = ruleId; From d3427ed7691fac642e893e0243f3bcd2c6121c67 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 18:06:46 +0900 Subject: [PATCH 183/316] =?UTF-8?q?fix=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 3 --- src/rule/rule.service.ts | 8 +++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 3e1ce75..57d3908 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -109,7 +109,6 @@ export class FollowService { // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); - followDto.image = image.imageKey; if(image == null) followDto.image = null; else{ const userImageKey = image.imageKey; @@ -144,7 +143,6 @@ export class FollowService { // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); - followDto.image = image.imageKey; if(image == null) followDto.image = null; else{ const userImageKey = image.imageKey; @@ -184,7 +182,6 @@ export class FollowService { // 사용자 프로필 이미지 const image = user.profileImage; - followSearchDto.image = image.imageKey; if(image == null) followSearchDto.image = null; else{ const userImageKey = image.imageKey; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 382881b..5ee5580 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -83,7 +83,7 @@ export class RuleService { rulePair.ruleDetail = sub.ruleDetail; return rulePair; - })) + })); // -3) 멤버 정보 dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { @@ -94,7 +94,6 @@ export class RuleService { // 사용자 프로필 이미지 const image = memberEntity.profileImage; - detailMember.image = image.imageKey; if(image == null) detailMember.image = null; else{ const userImageKey = image.imageKey; @@ -103,7 +102,7 @@ export class RuleService { return detailMember; })) return dto; - } + }; // [3] 여행 규칙 나가기 // -1) 초대 받은 팀원 -> 초대 삭제 @@ -118,7 +117,7 @@ export class RuleService { const invitationsList : RuleInvitationEntity[] = await RuleInvitationEntity.find({ where : {rule : {id: ruleId}}, relations : {member : true} - }) + }); const membersList : GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation) : Promise => { const memberEntity : UserEntity = invitation.member; @@ -132,7 +131,6 @@ export class RuleService { // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(memberEntity.id); - memberDto.image = image.imageKey; if(image == null) memberDto.image = null; else { const userImageKey = image.imageKey; From 3703f1c319cd317a7809602a33fc808204d02020 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 18:25:17 +0900 Subject: [PATCH 184/316] =?UTF-8?q?fix=20:=20relations=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 팔로우, 팔로워 리스트 조회 기능 에러 수정 --- src/user/user.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 684ce73..d905f23 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -154,7 +154,7 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { user: { id: userId } }, - relations: ['followUser'], + relations: {followUser: {profileImage : true}}, }); } catch (error) { @@ -166,7 +166,7 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { followUser: { id: userId } }, - relations: ['user'], + relations: {user: {profileImage : true}}, }); } catch (error) { From dc8f48c2829c15e114238c2818c59dfe210aac83 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 11 Feb 2024 20:39:15 +0900 Subject: [PATCH 185/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로컬 테스트 완료 --- src/rule/rule.controller.ts | 4 -- src/rule/rule.service.ts | 87 ++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 4d96d1c..1bab644 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -62,7 +62,6 @@ export class RuleController { } // [2] 여행 규칙 수정 - /* @Patch('/detail/:ruleId') @UseGuards(UserGuard) async updateRule(@Body() updateRuleDto: UpdateRuleDto, @Req() req: Request, @Param('ruleId') ruleId: number): Promise> { @@ -87,9 +86,6 @@ export class RuleController { } } - */ - - // [3] 여행 규칙 전체 리스트 조회 @Get('list') @UseGuards(UserGuard) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 35ac5be..c3f9244 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -256,8 +256,7 @@ export class RuleService { } // [7] 여행 규칙 수정 - /* - async updateRule(updateRuleDto: UpdateRuleDto, userId: number, ruleId: number) { + async updateRule(updateRuleDto: UpdateRuleDto, userId: number, ruleId: number): Promise { const rule = await RuleMainEntity.findOne({ where: {id : ruleId }, relations: { rules: true, invitations: {member : true} } @@ -265,42 +264,106 @@ export class RuleService { rule.mainTitle = updateRuleDto.mainTitle - // 상세 규칙 수정 - // 기존 세부 규칙 정보 + // (1) [상세 규칙 수정] + // 기존 세부 규칙 정보 리스트 const subs = rule.rules; // 새로운 세부 규칙 리스트 const updateSubsList = updateRuleDto.rulePairs; - // 수정 및 규칙 추가 - const result = await Promise.all(updateSubsList.map(async (updateSub) => { + // case1) 규칙 삭제 + for(const sub of subs) { + let isDeleteSub : boolean = true; + for(const updateSub of updateSubsList) { + if(sub.id == updateSub.id) { + isDeleteSub = false; + break; + } + } + if (isDeleteSub) { + await sub.softRemove(); + console.log('삭제하는 sub 규칙 : ', sub.id); + } + } + + // case2) 규칙 수정 및 규칙 추가 + for (const updateSub of updateSubsList) { // case1) 새로운 규칙 if (!updateSub.id) { const newSub = new RuleSubEntity(); newSub.ruleTitle = updateSub.ruleTitle; newSub.ruleDetail = updateSub.ruleDetail; - return new + await newSub.save(); + console.log('새로 저장하는 sub 규칙 : ', newSub.id); } // case2) 수정 규칙 - if (!!updateSub.id && !!updateSub.ruleTitle && !!updateSub.ruleDetail) { + else { const oldSub = await RuleSubEntity.findOne({ where: {id: updateSub.id} }) oldSub.ruleTitle = updateSub.ruleTitle; oldSub.ruleDetail = updateSub.ruleDetail; + await oldSub.save(); + console.log('수정하는 규칙 ID : ', oldSub); + } + } + + // (2) [여행 규칙 멤버 수정] + // 기존 멤버 초대 리스트 + const oldInvitations = await RuleInvitationEntity.find({ + where: {rule: {id : ruleId}}, + relations: {member: true} + }) + // 수정된 멤버 ID 리스트 + const updateMemberIds = updateRuleDto.membersId; + + // case1) 멤버 삭제 + for(const invitation of oldInvitations) { + const member = invitation.member; + let isDeleteMember : boolean = true; + + for(const updateMemberId of updateMemberIds) { + if(member.id == updateMemberId) { + isDeleteMember = false; + break; + } + } + if(isDeleteMember) { + await invitation.softRemove(); + console.log('삭제하는 멤버 ID : ', invitation.id); } - // case3) 삭제 규칙 - if (!!updateSub.id && !updateSub.ruleTitle && !updateSub.ruleDetail) { + } + + // case2) 멤버 추가 + for (const updateMemberId of updateMemberIds) { + const member = await UserEntity.findExistUser(updateMemberId); + let isPostMember : boolean = true; + + for(const oldInvitation of oldInvitations) { + const oldMember = oldInvitation.member; + if(oldMember.id == updateMemberId) { + isPostMember = false; + break; + } } - }} - */ + if(isPostMember) { + const newInvitation = new RuleInvitationEntity(); + newInvitation.member = await UserEntity.findExistUser(updateMemberId); + newInvitation.rule = rule; + await newInvitation.save(); + console.log('새로 초대한 멤버 ID : ', updateMemberId); + } + } + console.log('--여행 규칙 수정이 완료되었습니다--') + return rule.id; + } // [member] 초대 받은 멤버 리스트 생성 From 4a913dad57c0fda091945b1ed20fe3c403f89fde Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 21:42:29 +0900 Subject: [PATCH 186/316] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=ED=8C=94=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EB=88=84=EB=9D=BD=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-profile-response.dto.ts | 5 +++-- src/mate/mate.service.ts | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/mate/dto/mate-profile-response.dto.ts b/src/mate/dto/mate-profile-response.dto.ts index fc4861f..aeb8aa2 100644 --- a/src/mate/dto/mate-profile-response.dto.ts +++ b/src/mate/dto/mate-profile-response.dto.ts @@ -7,6 +7,7 @@ export class MateProfileResponseDto { introduction: string; is_followed: boolean; - follower: number; - following: number; + signatures: number; // 시그니처 개수 + follower: number; // 팔로워 수 + following: number; // 팔로잉 수 } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index d6e0ac9..c279123 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -256,6 +256,7 @@ export class MateService{ const targetUserEntity = await this.userService.findUserById(targetUserId); console.log(targetUserEntity); + // 타겟 유저 프로필 가져오기 const mateProfileResponseDto:MateProfileResponseDto = new MateProfileResponseDto(); mateProfileResponseDto._id = targetUserEntity.id; mateProfileResponseDto.nickname = targetUserEntity.nickname; @@ -272,19 +273,22 @@ export class MateService{ // 현재 로그인한 유저가 타켓 유저를 팔로우하는지 여부 가져오기 if(loginUserId == targetUserId){ // 현재 로그인 유저와 타겟 유저가 같다면 is_followed = null mateProfileResponseDto.is_followed = null; - }else{ const loginUserEntity = await this.userService.findUserById(loginUserId); - mateProfileResponseDto.is_followed = await this.userService.checkIfFollowing(loginUserEntity,targetUserId); - + mateProfileResponseDto.is_followed = await this.userService.checkIfFollowing( loginUserEntity, targetUserId); } + // 팔로잉 수 const followingList = await this.userService.getFollowingList(targetUserId); mateProfileResponseDto.following = followingList.length; + // 팔로워 수 const followerList = await this.userService.getFollowerList(targetUserId); mateProfileResponseDto.follower = followerList.length; + // 시그니처 개수 + mateProfileResponseDto.signatures = await this.signatureService.getSignatureCnt(targetUserId); + return mateProfileResponseDto; } From a3c3d83de1ab954076961f0214551f5db9acba5b Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 21:43:04 +0900 Subject: [PATCH 187/316] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EA=B0=9C=EC=88=98=20=EC=86=8D=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index ae5db25..492e67b 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -397,4 +397,12 @@ export class SignatureService { take: take, // 최신 시그니처 가져오기 }); } + + async getSignatureCnt(userId: number): Promise { // 시그니처 개수 반환 + return await SignatureEntity.count({ + where: { + user: { id: userId }, + } + }); + } } From cc8486d92dde46185b43d188c60ab51ecaa23085 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:25:17 +0900 Subject: [PATCH 188/316] =?UTF-8?q?fix:=20checkIfFollowing=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EC=A0=88=20id=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index b7e9418..55bf285 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -60,8 +60,8 @@ export class UserService { const isFollowing = await UserFollowingEntity.findOne({ where: { - user: { id: targetUserId }, - followUser: { id: user.id } + user: { id: user.id }, + followUser: { id: targetUserId } } }); From ca7041a9b8ebe776e25def0dac53e76bd4c55ea4 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 11 Feb 2024 23:45:28 +0900 Subject: [PATCH 189/316] =?UTF-8?q?feat:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 12 +++ package.json | 2 + src/user/user.controller.ts | 9 +++ src/user/user.service.ts | 146 +++++++++++++++++++++++++++++++----- 4 files changed, 152 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0c3fb0..5f6553b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "mysql2": "^3.9.0", + "node-fetch": "^2.7.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.20", @@ -42,6 +43,7 @@ "@types/jsonwebtoken": "^9.0.5", "@types/multer-s3": "^3.0.3", "@types/node": "^20.3.1", + "@types/node-fetch": "^2.6.11", "@types/supertest": "^2.0.12", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.59.11", @@ -3832,6 +3834,16 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/qs": { "version": "6.9.11", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", diff --git a/package.json b/package.json index 5a1931f..10da63d 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "mysql2": "^3.9.0", + "node-fetch": "^2.7.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.20", @@ -53,6 +54,7 @@ "@types/jsonwebtoken": "^9.0.5", "@types/multer-s3": "^3.0.3", "@types/node": "^20.3.1", + "@types/node-fetch": "^2.6.11", "@types/supertest": "^2.0.12", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.59.11", diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 54f2a36..1e27c0f 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -13,6 +13,15 @@ export class UserController { return this.userService.Login(email, password); } + @Post('/login/oauth') + SNSLogin( + @Body('type') type: 'KAKAO' | 'GOOGLE', + @Body('token') token: string, + @Body('redirect_uri') redirectUrl: string, + ) { + return this.userService.SNSLogin(type, token, redirectUrl); + } + @Post('/profile') @UseGuards(UserGuard) UpdateProfile(@Body() body: Partial, @Req() req: Request) { diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 55bf285..86cb9fa 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,14 +1,14 @@ import { HttpException, Injectable, Logger } from '@nestjs/common'; import * as bcrypt from 'bcrypt'; import * as jsonwebtoken from 'jsonwebtoken'; +import fetch from 'node-fetch'; import { UserEntity } from './user.entity'; import { IReqUser, IUserProfile } from './user.dto'; import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; -import {Like} from "typeorm"; -import {RuleInvitationEntity} from "../rule/domain/rule.invitation.entity"; +import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; @Injectable() export class UserService { @@ -28,6 +28,49 @@ export class UserService { }); } + private objectToQueryString(obj: Record) { + return Object.entries(obj) + .map(([key, value]) => `${key}=${encodeURIComponent(value.toString())}`) + .join('&'); + } + + private async getKakaoToken(code: string, redirectUrl: string) { + const response = await fetch(`https://kauth.kakao.com/oauth/token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + body: this.objectToQueryString({ + grant_type: 'authorization_code', + client_id: process.env.KAKAO_CLIENT_ID, + redirect_uri: redirectUrl, + code, + }), + }); + + return await response.json(); + } + + private async getKakaoInformation(accessToken: string) { + const response = await fetch( + `https://kapi.kakao.com/v2/user/me?${this.objectToQueryString({ + property_keys: JSON.stringify([ + 'kakao_account.profile', + 'kakao_account.email', + 'kakao_account.name', + ]), + })}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + }, + ); + + return await response.json(); + } + async Login(email: string, password: string) { console.log(email, password); const user = await UserEntity.findOne({ @@ -52,6 +95,78 @@ export class UserService { }; } + async SNSLogin(type: 'KAKAO' | 'GOOGLE', code: string, redirectUrl: string) { + if (type === 'KAKAO') { + // 인가 코드 받기 + const authToken = await this.getKakaoToken(code, redirectUrl); + if (authToken.error) { + return new ResponseDto( + ResponseCode.UNKNOWN_AUTHENTICATION_ERROR, + false, + '인가 코드가 유효하지 않습니다', + null, + ); + } + + // 사용자 정보 받기 + const kakaoInfo = await this.getKakaoInformation(authToken.access_token); + + const userId = kakaoInfo.id; + const userProfile = kakaoInfo.kakao_account.profile; + const userEmail = kakaoInfo.kakao_account.email; + + // 사용자 정보로 DB 조회 + let userEntity = await UserEntity.findOne({ + where: { + oauthType: 'KAKAO', + oauthToken: userId.toString(), + }, + relations: { + profileImage: true, + }, + }); + + let isNewUser = false; + if (!userEntity) { + isNewUser = true; + userEntity = new UserEntity(); + userEntity.oauthType = 'KAKAO'; + userEntity.oauthToken = userId.toString(); + + userEntity.introduction = ''; + userEntity.visibility = 'PUBLIC'; + userEntity.name = ''; + userEntity.age = 0; + } + + userEntity.email = userEmail; + userEntity.password = ''; + userEntity.nickname = userProfile?.nickname; + + // Todo: 프로필 이미지 저장하기 + await userEntity.save(); + + return { + status: 200, + success: true, + message: '로그인 성공', + token: this._generateToken({ + id: userEntity.id, + }), + register_required: isNewUser, + }; + } else if (type === 'GOOGLE') { + // Todo + } else { + return new ResponseDto( + ResponseCode.INTERNAL_SERVEr_ERROR, + false, + '잘못된 요청입니다', + null, + ); + } + } + async checkIfFollowing( user: UserEntity, targetUserId: number, @@ -61,20 +176,19 @@ export class UserService { const isFollowing = await UserFollowingEntity.findOne({ where: { user: { id: user.id }, - followUser: { id: targetUserId } - } + followUser: { id: targetUserId }, + }, }); - if(isFollowing) return true; + if (isFollowing) return true; else return false; - } async findUserById(userId: number): Promise { try { const user: UserEntity = await UserEntity.findOne({ where: { id: userId }, - relations: [ 'profileImage' ], + relations: ['profileImage'], }); return user; } catch (error) { @@ -91,7 +205,6 @@ export class UserService { console.log('겟프로필이미지: ', profileImageEntity); return profileImageEntity; - } catch (error) { console.log('Error on getProfileImage: ' + error); } @@ -155,9 +268,8 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { user: { id: userId } }, - relations: {followUser: {profileImage : true}}, + relations: { followUser: { profileImage: true } }, }); - } catch (error) { console.log('Error on getFollowingList: ' + error); } @@ -167,9 +279,8 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { followUser: { id: userId } }, - relations: {user: {profileImage : true}}, + relations: { user: { profileImage: true } }, }); - } catch (error) { console.log('Error on getFollowingList: ' + error); } @@ -252,7 +363,6 @@ export class UserService { } catch (error) { console.log('Error on findFollowingMates: ', error); throw error; - } } @@ -300,7 +410,7 @@ export class UserService { */ - async isAlreadyFollowing(userId:number, followingId: number) { + async isAlreadyFollowing(userId: number, followingId: number) { const userEntity = await this.findUserById(userId); const followingEntity = await this.findUserById(followingId); console.log('현재 로그인한 사용자 : ', userEntity.id); @@ -308,8 +418,10 @@ export class UserService { const isFollowing = await UserFollowingEntity.findOne({ where: { - user : {id : userId}, followUser : {id : followingId}, - }}); + user: { id: userId }, + followUser: { id: followingId }, + }, + }); // 팔로우 관계 : true 반환 return !!isFollowing; @@ -317,7 +429,7 @@ export class UserService { async checkAlreadyMember(user: UserEntity, ruleID: number) { const rule = await RuleInvitationEntity.findOne({ - where: {member: {id: user.id}, rule: {id: ruleID}} + where: { member: { id: user.id }, rule: { id: ruleID } }, }); // 이미 규칙 멤버인 경우 : true 반환 console.log('rule : ', rule); From ad656c92bbc9c78259c2ea0f46c5610d90cd944d Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 11 Feb 2024 23:58:48 +0900 Subject: [PATCH 190/316] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20entity?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notification/notification.entity.ts | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/notification/notification.entity.ts diff --git a/src/notification/notification.entity.ts b/src/notification/notification.entity.ts new file mode 100644 index 0000000..fa52677 --- /dev/null +++ b/src/notification/notification.entity.ts @@ -0,0 +1,43 @@ +import { + BaseEntity, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { UserEntity } from '../user/user.entity'; + +@Entity() +export class NotificationEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @JoinColumn() + @ManyToOne(() => UserEntity) + notificationReceiver: UserEntity; + + @Column({ type: 'enum', enum: ['LIKE', 'COMMENT'] }) + notificationType: 'LIKE' | 'COMMENT'; + + @Column() + notificationContent: string; + + @Column() + notificationItemId: number; + + @Column({ default: false }) + notificationRead: boolean; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; +} \ No newline at end of file From 008ee52cfeca868923b11928daf723daaf2800f9 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 11 Feb 2024 23:58:56 +0900 Subject: [PATCH 191/316] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 + src/notification/notification.controller.ts | 15 ++++++ src/notification/notification.entity.ts | 2 +- src/notification/notification.module.ts | 9 ++++ src/notification/notification.service.ts | 53 +++++++++++++++++++++ src/response/response-code.enum.ts | 1 + 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/notification/notification.controller.ts create mode 100644 src/notification/notification.module.ts create mode 100644 src/notification/notification.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 4e9c740..606fbaf 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -20,6 +20,7 @@ import { MemberModule } from './member/member.module'; import { SearchModule } from './search/search.module'; import { MapModule } from './map/map.module'; import { MateModule } from './mate/mate.module'; +import { NotificationModule } from './notification/notification.module'; @Module({ imports: [ @@ -43,6 +44,7 @@ import { MateModule } from './mate/mate.module'; SearchModule, MapModule, MateModule, + NotificationModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/notification/notification.controller.ts b/src/notification/notification.controller.ts new file mode 100644 index 0000000..409fd03 --- /dev/null +++ b/src/notification/notification.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { NotificationService } from './notification.service'; +import { Request } from 'express'; +import { UserGuard } from '../user/user.guard'; + +@Controller('notification') +export class NotificationController { + constructor(private readonly notificationService: NotificationService) {} + + @Get() + @UseGuards(UserGuard) + ListNotification(@Req() req: Request) { + return this.notificationService.listNotifications(req.user.id); + } +} diff --git a/src/notification/notification.entity.ts b/src/notification/notification.entity.ts index fa52677..a0395ae 100644 --- a/src/notification/notification.entity.ts +++ b/src/notification/notification.entity.ts @@ -40,4 +40,4 @@ export class NotificationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/notification/notification.module.ts b/src/notification/notification.module.ts new file mode 100644 index 0000000..c2ad498 --- /dev/null +++ b/src/notification/notification.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { NotificationService } from './notification.service'; +import { NotificationController } from './notification.controller'; + +@Module({ + controllers: [NotificationController], + providers: [NotificationService], +}) +export class NotificationModule {} diff --git a/src/notification/notification.service.ts b/src/notification/notification.service.ts new file mode 100644 index 0000000..c17ba2d --- /dev/null +++ b/src/notification/notification.service.ts @@ -0,0 +1,53 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { NotificationEntity } from './notification.entity'; +import { ResponseDto } from '../response/response.dto'; +import { ResponseCode } from '../response/response-code.enum'; + +@Injectable() +export class NotificationService { + private readonly logger = new Logger(NotificationService.name); + + static createNotificationContent(type: 'LIKE' | 'COMMENT', nickname: string) { + return `${nickname}님이 내 시그니처에 ${ + type === 'LIKE' ? '좋아요' : '댓글' + }을 남겼습니다.`; + } + + async listNotifications(userId: number) { + try { + const notifications = await NotificationEntity.find({ + where: { + notificationReceiver: { + id: userId, + }, + }, + order: { + created: 'DESC', + }, + take: 100, + }); + + return new ResponseDto( + ResponseCode.GET_NOTIFICATION_SUCCESS, + true, + '알림 조회 성공', + notifications.map((notification) => ({ + id: notification.id, + type: notification.notificationType, + content: notification.notificationContent, + itemId: notification.notificationItemId, + isRead: notification.notificationRead, + created: notification.created, + })), + ); + } catch (e) { + this.logger.error(e); + return new ResponseDto( + ResponseCode.INTERNAL_SERVEr_ERROR, + false, + '서버 내부 오류', + null, + ); + } + } +} diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index dd88022..ace2bb0 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -20,6 +20,7 @@ export enum ResponseCode { DELETE_INVITATION_SUCCESS = 'OK', GET_RULE_LIST_SUCCESS = 'OK', PATCH_RULE_SUCCESS = 'OK', + GET_NOTIFICATION_SUCCESS = 'OK', GET_USER_PROFILE_SUCCESS = 'OK', From 4d8cb6d8ef6d024c20023c547cd2bf1389e83a12 Mon Sep 17 00:00:00 2001 From: kaaang Date: Mon, 12 Feb 2024 00:03:26 +0900 Subject: [PATCH 192/316] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=EC=8B=9C=20=EC=95=8C=EB=A6=BC=EC=9D=B4=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 321 +++++++++++++++++------------ 1 file changed, 187 insertions(+), 134 deletions(-) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 492e67b..19615c7 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -1,6 +1,11 @@ // signature.service.ts -import { BadRequestException, HttpException, Injectable, NotFoundException } from '@nestjs/common'; +import { + BadRequestException, + HttpException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { CreateSignatureDto } from './dto/create-signature.dto'; import { SignatureEntity } from './domain/signature.entity'; import { HomeSignatureDto } from './dto/home-signature.dto'; @@ -12,50 +17,57 @@ import { AuthorSignatureDto } from './dto/author-signature.dto'; import { HeaderSignatureDto } from './dto/header-signature.dto'; import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { GetLikeListDto } from './dto/get-like-list.dto' +import { GetLikeListDto } from './dto/get-like-list.dto'; import { LikeProfileDto } from './dto/like-profile.dto'; import { S3UtilService } from '../utils/S3.service'; import { ResponsePageSignatureDto } from './dto/response-page-signature.dto'; +import { NotificationEntity } from '../notification/notification.entity'; +import { NotificationService } from '../notification/notification.service'; @Injectable() export class SignatureService { - constructor( private readonly userService: UserService, private readonly s3Service: S3UtilService, ) {} - async createSignature(createSignatureDto: CreateSignatureDto, userId: number): Promise { - try{ + async createSignature( + createSignatureDto: CreateSignatureDto, + userId: number, + ): Promise { + try { // [1] 시그니처 저장 - const signature: SignatureEntity = await SignatureEntity.createSignature(createSignatureDto, userId); + const signature: SignatureEntity = await SignatureEntity.createSignature( + createSignatureDto, + userId, + ); if (!signature) throw new BadRequestException(); - else{ + else { // [2] 각 페이지 저장 - try{ - for(const pageSignatureDto of createSignatureDto.pages){ - await this.saveSignaturePage(pageSignatureDto,signature); + try { + for (const pageSignatureDto of createSignatureDto.pages) { + await this.saveSignaturePage(pageSignatureDto, signature); } return signature.id; - - }catch (e){ - + } catch (e) { // 만약 페이지 저장 중에 오류 발생해 저장이 중단되면 시그니처도 삭제하도록 await this.deleteSignature(signature); - console.log("Error on createSignatruePage: ", e); + console.log('Error on createSignatruePage: ', e); throw e; } } - } - catch(e){ - console.log("Error on createSignatrue: ", e); + } catch (e) { + console.log('Error on createSignatrue: ', e); throw e; } } - async saveSignaturePage(pageSignatureDto:PageSignatureDto, signature:SignatureEntity){ - const signaturePage:SignaturePageEntity = new SignaturePageEntity(); + async saveSignaturePage( + pageSignatureDto: PageSignatureDto, + signature: SignatureEntity, + ) { + const signaturePage: SignaturePageEntity = new SignaturePageEntity(); signaturePage.signature = signature; signaturePage.content = pageSignatureDto.content; @@ -63,11 +75,14 @@ export class SignatureService { signaturePage.page = pageSignatureDto.page; // 랜덤 이미지 키 생성 - const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; + const key = `signature/${this.s3Service.generateRandomImageKey( + 'signaturePage.png', + )}`; // Base64 이미지 업로드 const uploadedImage = await this.s3Service.putObjectFromBase64( - key, pageSignatureDto.image + key, + pageSignatureDto.image, ); console.log(uploadedImage); @@ -76,13 +91,13 @@ export class SignatureService { await signaturePage.save(); } - async homeSignature(userId: number): Promise { try { - console.log("userId; ",userId); - const homeSignatureList: HomeSignatureDto[] = await this.findMySignature(userId); + console.log('userId; ', userId); + const homeSignatureList: HomeSignatureDto[] = await this.findMySignature( + userId, + ); return homeSignatureList; - } catch (error) { // 예외 처리 console.error('Error on HomeSignature: ', error); @@ -90,14 +105,14 @@ export class SignatureService { } } - async findMySignature(user_id: number):Promise { - const mySignatureList:HomeSignatureDto[] = []; + async findMySignature(user_id: number): Promise { + const mySignatureList: HomeSignatureDto[] = []; const signatures = await SignatureEntity.find({ - where: { user: { id: user_id} }, + where: { user: { id: user_id } }, }); - for(const signature of signatures){ - const homeSignature:HomeSignatureDto = new HomeSignatureDto(); + for (const signature of signatures) { + const homeSignature: HomeSignatureDto = new HomeSignatureDto(); homeSignature._id = signature.id; homeSignature.title = signature.title; @@ -114,52 +129,58 @@ export class SignatureService { async checkIfLiked(user: UserEntity, signatureId: number): Promise { const signatureLike = await SignatureLikeEntity.findOne({ - where:{ + where: { user: { id: user.id }, - signature: {id: signatureId} - } + signature: { id: signatureId }, + }, }); - if(signatureLike) return true; + if (signatureLike) return true; else return false; - } - async detailSignature(userId: number, signatureId: number):Promise { - try{ - const detailSignatureDto:DetailSignatureDto = new DetailSignatureDto(); + async detailSignature( + userId: number, + signatureId: number, + ): Promise { + try { + const detailSignatureDto: DetailSignatureDto = new DetailSignatureDto(); // [1] 시그니처 객체, 로그인 유저 객체 가져오기 - const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); - if(signature == null) return null; - console.log("시그니처 정보: ", signature); + const signature: SignatureEntity = + await SignatureEntity.findSignatureById(signatureId); + if (signature == null) return null; + console.log('시그니처 정보: ', signature); - const loginUser:UserEntity = await this.userService.findUserById(userId); - console.log("로그인한 유저 정보: ", loginUser); + const loginUser: UserEntity = await this.userService.findUserById(userId); + console.log('로그인한 유저 정보: ', loginUser); /****************************************/ // [2] 시그니처 작성자 정보 가져오기 const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); - if(signature.user){ + if (signature.user) { authorDto._id = signature.user.id; authorDto.name = signature.user.nickname; const image = await this.userService.getProfileImage(signature.user.id); - if(image == null) authorDto.image = null; + if (image == null) authorDto.image = null; else authorDto.image = await this.s3Service.getImageUrl(image.imageKey); - if(loginUser.id == signature.user.id) { // 시그니처 작성자가 본인이면 is_followed == null + if (loginUser.id == signature.user.id) { + // 시그니처 작성자가 본인이면 is_followed == null authorDto.is_followed = null; - } - else{ + } else { // 해당 시그니처 작성자를 팔로우하고 있는지 확인 - authorDto.is_followed = await this.userService.checkIfFollowing(loginUser,signature.user.id); + authorDto.is_followed = await this.userService.checkIfFollowing( + loginUser, + signature.user.id, + ); } detailSignatureDto.author = authorDto; - } - else{ // 해당 시그니처를 작성한 유저가 존재하지 않는 경우(탈퇴한 경우) - console.log("작성자 유저가 존재하지 않습니다."); + } else { + // 해당 시그니처를 작성한 유저가 존재하지 않는 경우(탈퇴한 경우) + console.log('작성자 유저가 존재하지 않습니다.'); authorDto._id = null; authorDto.name = null; authorDto.image = null; @@ -180,7 +201,10 @@ export class SignatureService { headerSignatureDto.date = await SignatureEntity.formatDateString(date); // 해당 시그니처 좋아요 눌렀는지 확인하기 - headerSignatureDto.is_liked = await this.checkIfLiked(loginUser,signatureId); + headerSignatureDto.is_liked = await this.checkIfLiked( + loginUser, + signatureId, + ); detailSignatureDto.header = headerSignatureDto; @@ -188,10 +212,12 @@ export class SignatureService { // [4] 각 페이지 내용 가져오기 const signaturePageDto: ResponsePageSignatureDto[] = []; - const pages: SignaturePageEntity[] = await SignaturePageEntity.findSignaturePages(signatureId); + const pages: SignaturePageEntity[] = + await SignaturePageEntity.findSignaturePages(signatureId); - for(const page of pages){ - const pageDto:ResponsePageSignatureDto = new ResponsePageSignatureDto(); + for (const page of pages) { + const pageDto: ResponsePageSignatureDto = + new ResponsePageSignatureDto(); pageDto._id = page.id; pageDto.page = page.page; pageDto.content = page.content; @@ -204,205 +230,232 @@ export class SignatureService { } detailSignatureDto.pages = signaturePageDto; - return detailSignatureDto; - } - catch(error){ + } catch (error) { // 예외 처리 console.error('Error on DetailSignature: ', error); throw new HttpException('Internal Server Error', 500); } } - async findIfAlreadyLiked(userId: number, signatureId: number): Promise { - + async findIfAlreadyLiked( + userId: number, + signatureId: number, + ): Promise { const signatureLike = await SignatureLikeEntity.findOne({ - where:{ + where: { user: { id: userId }, - signature: {id: signatureId} - } + signature: { id: signatureId }, + }, }); - if(signatureLike) return signatureLike + if (signatureLike) return signatureLike; else null; - } - async addLikeOnSignature(userId: number, signatureId: number) { - // [1] 시그니처 객체, 로그인 유저 객체 가져오기 - const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); - console.log("시그니처 정보: ", signature); + const signature: SignatureEntity = await SignatureEntity.findSignatureById( + signatureId, + ); + console.log('시그니처 정보: ', signature); - const loginUser:UserEntity = await this.userService.findUserById(userId); - console.log("로그인한 유저 정보: ", loginUser); + const loginUser: UserEntity = await this.userService.findUserById(userId); + console.log('로그인한 유저 정보: ', loginUser); // [2] 좋아요 테이블에 인스턴스 추가하기 - await SignatureLikeEntity.createLike(signature,loginUser); + await SignatureLikeEntity.createLike(signature, loginUser); // [3] 해당 시그니처 좋아요 개수 추가하기 - signature.liked ++; + signature.liked++; await SignatureEntity.save(signature); - return signature; + // 알림 표시 + // Todo: 좋아요를 했다가 해제한 경우에 알림을 어떻게 처리할 것인가? + const notification = new NotificationEntity(); + notification.notificationReceiver = signature.user; + notification.notificationType = 'LIKE'; + notification.notificationContent = + NotificationService.createNotificationContent('LIKE', loginUser.nickname); + notification.notificationItemId = signature.id; + await notification.save(); + return signature; } - async deleteLikeOnSignature(signatureLike:SignatureLikeEntity, signatureId:number) { - + async deleteLikeOnSignature( + signatureLike: SignatureLikeEntity, + signatureId: number, + ) { // [1] 해당 좋아요 기록 삭제 const deleted_like = await SignatureLikeEntity.softRemove(signatureLike); // [2] 시그니처 좋아요 개수 -1 - const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); - signature.liked --; + const signature: SignatureEntity = await SignatureEntity.findSignatureById( + signatureId, + ); + signature.liked--; const newSignature = await SignatureEntity.save(signature); return signature; } - async deleteSignature(signature){ - try{ - + async deleteSignature(signature) { + try { // [1] 페이지부터 삭제 - const deleteSignaturePages: SignaturePageEntity[] = await SignaturePageEntity.find({ - where:{ signature:{ id: signature.id } } - }); + const deleteSignaturePages: SignaturePageEntity[] = + await SignaturePageEntity.find({ + where: { signature: { id: signature.id } }, + }); - for( const deletePage of deleteSignaturePages ){ + for (const deletePage of deleteSignaturePages) { await SignaturePageEntity.softRemove(deletePage); } // [2] 시그니처 삭제 await SignatureEntity.softRemove(signature); - - } - catch(error){ - console.log("Error on deleting Signature: ",error); + } catch (error) { + console.log('Error on deleting Signature: ', error); throw error; } } - async patchSignature(signatureId: number, patchSignatureDto: CreateSignatureDto) { - + async patchSignature( + signatureId: number, + patchSignatureDto: CreateSignatureDto, + ) { // [1] 시그니처 객체 가져오기 - const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); - if(signature == null) return null; - console.log("시그니처 정보: ", signature); + const signature: SignatureEntity = await SignatureEntity.findSignatureById( + signatureId, + ); + if (signature == null) return null; + console.log('시그니처 정보: ', signature); // [2] 시그니처 수정 signature.title = patchSignatureDto.title; await SignatureEntity.save(signature); // [3] 기존 페이지 가져오기 - const originalSignaturePages: SignaturePageEntity[] = await SignaturePageEntity.find({ - where:{ signature:{ id: signature.id } } - }); + const originalSignaturePages: SignaturePageEntity[] = + await SignaturePageEntity.find({ + where: { signature: { id: signature.id } }, + }); // [4] 기존 페이지 수정 및 새로운 페이지 추가하기 - for(const patchedPage of patchSignatureDto.pages){ - if(!patchedPage._id){ // id가 없으면 새로 추가할 페이지 + for (const patchedPage of patchSignatureDto.pages) { + if (!patchedPage._id) { + // id가 없으면 새로 추가할 페이지 await this.saveSignaturePage(patchedPage, signature); } - for( const originalPage of originalSignaturePages ){ - if(patchedPage._id == originalPage.id){ + for (const originalPage of originalSignaturePages) { + if (patchedPage._id == originalPage.id) { originalPage.content = patchedPage.content; originalPage.location = patchedPage.location; // 랜덤 이미지 키 생성 - const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; + const key = `signature/${this.s3Service.generateRandomImageKey( + 'signaturePage.png', + )}`; // Base64 이미지 업로드 const uploadedImage = await this.s3Service.putObjectFromBase64( - key, patchedPage.image + key, + patchedPage.image, ); // 이미지 키 저장 console.log(uploadedImage); originalPage.image = key; - } await SignaturePageEntity.save(originalPage); } } return signatureId; - } - - async getSignatureLikeList(userId: number, signatureId: number): Promise { - - try{ + async getSignatureLikeList( + userId: number, + signatureId: number, + ): Promise { + try { const signature = await SignatureEntity.findSignatureById(signatureId); - if(!signature) { - throw new NotFoundException(`Signature with ID ${signatureId} not found`); + if (!signature) { + throw new NotFoundException( + `Signature with ID ${signatureId} not found`, + ); } const getLikeListDto: GetLikeListDto = new GetLikeListDto(); - const signatureLikeEntities = await SignatureLikeEntity.findSignatureLikes(signatureId); + const signatureLikeEntities = + await SignatureLikeEntity.findSignatureLikes(signatureId); // 총 좋아요 개수 getLikeListDto.liked = signatureLikeEntities.length; const likeProfileDtos: LikeProfileDto[] = []; - for(const signatureLikeEntity of signatureLikeEntities){ + for (const signatureLikeEntity of signatureLikeEntities) { const likeProfileDto = new LikeProfileDto(); - if (signatureLikeEntity.user) { - likeProfileDto._id = signatureLikeEntity.user.id; likeProfileDto.introduction = signatureLikeEntity.user.introduction; likeProfileDto.nickname = signatureLikeEntity.user.nickname; // 프로필 이미지 꺼내오기 - const image = await this.userService.getProfileImage(signatureLikeEntity.user.id); - if(image == null)likeProfileDto.image = null; - else{ + const image = await this.userService.getProfileImage( + signatureLikeEntity.user.id, + ); + if (image == null) likeProfileDto.image = null; + else { const userImageKey = image.imageKey; - likeProfileDto.image = await this.s3Service.getImageUrl(userImageKey); + likeProfileDto.image = await this.s3Service.getImageUrl( + userImageKey, + ); } // 만약 좋아요 누른 사용자가 본인이 아니라면 is_followed 값을 체크하고 본인이면 null로 보내준다. - if(signatureLikeEntity.user.id != userId){ - const loginUser= await this.userService.findUserById(userId); - likeProfileDto.is_followed = await this.userService.checkIfFollowing(loginUser,signatureLikeEntity.user.id); - } - else likeProfileDto.is_followed = null; + if (signatureLikeEntity.user.id != userId) { + const loginUser = await this.userService.findUserById(userId); + likeProfileDto.is_followed = + await this.userService.checkIfFollowing( + loginUser, + signatureLikeEntity.user.id, + ); + } else likeProfileDto.is_followed = null; likeProfileDtos.push(likeProfileDto); } } getLikeListDto.profiles = likeProfileDtos; return getLikeListDto; - - }catch(error){ - console.log("Error on GetSignatureLikeList: ", error); + } catch (error) { + console.log('Error on GetSignatureLikeList: ', error); throw error; } } - async getMyRecentSignatures(userId: number, take:number) { // 가장 최신 시그니처 반환 + async getMyRecentSignatures(userId: number, take: number) { + // 가장 최신 시그니처 반환 // 1. 메이트 탐색의 기준이 될 장소 가져오기 = 사용자의 가장 최신 시그니처의 첫 번째 페이지 장소 return await SignatureEntity.find({ where: { - user:{ id: userId }, + user: { id: userId }, }, order: { - created: 'DESC' // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 + created: 'DESC', // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 }, - take: take, // 최신 시그니처 가져오기 + take: take, // 최신 시그니처 가져오기 }); } - async getSignatureCnt(userId: number): Promise { // 시그니처 개수 반환 + async getSignatureCnt(userId: number): Promise { + // 시그니처 개수 반환 return await SignatureEntity.count({ where: { user: { id: userId }, - } + }, }); } } From 8aeaf74076097d1965f40514b0c0726ecbb7ba63 Mon Sep 17 00:00:00 2001 From: kaaang Date: Mon, 12 Feb 2024 00:04:04 +0900 Subject: [PATCH 193/316] =?UTF-8?q?update:=20=ED=99=95=EC=9D=B8=ED=95=9C?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=EC=9D=80=20=EC=9D=BD=EC=9D=8C=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=90=98=EB=8F=84=EB=A1=9D=20=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notification/notification.service.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/notification/notification.service.ts b/src/notification/notification.service.ts index c17ba2d..4a3e89d 100644 --- a/src/notification/notification.service.ts +++ b/src/notification/notification.service.ts @@ -27,6 +27,18 @@ export class NotificationService { take: 100, }); + await NotificationEntity.update( + { + notificationReceiver: { + id: userId, + }, + notificationRead: false, + }, + { + notificationRead: true, + }, + ); + return new ResponseDto( ResponseCode.GET_NOTIFICATION_SUCCESS, true, From 85f6be19b89b636e14bf785f5ace54e5d39c4c85 Mon Sep 17 00:00:00 2001 From: kaaang Date: Mon, 12 Feb 2024 00:37:59 +0900 Subject: [PATCH 194/316] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A7=80=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=EB=B3=B4=EA=B8=B0=20API=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 1 + src/user/user.controller.ts | 8 ++- src/user/user.service.ts | 85 ++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index dd88022..59d0122 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -20,6 +20,7 @@ export enum ResponseCode { DELETE_INVITATION_SUCCESS = 'OK', GET_RULE_LIST_SUCCESS = 'OK', PATCH_RULE_SUCCESS = 'OK', + GET_DIARY_SUCCESS = 'OK', GET_USER_PROFILE_SUCCESS = 'OK', diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 54f2a36..520d545 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Post, Req, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Query, Req, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; import { IUserProfile } from './user.dto'; import { UserGuard } from './user.guard'; @@ -45,4 +45,10 @@ export class UserController { DeleteAccount(@Req() req: Request) { return this.userService.deleteAccount(req.user.id); } + + @Get('/diaries') + @UseGuards(UserGuard) + ListDiaries(@Req() req: Request, @Query('cursor') cursor: string) { + return this.userService.listDiaries(req.user.id, cursor); + } } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 55bf285..3503010 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -7,13 +7,17 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; -import {Like} from "typeorm"; -import {RuleInvitationEntity} from "../rule/domain/rule.invitation.entity"; +import { LessThan, Like } from 'typeorm'; +import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; +import { DiaryEntity } from '../diary/models/diary.entity'; +import { S3UtilService } from '../utils/S3.service'; @Injectable() export class UserService { private readonly logger: Logger = new Logger(UserService.name); + constructor(private s3Service: S3UtilService) {} + private _hashPassword(password: string): string { return bcrypt.hashSync(password, 10); } @@ -61,20 +65,19 @@ export class UserService { const isFollowing = await UserFollowingEntity.findOne({ where: { user: { id: user.id }, - followUser: { id: targetUserId } - } + followUser: { id: targetUserId }, + }, }); - if(isFollowing) return true; + if (isFollowing) return true; else return false; - } async findUserById(userId: number): Promise { try { const user: UserEntity = await UserEntity.findOne({ where: { id: userId }, - relations: [ 'profileImage' ], + relations: ['profileImage'], }); return user; } catch (error) { @@ -91,7 +94,6 @@ export class UserService { console.log('겟프로필이미지: ', profileImageEntity); return profileImageEntity; - } catch (error) { console.log('Error on getProfileImage: ' + error); } @@ -155,9 +157,8 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { user: { id: userId } }, - relations: {followUser: {profileImage : true}}, + relations: { followUser: { profileImage: true } }, }); - } catch (error) { console.log('Error on getFollowingList: ' + error); } @@ -167,9 +168,8 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { followUser: { id: userId } }, - relations: {user: {profileImage : true}}, + relations: { user: { profileImage: true } }, }); - } catch (error) { console.log('Error on getFollowingList: ' + error); } @@ -252,7 +252,6 @@ export class UserService { } catch (error) { console.log('Error on findFollowingMates: ', error); throw error; - } } @@ -300,7 +299,7 @@ export class UserService { */ - async isAlreadyFollowing(userId:number, followingId: number) { + async isAlreadyFollowing(userId: number, followingId: number) { const userEntity = await this.findUserById(userId); const followingEntity = await this.findUserById(followingId); console.log('현재 로그인한 사용자 : ', userEntity.id); @@ -308,8 +307,10 @@ export class UserService { const isFollowing = await UserFollowingEntity.findOne({ where: { - user : {id : userId}, followUser : {id : followingId}, - }}); + user: { id: userId }, + followUser: { id: followingId }, + }, + }); // 팔로우 관계 : true 반환 return !!isFollowing; @@ -317,10 +318,60 @@ export class UserService { async checkAlreadyMember(user: UserEntity, ruleID: number) { const rule = await RuleInvitationEntity.findOne({ - where: {member: {id: user.id}, rule: {id: ruleID}} + where: { member: { id: user.id }, rule: { id: ruleID } }, }); // 이미 규칙 멤버인 경우 : true 반환 console.log('rule : ', rule); return !!rule; } + + async listDiaries(userId: number, cursor?: string, take = 30) { + if (!cursor || cursor === '') { + cursor = undefined; + } + + const diaries = await DiaryEntity.find({ + where: { + author: { + id: userId, + }, + id: cursor ? LessThan(Number(cursor)) : undefined, + }, + relations: { + image: true, + schedule: { + location: true, + }, + }, + order: { + id: 'DESC', + }, + take, + }); + + return new ResponseDto( + ResponseCode.GET_DIARY_SUCCESS, + true, + '일지 목록 조회 성공', + { + diaries: await Promise.all( + diaries.map(async (diary) => ({ + id: diary.id, + title: diary.title, + place: diary.place, + weather: diary.weather, + mood: diary.mood, + content: diary.content, + location: diary.schedule?.location?.name, + diary_image: diary.image + ? { + id: diary.image.id, + url: await this.s3Service.getImageUrl(diary.image.imageUrl), + } + : null, + })), + ), + }, + ); + } } From 872c9a738833ab1738af584d98a5c323aba79daa Mon Sep 17 00:00:00 2001 From: kaaang Date: Mon, 12 Feb 2024 00:42:22 +0900 Subject: [PATCH 195/316] =?UTF-8?q?fix:=20S3=20Util=20Service=EB=A5=BC=20u?= =?UTF-8?q?ser=20module=EC=97=90=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/user/user.module.ts b/src/user/user.module.ts index e21d51f..ed8377f 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; import { UserService } from './user.service'; import { UserController } from './user.controller'; +import { S3UtilService } from '../utils/S3.service'; @Module({ controllers: [UserController], - providers: [UserService], + providers: [UserService, S3UtilService], }) export class UserModule {} From 9a6c7eadf5d07cd0ca7bde27044a10da68626a62 Mon Sep 17 00:00:00 2001 From: kaaang Date: Mon, 12 Feb 2024 00:47:40 +0900 Subject: [PATCH 196/316] =?UTF-8?q?fix:=20=EC=9D=BC=EC=A7=80=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B6=88?= =?UTF-8?q?=EB=9F=AC=EC=98=A4=EC=A7=80=20=EB=AA=BB=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 3503010..a323826 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -7,7 +7,7 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; -import { LessThan, Like } from 'typeorm'; +import { LessThan } from 'typeorm'; import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import { DiaryEntity } from '../diary/models/diary.entity'; import { S3UtilService } from '../utils/S3.service'; @@ -330,16 +330,24 @@ export class UserService { cursor = undefined; } + // user -> journey -> schedules -> diary -> diaryImage const diaries = await DiaryEntity.find({ where: { - author: { - id: userId, + schedule: { + journey: { + user: { + id: userId, + }, + }, }, id: cursor ? LessThan(Number(cursor)) : undefined, }, relations: { image: true, schedule: { + journey: { + user: true, + }, location: true, }, }, From 656b63cd22542802f5191c34f8f8404ae169c4e8 Mon Sep 17 00:00:00 2001 From: kaaang Date: Mon, 12 Feb 2024 01:02:57 +0900 Subject: [PATCH 197/316] =?UTF-8?q?update:=20=EC=9D=BC=EC=A7=80=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9D=91=EB=8B=B5=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index a323826..76083c6 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -348,7 +348,6 @@ export class UserService { journey: { user: true, }, - location: true, }, }, order: { @@ -370,7 +369,7 @@ export class UserService { weather: diary.weather, mood: diary.mood, content: diary.content, - location: diary.schedule?.location?.name, + date: diary.schedule.date, diary_image: diary.image ? { id: diary.image.id, From 14dd31455d23c0b81ff3aaa5bc4226a6fa2dfedb Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 12 Feb 2024 01:29:25 +0900 Subject: [PATCH 198/316] =?UTF-8?q?feat=20:=20=EC=9D=BC=EC=A0=95=20id=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 6346be7..9c731d2 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -160,6 +160,7 @@ export class MapService { } return { journeyId: journeyId, + scheduleId: schedule.id, date: schedule.date, diary: diary, diaryImage: { From cd226e4763ebb0c2b23a4b0bd4657e38a9419e70 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 02:02:16 +0900 Subject: [PATCH 199/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=8C=93=EA=B8=80=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로컬 테스트 완료 --- .../cursor-page/cursor-page-option.dto.ts | 2 +- src/response/response-code.enum.ts | 2 + src/rule/dto/comment-pair.dto.ts | 19 ------ ...cursor-page-options-parameter.interface.ts | 8 +++ src/rule/dto/cursor-page-order.enum.ts | 4 ++ src/rule/dto/cursor-page.dto.ts | 15 +++++ src/rule/dto/cursor-page.meta.dto.ts | 16 +++++ src/rule/dto/cursor-page.options.dto.ts | 19 ++++++ src/rule/dto/cursot-page-order.enums.ts | 15 +++++ src/rule/dto/detail-comment.dto.ts | 11 ---- src/rule/dto/get-comment.dto.ts | 23 +++++++ ...member.dto.ts => get-search-member.dto.ts} | 0 src/rule/rule.controller.ts | 40 ++++++++++-- src/rule/rule.service.ts | 65 +++++++++++++++++-- 14 files changed, 197 insertions(+), 42 deletions(-) delete mode 100644 src/rule/dto/comment-pair.dto.ts create mode 100644 src/rule/dto/cursor-page-options-parameter.interface.ts create mode 100644 src/rule/dto/cursor-page-order.enum.ts create mode 100644 src/rule/dto/cursor-page.dto.ts create mode 100644 src/rule/dto/cursor-page.meta.dto.ts create mode 100644 src/rule/dto/cursor-page.options.dto.ts create mode 100644 src/rule/dto/cursot-page-order.enums.ts delete mode 100644 src/rule/dto/detail-comment.dto.ts create mode 100644 src/rule/dto/get-comment.dto.ts rename src/rule/dto/{get.search.member.dto.ts => get-search-member.dto.ts} (100%) diff --git a/src/mate/cursor-page/cursor-page-option.dto.ts b/src/mate/cursor-page/cursor-page-option.dto.ts index a8c9208..5148d04 100644 --- a/src/mate/cursor-page/cursor-page-option.dto.ts +++ b/src/mate/cursor-page/cursor-page-option.dto.ts @@ -1,4 +1,4 @@ -// cursor-page.options.ts +// cursor-page.options.dto.ts import { Type } from "class-transformer"; import { IsEnum, IsOptional } from "class-validator"; diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index dd88022..b64697b 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -28,6 +28,7 @@ export enum ResponseCode { COMMENT_UPDATE_SUCCESS = 'OK', COMMENT_DELETE_SUCCESS = 'OK', FOLLOW_SUCCESS = 'OK', + GET_COMMENT_DETAIL_SUCCESS = 'OK', @@ -83,6 +84,7 @@ export enum ResponseCode { GET_USER_PROFILE_FAIL = 'BAD_REQUEST', COMMENT_UPDATE_FAIL = 'BAD_REQUEST', COMMENT_DELETE_FAIL = 'BAD_REQUEST', + GET_COMMENT_DETAIL_FAIL = 'BAD_REQUEST', diff --git a/src/rule/dto/comment-pair.dto.ts b/src/rule/dto/comment-pair.dto.ts deleted file mode 100644 index 75989b2..0000000 --- a/src/rule/dto/comment-pair.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IsNotEmpty, IsNumber, IsString, IsDate } from 'class-validator'; - -export class CommentPairDto { - @IsNotEmpty() - @IsNumber() - id: number; - - @IsNotEmpty() - @IsString() - image: string; - - @IsNotEmpty() - @IsString() - text: string; - - @IsNotEmpty() - @IsDate() - created: Date; -} \ No newline at end of file diff --git a/src/rule/dto/cursor-page-options-parameter.interface.ts b/src/rule/dto/cursor-page-options-parameter.interface.ts new file mode 100644 index 0000000..de61566 --- /dev/null +++ b/src/rule/dto/cursor-page-options-parameter.interface.ts @@ -0,0 +1,8 @@ +import { CursorPageOptionsDto } from "./cursor-page.options.dto"; + +export interface CursorPageMetaDtoParameters { + cursorPageOptionsDto: CursorPageOptionsDto; + total: number; + hasNextData: boolean; + cursor: number; +} \ No newline at end of file diff --git a/src/rule/dto/cursor-page-order.enum.ts b/src/rule/dto/cursor-page-order.enum.ts new file mode 100644 index 0000000..0c83c3f --- /dev/null +++ b/src/rule/dto/cursor-page-order.enum.ts @@ -0,0 +1,4 @@ +export enum Order { + ASC = "asc", + DESC = "desc" +} \ No newline at end of file diff --git a/src/rule/dto/cursor-page.dto.ts b/src/rule/dto/cursor-page.dto.ts new file mode 100644 index 0000000..08c2417 --- /dev/null +++ b/src/rule/dto/cursor-page.dto.ts @@ -0,0 +1,15 @@ +import { IsArray } from "class-validator"; +import { CursorPageMetaDto } from "./cursor-page.meta.dto"; + +export class CursorPageDto { + + @IsArray() + readonly data: T[]; + + readonly meta: CursorPageMetaDto; + + constructor(data: T[], meta: CursorPageMetaDto) { + this.data = data; + this.meta = meta; + } +} \ No newline at end of file diff --git a/src/rule/dto/cursor-page.meta.dto.ts b/src/rule/dto/cursor-page.meta.dto.ts new file mode 100644 index 0000000..4fc9fac --- /dev/null +++ b/src/rule/dto/cursor-page.meta.dto.ts @@ -0,0 +1,16 @@ +import { CursorPageMetaDtoParameters } from "./cursor-page-options-parameter.interface"; + +export class CursorPageMetaDto { + + readonly total: number; + readonly take: number; + readonly hasNextData: boolean; + readonly cursor: number; + + constructor({cursorPageOptionsDto, total, hasNextData, cursor}: CursorPageMetaDtoParameters) { + this.take = cursorPageOptionsDto.take; + this.total = total; + this.hasNextData = hasNextData; + this.cursor = cursor; + } +} \ No newline at end of file diff --git a/src/rule/dto/cursor-page.options.dto.ts b/src/rule/dto/cursor-page.options.dto.ts new file mode 100644 index 0000000..d2b2a6e --- /dev/null +++ b/src/rule/dto/cursor-page.options.dto.ts @@ -0,0 +1,19 @@ +import { Type } from "class-transformer"; +import { IsEnum, IsOptional } from "class-validator"; +import { Order } from "./cursor-page-order.enum"; + +export class CursorPageOptionsDto { + + @Type(() => String) + @IsEnum(Order) + @IsOptional() + readonly sort?: Order = Order.DESC; + + @Type(() => Number) + @IsOptional() + readonly take?: number = 5; + + @Type(() => String) + @IsOptional() + readonly cursorId?: number = "" as any; +} \ No newline at end of file diff --git a/src/rule/dto/cursot-page-order.enums.ts b/src/rule/dto/cursot-page-order.enums.ts new file mode 100644 index 0000000..08c2417 --- /dev/null +++ b/src/rule/dto/cursot-page-order.enums.ts @@ -0,0 +1,15 @@ +import { IsArray } from "class-validator"; +import { CursorPageMetaDto } from "./cursor-page.meta.dto"; + +export class CursorPageDto { + + @IsArray() + readonly data: T[]; + + readonly meta: CursorPageMetaDto; + + constructor(data: T[], meta: CursorPageMetaDto) { + this.data = data; + this.meta = meta; + } +} \ No newline at end of file diff --git a/src/rule/dto/detail-comment.dto.ts b/src/rule/dto/detail-comment.dto.ts deleted file mode 100644 index 7675dda..0000000 --- a/src/rule/dto/detail-comment.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsArray, ValidateNested } from 'class-validator'; -import { Type } from 'class-transformer'; -import { CommentPairDto } from './comment-pair.dto'; - -export class DetailCommentDto { - @IsArray() - @ValidateNested({ each: true }) - @Type(() => CommentPairDto) - commentPairs: CommentPairDto[]; -} - diff --git a/src/rule/dto/get-comment.dto.ts b/src/rule/dto/get-comment.dto.ts new file mode 100644 index 0000000..1576c87 --- /dev/null +++ b/src/rule/dto/get-comment.dto.ts @@ -0,0 +1,23 @@ +import {IsDate, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; + +export class GetCommentDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + content: string; + + @IsNotEmpty() + @IsDate() + updated: Date; + + @IsNotEmpty() + @IsString() + name: string; + + @IsOptional() + @IsString() + image: string; +} \ No newline at end of file diff --git a/src/rule/dto/get.search.member.dto.ts b/src/rule/dto/get-search-member.dto.ts similarity index 100% rename from src/rule/dto/get.search.member.dto.ts rename to src/rule/dto/get-search-member.dto.ts diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 1bab644..8c971bd 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -5,9 +5,9 @@ import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; -import {FollowSearchDto} from "../follow/dto/follow.search.dto"; -import {GetSearchMemberDto} from "./dto/get.search.member.dto"; +import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import { UpdateRuleDto } from "./dto/update-rule.dto"; +import {CursorPageOptionsDto} from "./dto/cursor-page.options.dto"; @Controller('mate/rule') export class RuleController { @@ -36,7 +36,33 @@ export class RuleController { } } - // [2] 여행 규칙 상세 페이지 조회 (게시글) + // [2] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 + @Get('/detail/:ruleId') + @UseGuards(UserGuard) + async getComment(@Req() req: Request, + @Param('ruleId') ruleId: number, + @Query() cursorPageOptionsDto: CursorPageOptionsDto + ): Promise> { + try { + const result = await this.ruleService.getComment(cursorPageOptionsDto, ruleId); + + return new ResponseDto( + ResponseCode.GET_COMMENT_DETAIL_SUCCESS, + true, + "여행 규칙 상세 페이지 (댓글) 조회 성공", + result + ); + } catch (e) { + return new ResponseDto( + ResponseCode.GET_COMMENT_DETAIL_FAIL, + false, + "여행 규칙 상세 페이지 (댓글) 조회 실패", + null + ); + } + } + + // [3] 여행 규칙 상세 페이지 조회 (게시글) @Get('/detail/:ruleId') @UseGuards(UserGuard) async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { @@ -61,7 +87,7 @@ export class RuleController { } } - // [2] 여행 규칙 수정 + // [4] 여행 규칙 수정 @Patch('/detail/:ruleId') @UseGuards(UserGuard) async updateRule(@Body() updateRuleDto: UpdateRuleDto, @Req() req: Request, @Param('ruleId') ruleId: number): Promise> { @@ -86,7 +112,7 @@ export class RuleController { } } - // [3] 여행 규칙 전체 리스트 조회 + // [5] 여행 규칙 전체 리스트 조회 @Get('list') @UseGuards(UserGuard) async getRuleList(@Req() req: Request): Promise> { @@ -109,7 +135,7 @@ export class RuleController { } } - // [3] 여행 규칙 생성 + // [6] 여행 규칙 생성 @Post('/write') @UseGuards(UserGuard) async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { @@ -132,7 +158,7 @@ export class RuleController { } } - // [4] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 + // [7] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 @Get('/write/search/:ruleId') @UseGuards(UserGuard) async getSearchMember( diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index c3f9244..a52e592 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,4 +1,4 @@ -import {Injectable, HttpException, BadRequestException} from '@nestjs/common'; +import {Injectable, HttpException, BadRequestException, HttpStatus} from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; @@ -9,10 +9,14 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import {Like} from "typeorm"; -import {GetSearchMemberDto} from "./dto/get.search.member.dto"; +import {LessThan, Like} from "typeorm"; +import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; -import {FollowSearchDto} from "../follow/dto/follow.search.dto"; +import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; +import {CommentEntity} from "../comment/domain/comment.entity"; +import {GetCommentDto } from "./dto/get-comment.dto"; +import {CursorPageDto} from "./dto/cursor-page.dto"; +import {CursorPageMetaDto} from "./dto/cursor-page.meta.dto"; @Injectable() export class RuleService { @@ -108,6 +112,59 @@ export class RuleService { return dto; }; + // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 + async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number): Promise> { + // (1) 데이터 조회 + const [comments, total] = await CommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + rule: { id: ruleId }, + id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null, + }, + relations: {user:{profileImage: true}}, + order: { + id: cursorPageOptionsDto.sort.toUpperCase() as any, + }, + }); + + const result = await Promise.all(comments.map(async (comment) => { + const getCommentDto = new GetCommentDto(); + + getCommentDto.id = comment.id; + getCommentDto.name = comment.user.name; + getCommentDto.content = comment.content; + getCommentDto.updated = comment.updated; + + // 사용자 프로필 이미지 + const image = comment.user.profileImage; + if(image == null) getCommentDto.image = null; + else { + const userImageKey = image.imageKey; + getCommentDto.image = await this.s3Service.getImageUrl(userImageKey); + } + + return getCommentDto; + })); + + // (2) 페이징 및 정렬 기준 설정 + const takePerPage = cursorPageOptionsDto.take; + const isLastPage = total <= takePerPage; + + let hasNextData = true; + let cursor: number; + + if (isLastPage || result.length <= 0) { + hasNextData = false; + cursor = null; + } else { + cursor = result[result.length - 1].id; + } + + const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto(result, cursorPageMetaDto); + } + // [3] 여행 규칙 나가기 // -1) 초대 받은 팀원 -> 초대 삭제 async deleteInvitation(ruleId: number, userId: number): Promise { From e3621cfaf2fa9a263356aa09432b4102860f6f44 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 12 Feb 2024 02:52:23 +0900 Subject: [PATCH 200/316] =?UTF-8?q?feat:=EC=9D=B4=EB=AF=B8=EC=A7=80url?= =?UTF-8?q?=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/models/diary.image.entity.ts | 1 + src/map/map.module.ts | 2 ++ src/map/map.service.ts | 13 +++++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index b39a49c..c9ab3f6 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -10,6 +10,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { DiaryEntity } from './diary.entity'; +import { S3UtilService } from 'src/utils/S3.service'; @Entity() export class DiaryImageEntity extends BaseEntity { diff --git a/src/map/map.module.ts b/src/map/map.module.ts index c3a9b8b..14895f4 100644 --- a/src/map/map.module.ts +++ b/src/map/map.module.ts @@ -2,8 +2,10 @@ import { Module } from '@nestjs/common'; import { MapController } from './map.controller'; import { MapService } from './map.service'; +import { S3Module } from 'src/utils/S3.module'; @Module({ + imports: [S3Module], controllers: [MapController], providers: [MapService], }) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 9c731d2..5e7a1a4 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -10,9 +10,12 @@ import { LocationEntity } from 'src/location/location.entity'; import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; import { CursorBasedPaginationRequestDto } from './cursor-based-pagination-request.dto.ts'; +import { S3UtilService } from 'src/utils/S3.service'; @Injectable() export class MapService { + constructor(private readonly s3UtilService: S3UtilService) {} + /*캘린더에서 사용자의 월별 일정 불러오기*/ async getMonthlySchedules( userId: number, @@ -155,6 +158,9 @@ export class MapService { return null; } const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + const imageUrl = await this.s3UtilService.getImageUrl( + diaryImg.imageUrl, + ); if (!diaryImg) { return null; } @@ -165,7 +171,7 @@ export class MapService { diary: diary, diaryImage: { id: diaryImg.id, - imageUrl: diaryImg.imageUrl, + imageUrl: imageUrl, }, }; }), @@ -247,9 +253,12 @@ export class MapService { return null; } const diaryImage = await DiaryImageEntity.findExistImgUrl(diary); + const imageUrl = await this.s3UtilService.getImageUrl( + diaryImage.imageUrl, + ); return { imageId: diaryImage.id, - imageUrl: diaryImage.imageUrl, + imageUrl: imageUrl, }; }), ); From 9b8cc638151175bd34e1bd465e2482e57a17099b Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 02:56:29 +0900 Subject: [PATCH 201/316] =?UTF-8?q?fix=20:=20controller=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.controller.ts | 8 +- src/comment/comment.service.ts | 3 + src/follow/follow.controller.ts | 84 ++++++------- src/follow/follow.service.ts | 192 +++++++++++++++--------------- src/rule/rule.controller.ts | 151 ++++++++++++----------- 5 files changed, 221 insertions(+), 217 deletions(-) diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index 076fefb..d3932ad 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -8,13 +8,13 @@ import { Request } from 'express'; import {UserEntity} from "../user/user.entity"; import {RuleMainEntity} from "../rule/domain/rule.main.entity"; -@Controller('mate/rule') +@Controller('mate/rule/comment') export class CommentController { constructor( private readonly commentService: CommentService, ) {} - // 여행 규칙 코멘트 생성 + // [1] 댓글 작성 @Post('/:ruleId') @UseGuards(UserGuard) async createComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { @@ -37,7 +37,7 @@ export class CommentController { } } - // 여행 규칙 코멘트 수정 + // [2] 댓글 수정 @Patch('/:ruleId/:commentId') @UseGuards(UserGuard) async updateComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Param('commentId') commentId: number,@Req() req: Request): Promise> { @@ -57,7 +57,7 @@ export class CommentController { } } - // 여행 규칙 코멘트 삭제 + // [3] 댓글 삭제 @Delete('/:ruleId/:commentId') @UseGuards(UserGuard) async deleteComment(@Param('ruleId') ruleId: number, @Param('commentId') commentId: number,@Req() req: Request): Promise> { diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 2540601..1aff2a4 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -7,6 +7,7 @@ import {UserEntity} from "../user/user.entity"; @Injectable() export class CommentService { + // [1] 댓글 작성 async createComment(dto: CreateCommentDto, ruleId: number, userId: number): Promise { const comment = new CommentEntity(); @@ -28,6 +29,7 @@ export class CommentService { return comment.id; } + // [2] 댓글 수정 async updateComment(dto: CreateCommentDto, ruleId: number, userId: number, commentId: number) : Promise { try { // 사용자, 규칙, 댓글 검증 @@ -54,6 +56,7 @@ export class CommentService { } } + // [3] 댓글 삭제 async deleteComment(ruleId: number, userId: number, commentId: number) : Promise { try { // 사용자, 규칙, 댓글 검증 diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 33729b2..85ef008 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -7,51 +7,52 @@ import { UserGuard } from '../user/user.guard'; import {query, Request} from 'express'; import { FollowSearchDto } from "./dto/follow.search.dto"; -@Controller('mate') +@Controller('mate/follow') export class FollowController { constructor( private readonly followService: FollowService, private readonly userService: UserService, ) {} - // [1] 팔로우 - @Patch('/search/follow/:followingId') + // [1] 메이트 검색 + @Get('/search/:searchTerm') @UseGuards(UserGuard) - async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { + async getSearchResult( + @Query('searchTerm')searchTerm : string, + @Req() req: Request): Promise> { try { - const result = await this.followService.checkFollow(req.user.id, followingId); + const followSearchDto : FollowSearchDto[] = await this.followService.getSearchResult(req.user.id, searchTerm) return new ResponseDto( - ResponseCode.FOLLOW_SUCCESS, + ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, - "팔로우 / 언팔로우 성공", - result + "검색 결과 리스트 불러오기 성공", + followSearchDto ); - } catch (e) { + } catch (error) { return new ResponseDto( - ResponseCode.FOLLOW_FAIL, + ResponseCode.GET_SEARCH_RESULT_FAIL, false, - e.message, + "검색 결과 리스트 불러오기 실패", null ); } } - // [2] 팔로우 리스트 조회 - @Get('/followList') + // [2] 팔로워 리스트 조회 + @Get('/followerList') @UseGuards(UserGuard) - async getFollowList(@Req() req: Request): Promise> { - console.log('controller'); + async getFollowerList(@Req() req: Request): Promise> { try { - const followList = await this.followService.getFollowList(req.user.id); + const followerList = await this.followService.getFollowerList(req.user.id); return new ResponseDto( - ResponseCode.GET_FOLLOWING_LIST_SUCCESS, - true, - "팔로우 리스트 불러오기 성공", - followList + ResponseCode.GET_FOLLOWER_LIST_SUCCESS, + true, + "팔로워 리스트 불러오기 성공", + followerList ); } catch (e) { return new ResponseDto( - ResponseCode.GET_FOLLOWING_LIST_FAIL, + ResponseCode.GET_FOLLOWER_LIST_FAIL, false, e.message, null @@ -59,21 +60,22 @@ export class FollowController { } } - // [3] 팔로워 리스트 조회 - @Get('/followerList') + // [3] 팔로우 리스트 조회 + @Get('/followList') @UseGuards(UserGuard) - async getFollowerList(@Req() req: Request): Promise> { + async getFollowList(@Req() req: Request): Promise> { + console.log('controller'); try { - const followerList = await this.followService.getFollowerList(req.user.id); + const followList = await this.followService.getFollowList(req.user.id); return new ResponseDto( - ResponseCode.GET_FOLLOWER_LIST_SUCCESS, - true, - "팔로워 리스트 불러오기 성공", - followerList + ResponseCode.GET_FOLLOWING_LIST_SUCCESS, + true, + "팔로우 리스트 불러오기 성공", + followList ); } catch (e) { return new ResponseDto( - ResponseCode.GET_FOLLOWER_LIST_FAIL, + ResponseCode.GET_FOLLOWING_LIST_FAIL, false, e.message, null @@ -81,25 +83,23 @@ export class FollowController { } } - // [4] 메이트 검색 - @Get('/search') + // [4] 팔로우 + @Patch('/:followingId') @UseGuards(UserGuard) - async getSearchResult( - @Query('searchTerm')searchTerm : string, - @Req() req: Request): Promise> { + async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { try { - const followSearchDto : FollowSearchDto[] = await this.followService.getSearchResult(req.user.id, searchTerm) + const result = await this.followService.checkFollow(req.user.id, followingId); return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_SUCCESS, + ResponseCode.FOLLOW_SUCCESS, true, - "검색 결과 리스트 불러오기 성공", - followSearchDto + "팔로우 / 언팔로우 성공", + result ); - } catch (error) { + } catch (e) { return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_FAIL, + ResponseCode.FOLLOW_FAIL, false, - "검색 결과 리스트 불러오기 실패", + e.message, null ); } diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 57d3908..4bfc446 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -15,78 +15,45 @@ export class FollowService { private readonly s3Service: S3UtilService, ) {} - // 팔로우 가능한 사이인지 검증 - async checkFollow(userId : number, followingId : number): Promise { - try { - // case1) 유효한 유저인지 검증 - const userEntity : UserEntity = await this.userService.findUserById(userId); - const followingUser = await UserEntity.findExistUser(followingId); - if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); - console.log('현재 로그인한 유저 : ', userEntity); - console.log('팔로우 대상 유저 : ', followingUser); - - // case2) 본인을 팔로우한 경우 - if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); - - // case3) 팔로우 관계 확인 - const isAlreadyFollowing = await this.userService.isAlreadyFollowing(userId, followingId); - console.log('Is already following? : ', isAlreadyFollowing); - - // [2] 이미 팔로우 한 사이, 팔로우 취소 - if (isAlreadyFollowing) { - console.log('언팔로우 service 호출'); - return this.deleteFollow(userId, followingId); - } else { - // [1] 팔로우 - console.log('팔로우 service 호출'); - return this.createFollow(userId, followingId); - } - } catch (e) { - console.log('팔로우 요청에 실패하였습니다'); - throw new Error(e.message); - } - } + // [1] 메이트 검색 + async getSearchResult(userId: number, searchTerm: string) { + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); + const resultUsers = await UserEntity.find({ + where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], + relations: {profileImage : true, follower : true, following : true} + }); - // [1] 팔로우 - async createFollow(userId : number, followingId : number): Promise { + const userEntity = await UserEntity.findExistUser(userId); - try { - const userEntity : UserEntity = await this.userService.findUserById(userId); - const followingUser = await UserEntity.findExistUser(followingId); - if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); - console.log('현재 로그인한 유저 : ', userEntity); - console.log('팔로우 대상 유저 : ', followingUser); - if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); + const searchResult = await Promise.all(resultUsers.map(async (user) => { + const followSearchDto = new FollowSearchDto(); - const userFollowingEntity = new UserFollowingEntity(); - userFollowingEntity.user = userEntity; - userFollowingEntity.followUser = followingUser; + console.log('현재의 유저 : ', user.id); + followSearchDto.id = user.id; + followSearchDto.nickName = user.nickname; + followSearchDto.introduction = user.introduction; - await userFollowingEntity.save(); - return userFollowingEntity.id; - } catch (e) { - console.log('팔로우 요청에 실패하였습니다'); - throw new Error(e.message); - } - } + followSearchDto.followerCnt = user.follower.length; + followSearchDto.followingCnt = user.following.length; - // [2] 언팔로우 - async deleteFollow(userId: number, followingId:number): Promise { - console.log('언팔로우 서비스 호출'); - const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: - { user : {id : userId}, followUser : {id : followingId}} - }); + // 팔로우 여부 + followSearchDto.isFollowing = await this.userService.checkIfFollowing(userEntity, followSearchDto.id); - try{ - await followEntity.softRemove(); - return followEntity.id; - }catch(e){ - console.error('언팔로우 요청에 실패하였습니다: '); - throw new Error(e.message); - } + // 사용자 프로필 이미지 + const image = user.profileImage; + if(image == null) followSearchDto.image = null; + else{ + const userImageKey = image.imageKey; + followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return followSearchDto; + })) + return searchResult; } - // [3] 팔로우 리스트 조회 + // [2] 팔로우 리스트 조회 async getFollowList(userId: number): Promise { // 현재 로그인한 사용자 const user : UserEntity = await this.userService.findUserById(userId); @@ -120,7 +87,7 @@ export class FollowService { return informs; } - // [4] 팔로워 리스트 조회 + // [3] 팔로워 리스트 조회 async getFollowerList(userId: number): Promise { // 현재 로그인한 사용자 const user : UserEntity = await this.userService.findUserById(userId); @@ -154,41 +121,76 @@ export class FollowService { return informs; } - // [5] 메이트 검색 - async getSearchResult(userId: number, searchTerm: string) { - // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - console.log('검색 값: ', searchTerm); - const resultUsers = await UserEntity.find({ - where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], - relations: {profileImage : true, follower : true, following : true} - }); + // 팔로우 가능한 사이인지 검증 + async checkFollow(userId : number, followingId : number): Promise { + try { + // case1) 유효한 유저인지 검증 + const userEntity : UserEntity = await this.userService.findUserById(userId); + const followingUser = await UserEntity.findExistUser(followingId); + if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUser); - const userEntity = await UserEntity.findExistUser(userId); + // case2) 본인을 팔로우한 경우 + if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); - const searchResult = await Promise.all(resultUsers.map(async (user) => { - const followSearchDto = new FollowSearchDto(); + // case3) 팔로우 관계 확인 + const isAlreadyFollowing = await this.userService.isAlreadyFollowing(userId, followingId); + console.log('Is already following? : ', isAlreadyFollowing); - console.log('현재의 유저 : ', user.id); - followSearchDto.id = user.id; - followSearchDto.nickName = user.nickname; - followSearchDto.introduction = user.introduction; + // [2] 이미 팔로우 한 사이, 팔로우 취소 + if (isAlreadyFollowing) { + console.log('언팔로우 service 호출'); + return this.deleteFollow(userId, followingId); + } else { + // [1] 팔로우 + console.log('팔로우 service 호출'); + return this.createFollow(userId, followingId); + } + } catch (e) { + console.log('팔로우 요청에 실패하였습니다'); + throw new Error(e.message); + } + } - followSearchDto.followerCnt = user.follower.length; - followSearchDto.followingCnt = user.following.length; + // [4-1] 팔로우 + async createFollow(userId : number, followingId : number): Promise { - // 팔로우 여부 - followSearchDto.isFollowing = await this.userService.checkIfFollowing(userEntity, followSearchDto.id); + try { + const userEntity : UserEntity = await this.userService.findUserById(userId); + const followingUser = await UserEntity.findExistUser(followingId); + if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUser); + if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); - // 사용자 프로필 이미지 - const image = user.profileImage; - if(image == null) followSearchDto.image = null; - else{ - const userImageKey = image.imageKey; - followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return followSearchDto; - })) - return searchResult; + const userFollowingEntity = new UserFollowingEntity(); + userFollowingEntity.user = userEntity; + userFollowingEntity.followUser = followingUser; + + await userFollowingEntity.save(); + return userFollowingEntity.id; + } catch (e) { + console.log('팔로우 요청에 실패하였습니다'); + throw new Error(e.message); + } + } + + // [4-2] 언팔로우 + async deleteFollow(userId: number, followingId:number): Promise { + console.log('언팔로우 서비스 호출'); + const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: + { user : {id : userId}, followUser : {id : followingId}} + }); + + try{ + await followEntity.softRemove(); + return followEntity.id; + }catch(e){ + console.error('언팔로우 요청에 실패하였습니다: '); + throw new Error(e.message); + } } + + } diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 8c971bd..12531d9 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -15,8 +15,34 @@ export class RuleController { private readonly ruleService: RuleService, ) {} - // [1] 여행 규칙 멤버 리스트 조회 - @Get('member/:ruleId') + // [1] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 + @Get('/detail/comment/:ruleId') + @UseGuards(UserGuard) + async getComment(@Req() req: Request, + @Param('ruleId') ruleId: number, + @Query() cursorPageOptionsDto: CursorPageOptionsDto + ): Promise> { + try { + const result = await this.ruleService.getComment(cursorPageOptionsDto, ruleId); + + return new ResponseDto( + ResponseCode.GET_COMMENT_DETAIL_SUCCESS, + true, + "여행 규칙 상세 페이지 (댓글) 조회 성공", + result + ); + } catch (e) { + return new ResponseDto( + ResponseCode.GET_COMMENT_DETAIL_FAIL, + false, + "여행 규칙 상세 페이지 (댓글) 조회 실패", + null + ); + } + } + + // [2] 여행 규칙 멤버 리스트 조회 + @Get('/detail/member/:ruleId') async getMemberList(@Param('ruleId') ruleId : number) : Promise> { try { const memberList = await this.ruleService.getMemberList(ruleId); @@ -36,33 +62,32 @@ export class RuleController { } } - // [2] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 - @Get('/detail/:ruleId') + // [3] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 + @Get('/detail/search/:ruleId') @UseGuards(UserGuard) - async getComment(@Req() req: Request, - @Param('ruleId') ruleId: number, - @Query() cursorPageOptionsDto: CursorPageOptionsDto - ): Promise> { + async getSearchMember( + @Query('searchTerm')searchTerm : string, + @Param('ruleId') ruleId: number, + @Req() req: Request): Promise> { try { - const result = await this.ruleService.getComment(cursorPageOptionsDto, ruleId); - + const getSearchMemberDto : GetSearchMemberDto[] = await this.ruleService.getSearchMember(req.user.id, ruleId, searchTerm) return new ResponseDto( - ResponseCode.GET_COMMENT_DETAIL_SUCCESS, + ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, - "여행 규칙 상세 페이지 (댓글) 조회 성공", - result + "초대할 메이트 검색 결과 리스트 불러오기 성공", + getSearchMemberDto ); - } catch (e) { + } catch (error) { return new ResponseDto( - ResponseCode.GET_COMMENT_DETAIL_FAIL, + ResponseCode.GET_SEARCH_RESULT_FAIL, false, - "여행 규칙 상세 페이지 (댓글) 조회 실패", + "초대할 메이트 검색 결과 리스트 불러오기 실패", null ); } } - // [3] 여행 규칙 상세 페이지 조회 (게시글) + // [4] 여행 규칙 상세 페이지 조회 (게시글) @Get('/detail/:ruleId') @UseGuards(UserGuard) async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { @@ -87,7 +112,7 @@ export class RuleController { } } - // [4] 여행 규칙 수정 + // [5] 여행 규칙 수정 @Patch('/detail/:ruleId') @UseGuards(UserGuard) async updateRule(@Body() updateRuleDto: UpdateRuleDto, @Req() req: Request, @Param('ruleId') ruleId: number): Promise> { @@ -112,79 +137,30 @@ export class RuleController { } } - // [5] 여행 규칙 전체 리스트 조회 - @Get('list') - @UseGuards(UserGuard) - async getRuleList(@Req() req: Request): Promise> { - const result = await this.ruleService.getRuleList(req.user.id); - - if(!result){ - return new ResponseDto( - ResponseCode.GET_RULE_LIST_FAIL, - false, - "여행 규칙 전체 리스트 조회 실패", - null); - - } - else{ - return new ResponseDto( - ResponseCode.GET_RULE_LIST_SUCCESS, - true, - "여행 규칙 전체 리스트 조회 성공", - result); - } - } - // [6] 여행 규칙 생성 - @Post('/write') + @Post('/detail') @UseGuards(UserGuard) async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { const result = await this.ruleService.createRule(createRuleDto, req.user.id); if(!result){ return new ResponseDto( - ResponseCode.RULE_CREATION_FAIL, - false, - "여행 규칙 생성 실패", - null); + ResponseCode.RULE_CREATION_FAIL, + false, + "여행 규칙 생성 실패", + null); } else{ return new ResponseDto( - ResponseCode.RULE_CREATED, - true, - "여행 규칙 생성 성공", - result); - } - } - - // [7] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 - @Get('/write/search/:ruleId') - @UseGuards(UserGuard) - async getSearchMember( - @Query('searchTerm')searchTerm : string, - @Param('ruleId') ruleId: number, - @Req() req: Request): Promise> { - try { - const getSearchMemberDto : GetSearchMemberDto[] = await this.ruleService.getSearchMember(req.user.id, ruleId, searchTerm) - return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_SUCCESS, + ResponseCode.RULE_CREATED, true, - "초대할 메이트 검색 결과 리스트 불러오기 성공", - getSearchMemberDto - ); - } catch (error) { - return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_FAIL, - false, - "초대할 메이트 검색 결과 리스트 불러오기 실패", - null - ); + "여행 규칙 생성 성공", + result); } } - - // 여행 규칙 나가기 + // [7] 여행 규칙 나가기 /* @Delete('/:ruleId') @UseGuards(UserGuard) @@ -212,4 +188,27 @@ export class RuleController { } } */ + + // [8] 여행 규칙 전체 리스트 조회 + @Get() + @UseGuards(UserGuard) + async getRuleList(@Req() req: Request): Promise> { + const result = await this.ruleService.getRuleList(req.user.id); + + if(!result){ + return new ResponseDto( + ResponseCode.GET_RULE_LIST_FAIL, + false, + "여행 규칙 전체 리스트 조회 실패", + null); + + } + else{ + return new ResponseDto( + ResponseCode.GET_RULE_LIST_SUCCESS, + true, + "여행 규칙 전체 리스트 조회 성공", + result); + } + } } \ No newline at end of file From 1be38b7a02f984c89c1a8abfa593d824954e3184 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 12 Feb 2024 02:59:31 +0900 Subject: [PATCH 202/316] =?UTF-8?q?feat=20:=20listDairies=20take=EA=B0=92?= =?UTF-8?q?=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 76083c6..1c4a17f 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -325,7 +325,7 @@ export class UserService { return !!rule; } - async listDiaries(userId: number, cursor?: string, take = 30) { + async listDiaries(userId: number, cursor?: string, take = 10) { if (!cursor || cursor === '') { cursor = undefined; } From a8dc363002b4b11133b4b8414284bec114a095bf Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 12 Feb 2024 03:16:22 +0900 Subject: [PATCH 203/316] =?UTF-8?q?scheduleId=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 1c4a17f..461f07b 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -363,6 +363,7 @@ export class UserService { { diaries: await Promise.all( diaries.map(async (diary) => ({ + scheduleId: diary.schedule.id, id: diary.id, title: diary.title, place: diary.place, From 00808bb970d14fc90063619cb421486465d91908 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 03:55:38 +0900 Subject: [PATCH 204/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=88=98=EC=A0=95=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mainTitle 저장 추가 --- src/rule/rule.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index a52e592..30d170e 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -320,6 +320,7 @@ export class RuleService { }) rule.mainTitle = updateRuleDto.mainTitle + await rule.save(); // (1) [상세 규칙 수정] // 기존 세부 규칙 정보 리스트 From 00a0ef69ba754a09d974a5b67b3da5bd0ecb2831 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 04:02:22 +0900 Subject: [PATCH 205/316] =?UTF-8?q?fix=20:=20=EC=B4=88=EB=8C=80=ED=95=A0?= =?UTF-8?q?=20=EC=97=AC=ED=96=89=20=EB=A9=A4=EB=B2=84=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit relation 수정 프로필 이미지 수정 --- src/rule/rule.service.ts | 4 +--- src/user/user.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 30d170e..5023cdc 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -296,12 +296,10 @@ export class RuleService { dto.introduction = user.introduction; // 이미 여행 규칙에 참여하는 멤버인지 여부 - dto.isInvited = await this.userService.checkAlreadyMember(user, ruleId); + dto.isInvited = await this.userService.checkAlreadyMember(user.id, ruleId); - dto.image // 사용자 프로필 이미지 const image = user.profileImage; - dto.image = image.imageKey; if(image == null) dto.image = null; else{ const userImageKey = image.imageKey; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 461f07b..c530b44 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -316,9 +316,9 @@ export class UserService { return !!isFollowing; } - async checkAlreadyMember(user: UserEntity, ruleID: number) { + async checkAlreadyMember(userId: number, ruleID: number) { const rule = await RuleInvitationEntity.findOne({ - where: { member: { id: user.id }, rule: { id: ruleID } }, + where: { member: { id: userId }, rule: { id: ruleID } }, }); // 이미 규칙 멤버인 경우 : true 반환 console.log('rule : ', rule); From 48d056544f99de1b9ffd8d78858907e3b5fb1270 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 04:14:21 +0900 Subject: [PATCH 206/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=A9=A4=EB=B2=84=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(=EB=A1=9C=EA=B7=B8=EC=9D=B8=ED=95=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [예외상황 추가] 현재 로그인한 사용자인 경우, 삭제하는 멤버에서 제외 --- src/rule/rule.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 5023cdc..9005a3e 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -380,6 +380,9 @@ export class RuleService { const member = invitation.member; let isDeleteMember : boolean = true; + // (예외 상황) 현재 로그인한 사용자 + if (member.id == userId) break; + for(const updateMemberId of updateMemberIds) { if(member.id == updateMemberId) { isDeleteMember = false; From c12aa97bfd81d5eb849cdc198fa5d91bda180a89 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 04:25:08 +0900 Subject: [PATCH 207/316] =?UTF-8?q?fix=20:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EC=97=90=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EB=B3=B8=EC=9D=B8=20=EC=95=88=EB=9C=A8=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit where 조건 수정 --- src/rule/rule.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9005a3e..f97c34f 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -9,7 +9,7 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import {LessThan, Like} from "typeorm"; +import {Equal, LessThan, Like, Not} from "typeorm"; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; @@ -283,7 +283,10 @@ export class RuleService { // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 console.log('검색 값: ', searchTerm); const resultUsers = await UserEntity.find({ - where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], + where: [ + { name: Like(`%${searchTerm}%`) }, + { nickname: Like(`%${searchTerm}%`)}, + { id: Not(Equal(userId)) }], // 사용자 본인은 검색결과에 뜨지 않도록 relations: {profileImage : true, ruleParticipate: true} }); From bdcbc5a6af974a555f3c022793106b61e3808dd0 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 04:49:24 +0900 Subject: [PATCH 208/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=82=98=EA=B0=80=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.controller.ts | 13 +++---------- src/rule/rule.service.ts | 27 ++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 12531d9..6702f12 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -161,33 +161,26 @@ export class RuleController { } // [7] 여행 규칙 나가기 - /* @Delete('/:ruleId') @UseGuards(UserGuard) async deleteInvitation(@Req() req: Request, @Param('ruleId') ruleId: number){ - - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - const userId = 2; - try { - await this.ruleService.deleteInvitation(ruleId, userId); + await this.ruleService.deleteInvitation(ruleId, req.user.id); return new ResponseDto( ResponseCode.DELETE_INVITATION_SUCCESS, true, "여행 규칙 나가기 성공", null ); - } catch (error) { + } catch (e) { return new ResponseDto( ResponseCode.DELETE_INVITATION_FAIL, false, - "여행 규칙 나가기 실패", + e.message, null ); } } - */ // [8] 여행 규칙 전체 리스트 조회 @Get() diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f97c34f..c9c9331 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -165,12 +165,33 @@ export class RuleService { return new CursorPageDto(result, cursorPageMetaDto); } - // [3] 여행 규칙 나가기 + // [4] 여행 규칙 나가기 // -1) 초대 받은 팀원 -> 초대 삭제 async deleteInvitation(ruleId: number, userId: number): Promise { - const invitation : RuleInvitationEntity = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, userId); + try { + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id : userId}, + }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); - return invitation.softRemove(); + // 검증2) 규칙이 존재하지 않는 경우 + const ruleMain = await RuleMainEntity.findOne({ + where : {id : ruleId}, + }); + if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); + + // 검증3) 규칙에 참여하는 사용자가 아닌 경우 + const invitation = await RuleInvitationEntity.findOne({ + where: {member: {id: userId}, rule: {id: ruleId}}, + }) + if (!!invitation) { + return invitation.softRemove(); + } else throw new Error('사용자가 참여하는 규칙이 아닙니다'); + } catch (e) { + console.log('여행 규칙 나가기 실패'); + throw new Error(e.message); + } } // [4] 여행 규칙 멤버 리스트 조회 From bbca6325ccc381ce75116ae5b1d889556a37329c Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 05:06:05 +0900 Subject: [PATCH 209/316] =?UTF-8?q?fix=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=ED=95=A0=20=EC=9C=A0=EC=A0=80=20=EA=B2=80=EC=83=89=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 85ef008..2d3229a 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -15,7 +15,7 @@ export class FollowController { ) {} // [1] 메이트 검색 - @Get('/search/:searchTerm') + @Get('/search') @UseGuards(UserGuard) async getSearchResult( @Query('searchTerm')searchTerm : string, From c95b3ea1982fbcb7a7e984a696ad87899185e220 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 05:21:15 +0900 Subject: [PATCH 210/316] =?UTF-8?q?feat=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0,?= =?UTF-8?q?=20=EC=96=B8=ED=8C=94=EB=A1=9C=EC=9A=B0=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EC=B6=9C=EB=A0=A5=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 21 +++++++++++++++------ src/follow/follow.service.ts | 14 ++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 2d3229a..9d507a3 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -89,12 +89,21 @@ export class FollowController { async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { try { const result = await this.followService.checkFollow(req.user.id, followingId); - return new ResponseDto( - ResponseCode.FOLLOW_SUCCESS, - true, - "팔로우 / 언팔로우 성공", - result - ); + if (!!result.deleted) { + return new ResponseDto( + ResponseCode.FOLLOW_SUCCESS, + true, + "언팔로우 성공", + result.id + ); + } else { + return new ResponseDto( + ResponseCode.FOLLOW_SUCCESS, + true, + "팔로우 성공", + result.id + ); + } } catch (e) { return new ResponseDto( ResponseCode.FOLLOW_FAIL, diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 4bfc446..205c016 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -121,8 +121,8 @@ export class FollowService { return informs; } - // 팔로우 가능한 사이인지 검증 - async checkFollow(userId : number, followingId : number): Promise { + // [4] 팔로우 가능한 사이인지 검증 + async checkFollow(userId : number, followingId : number): Promise { try { // case1) 유효한 유저인지 검증 const userEntity : UserEntity = await this.userService.findUserById(userId); @@ -154,7 +154,7 @@ export class FollowService { } // [4-1] 팔로우 - async createFollow(userId : number, followingId : number): Promise { + async createFollow(userId : number, followingId : number): Promise { try { const userEntity : UserEntity = await this.userService.findUserById(userId); @@ -169,7 +169,7 @@ export class FollowService { userFollowingEntity.followUser = followingUser; await userFollowingEntity.save(); - return userFollowingEntity.id; + return userFollowingEntity; } catch (e) { console.log('팔로우 요청에 실패하였습니다'); throw new Error(e.message); @@ -177,7 +177,7 @@ export class FollowService { } // [4-2] 언팔로우 - async deleteFollow(userId: number, followingId:number): Promise { + async deleteFollow(userId: number, followingId:number): Promise { console.log('언팔로우 서비스 호출'); const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: { user : {id : userId}, followUser : {id : followingId}} @@ -185,12 +185,10 @@ export class FollowService { try{ await followEntity.softRemove(); - return followEntity.id; + return followEntity; }catch(e){ console.error('언팔로우 요청에 실패하였습니다: '); throw new Error(e.message); } } - - } From 26f79fecdf128c9dda1a5dd10bbb76f659aa8702 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 06:40:37 +0900 Subject: [PATCH 211/316] =?UTF-8?q?fix=20:=20url=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index d3932ad..3ff18f1 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -8,7 +8,7 @@ import { Request } from 'express'; import {UserEntity} from "../user/user.entity"; import {RuleMainEntity} from "../rule/domain/rule.main.entity"; -@Controller('mate/rule/comment') +@Controller('mate/rule/detail/comment') export class CommentController { constructor( private readonly commentService: CommentService, From 8d0a1ac3973b29b027bc09684cdcc09c7ac21ff8 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 06:47:24 +0900 Subject: [PATCH 212/316] =?UTF-8?q?feat=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=ED=95=A0=20=EB=A9=94=EC=9D=B4=ED=8A=B8=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EB=AC=B4=ED=95=9C=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.controller.ts | 15 +++++------ src/follow/follow.service.ts | 46 +++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 9d507a3..0bd3d76 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -1,32 +1,31 @@ -import {Controller, Post, Req, UseGuards, Param, Delete, Get, Patch, Query} from '@nestjs/common'; +import {Controller, Req, UseGuards, Param, Get, Patch, Query} from '@nestjs/common'; import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -import { UserService } from 'src/user/user.service'; import { UserGuard } from '../user/user.guard'; -import {query, Request} from 'express'; -import { FollowSearchDto } from "./dto/follow.search.dto"; +import { Request} from 'express'; +import {CursorPageOptionsDto} from "../rule/dto/cursor-page.options.dto"; @Controller('mate/follow') export class FollowController { constructor( private readonly followService: FollowService, - private readonly userService: UserService, ) {} - // [1] 메이트 검색 + // [1] 메이트 검색 - 무한스크롤 적용 @Get('/search') @UseGuards(UserGuard) async getSearchResult( @Query('searchTerm')searchTerm : string, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, @Req() req: Request): Promise> { try { - const followSearchDto : FollowSearchDto[] = await this.followService.getSearchResult(req.user.id, searchTerm) + const result = await this.followService.getSearchResult(cursorPageOptionsDto, req.user.id, searchTerm); return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, "검색 결과 리스트 불러오기 성공", - followSearchDto + result ); } catch (error) { return new ResponseDto( diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 205c016..0cd256c 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -5,8 +5,12 @@ import { UserEntity } from "../user/user.entity"; import { UserService } from "../user/user.service"; import { S3UtilService } from "../utils/S3.service"; import {SignatureEntity} from "../signature/domain/signature.entity"; -import {Like} from "typeorm"; +import {LessThan, Like} from "typeorm"; import {FollowSearchDto} from "./dto/follow.search.dto"; +import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; +import {CommentEntity} from "../comment/domain/comment.entity"; +import {CursorPageMetaDto} from "../rule/dto/cursor-page.meta.dto"; +import {CursorPageDto} from "../rule/dto/cursor-page.dto"; @Injectable() export class FollowService { @@ -16,17 +20,26 @@ export class FollowService { ) {} // [1] 메이트 검색 - async getSearchResult(userId: number, searchTerm: string) { + async getSearchResult(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string) { + // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - console.log('검색 값: ', searchTerm); - const resultUsers = await UserEntity.find({ - where: [{ name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`) }], - relations: {profileImage : true, follower : true, following : true} + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: [ + {id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null}, + {name: Like(`%${searchTerm}%`)}, + {nickname: Like(`%${searchTerm}%`)} + ], + relations: {profileImage : true, follower : true, following : true}, + order: { + id: cursorPageOptionsDto.sort.toUpperCase() as any, + }, }); const userEntity = await UserEntity.findExistUser(userId); + const searchResult = await Promise.all(resultUsers.map(async (user) => { const followSearchDto = new FollowSearchDto(); @@ -49,8 +62,25 @@ export class FollowService { followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); } return followSearchDto; - })) - return searchResult; + })); + + // (2) 페이징 및 정렬 기준 설정 + const takePerPage = cursorPageOptionsDto.take; + const isLastPage = total <= takePerPage; + + let hasNextData = true; + let cursor: number; + + if (isLastPage || searchResult.length <= 0) { + hasNextData = false; + cursor = null; + } else { + cursor = searchResult[searchResult.length - 1].id; + } + + const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto(searchResult, cursorPageMetaDto); } // [2] 팔로우 리스트 조회 From db5e3cdd596702d23432b6e0fdd9106fbad6c764 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 07:02:31 +0900 Subject: [PATCH 213/316] =?UTF-8?q?fix=20:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 1aff2a4..b4df082 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -60,9 +60,14 @@ export class CommentService { async deleteComment(ruleId: number, userId: number, commentId: number) : Promise { try { // 사용자, 규칙, 댓글 검증 - const user = await UserEntity.findExistUser(userId); + const user = await UserEntity.findOne({ + where: {id : userId}, + }); if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); - const rule = await RuleMainEntity.findRuleById(ruleId); + + const rule = await RuleMainEntity.findOne({ + where: {id: ruleId}, + }) if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); const comment = await CommentEntity.findOne({ @@ -74,6 +79,7 @@ export class CommentService { if (comment.id == commentId) { await comment.softRemove(); + console.log('댓글 삭제 성공'); return comment.id; } } catch (e) { From e2f366b30d21a0d0bddf2a82a71e77a16c3191b2 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 07:40:42 +0900 Subject: [PATCH 214/316] =?UTF-8?q?feat=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=B4=88=EB=8C=80=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20(=EB=AC=B4=ED=95=9C=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EC=A0=81=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 2 +- src/follow/follow.controller.ts | 2 +- src/rule/rule.controller.ts | 9 ++++--- src/rule/rule.service.ts | 44 +++++++++++++++++++++++---------- 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index b4df082..d4039d0 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -71,7 +71,7 @@ export class CommentService { if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); const comment = await CommentEntity.findOne({ - where: {user: {id: userId}, rule: {id: ruleId}} + where: {user: {id: userId}, rule: {id: ruleId}}, }) if (!comment) throw new NotFoundException("데이터를 찾을 수 없습니다"); diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index 0bd3d76..d3010da 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -12,7 +12,7 @@ export class FollowController { private readonly followService: FollowService, ) {} - // [1] 메이트 검색 - 무한스크롤 적용 + // [1] 메이트 검색 - 무한 스크롤 적용 @Get('/search') @UseGuards(UserGuard) async getSearchResult( diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 6702f12..ce4d665 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -8,6 +8,8 @@ import { Request } from 'express'; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import { UpdateRuleDto } from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "./dto/cursor-page.options.dto"; +import {CursorPageDto} from "./dto/cursor-page.dto"; +import {UserEntity} from "../user/user.entity"; @Controller('mate/rule') export class RuleController { @@ -15,7 +17,7 @@ export class RuleController { private readonly ruleService: RuleService, ) {} - // [1] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 + // [1] 여행 규칙 상세 페이지 조회 (댓글) - 무한 스크롤 적용 @Get('/detail/comment/:ruleId') @UseGuards(UserGuard) async getComment(@Req() req: Request, @@ -67,15 +69,16 @@ export class RuleController { @UseGuards(UserGuard) async getSearchMember( @Query('searchTerm')searchTerm : string, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { try { - const getSearchMemberDto : GetSearchMemberDto[] = await this.ruleService.getSearchMember(req.user.id, ruleId, searchTerm) + const result : CursorPageDto = await this.ruleService.getSearchMember(cursorPageOptionsDto, req.user.id, ruleId, searchTerm) return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, "초대할 메이트 검색 결과 리스트 불러오기 성공", - getSearchMemberDto + result ); } catch (error) { return new ResponseDto( diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index c9c9331..8e68d9e 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -240,11 +240,6 @@ export class RuleService { }, }, }, - // 'ruleParticipate', - // 'ruleParticipate.rule', - // 'ruleParticipate.rule.invitations', - // 'ruleParticipate.rule.invitations.member', - // 'ruleParticipate.rule.invitations.member.profileImage' }, }); @@ -299,19 +294,25 @@ export class RuleService { } // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 - async getSearchMember(userId: number, ruleId: number, searchTerm: string): Promise { + async getSearchMember(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { + // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - console.log('검색 값: ', searchTerm); - const resultUsers = await UserEntity.find({ + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, where: [ - { name: Like(`%${searchTerm}%`) }, + {id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null}, + { name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`)}, - { id: Not(Equal(userId)) }], // 사용자 본인은 검색결과에 뜨지 않도록 - relations: {profileImage : true, ruleParticipate: true} + { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 + ], + relations: {profileImage : true, ruleParticipate: true}, + order: { + id: cursorPageOptionsDto.sort.toUpperCase() as any, + }, }); - const result = await Promise.all(resultUsers.map(async (user) => { + const searchResult = await Promise.all(resultUsers.map(async (user) => { const dto: GetSearchMemberDto = new GetSearchMemberDto(); dto.id = user.id; @@ -331,7 +332,24 @@ export class RuleService { } return dto; })) - return result; + + // (2) 페이징 및 정렬 기준 설정 + const takePerPage = cursorPageOptionsDto.take; + const isLastPage = total <= takePerPage; + + let hasNextData = true; + let cursor: number; + + if (isLastPage || searchResult.length <= 0) { + hasNextData = false; + cursor = null; + } else { + cursor = searchResult[searchResult.length - 1].id; + } + + const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto(searchResult, cursorPageMetaDto); } // [7] 여행 규칙 수정 From b924a03f47f940b8b75a40d1b41dcbcf655ba989 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 08:05:16 +0900 Subject: [PATCH 215/316] =?UTF-8?q?feat=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=B4=88=EB=8C=80=ED=95=A0=20=EB=A9=A4=EB=B2=84=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20(=EB=AC=B4=ED=95=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 8e68d9e..ae6dd50 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -298,6 +298,7 @@ export class RuleService { // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ @@ -306,7 +307,7 @@ export class RuleService { { nickname: Like(`%${searchTerm}%`)}, { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 ], - relations: {profileImage : true, ruleParticipate: true}, + relations: {profileImage : true, ruleParticipate: {rule: true}}, order: { id: cursorPageOptionsDto.sort.toUpperCase() as any, }, @@ -331,7 +332,9 @@ export class RuleService { dto.image = await this.s3Service.getImageUrl(userImageKey); } return dto; - })) + })); + + console.log('searchResult : ',searchResult); // (2) 페이징 및 정렬 기준 설정 const takePerPage = cursorPageOptionsDto.take; From f4eb8b4186b8cb60af8b7f4dceae35b2e544657a Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 12 Feb 2024 15:08:58 +0900 Subject: [PATCH 216/316] =?UTF-8?q?=20fix=20:=20date-fns=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=EC=84=9C=20=EB=82=A0=EC=A7=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 10 ++++ package.json | 1 + src/journey/journey.service.ts | 41 +++++++++++---- src/journey/model/journey.entity.ts | 15 ++++-- src/map/map.service.ts | 78 ++++++++++++++--------------- src/schedule/schedule.entity.ts | 3 +- 6 files changed, 92 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0c3fb0..dd094cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "date-fns": "^3.3.1", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", @@ -5526,6 +5527,15 @@ "node": ">= 8" } }, + "node_modules/date-fns": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz", + "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.10", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", diff --git a/package.json b/package.json index 5a1931f..d3f9017 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "date-fns": "^3.3.1", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 0581f93..9ad7247 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -4,6 +4,7 @@ import { Injectable, NotFoundException, } from '@nestjs/common'; +import { addDays, isAfter } from 'date-fns'; import { JourneyEntity } from './model/journey.entity'; import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; @@ -29,20 +30,38 @@ export class JourneyService { const journey = await JourneyEntity.createJourney(user, createJourneyDto); //일정 배너 생성하기 : (startDate - endDate + 1)개 - let currentDate = new Date(createJourneyDto.startDate); - const lastDate = new Date(createJourneyDto.endDate); - - while (currentDate <= lastDate) { - const schedule = await ScheduleEntity.createSchedule( - journey, - currentDate, - ); - currentDate = new Date(currentDate); - currentDate.setDate(currentDate.getDate() + 1); - } + //일정 배너 생성하기 : (startDate - endDate + 1)개 + const startDate = new Date(createJourneyDto.startDate); + const endDate = new Date(createJourneyDto.endDate); + + const schedules = await this.createSchedules(journey, startDate, endDate); return errResponse(BaseResponse.JOURNEY_CREATED); } + private async createSchedules( + journey: JourneyEntity, + startDate: Date, + endDate: Date, + ) { + let currentDate = startDate; + const schedules = []; + + // endDate가 startDate 이후인지 확인 + if (isAfter(endDate, startDate)) { + while (currentDate <= endDate) { + const schedule = await ScheduleEntity.createSchedule( + journey, + currentDate, + ); + schedules.push(schedule); + currentDate = addDays(currentDate, 1); // 다음 날짜로 이동 + } + } else { + throw new Error('endDate는 startDate보다 이후여야 합니다.'); + } + + return schedules; + } //여정 수정하기 async updateJourney(user, journeyId: number, title: string) { diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 32d773e..1c546fb 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -14,7 +14,7 @@ import { LessThanOrEqual, MoreThanOrEqual, } from 'typeorm'; - +import { startOfMonth, endOfMonth, isWithinInterval } from 'date-fns'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { MonthInfoDto } from 'src/map/month-info.dto'; @@ -119,11 +119,18 @@ export class JourneyEntity extends BaseEntity { const journeys: JourneyEntity[] = await JourneyEntity.find({ where: { user: { id: userId }, - startDate: Between(new Date(0), date), - endDate: Between(date, new Date('9999-12-31')), }, }); - return journeys; + + // 매개변수로 받은 날짜가 어느 여정에 포함되어 있는지 확인 + const matchingJourney = journeys.find((journey) => { + return isWithinInterval(date, { + start: journey.startDate, + end: journey.endDate, + }); + }); + + return matchingJourney; } //사용자의 월별 여정 조회 diff --git a/src/map/map.service.ts b/src/map/map.service.ts index 5e7a1a4..e54c86d 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -23,56 +23,54 @@ export class MapService { options: CursorBasedPaginationRequestDto, ) { const user = await UserEntity.findExistUser(userId); - const journeys = await JourneyEntity.findExistJourneyByDate(user.id, date); - - // 커서 값에 해당하는 배너들을 가져옴 - const paginatedJourneys = journeys.slice( - options.cursor, - options.cursor + options.pageSize, + const journey = await JourneyEntity.findExistJourneyByDate(user.id, date); + const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( + journey.id, ); - if (paginatedJourneys.length === 0) { - return { - data: response(BaseResponse.SCHEDULE_NOT_FOUND, { nextCursor: null }), - }; - } - const result = await Promise.all( - paginatedJourneys.map(async (journey) => { - const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( - journey.id, - ); - const scheduleList = await Promise.all( - schedules.map(async (schedule) => { - const locations = await this.getLocationList([schedule]); // getLocationList에 schedule 배열을 전달 - const detailSchedules = await this.getDetailScheduleList([ - schedule, - ]); // getDetailScheduleList에 schedule 배열을 전달 - const diary = await this.getDiaryStatus([schedule]); // getDiaryStatus에 schedule 배열을 전달 - - return { - scheduleId: schedule.id, - title: schedule.title, - date: schedule.date, - location: locations, - detailSchedules: detailSchedules, - diary: diary, - }; - }), - ); + const journeyInfo = { + userId: user.id, + journeyId: journey.id, + startDate: journey.startDate, + endDate: journey.endDate, + }; + const scheduleList = await Promise.all( + schedules.map(async (schedule) => { + const locations = await this.getLocationList([schedule]); // getLocationList에 schedule 배열을 전달 + const detailSchedules = await this.getDetailScheduleList([schedule]); // getDetailScheduleList에 schedule 배열을 전달 + const diary = await this.getDiaryStatus([schedule]); // getDiaryStatus에 schedule 배열을 전달 return { - userId: user.id, - journeyId: journey.id, - startDate: journey.startDate, - endDate: journey.endDate, - scheduleList: scheduleList, + scheduleId: schedule.id, + title: schedule.title, + date: schedule.date, + location: locations, + detailSchedules: detailSchedules, + diary: diary, }; }), ); + // return { + // userId: user.id, + // journeyId: journey.id, + // startDate: journey.startDate, + // endDate: journey.endDate, + // scheduleList: scheduleList, + // }; + + // 페이징 처리 + const paginatedSchedules = scheduleList.slice( + options.cursor, + options.cursor + options.pageSize, + ); + // 다음 페이지를 위한 커서 값 계산 const nextCursor = Number(options.cursor) + Number(options.pageSize); return { - data: response(BaseResponse.GET_SCHEDULE_SUCCESS, result), + data: response(BaseResponse.GET_SCHEDULE_SUCCESS, { + journeyInfo, + scheduleList, + }), nextCursor: nextCursor, }; } diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 47d0a37..9c9db63 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -12,6 +12,7 @@ import { Between, } from 'typeorm'; import { NotFoundException } from '@nestjs/common'; +import { startOfMonth, endOfMonth } from 'date-fns'; import { BaseResponse } from 'src/response/response.status'; import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity'; import { LocationEntity } from 'src/location/location.entity'; @@ -61,7 +62,7 @@ export class ScheduleEntity extends BaseEntity { //일정 작성하기 static async createSchedule(journey: JourneyEntity, currentDate) { const schedule = new ScheduleEntity(); - schedule.date = currentDate.toISOString().split('T')[0]; + schedule.date = currentDate; schedule.journey = journey; return await schedule.save(); } From dd37523ea3546cf62744315153725f4d902d8e35 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:47:33 +0900 Subject: [PATCH 217/316] =?UTF-8?q?fix:=20=EC=97=AC=ED=96=89=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EB=8C=93=EA=B8=80=20=EB=AC=B4=ED=95=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=B4=88=EA=B8=B0=EA=B0=92=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 --- src/rule/rule.service.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index ae6dd50..0b82f58 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -114,12 +114,29 @@ export class RuleService { // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number): Promise> { + + let cursorId: number = 0; + + // (0) 초기값 가져오기 + if(cursorPageOptionsDto.cursorId == 0){ + const recentComment = await CommentEntity.findOne({ + where: { rule: { id: ruleId } }, + order: { + id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 작성한 댓글 가져오기 + } + }); + cursorId = recentComment.id + 1; + } + else cursorId = cursorPageOptionsDto.cursorId; + + + // (1) 데이터 조회 const [comments, total] = await CommentEntity.findAndCount({ take: cursorPageOptionsDto.take, where: { rule: { id: ruleId }, - id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null, + id: cursorId ? LessThan(cursorId) : null, }, relations: {user:{profileImage: true}}, order: { From bb9b11356d1c663bb9b40f0064acf9685dc17ad2 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:56:22 +0900 Subject: [PATCH 218/316] =?UTF-8?q?fix:=20=EC=97=AC=ED=96=89=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EB=8C=93=EA=B8=80=20=EC=A0=95=EB=A0=AC=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 0b82f58..1289ba2 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -9,7 +9,7 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import {Equal, LessThan, Like, Not} from "typeorm"; +import { Equal, LessThan, Like, MoreThan, Not } from 'typeorm'; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; @@ -117,30 +117,16 @@ export class RuleService { let cursorId: number = 0; - // (0) 초기값 가져오기 - if(cursorPageOptionsDto.cursorId == 0){ - const recentComment = await CommentEntity.findOne({ - where: { rule: { id: ruleId } }, - order: { - id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 작성한 댓글 가져오기 - } - }); - cursorId = recentComment.id + 1; - } - else cursorId = cursorPageOptionsDto.cursorId; - - - // (1) 데이터 조회 const [comments, total] = await CommentEntity.findAndCount({ take: cursorPageOptionsDto.take, where: { rule: { id: ruleId }, - id: cursorId ? LessThan(cursorId) : null, + id: cursorId ? MoreThan(cursorId) : null, }, relations: {user:{profileImage: true}}, order: { - id: cursorPageOptionsDto.sort.toUpperCase() as any, + id: 'ASC' }, }); From fa9024116ea442104e7cbb321a822d486ccc50c4 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:03:54 +0900 Subject: [PATCH 219/316] =?UTF-8?q?HotFix:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=8C=93=EA=B8=80=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 0b82f58..8fb0915 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -115,7 +115,7 @@ export class RuleService { // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number): Promise> { - let cursorId: number = 0; + const cursorId: number = cursorPageOptionsDto.cursorId; // (0) 초기값 가져오기 if(cursorPageOptionsDto.cursorId == 0){ From dbbbe274de8d48d4e8858229552e9f935967711c Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:06:30 +0900 Subject: [PATCH 220/316] Update rule.service.ts --- src/rule/rule.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9d6afaf..f663162 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -126,7 +126,7 @@ export class RuleService { }, relations: {user:{profileImage: true}}, order: { - id: 'ASC' + id: "ASC" as any, }, }); From a058d16505bf7db2f7374ea88e8ed91f377df517 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 17:08:20 +0900 Subject: [PATCH 221/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=88=98=EC=A0=95=20rulePairs=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index ae6dd50..79472db 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -391,7 +391,8 @@ export class RuleService { for (const updateSub of updateSubsList) { // case1) 새로운 규칙 if (!updateSub.id) { - const newSub = new RuleSubEntity(); + const newSub = new RuleSubEntity() + newSub.main = rule; newSub.ruleTitle = updateSub.ruleTitle; newSub.ruleDetail = updateSub.ruleDetail; From e74bd95b68cf90c016a3c672921f46b6f3da2e45 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 17:55:26 +0900 Subject: [PATCH 222/316] =?UTF-8?q?fix=20:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index d4039d0..956427e 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -70,14 +70,11 @@ export class CommentService { }) if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); + // 해당 규칙에, 해당 사용자가 작성한, 해당 댓글 ID를 가진 댓글이 있는지 검증 const comment = await CommentEntity.findOne({ - where: {user: {id: userId}, rule: {id: ruleId}}, + where: {id: commentId, user:{id: userId}, rule:{id: ruleId}} }) - if (!comment) throw new NotFoundException("데이터를 찾을 수 없습니다"); - - if (comment.id != commentId) throw new NotFoundException('해당 댓글을 작성한 사용자가 아닙니다'); - - if (comment.id == commentId) { + if (!!comment) { await comment.softRemove(); console.log('댓글 삭제 성공'); return comment.id; From 29b18bd903e3b81d3586dade2ded4005983bd5a4 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 18:06:42 +0900 Subject: [PATCH 223/316] =?UTF-8?q?fix=20:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EC=82=AD=EC=A0=9C=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 43 +++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 956427e..2f1e288 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -33,25 +33,31 @@ export class CommentService { async updateComment(dto: CreateCommentDto, ruleId: number, userId: number, commentId: number) : Promise { try { // 사용자, 규칙, 댓글 검증 - const user = await UserEntity.findExistUser(userId); + const user = await UserEntity.findOne({ + where: {id : userId}, + }); if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); - const rule = await RuleMainEntity.findRuleById(ruleId); + const rule = await RuleMainEntity.findOne({ + where: {id: ruleId}, + }) if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); - const comment = await CommentEntity.findOne({ - where: {user: {id: userId}, rule: {id: ruleId}} - }) - if(!comment) throw new NotFoundException("데이터를 찾을 수 없습니다"); - - if(comment.id != commentId) throw new NotFoundException('해당 댓글 수정 권한이 없는 사용자입니다'); + where: {id: commentId} + }); + if (!comment) throw new NotFoundException('존재하지 않는 댓글 입니다'); - if (comment.id == commentId) { + const checkValidateUser = await CommentEntity.findOne({ + where: {id: commentId, user: {id: userId}, rule: {id: ruleId}}} + ) + if (!!checkValidateUser) { comment.content = dto.content; await CommentEntity.save(comment); return comment.id; + } else { + throw new NotFoundException('해당 댓글 수정 권한이 없는 사용자 입니다'); } } catch (e) { - console.log('해당 댓글 수정 권한이 없는 사용자 입니다'); + console.log('검증 과정에서 에러 발생'); throw new Error(e.message); } } @@ -64,23 +70,28 @@ export class CommentService { where: {id : userId}, }); if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); - const rule = await RuleMainEntity.findOne({ where: {id: ruleId}, }) if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); + const comment = await CommentEntity.findOne({ + where: {id: commentId} + }); + if (!comment) throw new NotFoundException('존재하지 않는 댓글 입니다'); // 해당 규칙에, 해당 사용자가 작성한, 해당 댓글 ID를 가진 댓글이 있는지 검증 - const comment = await CommentEntity.findOne({ - where: {id: commentId, user:{id: userId}, rule:{id: ruleId}} - }) - if (!!comment) { + const checkValidateUser = await CommentEntity.findOne({ + where: {id: commentId, user: {id: userId}, rule: {id: ruleId}}} + ) + if (!!checkValidateUser) { await comment.softRemove(); console.log('댓글 삭제 성공'); return comment.id; + } else { + throw new NotFoundException('해당 댓글 삭제 권한이 없는 사용자 입니다'); } } catch (e) { - console.log('해당 댓글 삭제 권한이 없는 사용자 입니다'); + console.log('검증 과정에서 에러 발생'); throw new Error(e.message); } } From c8b41a0394387f52e6bcb6ce6365fa0a8f3654ad Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 19:17:07 +0900 Subject: [PATCH 224/316] =?UTF-8?q?feat=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20getDto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/get-comment.dto.ts | 4 ++++ src/rule/rule.service.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/src/rule/dto/get-comment.dto.ts b/src/rule/dto/get-comment.dto.ts index 1576c87..72189bd 100644 --- a/src/rule/dto/get-comment.dto.ts +++ b/src/rule/dto/get-comment.dto.ts @@ -5,6 +5,10 @@ export class GetCommentDto { @IsNumber() id: number; + @IsNotEmpty() + @IsNumber() + writerId: number; + @IsNotEmpty() @IsString() content: string; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f9b79b9..a8fe9a8 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -134,6 +134,7 @@ export class RuleService { const getCommentDto = new GetCommentDto(); getCommentDto.id = comment.id; + getCommentDto.writerId = comment.user.id; getCommentDto.name = comment.user.name; getCommentDto.content = comment.content; getCommentDto.updated = comment.updated; From 252935d8e8393adb3d5876ebb90d312b6eabb5ac Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 19:51:51 +0900 Subject: [PATCH 225/316] =?UTF-8?q?feat=20:=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/detail.rule.dto.ts | 4 +- src/rule/rule.controller.ts | 2 +- src/rule/rule.service.ts | 80 ++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/rule/dto/detail.rule.dto.ts b/src/rule/dto/detail.rule.dto.ts index 4300934..5b03ffe 100644 --- a/src/rule/dto/detail.rule.dto.ts +++ b/src/rule/dto/detail.rule.dto.ts @@ -2,6 +2,7 @@ import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-v import { Type } from 'class-transformer'; export class RulePairDto { + @IsNotEmpty() @IsNumber() id: number; @@ -15,6 +16,7 @@ export class RulePairDto { } export class DetailMemberDto { + @IsNotEmpty() @IsNumber() id: number; @@ -22,12 +24,12 @@ export class DetailMemberDto { @IsString() name: string; - @IsNotEmpty() @IsString() image: string; } export class DetailRuleDto { + @IsNotEmpty() @IsNumber() id: number; diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index ce4d665..5e7df24 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -95,7 +95,7 @@ export class RuleController { @UseGuards(UserGuard) async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { - const result = await this.ruleService.getDetail(ruleId); + const result = await this.ruleService.getDetail(req.user.id, ruleId); if(!result){ return new ResponseDto( diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index a8fe9a8..fabdc55 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -70,7 +70,7 @@ export class RuleService { } // [2] 여행 규칙 상세 페이지 조회 (게시글) - async getDetail(ruleId : number): Promise { + async getDetail(userId: number, ruleId : number): Promise { const dto = new DetailRuleDto(); const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); @@ -79,37 +79,55 @@ export class RuleService { relations: {member: {profileImage : true}} }) - // -1) 제목 - dto.id = ruleId; - dto.mainTitle = main.mainTitle; - - // -2) 규칙 - dto.rulePairs = await Promise.all(subs.map(async(sub):Promise => { - const rulePair = new RulePairDto(); - rulePair.id = sub.id; - rulePair.ruleTitle = sub.ruleTitle; - rulePair.ruleDetail = sub.ruleDetail; - - return rulePair; - })); - - // -3) 멤버 정보 - dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { - const detailMember = new DetailMemberDto; - const memberEntity = invitation.member; - detailMember.id = memberEntity.id; - detailMember.name = memberEntity.name; - - // 사용자 프로필 이미지 - const image = memberEntity.profileImage; - if(image == null) detailMember.image = null; - else{ - const userImageKey = image.imageKey; - detailMember.image = await this.s3Service.getImageUrl(userImageKey); + try { + // 요청을 보낸 현재 로그인 사용자가 해당 규칙의 멤버인지 검증 (권한) + const user = await UserEntity.findExistUser(userId); + let checkValidation = false; + for(const invitation of invitations) { + if(invitation.member.id == user.id) { + checkValidation = true; + break; + } + } + if(!checkValidation) { + throw new BadRequestException('해당 여행 규칙의 멤버가 아닙니다'); } - return detailMember; - })) - return dto; + + // -1) 제목 + dto.id = ruleId; + dto.mainTitle = main.mainTitle; + + // -2) 규칙 + dto.rulePairs = await Promise.all(subs.map(async(sub):Promise => { + const rulePair = new RulePairDto(); + rulePair.id = sub.id; + rulePair.ruleTitle = sub.ruleTitle; + rulePair.ruleDetail = sub.ruleDetail; + + return rulePair; + })); + + // -3) 멤버 정보 + dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { + const detailMember = new DetailMemberDto; + const memberEntity = invitation.member; + detailMember.id = memberEntity.id; + detailMember.name = memberEntity.name; + + // 사용자 프로필 이미지 + const image = memberEntity.profileImage; + if(image == null) detailMember.image = null; + else{ + const userImageKey = image.imageKey; + detailMember.image = await this.s3Service.getImageUrl(userImageKey); + } + return detailMember; + })) + return dto; + } catch (e) { + console.log('게시글 조회에 실패하였습니다'); + throw new Error(e.message); + } }; // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 From 3a50bb64b31254655cc06c1012f7b5e2cb5176dc Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 20:10:19 +0900 Subject: [PATCH 226/316] =?UTF-8?q?fix=20=20:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.controller.ts | 90 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 5e7df24..20fb372 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -37,7 +37,7 @@ export class RuleController { return new ResponseDto( ResponseCode.GET_COMMENT_DETAIL_FAIL, false, - "여행 규칙 상세 페이지 (댓글) 조회 실패", + e.message, null ); } @@ -54,11 +54,11 @@ export class RuleController { "여행 규칙 멤버 리스트 불러오기 성공", memberList ); - } catch (error) { + } catch (e) { return new ResponseDto( ResponseCode.GET_MEMBER_LIST_FAIL, false, - "여행 규칙 멤버 리스트 불러오기 실패", + e.message, null ); } @@ -80,11 +80,11 @@ export class RuleController { "초대할 메이트 검색 결과 리스트 불러오기 성공", result ); - } catch (error) { + } catch (e) { return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_FAIL, false, - "초대할 메이트 검색 결과 리스트 불러오기 실패", + e.message, null ); } @@ -97,21 +97,21 @@ export class RuleController { const result = await this.ruleService.getDetail(req.user.id, ruleId); - if(!result){ - return new ResponseDto( - ResponseCode.GET_RULE_DETAIL_FAIL, - false, - "여행 규칙 상세 페이지 (게시글) 조회 실패", - null - ); - } - else{ + try { + const result = await this.ruleService.getDetail(req.user.id, ruleId); return new ResponseDto( ResponseCode.GET_RULE_DETAIL_SUCCESS, true, "여행 규칙 상세 페이지 (게시글) 조회 성공", result ); + } catch (e) { + return new ResponseDto( + ResponseCode.GET_RULE_DETAIL_FAIL, + false, + e.message, + null + ); } } @@ -120,23 +120,21 @@ export class RuleController { @UseGuards(UserGuard) async updateRule(@Body() updateRuleDto: UpdateRuleDto, @Req() req: Request, @Param('ruleId') ruleId: number): Promise> { - const result = await this.ruleService.updateRule(updateRuleDto, req.user.id, ruleId); - - if(!result){ - return new ResponseDto( - ResponseCode.PATCH_RULE_FAIL, - false, - "여행 규칙 수정 실패", - null - ); - } - else{ + try { + const result = await this.ruleService.updateRule(updateRuleDto, req.user.id, ruleId); return new ResponseDto( ResponseCode.PATCH_RULE_SUCCESS, true, "여행 규칙 수정 성공", result ); + } catch (e) { + return new ResponseDto( + ResponseCode.PATCH_RULE_FAIL, + false, + e.message, + null + ); } } @@ -144,22 +142,21 @@ export class RuleController { @Post('/detail') @UseGuards(UserGuard) async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { - const result = await this.ruleService.createRule(createRuleDto, req.user.id); - - if(!result){ - return new ResponseDto( - ResponseCode.RULE_CREATION_FAIL, - false, - "여행 규칙 생성 실패", - null); - - } - else{ + try { + const result = await this.ruleService.createRule(createRuleDto, req.user.id); return new ResponseDto( ResponseCode.RULE_CREATED, true, "여행 규칙 생성 성공", - result); + result + ); + } catch (e) { + return new ResponseDto( + ResponseCode.RULE_CREATION_FAIL, + false, + e.message, + null + ); } } @@ -189,22 +186,19 @@ export class RuleController { @Get() @UseGuards(UserGuard) async getRuleList(@Req() req: Request): Promise> { - const result = await this.ruleService.getRuleList(req.user.id); - - if(!result){ - return new ResponseDto( - ResponseCode.GET_RULE_LIST_FAIL, - false, - "여행 규칙 전체 리스트 조회 실패", - null); - - } - else{ + try { + const result = await this.ruleService.getRuleList(req.user.id); return new ResponseDto( ResponseCode.GET_RULE_LIST_SUCCESS, true, "여행 규칙 전체 리스트 조회 성공", result); + } catch (e) { + return new ResponseDto( + ResponseCode.GET_RULE_LIST_FAIL, + false, + e.message, + null); } } } \ No newline at end of file From 3ebb00858d58b88169bf070e9cf3a1b369d0cc48 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 21:14:31 +0900 Subject: [PATCH 227/316] =?UTF-8?q?feat=20:=20=EC=B4=88=EB=8C=80=ED=95=A0?= =?UTF-8?q?=20=EB=A9=A4=EB=B2=84=20=EA=B2=80=EC=83=89=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit case1. 여행 규칙 생성 case2. 여행 규칙 수정 --- .../dto/get-search-member-at-create.dto.ts | 27 +++++ src/rule/rule.controller.ts | 32 ++++- src/rule/rule.service.ts | 110 +++++++++++++++++- 3 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/rule/dto/get-search-member-at-create.dto.ts diff --git a/src/rule/dto/get-search-member-at-create.dto.ts b/src/rule/dto/get-search-member-at-create.dto.ts new file mode 100644 index 0000000..3c135d1 --- /dev/null +++ b/src/rule/dto/get-search-member-at-create.dto.ts @@ -0,0 +1,27 @@ +import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; + +export class GetSearchMemberAtCreateDto { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + email: string; + + @IsOptional() + @IsString() + introduction: string; + + @IsOptional() + @IsString() + image: string; + + @IsOptional() + @IsBoolean() + isInvited: boolean; +} \ No newline at end of file diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 5e7df24..a26147c 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -9,7 +9,7 @@ import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import { UpdateRuleDto } from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "./dto/cursor-page.options.dto"; import {CursorPageDto} from "./dto/cursor-page.dto"; -import {UserEntity} from "../user/user.entity"; +import {GetSearchMemberAtCreateDto} from "./dto/get-search-member-at-create.dto"; @Controller('mate/rule') export class RuleController { @@ -65,15 +65,41 @@ export class RuleController { } // [3] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 + // [3-1] case1. 여행 규칙 생성 + @Get('/detail/search') + @UseGuards(UserGuard) + async getSearchMemberAtCreate( + @Query('searchTerm')searchTerm : string, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, + @Req() req: Request): Promise> { + try { + const result : CursorPageDto = await this.ruleService.getSearchMemberAtCreate(cursorPageOptionsDto, req.user.id, searchTerm) + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + "초대할 메이트 검색 결과 리스트 불러오기 성공", + result + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + "초대할 메이트 검색 결과 리스트 불러오기 실패", + null + ); + } + } + + // [3-2] case2. 여행 규칙 수정 @Get('/detail/search/:ruleId') @UseGuards(UserGuard) - async getSearchMember( + async getSearchMemberAtUpdate( @Query('searchTerm')searchTerm : string, @Query() cursorPageOptionsDto: CursorPageOptionsDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { try { - const result : CursorPageDto = await this.ruleService.getSearchMember(cursorPageOptionsDto, req.user.id, ruleId, searchTerm) + const result : CursorPageDto = await this.ruleService.getSearchMemberAtUpdate(cursorPageOptionsDto, req.user.id, ruleId, searchTerm) return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_SUCCESS, true, diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index fabdc55..9268fbc 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -17,6 +17,7 @@ import {CommentEntity} from "../comment/domain/comment.entity"; import {GetCommentDto } from "./dto/get-comment.dto"; import {CursorPageDto} from "./dto/cursor-page.dto"; import {CursorPageMetaDto} from "./dto/cursor-page.meta.dto"; +import {GetSearchMemberAtCreateDto} from "./dto/get-search-member-at-create.dto"; @Injectable() export class RuleService { @@ -316,7 +317,114 @@ export class RuleService { } // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 - async getSearchMember(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { + // case1. 여행 규칙 생성 / case2. 여행 규칙 수정 분리 + // case1. 여행 규칙 생성 + async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { + let cursorId: number = 0; + + // (0) 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 + if(cursorPageOptionsDto.cursorId == 0){ + const newUser = await UserEntity.find({ + order: { + id: 'DESC' + }, + take: 1 + }); + const max = newUser[0].id + 1; + console.log('max id: ',max); + + const min = 5; + // TODO 사용자 늘어나면 min 값 늘리기 + cursorId = Math.floor(Math.random() * (max - min + 1)) + min; + console.log('random cursor: ', cursorId); + + } + else { + cursorId = cursorPageOptionsDto.cursorId; + } + + // (1) 데이터 조회 + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: [ + {id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null}, + { name: Like(`%${searchTerm}%`) }, + { nickname: Like(`%${searchTerm}%`)}, + { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 + ], + relations: {profileImage : true, ruleParticipate: {rule: true}}, + order: { + id: cursorPageOptionsDto.sort.toUpperCase() as any, + }, + }); + + const searchResult = await Promise.all(resultUsers.map(async (user) => { + const dtoAtCreate: GetSearchMemberAtCreateDto = new GetSearchMemberAtCreateDto(); + + dtoAtCreate.id = user.id; + dtoAtCreate.name = user.name; + dtoAtCreate.email = user.email; + dtoAtCreate.introduction = user.introduction; + + // 사용자 프로필 이미지 + const image = user.profileImage; + if(image == null) dtoAtCreate.image = null; + else{ + const userImageKey = image.imageKey; + dtoAtCreate.image = await this.s3Service.getImageUrl(userImageKey); + } + return dtoAtCreate; + })); + + console.log('searchResult : ',searchResult); + + // (2) 페이징 및 정렬 기준 설정 + const takePerPage = cursorPageOptionsDto.take; + const isLastPage = total <= takePerPage; + + let hasNextData = true; + let cursor: number; + + if (isLastPage || searchResult.length <= 0) { + hasNextData = false; + cursor = null; + } else { + cursor = searchResult[searchResult.length - 1].id; + } + + const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto(searchResult, cursorPageMetaDto); + } + + // [6-2] case2. 여행 규칙 수정 + async getSearchMemberAtUpdate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { + let cursorId: number = 0; + + // (0) 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 + if(cursorPageOptionsDto.cursorId == 0){ + const newUser = await UserEntity.find({ + order: { + id: 'DESC' + }, + take: 1 + }); + const max = newUser[0].id + 1; + console.log('max id: ',max); + + const min = 5; + // TODO 사용자 늘어나면 min 값 늘리기 + cursorId = Math.floor(Math.random() * (max - min + 1)) + min; + console.log('random cursor: ', cursorId); + + } + else { + cursorId = cursorPageOptionsDto.cursorId; + } + // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 From 5fbab7543c3793b5a7f530a3def85752b8476c00 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 21:45:05 +0900 Subject: [PATCH 228/316] =?UTF-8?q?feat=20:=20=EB=AC=B4=ED=95=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=B4=88=EA=B8=B0=20=EA=B0=92=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 2 ++ src/rule/rule.service.ts | 48 +++--------------------------------- 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 0cd256c..233bd1d 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -21,6 +21,8 @@ export class FollowService { // [1] 메이트 검색 async getSearchResult(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string) { + let cursorId: number = 0; + // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9268fbc..9622616 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -134,7 +134,7 @@ export class RuleService { // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number): Promise> { - const cursorId: number = cursorPageOptionsDto.cursorId; + let cursorId: number = 0; // (1) 데이터 조회 const [comments, total] = await CommentEntity.findAndCount({ @@ -322,27 +322,6 @@ export class RuleService { async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { let cursorId: number = 0; - // (0) 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 - if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.find({ - order: { - id: 'DESC' - }, - take: 1 - }); - const max = newUser[0].id + 1; - console.log('max id: ',max); - - const min = 5; - // TODO 사용자 늘어나면 min 값 늘리기 - cursorId = Math.floor(Math.random() * (max - min + 1)) + min; - console.log('random cursor: ', cursorId); - - } - else { - cursorId = cursorPageOptionsDto.cursorId; - } - // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 @@ -350,7 +329,7 @@ export class RuleService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null}, + {id: cursorId ? LessThan(cursorId) : null}, { name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`)}, { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 @@ -404,27 +383,6 @@ export class RuleService { async getSearchMemberAtUpdate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { let cursorId: number = 0; - // (0) 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 - if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.find({ - order: { - id: 'DESC' - }, - take: 1 - }); - const max = newUser[0].id + 1; - console.log('max id: ',max); - - const min = 5; - // TODO 사용자 늘어나면 min 값 늘리기 - cursorId = Math.floor(Math.random() * (max - min + 1)) + min; - console.log('random cursor: ', cursorId); - - } - else { - cursorId = cursorPageOptionsDto.cursorId; - } - // (1) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 @@ -432,7 +390,7 @@ export class RuleService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null}, + {id: cursorId ? LessThan(cursorId) : null}, { name: Like(`%${searchTerm}%`) }, { nickname: Like(`%${searchTerm}%`)}, { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 From 17f53384750e387986681d7d06a1fcbabf4cc3b6 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 22:04:44 +0900 Subject: [PATCH 229/316] =?UTF-8?q?fix=20:=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 13 +++++++------ src/rule/dto/cursot-page-order.enums.ts | 15 -------------- src/rule/dto/meta-to-front.dto.ts | 20 ------------------- src/rule/rule.service.ts | 26 +++++++++++++------------ 4 files changed, 21 insertions(+), 53 deletions(-) delete mode 100644 src/rule/dto/cursot-page-order.enums.ts delete mode 100644 src/rule/dto/meta-to-front.dto.ts diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 233bd1d..e1dcead 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -35,7 +35,7 @@ export class FollowService { ], relations: {profileImage : true, follower : true, following : true}, order: { - id: cursorPageOptionsDto.sort.toUpperCase() as any, + id: "DESC" as any, }, }); @@ -67,17 +67,18 @@ export class FollowService { })); // (2) 페이징 및 정렬 기준 설정 - const takePerPage = cursorPageOptionsDto.take; - const isLastPage = total <= takePerPage; - let hasNextData = true; let cursor: number; - if (isLastPage || searchResult.length <= 0) { + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + + if (isLastScroll) { hasNextData = false; cursor = null; } else { - cursor = searchResult[searchResult.length - 1].id; + cursor = lastDataPerScroll.id; } const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); diff --git a/src/rule/dto/cursot-page-order.enums.ts b/src/rule/dto/cursot-page-order.enums.ts deleted file mode 100644 index 08c2417..0000000 --- a/src/rule/dto/cursot-page-order.enums.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsArray } from "class-validator"; -import { CursorPageMetaDto } from "./cursor-page.meta.dto"; - -export class CursorPageDto { - - @IsArray() - readonly data: T[]; - - readonly meta: CursorPageMetaDto; - - constructor(data: T[], meta: CursorPageMetaDto) { - this.data = data; - this.meta = meta; - } -} \ No newline at end of file diff --git a/src/rule/dto/meta-to-front.dto.ts b/src/rule/dto/meta-to-front.dto.ts deleted file mode 100644 index 6be0eac..0000000 --- a/src/rule/dto/meta-to-front.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IsNotEmpty, IsNumber, IsBoolean } from 'class-validator'; - -// [meta] Back -> Front -export class MetaToFrontDto { - @IsNotEmpty() - @IsNumber() - total: number; - - @IsNotEmpty() - @IsNumber() - take: number; - - @IsNotEmpty() - @IsBoolean() - hasNextData: boolean; - - @IsNotEmpty() - @IsNumber() - cursor: number; -} \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9622616..6378b31 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -170,17 +170,18 @@ export class RuleService { })); // (2) 페이징 및 정렬 기준 설정 - const takePerPage = cursorPageOptionsDto.take; - const isLastPage = total <= takePerPage; - let hasNextData = true; let cursor: number; - if (isLastPage || result.length <= 0) { + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = comments[comments.length - 1]; + + if (isLastScroll) { hasNextData = false; cursor = null; } else { - cursor = result[result.length - 1].id; + cursor = lastDataPerScroll.id; } const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); @@ -336,7 +337,7 @@ export class RuleService { ], relations: {profileImage : true, ruleParticipate: {rule: true}}, order: { - id: cursorPageOptionsDto.sort.toUpperCase() as any, + id: "DESC" as any, }, }); @@ -361,17 +362,18 @@ export class RuleService { console.log('searchResult : ',searchResult); // (2) 페이징 및 정렬 기준 설정 - const takePerPage = cursorPageOptionsDto.take; - const isLastPage = total <= takePerPage; - let hasNextData = true; let cursor: number; - if (isLastPage || searchResult.length <= 0) { + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + + if (isLastScroll) { hasNextData = false; cursor = null; } else { - cursor = searchResult[searchResult.length - 1].id; + cursor = lastDataPerScroll.id; } const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); @@ -397,7 +399,7 @@ export class RuleService { ], relations: {profileImage : true, ruleParticipate: {rule: true}}, order: { - id: cursorPageOptionsDto.sort.toUpperCase() as any, + id: "DESC" as any, }, }); From 049cc2db18907aaf666e5000d7e32410e1e5cd43 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 23:06:38 +0900 Subject: [PATCH 230/316] =?UTF-8?q?fix=20:=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=B2=98=EC=9D=8C=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 6378b31..f1710c9 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -385,7 +385,22 @@ export class RuleService { async getSearchMemberAtUpdate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { let cursorId: number = 0; - // (1) 데이터 조회 + // (1) 처음 요청인 경우 cursorId 설정 + if(cursorPageOptionsDto.cursorId == 0){ + const newUser = await UserEntity.findOne({ + order: { + id: 'DESC' // 가장 최근에 가입한 유저 + } + }); + const cursorId = newUser[0].id + 1; + + console.log('random cursor: ', cursorId); + } + else { + cursorId = cursorPageOptionsDto.cursorId; + } + + // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 console.log('검색 값: ', searchTerm); @@ -426,18 +441,19 @@ export class RuleService { console.log('searchResult : ',searchResult); - // (2) 페이징 및 정렬 기준 설정 - const takePerPage = cursorPageOptionsDto.take; - const isLastPage = total <= takePerPage; - + // (3) 페이징 및 정렬 기준 설정 let hasNextData = true; let cursor: number; - if (isLastPage || searchResult.length <= 0) { + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = searchResult[searchResult.length - 1]; + + if (isLastScroll) { hasNextData = false; cursor = null; } else { - cursor = searchResult[searchResult.length - 1].id; + cursor = lastDataPerScroll.id; } const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); From be6ce51b8e18f421eea731fd2017e101fca50383 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Mon, 12 Feb 2024 23:12:53 +0900 Subject: [PATCH 231/316] =?UTF-8?q?fix=20:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EA=B0=9C=EC=88=98=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index e54c86d..f1e94d1 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -69,7 +69,7 @@ export class MapService { return { data: response(BaseResponse.GET_SCHEDULE_SUCCESS, { journeyInfo, - scheduleList, + paginatedSchedules, }), nextCursor: nextCursor, }; From 508865ee07363c93a1bbebe3e30e41e20d3f851c Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 23:13:37 +0900 Subject: [PATCH 232/316] =?UTF-8?q?fix=20:=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=B2=98=EC=9D=8C=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f1710c9..76d434c 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -323,7 +323,22 @@ export class RuleService { async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { let cursorId: number = 0; - // (1) 데이터 조회 + // (1) 처음 요청인 경우 cursorId 설정 + if(cursorPageOptionsDto.cursorId == 0){ + const newUser = await UserEntity.findOne({ + order: { + id: 'DESC' // 가장 최근에 가입한 유저 + } + }); + const cursorId = newUser[0].id + 1; + + console.log('random cursor: ', cursorId); + } + else { + cursorId = cursorPageOptionsDto.cursorId; + } + + // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 console.log('검색 값: ', searchTerm); @@ -361,7 +376,7 @@ export class RuleService { console.log('searchResult : ',searchResult); - // (2) 페이징 및 정렬 기준 설정 + // (3) 페이징 및 정렬 기준 설정 let hasNextData = true; let cursor: number; From 2c2069b401b661304dcdce15fe535f767ed0b471 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Mon, 12 Feb 2024 23:34:47 +0900 Subject: [PATCH 233/316] =?UTF-8?q?fix=20:=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 76d434c..baa48e7 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -318,17 +318,18 @@ export class RuleService { } // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 - // case1. 여행 규칙 생성 / case2. 여행 규칙 수정 분리 + // 여행 규칙 생성 / 여행 규칙 수정 분리 // case1. 여행 규칙 생성 async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { let cursorId: number = 0; // (1) 처음 요청인 경우 cursorId 설정 if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.findOne({ + const newUser = await UserEntity.find({ order: { id: 'DESC' // 가장 최근에 가입한 유저 - } + }, + take: 1 }); const cursorId = newUser[0].id + 1; @@ -402,10 +403,11 @@ export class RuleService { // (1) 처음 요청인 경우 cursorId 설정 if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.findOne({ + const newUser = await UserEntity.find({ order: { id: 'DESC' // 가장 최근에 가입한 유저 - } + }, + take: 1 }); const cursorId = newUser[0].id + 1; From 00fe1d9ace6887227b6e2ff9e7b6d1a949bb7498 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 13 Feb 2024 00:40:27 +0900 Subject: [PATCH 234/316] =?UTF-8?q?fix=20:=20=EB=A7=88=EC=A7=80=EB=A7=89?= =?UTF-8?q?=20=EC=BB=A4=EC=84=9C=20=EA=B0=92=20null=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index f1e94d1..f1d486f 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -58,13 +58,18 @@ export class MapService { // }; // 페이징 처리 - const paginatedSchedules = scheduleList.slice( - options.cursor, - options.cursor + options.pageSize, - ); + const startIndex = options.cursor; + const endIndex = Number(options.cursor) + Number(options.pageSize); + const paginatedSchedules = scheduleList.slice(startIndex, endIndex); + + // 다음 페이지를 위한 커서 값 계산 + let nextCursor = null; + if (endIndex < scheduleList.length) { + nextCursor = endIndex; + } // 다음 페이지를 위한 커서 값 계산 - const nextCursor = Number(options.cursor) + Number(options.pageSize); + // const nextCursor = Number(options.cursor) + Number(options.pageSize); return { data: response(BaseResponse.GET_SCHEDULE_SUCCESS, { From 29eed001b88d4a0b0f121daaaa5576ded912a9ff Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 01:47:10 +0900 Subject: [PATCH 235/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=ED=8C=8C=EC=9D=BC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/signature.comment.entity.ts | 50 +++++++++++++++++++ src/signature/domain/signature.entity.ts | 5 ++ src/signature/signature.module.ts | 6 ++- src/user/user.entity.ts | 4 ++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 src/signature/domain/signature.comment.entity.ts diff --git a/src/signature/domain/signature.comment.entity.ts b/src/signature/domain/signature.comment.entity.ts new file mode 100644 index 0000000..ab259a6 --- /dev/null +++ b/src/signature/domain/signature.comment.entity.ts @@ -0,0 +1,50 @@ +// signature.comment.entity.ts + +import { + BaseEntity, Column, + CreateDateColumn, + DeleteDateColumn, + Entity, JoinColumn, ManyToOne, OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { SignatureEntity } from './signature.entity'; +import { UserEntity } from 'src/user/user.entity'; + +@Entity() +export class SignatureCommentEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(() => SignatureEntity, + (signature) => signature.comments) + @JoinColumn() + signature: SignatureEntity; + + @ManyToOne(() => UserEntity, + (user) => user.signatureComments) + @JoinColumn() + user: UserEntity; + + @OneToMany(() => SignatureCommentEntity, + (childComment) => childComment.parentComment) + @JoinColumn() + childComments: SignatureCommentEntity; + + @ManyToOne(() => SignatureCommentEntity, + (parentComment) => parentComment.childComments) + @JoinColumn() + parentComment: SignatureCommentEntity[]; + + @Column() + content: string; + + @CreateDateColumn() + created: Date; + + @UpdateDateColumn() + updated: Date; + + @DeleteDateColumn() + deleted: Date; +} \ No newline at end of file diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index c041e0a..ee9dc4b 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -15,6 +15,7 @@ import { HomeSignatureDto } from '../dto/home-signature.dto'; import { CreateSignatureDto } from '../dto/create-signature.dto'; import { SignaturePageEntity } from './signature.page.entity'; import { SignatureLikeEntity } from './signature.like.entity'; +import { SignatureCommentEntity } from './signature.comment.entity'; @Entity() @EventSubscriber() export class SignatureEntity extends BaseEntity implements EntitySubscriberInterface{ @@ -39,6 +40,10 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter (signatureLike) => signatureLike.signature) likes: SignatureLikeEntity[]; + @OneToMany(() => SignatureCommentEntity, + (signatureComment) => signatureComment.signature) + comments: SignatureCommentEntity[]; + listenTo() { return SignatureLikeEntity; } diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 256fdd3..0e20e36 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -9,9 +9,11 @@ import { DataSource } from 'typeorm'; import { EntityManager } from 'typeorm'; import { UserService } from '../user/user.service'; import { S3UtilService } from '../utils/S3.service'; +import { SignatureCommentController } from './signature.comment.controller'; +import { SignatureCommentService } from './signature.comment.service'; @Module({ - controllers: [SignatureController], - providers: [SignatureService,UserService, S3UtilService], + controllers: [SignatureController, SignatureCommentController], + providers: [SignatureService, SignatureCommentService, UserService, S3UtilService], }) export class SignatureModule {} diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 3fc3fe0..807e7d2 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -18,6 +18,7 @@ import { CommentEntity } from 'src/comment/domain/comment.entity'; import { JourneyEntity } from 'src/journey/model/journey.entity'; import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; +import { SignatureCommentEntity } from '../signature/domain/signature.comment.entity'; @Entity() export class UserEntity extends BaseEntity { @@ -81,6 +82,9 @@ export class UserEntity extends BaseEntity { @OneToMany(() => JourneyEntity, (journey) => journey.user) journeys: JourneyEntity[]; + @OneToMany(() => SignatureCommentEntity, (signatureComment) => signatureComment.user) + signatureComments: SignatureCommentEntity[]; + @CreateDateColumn() created: Date; From 9fd83dd156f9634f3e4d1aa645b3c70cc1ed0f00 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 01:47:20 +0900 Subject: [PATCH 236/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=83=9D=EC=84=B1=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/dto/create-comment.dto.ts | 6 +++ src/signature/signature.comment.controller.ts | 50 +++++++++++++++++++ src/signature/signature.comment.service.ts | 39 +++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/signature/dto/create-comment.dto.ts create mode 100644 src/signature/signature.comment.controller.ts create mode 100644 src/signature/signature.comment.service.ts diff --git a/src/signature/dto/create-comment.dto.ts b/src/signature/dto/create-comment.dto.ts new file mode 100644 index 0000000..72acced --- /dev/null +++ b/src/signature/dto/create-comment.dto.ts @@ -0,0 +1,6 @@ +// create-comment.dto.ts + +export class CreateCommentDto{ + content: string; // 댓글 내용 + +} \ No newline at end of file diff --git a/src/signature/signature.comment.controller.ts b/src/signature/signature.comment.controller.ts new file mode 100644 index 0000000..f5a61ca --- /dev/null +++ b/src/signature/signature.comment.controller.ts @@ -0,0 +1,50 @@ +// signature.comment.controller.ts + +import { Body, Controller, Param, Post, Req, UseGuards } from '@nestjs/common'; +import { SignatureCommentService } from './signature.comment.service'; +import { UserGuard } from '../user/user.guard'; +import { Request } from 'express'; +import { CreateSignatureDto } from './dto/create-signature.dto'; +import { CreateCommentDto } from './dto/create-comment.dto'; + +@Controller('signature/:signatureId') +export class SignatureCommentController{ + constructor(private readonly signatureCommentService: SignatureCommentService) {} + + @Post('/comment') + @UseGuards(UserGuard) + async createSignatureComment( + @Req() req: Request, + @Param('signatureId') signatureId: number, + @Body() newComment: CreateCommentDto, + ){ + try{ + const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId) + + } + catch(error){ + console.log('Error on createSigComment: ',error); + + } + } + + @Post('/comment/:commentId') + @UseGuards(UserGuard) + async createSignatureReplyComment( + @Req() req: Request, + @Param('signatureId') signatureId: number, + @Param('commentId') commentId: number, + @Body() newComment: CreateCommentDto, + ){ + try{ + + } + catch(error){ + console.log('Error on createSigChildComment: ',error); + + } + } + + + +} \ No newline at end of file diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts new file mode 100644 index 0000000..41c2f58 --- /dev/null +++ b/src/signature/signature.comment.service.ts @@ -0,0 +1,39 @@ +// signature.comment.service.ts + +import { Injectable, NotFoundException } from '@nestjs/common'; +import { UserService } from '../user/user.service'; +import { S3UtilService } from '../utils/S3.service'; +import { SignatureService } from './signature.service'; +import { CreateCommentDto } from './dto/create-comment.dto'; +import { SignatureCommentEntity } from './domain/signature.comment.entity'; +import { UserEntity } from '../user/user.entity'; +import { SignatureEntity } from './domain/signature.entity'; + +@Injectable() +export class SignatureCommentService{ + + constructor( + private readonly signatureService: SignatureService, + private readonly userService: UserService, + private readonly s3Service: S3UtilService, + ) {} + + async createSignatureComment(createCommentDto: CreateCommentDto, userId: number, signatureId: number ){ + const comment = new SignatureCommentEntity(); + + const user = await UserEntity.findOneOrFail({ where: { id: userId }}); + const signature = await SignatureEntity.findOneOrFail( { where: { id: signatureId }}) + + if( !user || !signature ) { + throw new NotFoundException('404 Not Found'); + } + else { + comment.user = user; + comment.signature = signature; + comment.content = createCommentDto.content; + await comment.save(); + + return comment.id; + } + } +} \ No newline at end of file From 2fb56ed7bd0eb05aed88f62c0645f048368d001a Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 01:52:32 +0900 Subject: [PATCH 237/316] =?UTF-8?q?fix=20:=20where=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 28 ++++++++++++++++++++++------ src/rule/rule.service.ts | 26 +++++++++++++++----------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index e1dcead..7b6101c 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -21,17 +21,34 @@ export class FollowService { // [1] 메이트 검색 async getSearchResult(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string) { + let cursorId: number = 0; - // (1) 데이터 조회 + // (1) 처음 요청인 경우 cursorId 설정 + if(cursorPageOptionsDto.cursorId == 0){ + const newUser = await UserEntity.find({ + order: { + id: 'DESC' // 가장 최근에 가입한 유저 + }, + take: 1 + }); + const cursorId = newUser[0].id + 1; + + console.log('random cursor: ', cursorId); + } + else { + cursorId = cursorPageOptionsDto.cursorId; + } + + // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorPageOptionsDto.cursorId ? LessThan(cursorPageOptionsDto.cursorId) : null}, - {name: Like(`%${searchTerm}%`)}, - {nickname: Like(`%${searchTerm}%`)} + {id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`)}, + {id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`)}, ], relations: {profileImage : true, follower : true, following : true}, order: { @@ -41,7 +58,6 @@ export class FollowService { const userEntity = await UserEntity.findExistUser(userId); - const searchResult = await Promise.all(resultUsers.map(async (user) => { const followSearchDto = new FollowSearchDto(); @@ -66,7 +82,7 @@ export class FollowService { return followSearchDto; })); - // (2) 페이징 및 정렬 기준 설정 + // (3) 페이징 및 정렬 기준 설정 let hasNextData = true; let cursor: number; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index baa48e7..78b2d33 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -134,7 +134,7 @@ export class RuleService { // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number): Promise> { - let cursorId: number = 0; + const cursorId: number = cursorPageOptionsDto.cursorId; // (1) 데이터 조회 const [comments, total] = await CommentEntity.findAndCount({ @@ -323,6 +323,7 @@ export class RuleService { async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { let cursorId: number = 0; + console.log('cursorId : ', cursorPageOptionsDto); // (1) 처음 요청인 경우 cursorId 설정 if(cursorPageOptionsDto.cursorId == 0){ const newUser = await UserEntity.find({ @@ -333,23 +334,25 @@ export class RuleService { }); const cursorId = newUser[0].id + 1; - console.log('random cursor: ', cursorId); + console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); + console.log('cursor: ', cursorId); } else { cursorId = cursorPageOptionsDto.cursorId; + console.log('cursorPageOptionsDto.cursorId != 0 로 인식') } + console.log('cursor: ', cursorId); // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + // { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 console.log('검색 값: ', searchTerm); const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorId ? LessThan(cursorId) : null}, - { name: Like(`%${searchTerm}%`) }, - { nickname: Like(`%${searchTerm}%`)}, - { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 + { id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`) }, + { id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)}, ], relations: {profileImage : true, ruleParticipate: {rule: true}}, order: { @@ -382,7 +385,10 @@ export class RuleService { let cursor: number; const takePerScroll = cursorPageOptionsDto.take; + console.log('takePerScroll : ',takePerScroll); const isLastScroll = total <= takePerScroll; + console.log('isLastScroll : ', isLastScroll); + console.log('total : ', total) const lastDataPerScroll = resultUsers[resultUsers.length - 1]; if (isLastScroll) { @@ -411,7 +417,7 @@ export class RuleService { }); const cursorId = newUser[0].id + 1; - console.log('random cursor: ', cursorId); + console.log('cursor: ', cursorId); } else { cursorId = cursorPageOptionsDto.cursorId; @@ -424,10 +430,8 @@ export class RuleService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorId ? LessThan(cursorId) : null}, - { name: Like(`%${searchTerm}%`) }, - { nickname: Like(`%${searchTerm}%`)}, - { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 + { id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`) }, + { id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)}, ], relations: {profileImage : true, ruleParticipate: {rule: true}}, order: { From 0d5c7205502e95e97f99733f60216dcfd0948684 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 13 Feb 2024 02:06:08 +0900 Subject: [PATCH 238/316] =?UTF-8?q?fix=20:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=B0=98=ED=99=98=EA=B0=92=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index f1d486f..a2ed2e4 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -67,6 +67,14 @@ export class MapService { if (endIndex < scheduleList.length) { nextCursor = endIndex; } + const total = scheduleList.length; + const hasNextData = endIndex < total; + const meta = { + take: options.pageSize, + total: total, + hasNextData: hasNextData, + cursor: nextCursor, + }; // 다음 페이지를 위한 커서 값 계산 // const nextCursor = Number(options.cursor) + Number(options.pageSize); @@ -75,8 +83,8 @@ export class MapService { data: response(BaseResponse.GET_SCHEDULE_SUCCESS, { journeyInfo, paginatedSchedules, + meta, }), - nextCursor: nextCursor, }; } From b36acc6b69117126292b0e15a912bd899084a38d Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 02:30:19 +0900 Subject: [PATCH 239/316] =?UTF-8?q?fix=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20id=20=EA=B8=B0=EC=A4=80=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 78b2d33..23491c9 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -9,7 +9,7 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import { Equal, LessThan, Like, MoreThan, Not } from 'typeorm'; +import {Brackets, Equal, LessThan, Like, MoreThan, Not} from 'typeorm'; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; @@ -106,7 +106,7 @@ export class RuleService { rulePair.ruleDetail = sub.ruleDetail; return rulePair; - })); + })).then(rulePairs => rulePairs.sort((a, b) => a.id - b.id)); // -3) 멤버 정보 dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { @@ -351,8 +351,14 @@ export class RuleService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - { id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`) }, - { id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)}, + { + id: cursorId ? LessThan(cursorId) : null, + name: Like(`%${searchTerm}%`), + }, + { + id: cursorId ? LessThan(cursorId) : null, + nickname: Like(`%${searchTerm}%`), + } ], relations: {profileImage : true, ruleParticipate: {rule: true}}, order: { From dc4f323d970a160e56f30d9900caebf68f8270bd Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 02:45:49 +0900 Subject: [PATCH 240/316] =?UTF-8?q?fix=20:=20=EB=A9=A4=EB=B2=84,=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20id=20=EA=B8=B0=EC=A4=80=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 23491c9..03281a3 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -75,10 +75,12 @@ export class RuleService { const dto = new DetailRuleDto(); const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); + subs.sort((a, b) => a.id - b.id); const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ where: {rule: {id: ruleId}}, relations: {member: {profileImage : true}} - }) + }); + invitations.sort((a, b) => a.member.id - b.member.id); try { // 요청을 보낸 현재 로그인 사용자가 해당 규칙의 멤버인지 검증 (권한) @@ -106,7 +108,7 @@ export class RuleService { rulePair.ruleDetail = sub.ruleDetail; return rulePair; - })).then(rulePairs => rulePairs.sort((a, b) => a.id - b.id)); + })); // -3) 멤버 정보 dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { @@ -123,7 +125,7 @@ export class RuleService { detailMember.image = await this.s3Service.getImageUrl(userImageKey); } return detailMember; - })) + })); return dto; } catch (e) { console.log('게시글 조회에 실패하였습니다'); From 4a5bc8600b212c08ead7b7c8bcfd4344e8f4aad5 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 03:23:01 +0900 Subject: [PATCH 241/316] =?UTF-8?q?fix=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=EC=9D=84=20=EC=9C=84=ED=95=9C=20ruleNumber?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/create-rule.dto.ts | 4 ++++ src/rule/rule.service.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index b6d0422..80b3592 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -1,6 +1,10 @@ import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; class RulePairDto { + @IsNotEmpty() + @IsNumber() + ruleNumber: number; + @IsNotEmpty() @IsString() ruleTitle: string; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 03281a3..9e8a24b 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -34,6 +34,8 @@ export class RuleService { await main.save(); console.log(main); + dto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); + // -2) rule 저장 const subs = await Promise.all(dto.rulePairs.map(async (pair) => { const sub = new RuleSubEntity(); From 10933b6260b6ac17908a62c72aff5b98338b2c9a Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 03:32:26 +0900 Subject: [PATCH 242/316] =?UTF-8?q?fix=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=A0=80=EC=9E=A5=20=EC=8B=9C,=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/update-rule.dto.ts | 4 ++++ src/rule/rule.service.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/rule/dto/update-rule.dto.ts b/src/rule/dto/update-rule.dto.ts index e0ab74e..3ec862d 100644 --- a/src/rule/dto/update-rule.dto.ts +++ b/src/rule/dto/update-rule.dto.ts @@ -5,6 +5,10 @@ export class UpdateRulePairDto { @IsNumber() id: number; + @IsNotEmpty() + @IsNumber() + ruleNumber: number; + @IsNotEmpty() @IsString() ruleTitle: string; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 9e8a24b..0c1ef24 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -351,6 +351,8 @@ export class RuleService { // 검색 결과에 해당하는 값 찾기 // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 // { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 + + console.log('검색 값: ', searchTerm); const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, @@ -499,6 +501,8 @@ export class RuleService { relations: { rules: true, invitations: {member : true} } }) + updateRuleDto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); + rule.mainTitle = updateRuleDto.mainTitle await rule.save(); From bc135c9435f31339eb655956937446206966d20c Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 03:47:01 +0900 Subject: [PATCH 243/316] =?UTF-8?q?refactor:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20dto=20=ED=8C=8C=EC=9D=BC=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/{ => comment}/create-comment.dto.ts | 0 .../dto/{ => like}/get-like-list.dto.ts | 0 .../dto/{ => like}/like-profile.dto.ts | 0 .../dto/{ => like}/like-signature.dto.ts | 0 .../{ => signature}/author-signature.dto.ts | 0 .../{ => signature}/create-signature.dto.ts | 0 .../{ => signature}/detail-signature.dto.ts | 0 .../{ => signature}/header-signature.dto.ts | 0 .../dto/{ => signature}/home-signature.dto.ts | 0 .../dto/{ => signature}/page-signature.dto.ts | 0 .../response-page-signature.dto.ts | 0 src/signature/signature.comment.controller.ts | 71 +++++++++++++++++-- 12 files changed, 64 insertions(+), 7 deletions(-) rename src/signature/dto/{ => comment}/create-comment.dto.ts (100%) rename src/signature/dto/{ => like}/get-like-list.dto.ts (100%) rename src/signature/dto/{ => like}/like-profile.dto.ts (100%) rename src/signature/dto/{ => like}/like-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/author-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/create-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/detail-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/header-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/home-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/page-signature.dto.ts (100%) rename src/signature/dto/{ => signature}/response-page-signature.dto.ts (100%) diff --git a/src/signature/dto/create-comment.dto.ts b/src/signature/dto/comment/create-comment.dto.ts similarity index 100% rename from src/signature/dto/create-comment.dto.ts rename to src/signature/dto/comment/create-comment.dto.ts diff --git a/src/signature/dto/get-like-list.dto.ts b/src/signature/dto/like/get-like-list.dto.ts similarity index 100% rename from src/signature/dto/get-like-list.dto.ts rename to src/signature/dto/like/get-like-list.dto.ts diff --git a/src/signature/dto/like-profile.dto.ts b/src/signature/dto/like/like-profile.dto.ts similarity index 100% rename from src/signature/dto/like-profile.dto.ts rename to src/signature/dto/like/like-profile.dto.ts diff --git a/src/signature/dto/like-signature.dto.ts b/src/signature/dto/like/like-signature.dto.ts similarity index 100% rename from src/signature/dto/like-signature.dto.ts rename to src/signature/dto/like/like-signature.dto.ts diff --git a/src/signature/dto/author-signature.dto.ts b/src/signature/dto/signature/author-signature.dto.ts similarity index 100% rename from src/signature/dto/author-signature.dto.ts rename to src/signature/dto/signature/author-signature.dto.ts diff --git a/src/signature/dto/create-signature.dto.ts b/src/signature/dto/signature/create-signature.dto.ts similarity index 100% rename from src/signature/dto/create-signature.dto.ts rename to src/signature/dto/signature/create-signature.dto.ts diff --git a/src/signature/dto/detail-signature.dto.ts b/src/signature/dto/signature/detail-signature.dto.ts similarity index 100% rename from src/signature/dto/detail-signature.dto.ts rename to src/signature/dto/signature/detail-signature.dto.ts diff --git a/src/signature/dto/header-signature.dto.ts b/src/signature/dto/signature/header-signature.dto.ts similarity index 100% rename from src/signature/dto/header-signature.dto.ts rename to src/signature/dto/signature/header-signature.dto.ts diff --git a/src/signature/dto/home-signature.dto.ts b/src/signature/dto/signature/home-signature.dto.ts similarity index 100% rename from src/signature/dto/home-signature.dto.ts rename to src/signature/dto/signature/home-signature.dto.ts diff --git a/src/signature/dto/page-signature.dto.ts b/src/signature/dto/signature/page-signature.dto.ts similarity index 100% rename from src/signature/dto/page-signature.dto.ts rename to src/signature/dto/signature/page-signature.dto.ts diff --git a/src/signature/dto/response-page-signature.dto.ts b/src/signature/dto/signature/response-page-signature.dto.ts similarity index 100% rename from src/signature/dto/response-page-signature.dto.ts rename to src/signature/dto/signature/response-page-signature.dto.ts diff --git a/src/signature/signature.comment.controller.ts b/src/signature/signature.comment.controller.ts index f5a61ca..863a5df 100644 --- a/src/signature/signature.comment.controller.ts +++ b/src/signature/signature.comment.controller.ts @@ -1,11 +1,13 @@ // signature.comment.controller.ts -import { Body, Controller, Param, Post, Req, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Query, Req, UseGuards } from '@nestjs/common'; import { SignatureCommentService } from './signature.comment.service'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; -import { CreateSignatureDto } from './dto/create-signature.dto'; -import { CreateCommentDto } from './dto/create-comment.dto'; +import { CreateCommentDto } from './dto/comment/create-comment.dto'; +import { ResponseDto } from '../response/response.dto'; +import { ResponseCode } from '../response/response-code.enum'; +import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; @Controller('signature/:signatureId') export class SignatureCommentController{ @@ -13,7 +15,7 @@ export class SignatureCommentController{ @Post('/comment') @UseGuards(UserGuard) - async createSignatureComment( + async createSignatureComment( // 시그니처 댓글 생성하기 @Req() req: Request, @Param('signatureId') signatureId: number, @Body() newComment: CreateCommentDto, @@ -21,30 +23,85 @@ export class SignatureCommentController{ try{ const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId) + return new ResponseDto( + ResponseCode.CREATE_SIGNATURE_COMMENT_SUCCESS, + true, + "시그니처 댓글 생성 성공", + result + ); + } catch(error){ console.log('Error on createSigComment: ',error); - + return new ResponseDto( + ResponseCode.COMMENT_CREATION_FAIL, + false, + "시그니처 댓글 생성 실패", + null + ); } } @Post('/comment/:commentId') @UseGuards(UserGuard) - async createSignatureReplyComment( + async createSignatureReplyComment( // 시그니처 답글 생성하기 @Req() req: Request, @Param('signatureId') signatureId: number, @Param('commentId') commentId: number, @Body() newComment: CreateCommentDto, ){ try{ + const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId, commentId) + + return new ResponseDto( + ResponseCode.CREATE_SIGNATURE_COMMENT_SUCCESS, + true, + "시그니처 답글 생성 성공", + result + ); } catch(error){ - console.log('Error on createSigChildComment: ',error); + console.log('Error on createSigComment: ',error); + return new ResponseDto( + ResponseCode.COMMENT_CREATION_FAIL, + false, + "시그니처 답글 생성 실패", + null + ); + } + } + + @Get('/comment') + @UseGuards(UserGuard) + async getSignatureComment( // 시그니처 댓글 조회하기 (무한 스크롤) + @Req() req: Request, + @Param('signatureId') signatureId: number, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, + ){ + try{ + const result = await this.signatureCommentService.getSignatureComment(cursorPageOptionsDto, req.user.id, signatureId); + return new ResponseDto( + ResponseCode.GET_COMMENT_DETAIL_SUCCESS, + true, + "시그니처 댓글 가져오기 성공", + result + ); + } + catch(error){ + console.log('Error on createSigChildComment: ',error); + return new ResponseDto( + ResponseCode.GET_COMMENT_DETAIL_FAIL, + false, + "시그니처 댓글 가져오기 실패", + null + ); } } + + } \ No newline at end of file From 983b01f5527ddc73448e5fbb8bc592ab8d84da89 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 03:47:56 +0900 Subject: [PATCH 244/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20=EB=AC=B4?= =?UTF-8?q?=ED=95=9C=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/response/response-code.enum.ts | 2 +- .../domain/signature.comment.entity.ts | 4 +- .../dto/comment/get-comment-writer.dto.ts | 8 ++ .../dto/comment/get-signature-comment.dto.ts | 12 ++ src/signature/signature.comment.service.ts | 124 +++++++++++++++++- 5 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 src/signature/dto/comment/get-comment-writer.dto.ts create mode 100644 src/signature/dto/comment/get-signature-comment.dto.ts diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 54b04de..ed49370 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -32,7 +32,6 @@ export enum ResponseCode { GET_COMMENT_DETAIL_SUCCESS = 'OK', - DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', @@ -51,6 +50,7 @@ export enum ResponseCode { LIKE_ON_SIGNATURE_CREATED = 'CREATED', COMMENT_CREATED = 'CREATED', INVITATION_CREATED = 'CREATED', + CREATE_SIGNATURE_COMMENT_SUCCESS = 'CREATED', diff --git a/src/signature/domain/signature.comment.entity.ts b/src/signature/domain/signature.comment.entity.ts index ab259a6..277e0b1 100644 --- a/src/signature/domain/signature.comment.entity.ts +++ b/src/signature/domain/signature.comment.entity.ts @@ -29,12 +29,12 @@ export class SignatureCommentEntity extends BaseEntity { @OneToMany(() => SignatureCommentEntity, (childComment) => childComment.parentComment) @JoinColumn() - childComments: SignatureCommentEntity; + childComments: SignatureCommentEntity[]; @ManyToOne(() => SignatureCommentEntity, (parentComment) => parentComment.childComments) @JoinColumn() - parentComment: SignatureCommentEntity[]; + parentComment: SignatureCommentEntity; @Column() content: string; diff --git a/src/signature/dto/comment/get-comment-writer.dto.ts b/src/signature/dto/comment/get-comment-writer.dto.ts new file mode 100644 index 0000000..77d2314 --- /dev/null +++ b/src/signature/dto/comment/get-comment-writer.dto.ts @@ -0,0 +1,8 @@ +// get-comment-writer.dto.ts + +export class GetCommentWriterDto{ + _id: number; + name: string; + image: string; // 프로필 이미지 + is_writer: boolean; // 로그인 유저의 수정 삭제 가능 여부 +} \ No newline at end of file diff --git a/src/signature/dto/comment/get-signature-comment.dto.ts b/src/signature/dto/comment/get-signature-comment.dto.ts new file mode 100644 index 0000000..17672a4 --- /dev/null +++ b/src/signature/dto/comment/get-signature-comment.dto.ts @@ -0,0 +1,12 @@ +// get-signature-comment.dto.ts + +import { GetCommentWriterDto } from './get-comment-writer.dto'; + +export class GetSignatureCommentDto{ + _id: number; + parentId: number; + content: string; + writer: GetCommentWriterDto; + date: string; // 생성 | 수정일 + is_edited: boolean; // 댓글 수정 여부 +} \ No newline at end of file diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 41c2f58..471db8f 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -4,10 +4,19 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { UserService } from '../user/user.service'; import { S3UtilService } from '../utils/S3.service'; import { SignatureService } from './signature.service'; -import { CreateCommentDto } from './dto/create-comment.dto'; +import { CreateCommentDto } from './dto/comment/create-comment.dto'; import { SignatureCommentEntity } from './domain/signature.comment.entity'; import { UserEntity } from '../user/user.entity'; import { SignatureEntity } from './domain/signature.entity'; +import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; +import { CommentEntity } from '../comment/domain/comment.entity'; +import { MoreThan } from 'typeorm'; +import { GetCommentDto } from '../rule/dto/get-comment.dto'; +import { GetSignatureCommentDto } from './dto/comment/get-signature-comment.dto'; +import { GetCommentWriterDto } from './dto/comment/get-comment-writer.dto'; +import * as querystring from 'querystring'; +import { CursorPageMetaDto } from '../mate/cursor-page/cursor-page.meta.dto'; +import { CursorPageDto } from '../mate/cursor-page/cursor-page.dto'; @Injectable() export class SignatureCommentService{ @@ -18,22 +27,129 @@ export class SignatureCommentService{ private readonly s3Service: S3UtilService, ) {} - async createSignatureComment(createCommentDto: CreateCommentDto, userId: number, signatureId: number ){ + async createSignatureComment( // 댓글, 답글 생성하기 + createCommentDto: CreateCommentDto, + userId: number, + signatureId: number, + parentCommentId?: number){ + const comment = new SignatureCommentEntity(); const user = await UserEntity.findOneOrFail({ where: { id: userId }}); - const signature = await SignatureEntity.findOneOrFail( { where: { id: signatureId }}) + const signature = await SignatureEntity.findOneOrFail( { where: { id: signatureId }}); + if( !user || !signature ) { throw new NotFoundException('404 Not Found'); } else { + comment.user = user; comment.signature = signature; comment.content = createCommentDto.content; - await comment.save(); + + // parentCommentId가 존재할 경우 -> 답글 / 존재하지 않을 경우 -> 댓글 + if(parentCommentId){ // 대댓글: parentId는 파라미터로 받은 parentCommentId로 설정 + + const parentComment = await SignatureCommentEntity.findOneOrFail( { + where:{ id: parentCommentId + }}); + + if( !parentComment ) throw new NotFoundException('404 Not Found'); + else { + comment.parentComment = parentComment; + await comment.save(); + } + + } + else{ // 댓글: parentId는 본인으로 설정 + const savedComment = await comment.save(); + savedComment.parentComment = savedComment; + await savedComment.save(); + } return comment.id; } } + + async getSignatureComment( // 댓글 가져오기 + cursorPageOptionsDto: CursorPageOptionsDto, + userId: number, + signatureId: number, + ) { + + // 1. 'cursorId'부터 오름차순 정령된 댓글 'take'만큼 가져오기 + const [comments, total] = await SignatureCommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + signature: { id: signatureId }, + parentComment: { id: cursorPageOptionsDto.cursorId ? MoreThan(cursorPageOptionsDto.cursorId) : null }, + }, + relations: { + user: { profileImage: true }, + parentComment: true + }, + order: { + parentComment: { id: "ASC" as any,}, + }, + }); + + // 2. 댓글 response dto에 담기 + const result = await Promise.all(comments.map(async (comment) => { + const writerProfile = new GetCommentWriterDto(); + const getCommentDto = new GetSignatureCommentDto(); + + // 2-[1] 댓글 작성자 정보 담기 + writerProfile._id = comment.user.id; + writerProfile.name = comment.user.nickname; + + // 로그인한 사용자가 댓글 작성자인지 확인 + if( userId == comment.user.id ) writerProfile.is_writer = true; + else writerProfile.is_writer = false; + + // 작성자 프로필 이미지 + const image = comment.user.profileImage; + if(image == null) writerProfile.image = null; + else { + const userImageKey = image.imageKey; + writerProfile.image = await this.s3Service.getImageUrl(userImageKey); + } + + // 2-[2] 댓글 정보 담기 + getCommentDto._id = comment.id; + getCommentDto.content = comment.content; + getCommentDto.parentId = comment.parentComment.id; + getCommentDto.writer = writerProfile; + getCommentDto.date = await SignatureEntity.formatDateString(comment.updated); + + // 댓글 수정 여부 구하기 + if(comment.created != comment.updated) getCommentDto.is_edited = false; + else getCommentDto.is_edited = true; + + return getCommentDto; + + })); + + // 3. 스크롤 설정 + let hasNextData = true; + let cursor: number; + + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = comments[comments.length - 1]; + + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } + + const cursorPageMetaDto = new CursorPageMetaDto( + { cursorPageOptionsDto, total, hasNextData, cursor }); + + return new CursorPageDto( result, cursorPageMetaDto ); + + + } } \ No newline at end of file From e032a6eccda597b85458c3f42d064a840ccc2a22 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 03:48:17 +0900 Subject: [PATCH 245/316] =?UTF-8?q?refactor:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20import=20=ED=8C=8C=EC=9D=BC=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/domain/signature.entity.ts | 4 ++-- src/signature/domain/signature.page.entity.ts | 2 +- src/signature/signature.controller.ts | 10 +++++----- src/signature/signature.service.ts | 18 +++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index ee9dc4b..f873a84 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -11,8 +11,8 @@ import { UpdateDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; -import { HomeSignatureDto } from '../dto/home-signature.dto'; -import { CreateSignatureDto } from '../dto/create-signature.dto'; +import { HomeSignatureDto } from '../dto/signature/home-signature.dto'; +import { CreateSignatureDto } from '../dto/signature/create-signature.dto'; import { SignaturePageEntity } from './signature.page.entity'; import { SignatureLikeEntity } from './signature.like.entity'; import { SignatureCommentEntity } from './signature.comment.entity'; diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index e697c14..193c4d2 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -12,7 +12,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { SignatureEntity } from './signature.entity'; -import { PageSignatureDto } from '../dto/page-signature.dto'; +import { PageSignatureDto } from '../dto/signature/page-signature.dto'; @Entity() export class SignaturePageEntity extends BaseEntity { diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index d971567..ac41eb9 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -2,15 +2,15 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Req, UseGuards } from '@nestjs/common'; import { SignatureService } from './signature.service'; -import { CreateSignatureDto } from './dto/create-signature.dto'; +import { CreateSignatureDto } from './dto/signature/create-signature.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; -import { HomeSignatureDto } from './dto/home-signature.dto'; -import { DetailSignatureDto } from './dto/detail-signature.dto'; +import { HomeSignatureDto } from './dto/signature/home-signature.dto'; +import { DetailSignatureDto } from './dto/signature/detail-signature.dto'; import { SignatureEntity } from './domain/signature.entity'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { LikeSignatureDto } from './dto/like-signature.dto'; -import { GetLikeListDto } from './dto/get-like-list.dto'; +import { LikeSignatureDto } from './dto/like/like-signature.dto'; +import { GetLikeListDto } from './dto/like/get-like-list.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 492e67b..1e1accc 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -1,21 +1,21 @@ // signature.service.ts import { BadRequestException, HttpException, Injectable, NotFoundException } from '@nestjs/common'; -import { CreateSignatureDto } from './dto/create-signature.dto'; +import { CreateSignatureDto } from './dto/signature/create-signature.dto'; import { SignatureEntity } from './domain/signature.entity'; -import { HomeSignatureDto } from './dto/home-signature.dto'; +import { HomeSignatureDto } from './dto/signature/home-signature.dto'; import { UserEntity } from 'src/user/user.entity'; import { SignaturePageEntity } from './domain/signature.page.entity'; -import { PageSignatureDto } from './dto/page-signature.dto'; -import { DetailSignatureDto } from './dto/detail-signature.dto'; -import { AuthorSignatureDto } from './dto/author-signature.dto'; -import { HeaderSignatureDto } from './dto/header-signature.dto'; +import { PageSignatureDto } from './dto/signature/page-signature.dto'; +import { DetailSignatureDto } from './dto/signature/detail-signature.dto'; +import { AuthorSignatureDto } from './dto/signature/author-signature.dto'; +import { HeaderSignatureDto } from './dto/signature/header-signature.dto'; import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { GetLikeListDto } from './dto/get-like-list.dto' -import { LikeProfileDto } from './dto/like-profile.dto'; +import { GetLikeListDto } from './dto/like/get-like-list.dto' +import { LikeProfileDto } from './dto/like/like-profile.dto'; import { S3UtilService } from '../utils/S3.service'; -import { ResponsePageSignatureDto } from './dto/response-page-signature.dto'; +import { ResponsePageSignatureDto } from './dto/signature/response-page-signature.dto'; @Injectable() export class SignatureService { From 158a33a953985ea9d07003deaa58fe8722a70a5e Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 04:44:02 +0900 Subject: [PATCH 246/316] =?UTF-8?q?fix=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=20=EC=A1=B0=ED=9A=8C,=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 0c1ef24..998bd03 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -37,17 +37,15 @@ export class RuleService { dto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); // -2) rule 저장 - const subs = await Promise.all(dto.rulePairs.map(async (pair) => { + for(const pair of dto.rulePairs) { + console.log('현재 저장하는 ruleNumber : ', pair.ruleNumber); const sub = new RuleSubEntity(); sub.ruleTitle = pair.ruleTitle; sub.ruleDetail = pair.ruleDetail; sub.main = main; await sub.save(); - return sub; - })); - console.log(subs); - + } // -3) invitation 저장 const members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { @@ -111,6 +109,8 @@ export class RuleService { return rulePair; })); + dto.rulePairs.sort((a, b) => a.id - b.id); + // -3) 멤버 정보 dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { @@ -128,6 +128,8 @@ export class RuleService { } return detailMember; })); + dto.detailMembers.sort((a, b) => a.id - b.id); + return dto; } catch (e) { console.log('게시글 조회에 실패하였습니다'); @@ -512,6 +514,7 @@ export class RuleService { // 새로운 세부 규칙 리스트 const updateSubsList = updateRuleDto.rulePairs; + updateSubsList.sort((a, b) => a.ruleNumber - b.ruleNumber); // case1) 규칙 삭제 for(const sub of subs) { From 849eb6cb347cc8d16f9bca30f80c6bfce2e678a0 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 13 Feb 2024 13:55:30 +0900 Subject: [PATCH 247/316] =?UTF-8?q?fix=20:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 9ad7247..b1811f3 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -4,7 +4,7 @@ import { Injectable, NotFoundException, } from '@nestjs/common'; -import { addDays, isAfter } from 'date-fns'; +import { addDays, isAfter, isEqual } from 'date-fns'; import { JourneyEntity } from './model/journey.entity'; import { errResponse, response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; @@ -46,20 +46,24 @@ export class JourneyService { let currentDate = startDate; const schedules = []; - // endDate가 startDate 이후인지 확인 - if (isAfter(endDate, startDate)) { - while (currentDate <= endDate) { - const schedule = await ScheduleEntity.createSchedule( - journey, - currentDate, - ); - schedules.push(schedule); - currentDate = addDays(currentDate, 1); // 다음 날짜로 이동 - } + // startDate와 endDate가 같은 경우 하나의 schedule 생성 + if (isEqual(startDate, endDate)) { + const schedule = await ScheduleEntity.createSchedule(journey, startDate); + schedules.push(schedule); } else { - throw new Error('endDate는 startDate보다 이후여야 합니다.'); + let currentDate = startDate; + // endDate가 startDate 이후인지 확인하여 일정 배너 생성 + if (isAfter(endDate, startDate)) { + while (currentDate <= endDate) { + const schedule = await ScheduleEntity.createSchedule( + journey, + currentDate, + ); + schedules.push(schedule); + currentDate = addDays(currentDate, 1); // 다음 날짜로 이동 + } + } } - return schedules; } From 10527ec69d5ae01c3533e3207d3b9172e9892599 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:08:47 +0900 Subject: [PATCH 248/316] Update signature.comment.controller.ts --- src/signature/signature.comment.controller.ts | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/signature/signature.comment.controller.ts b/src/signature/signature.comment.controller.ts index 863a5df..76283c7 100644 --- a/src/signature/signature.comment.controller.ts +++ b/src/signature/signature.comment.controller.ts @@ -1,6 +1,6 @@ // signature.comment.controller.ts -import { Body, Controller, Get, Param, Post, Query, Req, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Param, Patch, Post, Query, Req, UseGuards } from '@nestjs/common'; import { SignatureCommentService } from './signature.comment.service'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; @@ -9,11 +9,11 @@ import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; -@Controller('signature/:signatureId') +@Controller('signature/:signatureId/comment') export class SignatureCommentController{ constructor(private readonly signatureCommentService: SignatureCommentService) {} - @Post('/comment') + @Post('/') @UseGuards(UserGuard) async createSignatureComment( // 시그니처 댓글 생성하기 @Req() req: Request, @@ -42,16 +42,16 @@ export class SignatureCommentController{ } } - @Post('/comment/:commentId') + @Post('/:parentId') @UseGuards(UserGuard) async createSignatureReplyComment( // 시그니처 답글 생성하기 @Req() req: Request, @Param('signatureId') signatureId: number, - @Param('commentId') commentId: number, + @Param('parentId') parentId: number, @Body() newComment: CreateCommentDto, ){ try{ - const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId, commentId) + const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId, parentId) return new ResponseDto( ResponseCode.CREATE_SIGNATURE_COMMENT_SUCCESS, @@ -72,7 +72,7 @@ export class SignatureCommentController{ } } - @Get('/comment') + @Get('/') @UseGuards(UserGuard) async getSignatureComment( // 시그니처 댓글 조회하기 (무한 스크롤) @Req() req: Request, @@ -100,8 +100,17 @@ export class SignatureCommentController{ } } + @Patch('/:commentId') + async patchSignatureComment( + @Param('signatureId') signatureId: number, + @Param('commentId') commentId: number, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, + ){ + try{ + } + catch(error){ - - + } + } } \ No newline at end of file From 23e538a9f20e55f0b597422ee96ad0ec6a1e5c7e Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:47:45 +0900 Subject: [PATCH 249/316] =?UTF-8?q?bugfix:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=88=98=EC=A0=95=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 1e1accc..5695d46 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -310,17 +310,27 @@ export class SignatureService { originalPage.content = patchedPage.content; originalPage.location = patchedPage.location; - // 랜덤 이미지 키 생성 - const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; + // base64로 들어온 이미지면 디코딩해서 새롭게 저장 / 아니면 그대로 두기 + if(patchedPage.image.startsWith("https://hereyou-cdn.kaaang.dev/signature/")){ + // 이미지 그대로 들어왔다면 이미지를 수정할 필요 없음 + console.log(patchedPage._id,": original Image - 수정할 필요 없음"); - // Base64 이미지 업로드 - const uploadedImage = await this.s3Service.putObjectFromBase64( - key, patchedPage.image - ); + } + else{ + // 새로운 이미지가 인코딩돼서 들어왔다면 해당 이미지를 새로 저장해야 + console.log(patchedPage._id,": patched Image - 이미지키 수정 진행"); + + // 랜덤 이미지 키 생성 + const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; - // 이미지 키 저장 - console.log(uploadedImage); - originalPage.image = key; + // Base64 이미지 업로드 + const uploadedImage = await this.s3Service.putObjectFromBase64( + key, patchedPage.image + ); + + // 이미지 키 저장 + originalPage.image = key; + } } await SignaturePageEntity.save(originalPage); From c4ab1247ad4d34de3b18f597c60f59e8190ef9ff Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:48:11 +0900 Subject: [PATCH 250/316] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 6ec13d0..7a29d92 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -8,7 +8,6 @@ import { UserProfileImageEntity } from './user.profile.image.entity'; import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; -import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import { LessThan } from 'typeorm'; import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import { DiaryEntity } from '../diary/models/diary.entity'; From 9a06cf1ee0d136a3ffde54dc7fc2a5fed68c5576 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Tue, 13 Feb 2024 15:18:32 +0900 Subject: [PATCH 251/316] =?UTF-8?q?fix=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=20=ED=98=95=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diary/diary.service.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/diary/diary.service.ts b/src/diary/diary.service.ts index a9b2149..bf83606 100644 --- a/src/diary/diary.service.ts +++ b/src/diary/diary.service.ts @@ -23,9 +23,15 @@ export class DiaryService { /*일지 수정하기*/ async updateDiary(diaryId, diaryInfo: PostDiaryDto) { const diary = await DiaryEntity.updateDiary(diaryId, diaryInfo); - const diaryImg = await this.getDiaryImgUrl(diary, diaryInfo.fileName); - await DiaryImageEntity.updateDiaryImg(diary, diaryImg); - return response(BaseResponse.DIARY_CREATED); + if ( + diaryInfo.fileName.startsWith('https://hereyou-cdn.kaaang.dev/diary/') + ) { + return response(BaseResponse.DIARY_CREATED); + } else { + const diaryImg = await this.getDiaryImgUrl(diary, diaryInfo.fileName); + await DiaryImageEntity.updateDiaryImg(diary, diaryImg); + return response(BaseResponse.DIARY_CREATED); + } } /*일지 사진 S3에 업로드 후 url 받기- 생성 */ From a2e47ba130056e97e1baf67f6a7bea6dbd2ea2d5 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:51:28 +0900 Subject: [PATCH 252/316] =?UTF-8?q?[Refactor]=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EB=B0=98=ED=99=98=EA=B0=92=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/dto/comment/get-signature-comment.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature/dto/comment/get-signature-comment.dto.ts b/src/signature/dto/comment/get-signature-comment.dto.ts index 17672a4..db5a53c 100644 --- a/src/signature/dto/comment/get-signature-comment.dto.ts +++ b/src/signature/dto/comment/get-signature-comment.dto.ts @@ -7,6 +7,6 @@ export class GetSignatureCommentDto{ parentId: number; content: string; writer: GetCommentWriterDto; - date: string; // 생성 | 수정일 + date: Date; // 생성 | 수정일 is_edited: boolean; // 댓글 수정 여부 } \ No newline at end of file From 06ab88cd243da9e3a2b3b925efa1b54fcbf8502e Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:52:24 +0900 Subject: [PATCH 253/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.controller.ts | 40 +++++++++++++++++-- src/signature/signature.comment.service.ts | 34 ++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/signature/signature.comment.controller.ts b/src/signature/signature.comment.controller.ts index 76283c7..d7a07ad 100644 --- a/src/signature/signature.comment.controller.ts +++ b/src/signature/signature.comment.controller.ts @@ -1,6 +1,18 @@ // signature.comment.controller.ts -import { Body, Controller, Get, Param, Patch, Post, Query, Req, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + ForbiddenException, + Get, + NotFoundException, + Param, + Patch, + Post, + Query, + Req, + UseGuards, +} from '@nestjs/common'; import { SignatureCommentService } from './signature.comment.service'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; @@ -101,15 +113,37 @@ export class SignatureCommentController{ } @Patch('/:commentId') - async patchSignatureComment( + @UseGuards(UserGuard) + async patchSignatureComment( // 시그니처 수정하기 @Param('signatureId') signatureId: number, @Param('commentId') commentId: number, - @Query() cursorPageOptionsDto: CursorPageOptionsDto, + @Body() patchedComment: CreateCommentDto, + @Req() req: Request, ){ try{ + const result = await this.signatureCommentService.patchSignatureComment(req.user.id,signatureId,commentId,patchedComment); + return new ResponseDto( + ResponseCode.COMMENT_UPDATE_SUCCESS, + true, + "시그니처 댓글 수정하기 성공", + result + ); } catch(error){ + console.log("Err on PatchSigComment: "+ error); + let errorMessage = ""; + + if(error instanceof NotFoundException) errorMessage = error.message; + else if(error instanceof ForbiddenException) errorMessage = error.message; + else errorMessage = "시그니처 댓글 수정하기 실패"; + + return new ResponseDto( + ResponseCode.COMMENT_UPDATE_FAIL, + false, + errorMessage, + null + ); } } diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 471db8f..54686f4 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -1,6 +1,6 @@ // signature.comment.service.ts -import { Injectable, NotFoundException } from '@nestjs/common'; +import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common'; import { UserService } from '../user/user.service'; import { S3UtilService } from '../utils/S3.service'; import { SignatureService } from './signature.service'; @@ -120,10 +120,10 @@ export class SignatureCommentService{ getCommentDto.content = comment.content; getCommentDto.parentId = comment.parentComment.id; getCommentDto.writer = writerProfile; - getCommentDto.date = await SignatureEntity.formatDateString(comment.updated); + getCommentDto.date = comment.updated; // 댓글 수정 여부 구하기 - if(comment.created != comment.updated) getCommentDto.is_edited = false; + if(comment.created.getTime() === comment.updated.getTime()) getCommentDto.is_edited = false; else getCommentDto.is_edited = true; return getCommentDto; @@ -152,4 +152,32 @@ export class SignatureCommentService{ } + + async patchSignatureComment( // 댓글 수정하기 + userId: number, + signatureId: number, + commentId: number, + patchedComment: CreateCommentDto) { + + // 시그니처 유효한지 확인 + const signature = await SignatureEntity.findOne({ where:{ id: signatureId }}); + if(!signature) throw new NotFoundException('존재하지 않는 시그니처입니다'); + + // 댓글 데이터 유효한지 확인 + const comment = await SignatureCommentEntity.findOne({ + where:{ id: commentId }, + relations: ['user'] + }, + ); + if(!comment) throw new NotFoundException('존재하지 않는 댓글입니다'); + + // 댓글 작성자가 로그인한 사용자 본인이 맞는지 확인 + if(comment.user.id != userId ) throw new ForbiddenException('댓글 수정 권한이 없습니다'); + + // 댓글 수정하기 + comment.content = patchedComment.content; + await comment.save(); + return comment.id; + + } } \ No newline at end of file From e0d846767b3f3100abfe01bdf072033452a1b457 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 18:58:09 +0900 Subject: [PATCH 254/316] =?UTF-8?q?fix=20:=20=EC=B0=B8=EC=97=AC=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=A0=84=EC=B2=B4=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20updated=20=EC=88=9C=EC=9C=BC=EB=A1=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.service.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 998bd03..7cde83f 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -272,14 +272,18 @@ export class RuleService { }, }, }); + console.log('현재 로그인한 사용자 : ', userEntity.id); try { const invitationEntities = userEntity.ruleParticipate; + // 수정한 날짜 updated 내림차순으로 정렬 + invitationEntities.sort((a, b) => new Date(b.rule.updated).getTime() - new Date(a.rule.updated).getTime()); + console.log('invitationEntities 출력 : ', invitationEntities); if (!!invitationEntities) { const ruleMains = await Promise.all(invitationEntities.map(async (invitation : RuleInvitationEntity) : Promise => { - console.log(invitation); - const ruleMain : RuleMainEntity = invitation.rule as RuleMainEntity; + console.log('참여하는 규칙 ID : ', invitation.rule.id); + const ruleMain : RuleMainEntity = invitation.rule; const ruleListDto : GetRuleListDto = new GetRuleListDto; console.log('ruleMain.id : ', ruleMain.id); @@ -356,6 +360,7 @@ export class RuleService { console.log('검색 값: ', searchTerm); + const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ From 72b9301f35e66053aa5cf7adbd1aa8266b49bf4a Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 19:27:07 +0900 Subject: [PATCH 255/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=88=98=EC=A0=95=20=EA=B3=BC=EC=A0=95?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=A9=A4=EB=B2=84=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 여행에 참여하는 멤버인지 검증 추가 --- src/rule/rule.service.ts | 227 +++++++++++++++++++++------------------ 1 file changed, 124 insertions(+), 103 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 998bd03..b6a1396 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -498,120 +498,141 @@ export class RuleService { // [7] 여행 규칙 수정 async updateRule(updateRuleDto: UpdateRuleDto, userId: number, ruleId: number): Promise { - const rule = await RuleMainEntity.findOne({ - where: {id : ruleId }, - relations: { rules: true, invitations: {member : true} } - }) - - updateRuleDto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); - - rule.mainTitle = updateRuleDto.mainTitle - await rule.save(); - - // (1) [상세 규칙 수정] - // 기존 세부 규칙 정보 리스트 - const subs = rule.rules; - - // 새로운 세부 규칙 리스트 - const updateSubsList = updateRuleDto.rulePairs; - updateSubsList.sort((a, b) => a.ruleNumber - b.ruleNumber); - - // case1) 규칙 삭제 - for(const sub of subs) { - let isDeleteSub : boolean = true; - for(const updateSub of updateSubsList) { - if(sub.id == updateSub.id) { - isDeleteSub = false; - break; - } - } - if (isDeleteSub) { - await sub.softRemove(); - console.log('삭제하는 sub 규칙 : ', sub.id); - } - } - // case2) 규칙 수정 및 규칙 추가 - for (const updateSub of updateSubsList) { - // case1) 새로운 규칙 - if (!updateSub.id) { - const newSub = new RuleSubEntity() - newSub.main = rule; - newSub.ruleTitle = updateSub.ruleTitle; - newSub.ruleDetail = updateSub.ruleDetail; - - await newSub.save(); - console.log('새로 저장하는 sub 규칙 : ', newSub.id); - } - // case2) 수정 규칙 - else { - const oldSub = await RuleSubEntity.findOne({ - where: {id: updateSub.id} - }) - oldSub.ruleTitle = updateSub.ruleTitle; - oldSub.ruleDetail = updateSub.ruleDetail; + try { + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id : userId}, + }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); - await oldSub.save(); - console.log('수정하는 규칙 ID : ', oldSub); - } - } + // 검증2) 규칙이 존재하지 않는 경우 + const rule = await RuleMainEntity.findOne({ + where: {id : ruleId }, + relations: { rules: true, invitations: {member : true} } + }) + if (!rule) throw new Error('규칙을 찾을 수 없습니다'); - // (2) [여행 규칙 멤버 수정] - // 기존 멤버 초대 리스트 - const oldInvitations = await RuleInvitationEntity.find({ - where: {rule: {id : ruleId}}, - relations: {member: true} - }) - // 수정된 멤버 ID 리스트 - const updateMemberIds = updateRuleDto.membersId; - - // case1) 멤버 삭제 - for(const invitation of oldInvitations) { - const member = invitation.member; - let isDeleteMember : boolean = true; - - // (예외 상황) 현재 로그인한 사용자 - if (member.id == userId) break; - - for(const updateMemberId of updateMemberIds) { - if(member.id == updateMemberId) { - isDeleteMember = false; - break; + // 검증3) 규칙에 참여하는 사용자인지 체크 + const invitation = await RuleInvitationEntity.findOne({ + where: {member: {id: userId}, rule: {id: ruleId}}, + }) + // -> 규칙에 참여하는 사용자인 경우 + if (!!invitation) { + updateRuleDto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); + + rule.mainTitle = updateRuleDto.mainTitle + await rule.save(); + + // (1) [상세 규칙 수정] + // 기존 세부 규칙 정보 리스트 + const subs = rule.rules; + + // 새로운 세부 규칙 리스트 + const updateSubsList = updateRuleDto.rulePairs; + updateSubsList.sort((a, b) => a.ruleNumber - b.ruleNumber); + + // case1) 규칙 삭제 + for(const sub of subs) { + let isDeleteSub : boolean = true; + for(const updateSub of updateSubsList) { + if(sub.id == updateSub.id) { + isDeleteSub = false; + break; + } + } + if (isDeleteSub) { + await sub.softRemove(); + console.log('삭제하는 sub 규칙 : ', sub.id); + } } - } - if(isDeleteMember) { - await invitation.softRemove(); - console.log('삭제하는 멤버 ID : ', invitation.id); - } - } - - // case2) 멤버 추가 - for (const updateMemberId of updateMemberIds) { - const member = await UserEntity.findExistUser(updateMemberId); - let isPostMember : boolean = true; + // case2) 규칙 수정 및 규칙 추가 + for (const updateSub of updateSubsList) { + // case1) 새로운 규칙 + if (!updateSub.id) { + const newSub = new RuleSubEntity() + newSub.main = rule; + newSub.ruleTitle = updateSub.ruleTitle; + newSub.ruleDetail = updateSub.ruleDetail; + + await newSub.save(); + console.log('새로 저장하는 sub 규칙 : ', newSub.id); + } + // case2) 수정 규칙 + else { + const oldSub = await RuleSubEntity.findOne({ + where: {id: updateSub.id} + }) + oldSub.ruleTitle = updateSub.ruleTitle; + oldSub.ruleDetail = updateSub.ruleDetail; + + await oldSub.save(); + console.log('수정하는 규칙 ID : ', oldSub); + } + } - for(const oldInvitation of oldInvitations) { - const oldMember = oldInvitation.member; - if(oldMember.id == updateMemberId) { - isPostMember = false; - break; + // (2) [여행 규칙 멤버 수정] + // 기존 멤버 초대 리스트 + const oldInvitations = await RuleInvitationEntity.find({ + where: {rule: {id : ruleId}}, + relations: {member: true} + }) + // 수정된 멤버 ID 리스트 + const updateMemberIds = updateRuleDto.membersId; + + // case1) 멤버 삭제 + for(const invitation of oldInvitations) { + const member = invitation.member; + let isDeleteMember : boolean = true; + + // (예외 상황) 현재 로그인한 사용자 + if (member.id == userId) break; + + for(const updateMemberId of updateMemberIds) { + if(member.id == updateMemberId) { + isDeleteMember = false; + break; + } + } + if(isDeleteMember) { + await invitation.softRemove(); + console.log('삭제하는 멤버 ID : ', invitation.id); + } } - } - if(isPostMember) { - const newInvitation = new RuleInvitationEntity(); + // case2) 멤버 추가 + for (const updateMemberId of updateMemberIds) { + const member = await UserEntity.findExistUser(updateMemberId); - newInvitation.member = await UserEntity.findExistUser(updateMemberId); - newInvitation.rule = rule; + let isPostMember : boolean = true; - await newInvitation.save(); - console.log('새로 초대한 멤버 ID : ', updateMemberId); - } - } + for(const oldInvitation of oldInvitations) { + const oldMember = oldInvitation.member; + if(oldMember.id == updateMemberId) { + isPostMember = false; + break; + } + } + + if(isPostMember) { + const newInvitation = new RuleInvitationEntity(); + + newInvitation.member = await UserEntity.findExistUser(updateMemberId); + newInvitation.rule = rule; - console.log('--여행 규칙 수정이 완료되었습니다--') - return rule.id; + await newInvitation.save(); + console.log('새로 초대한 멤버 ID : ', updateMemberId); + } + } + console.log('--여행 규칙 수정이 완료되었습니다--') + return rule.id; + } else throw new Error('사용자가 참여하는 규칙이 아닙니다'); // -> 여행 규칙에 참여하지 않는 경우 + + } catch (e) { + console.log('여행 규칙 수정 실패'); + throw new Error(e.message); + } } From 5ba5211eddae71ed57abea68b7b06956ba1878f3 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 21:13:02 +0900 Subject: [PATCH 256/316] =?UTF-8?q?fix=20:=20sort=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/dto/update-rule.dto.ts | 3 +- src/rule/rule.controller.ts | 6 +- src/rule/rule.service.ts | 731 +++++++++++++++++--------------- 3 files changed, 391 insertions(+), 349 deletions(-) diff --git a/src/rule/dto/update-rule.dto.ts b/src/rule/dto/update-rule.dto.ts index 3ec862d..7101689 100644 --- a/src/rule/dto/update-rule.dto.ts +++ b/src/rule/dto/update-rule.dto.ts @@ -1,7 +1,8 @@ -import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import {IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested, IsOptional} from 'class-validator'; import { Type } from 'class-transformer'; export class UpdateRulePairDto { + @IsOptional() @IsNumber() id: number; diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 479d37f..13153b3 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -25,7 +25,7 @@ export class RuleController { @Query() cursorPageOptionsDto: CursorPageOptionsDto ): Promise> { try { - const result = await this.ruleService.getComment(cursorPageOptionsDto, ruleId); + const result = await this.ruleService.getComment(cursorPageOptionsDto, ruleId, req.user.id); return new ResponseDto( ResponseCode.GET_COMMENT_DETAIL_SUCCESS, @@ -80,11 +80,11 @@ export class RuleController { "초대할 메이트 검색 결과 리스트 불러오기 성공", result ); - } catch (error) { + } catch (e) { return new ResponseDto( ResponseCode.GET_SEARCH_RESULT_FAIL, false, - "초대할 메이트 검색 결과 리스트 불러오기 실패", + e.message, null ); } diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 4905a12..f2f4cc5 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,4 +1,4 @@ -import {Injectable, HttpException, BadRequestException, HttpStatus} from '@nestjs/common'; +import {Injectable } from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; @@ -9,7 +9,7 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import {Brackets, Equal, LessThan, Like, MoreThan, Not} from 'typeorm'; +import { LessThan, Like, MoreThan } from 'typeorm'; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; @@ -24,113 +24,129 @@ export class RuleService { constructor( private readonly s3Service: S3UtilService, private readonly userService: UserService, - ) {} + ) { + } // [1] 여행 규칙 생성 async createRule(dto: CreateRuleDto, userId: number): Promise { - // -1) main 저장 - const main = new RuleMainEntity(); - main.mainTitle = dto.mainTitle; - await main.save(); - console.log(main); - - dto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); - - // -2) rule 저장 - for(const pair of dto.rulePairs) { - console.log('현재 저장하는 ruleNumber : ', pair.ruleNumber); - const sub = new RuleSubEntity(); - sub.ruleTitle = pair.ruleTitle; - sub.ruleDetail = pair.ruleDetail; - sub.main = main; - - await sub.save(); - } - // -3) invitation 저장 - const members = await Promise.all(dto.membersId.map(async (memberId) : Promise => { - const ruleInvitationEntity = new RuleInvitationEntity(); + try { + // 사용자 검증 + const inviterEntity = await UserEntity.findOneOrFail({where: {id: userId}}); + if (!inviterEntity) throw new Error('사용자를 찾을 수 없습니다'); + + // -1) main 저장 + const main = new RuleMainEntity(); + main.mainTitle = dto.mainTitle; + await main.save(); + console.log(main); + + dto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); + + // -2) rule 저장 + for (const pair of dto.rulePairs) { + console.log('현재 저장하는 ruleNumber : ', pair.ruleNumber); + const sub = new RuleSubEntity(); + sub.ruleTitle = pair.ruleTitle; + sub.ruleDetail = pair.ruleDetail; + sub.main = main; + + await sub.save(); + } - const userEntity = await UserEntity.findOneOrFail({ where: { id: memberId } }); - ruleInvitationEntity.rule = main; - ruleInvitationEntity.member = userEntity; + // -3) invitation 저장 + const members = await Promise.all(dto.membersId.map(async (memberId): Promise => { + const ruleInvitationEntity = new RuleInvitationEntity(); - await ruleInvitationEntity.save(); - return ruleInvitationEntity; - })); + const userEntity = await UserEntity.findOneOrFail({where: {id: memberId}}); + ruleInvitationEntity.rule = main; + ruleInvitationEntity.member = userEntity; - // -4) 여행 규칙 글 작성자 정보 저장 - const writerEntity = new RuleInvitationEntity(); + await ruleInvitationEntity.save(); + return ruleInvitationEntity; + })); + + // -4) 여행 규칙 글 작성자 정보 저장 + const writerEntity = new RuleInvitationEntity(); - const inviterEntity = await UserEntity.findOneOrFail({ where: { id: userId } }); - writerEntity.member = inviterEntity; - writerEntity.rule = main; - await writerEntity.save(); + writerEntity.member = inviterEntity; + writerEntity.rule = main; + await writerEntity.save(); - return main.id; + return main.id; + } catch (e) { + throw new Error(e.message); + } } // [2] 여행 규칙 상세 페이지 조회 (게시글) - async getDetail(userId: number, ruleId : number): Promise { + async getDetail(userId: number, ruleId: number): Promise { const dto = new DetailRuleDto(); + const sortedDto = new DetailRuleDto(); const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); - subs.sort((a, b) => a.id - b.id); const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ where: {rule: {id: ruleId}}, - relations: {member: {profileImage : true}} + relations: {member: {profileImage: true}} }); - invitations.sort((a, b) => a.member.id - b.member.id); try { - // 요청을 보낸 현재 로그인 사용자가 해당 규칙의 멤버인지 검증 (권한) - const user = await UserEntity.findExistUser(userId); - let checkValidation = false; - for(const invitation of invitations) { - if(invitation.member.id == user.id) { - checkValidation = true; - break; - } - } - if(!checkValidation) { - throw new BadRequestException('해당 여행 규칙의 멤버가 아닙니다'); - } - - // -1) 제목 - dto.id = ruleId; - dto.mainTitle = main.mainTitle; + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id: userId}, + }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); - // -2) 규칙 - dto.rulePairs = await Promise.all(subs.map(async(sub):Promise => { - const rulePair = new RulePairDto(); - rulePair.id = sub.id; - rulePair.ruleTitle = sub.ruleTitle; - rulePair.ruleDetail = sub.ruleDetail; + // 검증2) 규칙이 존재하지 않는 경우 + const rule = await RuleMainEntity.findOne({ + where: {id: ruleId}, + relations: {rules: true, invitations: {member: true}} + }) + if (!rule) throw new Error('규칙을 찾을 수 없습니다'); - return rulePair; - })); - dto.rulePairs.sort((a, b) => a.id - b.id); + // 검증3) 규칙에 참여하는 사용자인지 체크 + const invitation = await RuleInvitationEntity.findOne({ + where: {member: {id: userId}, rule: {id: ruleId}}, + }) + if(!!invitation) { + // -1) 제목 + sortedDto.id = ruleId; + sortedDto.mainTitle = main.mainTitle; - // -3) 멤버 정보 - dto.detailMembers = await Promise.all(invitations.map(async(invitation):Promise => { - const detailMember = new DetailMemberDto; - const memberEntity = invitation.member; - detailMember.id = memberEntity.id; - detailMember.name = memberEntity.name; + // -2) 규칙 + dto.rulePairs = await Promise.all(subs.map(async (sub): Promise => { + const rulePair = new RulePairDto(); + rulePair.id = sub.id; + rulePair.ruleTitle = sub.ruleTitle; + rulePair.ruleDetail = sub.ruleDetail; - // 사용자 프로필 이미지 - const image = memberEntity.profileImage; - if(image == null) detailMember.image = null; - else{ - const userImageKey = image.imageKey; - detailMember.image = await this.s3Service.getImageUrl(userImageKey); - } - return detailMember; - })); - dto.detailMembers.sort((a, b) => a.id - b.id); + return rulePair; + })); + sortedDto.rulePairs = dto.rulePairs.sort((a, b) => a.id - b.id); + + // -3) 멤버 정보 + dto.detailMembers = await Promise.all(invitations.map(async (invitation): Promise => { + const detailMember = new DetailMemberDto; + const memberEntity = invitation.member; + detailMember.id = memberEntity.id; + detailMember.name = memberEntity.name; + + // 사용자 프로필 이미지 + const image = memberEntity.profileImage; + if (image == null) detailMember.image = null; + else { + const userImageKey = image.imageKey; + detailMember.image = await this.s3Service.getImageUrl(userImageKey); + } + return detailMember; + })); + sortedDto.detailMembers = dto.detailMembers.sort((a, b) => a.id - b.id); - return dto; + return sortedDto; + } else { + throw new Error('여행 규칙에 참여하는 사용자가 아닙니다'); + } } catch (e) { console.log('게시글 조회에 실패하였습니다'); throw new Error(e.message); @@ -138,61 +154,85 @@ export class RuleService { }; // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 - async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number): Promise> { - - const cursorId: number = cursorPageOptionsDto.cursorId; - - // (1) 데이터 조회 - const [comments, total] = await CommentEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: { - rule: { id: ruleId }, - id: cursorId ? MoreThan(cursorId) : null, - }, - relations: {user:{profileImage: true}}, - order: { - id: "ASC" as any, - }, - }); + async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number, userId: number): Promise> { - const result = await Promise.all(comments.map(async (comment) => { - const getCommentDto = new GetCommentDto(); + try { + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id: userId}, + }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); - getCommentDto.id = comment.id; - getCommentDto.writerId = comment.user.id; - getCommentDto.name = comment.user.name; - getCommentDto.content = comment.content; - getCommentDto.updated = comment.updated; + // 검증2) 규칙이 존재하지 않는 경우 + const ruleMain = await RuleMainEntity.findOne({ + where: {id: ruleId}, + }); + if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); - // 사용자 프로필 이미지 - const image = comment.user.profileImage; - if(image == null) getCommentDto.image = null; - else { - const userImageKey = image.imageKey; - getCommentDto.image = await this.s3Service.getImageUrl(userImageKey); - } + // 검증3) 규칙에 참여하는 사용자가 아닌 경우 + const invitation = await RuleInvitationEntity.findOne({ + where: {member: {id: userId}, rule: {id: ruleId}}, + }) + if (!invitation) throw new Error('사용자가 참여하는 규칙이 아닙니다'); - return getCommentDto; - })); + console.log('--- 검증 완료 ---') - // (2) 페이징 및 정렬 기준 설정 - let hasNextData = true; - let cursor: number; + // (1) 데이터 조회 + const cursorId: number = cursorPageOptionsDto.cursorId; - const takePerScroll = cursorPageOptionsDto.take; - const isLastScroll = total <= takePerScroll; - const lastDataPerScroll = comments[comments.length - 1]; + const [comments, total] = await CommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + rule: {id: ruleId}, + id: cursorId ? MoreThan(cursorId) : null, + }, + relations: {user: {profileImage: true}}, + order: { + id: "ASC" as any, + }, + }); - if (isLastScroll) { - hasNextData = false; - cursor = null; - } else { - cursor = lastDataPerScroll.id; - } + const result = await Promise.all(comments.map(async (comment) => { + const getCommentDto = new GetCommentDto(); - const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); + getCommentDto.id = comment.id; + getCommentDto.writerId = comment.user.id; + getCommentDto.name = comment.user.name; + getCommentDto.content = comment.content; + getCommentDto.updated = comment.updated; - return new CursorPageDto(result, cursorPageMetaDto); + // 사용자 프로필 이미지 + const image = comment.user.profileImage; + if (image == null) getCommentDto.image = null; + else { + const userImageKey = image.imageKey; + getCommentDto.image = await this.s3Service.getImageUrl(userImageKey); + } + + return getCommentDto; + })); + + // (2) 페이징 및 정렬 기준 설정 + let hasNextData = true; + let cursor: number; + + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = comments[comments.length - 1]; + + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } + + const cursorPageMetaDto = new CursorPageMetaDto({cursorPageOptionsDto, total, hasNextData, cursor}); + + return new CursorPageDto(result, cursorPageMetaDto); + } catch (e) { + throw new Error(e.message); + } } // [4] 여행 규칙 나가기 @@ -201,13 +241,13 @@ export class RuleService { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id : userId}, + where: {id: userId}, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const ruleMain = await RuleMainEntity.findOne({ - where : {id : ruleId}, + where: {id: ruleId}, }); if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); @@ -226,65 +266,72 @@ export class RuleService { // [4] 여행 규칙 멤버 리스트 조회 async getMemberList(ruleId: number): Promise { - const invitationsList : RuleInvitationEntity[] = await RuleInvitationEntity.find({ - where : {rule : {id: ruleId}}, - relations : {member : true} - }); + try { + const invitationsList: RuleInvitationEntity[] = await RuleInvitationEntity.find({ + where: {rule: {id: ruleId}}, + relations: {member: true} + }); - const membersList : GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation) : Promise => { - const memberEntity : UserEntity = invitation.member; - const memberDto : GetMemberListDto = new GetMemberListDto(); + const membersList: GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation): Promise => { + const memberEntity: UserEntity = invitation.member; + const memberDto: GetMemberListDto = new GetMemberListDto(); - console.log('memberEntity : ', memberEntity); - memberDto.id = memberEntity.id; - memberDto.name = memberEntity.name; - memberDto.email = memberEntity.email; - memberDto.introduction = memberEntity.introduction; + console.log('memberEntity : ', memberEntity); + memberDto.id = memberEntity.id; + memberDto.name = memberEntity.name; + memberDto.email = memberEntity.email; + memberDto.introduction = memberEntity.introduction; - // 사용자 프로필 이미지 - const image = await this.userService.getProfileImage(memberEntity.id); - if(image == null) memberDto.image = null; - else { - const userImageKey = image.imageKey; - memberDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return memberDto; - })); - const sortedList = membersList.sort((a, b) => a.id - b.id); - return sortedList; + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage(memberEntity.id); + if (image == null) memberDto.image = null; + else { + const userImageKey = image.imageKey; + memberDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return memberDto; + })); + const sortedList = membersList.sort((a, b) => a.id - b.id); + return sortedList; + } catch (e) { + throw new Error(e.message); + } } // [5] 여행 규칙 전체 리스트 조회 - async getRuleList(userId: number) :Promise { - const userEntity = await UserEntity.findOne({ - where: { - id: userId, - }, - relations: { - ruleParticipate: { - rule: { - invitations: { - member: { - profileImage: true, + async getRuleList(userId: number): Promise { + + try { + // 검증) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: { + id: userId, + }, + relations: { + ruleParticipate: { + rule: { + invitations: { + member: { + profileImage: true, + }, }, }, }, }, - }, - }); - console.log('현재 로그인한 사용자 : ', userEntity.id); + }); + console.log('현재 로그인한 사용자 : ', user.id); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); - try { - const invitationEntities = userEntity.ruleParticipate; + const invitationEntities = user.ruleParticipate; // 수정한 날짜 updated 내림차순으로 정렬 invitationEntities.sort((a, b) => new Date(b.rule.updated).getTime() - new Date(a.rule.updated).getTime()); console.log('invitationEntities 출력 : ', invitationEntities); if (!!invitationEntities) { - const ruleMains = await Promise.all(invitationEntities.map(async (invitation : RuleInvitationEntity) : Promise => { + const ruleMains = await Promise.all(invitationEntities.map(async (invitation: RuleInvitationEntity): Promise => { console.log('참여하는 규칙 ID : ', invitation.rule.id); - const ruleMain : RuleMainEntity = invitation.rule; - const ruleListDto : GetRuleListDto = new GetRuleListDto; + const ruleMain: RuleMainEntity = invitation.rule; + const ruleListDto: GetRuleListDto = new GetRuleListDto; console.log('ruleMain.id : ', ruleMain.id); @@ -299,15 +346,15 @@ export class RuleService { return ruleMains; } } catch (e) { - console.error(e); console.log('참여하는 여행 규칙이 없습니다'); + throw new Error(e.message); } } - async getMemberPairs(ruleMain: RuleMainEntity) : Promise { + async getMemberPairs(ruleMain: RuleMainEntity): Promise { const invitations: RuleInvitationEntity[] = ruleMain.invitations; - const result : MemberPairDto[] = await Promise.all(invitations.map(async (invitation) : Promise => { + const result: MemberPairDto[] = await Promise.all(invitations.map(async (invitation): Promise => { const memberPair = new MemberPairDto; const user: UserEntity = invitation.member; @@ -317,188 +364,196 @@ export class RuleService { // 사용자 프로필 이미지 const image = user.profileImage; - if(image == null) memberPair.image = null; + if (image == null) memberPair.image = null; else { const userImageKey = image.imageKey; memberPair.image = await this.s3Service.getImageUrl(userImageKey); } - return memberPair; - })); + return memberPair; + })); return result; } - // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 + // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 - 무한 스크롤 // 여행 규칙 생성 / 여행 규칙 수정 분리 // case1. 여행 규칙 생성 async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { - let cursorId: number = 0; - console.log('cursorId : ', cursorPageOptionsDto); - // (1) 처음 요청인 경우 cursorId 설정 - if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.find({ - order: { - id: 'DESC' // 가장 최근에 가입한 유저 - }, - take: 1 - }); - const cursorId = newUser[0].id + 1; - - console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); + try { + let cursorId: number = 0; + + console.log('cursorId : ', cursorPageOptionsDto); + // (1) 처음 요청인 경우 cursorId 설정 + if (cursorPageOptionsDto.cursorId == 0) { + const newUser = await UserEntity.find({ + order: { + id: 'DESC' // 가장 최근에 가입한 유저 + }, + take: 1 + }); + const cursorId = newUser[0].id + 1; + + console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); + console.log('cursor: ', cursorId); + } else { + cursorId = cursorPageOptionsDto.cursorId; + console.log('cursorPageOptionsDto.cursorId != 0 로 인식') + } console.log('cursor: ', cursorId); - } - else { - cursorId = cursorPageOptionsDto.cursorId; - console.log('cursorPageOptionsDto.cursorId != 0 로 인식') - } - console.log('cursor: ', cursorId); - // (2) 데이터 조회 - // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - // { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 + // (2) 데이터 조회 + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + // { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 - console.log('검색 값: ', searchTerm); + console.log('검색 값: ', searchTerm); - const [resultUsers, total] = await UserEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: [ - { - id: cursorId ? LessThan(cursorId) : null, - name: Like(`%${searchTerm}%`), + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: [ + { + id: cursorId ? LessThan(cursorId) : null, + name: Like(`%${searchTerm}%`), + }, + { + id: cursorId ? LessThan(cursorId) : null, + nickname: Like(`%${searchTerm}%`), + } + ], + relations: {profileImage: true, ruleParticipate: {rule: true}}, + order: { + id: "DESC" as any, }, - { - id: cursorId ? LessThan(cursorId) : null, - nickname: Like(`%${searchTerm}%`), - } - ], - relations: {profileImage : true, ruleParticipate: {rule: true}}, - order: { - id: "DESC" as any, - }, - }); + }); - const searchResult = await Promise.all(resultUsers.map(async (user) => { - const dtoAtCreate: GetSearchMemberAtCreateDto = new GetSearchMemberAtCreateDto(); + const searchResult = await Promise.all(resultUsers.map(async (user) => { + const dtoAtCreate: GetSearchMemberAtCreateDto = new GetSearchMemberAtCreateDto(); - dtoAtCreate.id = user.id; - dtoAtCreate.name = user.name; - dtoAtCreate.email = user.email; - dtoAtCreate.introduction = user.introduction; + dtoAtCreate.id = user.id; + dtoAtCreate.name = user.name; + dtoAtCreate.email = user.email; + dtoAtCreate.introduction = user.introduction; - // 사용자 프로필 이미지 - const image = user.profileImage; - if(image == null) dtoAtCreate.image = null; - else{ - const userImageKey = image.imageKey; - dtoAtCreate.image = await this.s3Service.getImageUrl(userImageKey); - } - return dtoAtCreate; - })); + // 사용자 프로필 이미지 + const image = user.profileImage; + if (image == null) dtoAtCreate.image = null; + else { + const userImageKey = image.imageKey; + dtoAtCreate.image = await this.s3Service.getImageUrl(userImageKey); + } + return dtoAtCreate; + })); - console.log('searchResult : ',searchResult); + console.log('searchResult : ', searchResult); - // (3) 페이징 및 정렬 기준 설정 - let hasNextData = true; - let cursor: number; + // (3) 페이징 및 정렬 기준 설정 + let hasNextData = true; + let cursor: number; - const takePerScroll = cursorPageOptionsDto.take; - console.log('takePerScroll : ',takePerScroll); - const isLastScroll = total <= takePerScroll; - console.log('isLastScroll : ', isLastScroll); - console.log('total : ', total) - const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + const takePerScroll = cursorPageOptionsDto.take; + console.log('takePerScroll : ', takePerScroll); + const isLastScroll = total <= takePerScroll; + console.log('isLastScroll : ', isLastScroll); + console.log('total : ', total) + const lastDataPerScroll = resultUsers[resultUsers.length - 1]; - if (isLastScroll) { - hasNextData = false; - cursor = null; - } else { - cursor = lastDataPerScroll.id; - } + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } - const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); + const cursorPageMetaDto = new CursorPageMetaDto({cursorPageOptionsDto, total, hasNextData, cursor}); - return new CursorPageDto(searchResult, cursorPageMetaDto); + return new CursorPageDto(searchResult, cursorPageMetaDto); + } catch (e) { + throw new Error(e.message()); + } } // [6-2] case2. 여행 규칙 수정 async getSearchMemberAtUpdate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { - let cursorId: number = 0; - // (1) 처음 요청인 경우 cursorId 설정 - if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.find({ + try { + let cursorId: number = 0; + + // (1) 처음 요청인 경우 cursorId 설정 + if (cursorPageOptionsDto.cursorId == 0) { + const newUser = await UserEntity.find({ + order: { + id: 'DESC' // 가장 최근에 가입한 유저 + }, + take: 1 + }); + const cursorId = newUser[0].id + 1; + + console.log('cursor: ', cursorId); + } else { + cursorId = cursorPageOptionsDto.cursorId; + } + + // (2) 데이터 조회 + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + console.log('검색 값: ', searchTerm); + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: [ + {id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`)}, + {id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)}, + ], + relations: {profileImage: true, ruleParticipate: {rule: true}}, order: { - id: 'DESC' // 가장 최근에 가입한 유저 + id: "DESC" as any, }, - take: 1 }); - const cursorId = newUser[0].id + 1; - console.log('cursor: ', cursorId); - } - else { - cursorId = cursorPageOptionsDto.cursorId; - } + const searchResult = await Promise.all(resultUsers.map(async (user) => { + const dto: GetSearchMemberDto = new GetSearchMemberDto(); - // (2) 데이터 조회 - // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - console.log('검색 값: ', searchTerm); - const [resultUsers, total] = await UserEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: [ - { id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`) }, - { id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)}, - ], - relations: {profileImage : true, ruleParticipate: {rule: true}}, - order: { - id: "DESC" as any, - }, - }); + dto.id = user.id; + dto.name = user.name; + dto.email = user.email; + dto.introduction = user.introduction; - const searchResult = await Promise.all(resultUsers.map(async (user) => { - const dto: GetSearchMemberDto = new GetSearchMemberDto(); + // 이미 여행 규칙에 참여하는 멤버인지 여부 + dto.isInvited = await this.userService.checkAlreadyMember(user.id, ruleId); - dto.id = user.id; - dto.name = user.name; - dto.email = user.email; - dto.introduction = user.introduction; + // 사용자 프로필 이미지 + const image = user.profileImage; + if (image == null) dto.image = null; + else { + const userImageKey = image.imageKey; + dto.image = await this.s3Service.getImageUrl(userImageKey); + } + return dto; + })); - // 이미 여행 규칙에 참여하는 멤버인지 여부 - dto.isInvited = await this.userService.checkAlreadyMember(user.id, ruleId); + console.log('searchResult : ', searchResult); - // 사용자 프로필 이미지 - const image = user.profileImage; - if(image == null) dto.image = null; - else{ - const userImageKey = image.imageKey; - dto.image = await this.s3Service.getImageUrl(userImageKey); - } - return dto; - })); + // (3) 페이징 및 정렬 기준 설정 + let hasNextData = true; + let cursor: number; - console.log('searchResult : ',searchResult); + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = searchResult[searchResult.length - 1]; - // (3) 페이징 및 정렬 기준 설정 - let hasNextData = true; - let cursor: number; + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } - const takePerScroll = cursorPageOptionsDto.take; - const isLastScroll = total <= takePerScroll; - const lastDataPerScroll = searchResult[searchResult.length - 1]; + const cursorPageMetaDto = new CursorPageMetaDto({cursorPageOptionsDto, total, hasNextData, cursor}); - if (isLastScroll) { - hasNextData = false; - cursor = null; - } else { - cursor = lastDataPerScroll.id; + return new CursorPageDto(searchResult, cursorPageMetaDto); + } catch (e) { + throw new Error(e.message); } - - const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); - - return new CursorPageDto(searchResult, cursorPageMetaDto); } // [7] 여행 규칙 수정 @@ -507,14 +562,14 @@ export class RuleService { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id : userId}, + where: {id: userId}, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const rule = await RuleMainEntity.findOne({ - where: {id : ruleId }, - relations: { rules: true, invitations: {member : true} } + where: {id: ruleId}, + relations: {rules: true, invitations: {member: true}} }) if (!rule) throw new Error('규칙을 찾을 수 없습니다'); @@ -538,10 +593,10 @@ export class RuleService { updateSubsList.sort((a, b) => a.ruleNumber - b.ruleNumber); // case1) 규칙 삭제 - for(const sub of subs) { - let isDeleteSub : boolean = true; - for(const updateSub of updateSubsList) { - if(sub.id == updateSub.id) { + for (const sub of subs) { + let isDeleteSub: boolean = true; + for (const updateSub of updateSubsList) { + if (sub.id == updateSub.id) { isDeleteSub = false; break; } @@ -580,27 +635,27 @@ export class RuleService { // (2) [여행 규칙 멤버 수정] // 기존 멤버 초대 리스트 const oldInvitations = await RuleInvitationEntity.find({ - where: {rule: {id : ruleId}}, + where: {rule: {id: ruleId}}, relations: {member: true} }) // 수정된 멤버 ID 리스트 const updateMemberIds = updateRuleDto.membersId; // case1) 멤버 삭제 - for(const invitation of oldInvitations) { + for (const invitation of oldInvitations) { const member = invitation.member; - let isDeleteMember : boolean = true; + let isDeleteMember: boolean = true; // (예외 상황) 현재 로그인한 사용자 if (member.id == userId) break; - for(const updateMemberId of updateMemberIds) { - if(member.id == updateMemberId) { + for (const updateMemberId of updateMemberIds) { + if (member.id == updateMemberId) { isDeleteMember = false; break; } } - if(isDeleteMember) { + if (isDeleteMember) { await invitation.softRemove(); console.log('삭제하는 멤버 ID : ', invitation.id); } @@ -610,17 +665,17 @@ export class RuleService { for (const updateMemberId of updateMemberIds) { const member = await UserEntity.findExistUser(updateMemberId); - let isPostMember : boolean = true; + let isPostMember: boolean = true; - for(const oldInvitation of oldInvitations) { + for (const oldInvitation of oldInvitations) { const oldMember = oldInvitation.member; - if(oldMember.id == updateMemberId) { + if (oldMember.id == updateMemberId) { isPostMember = false; break; } } - if(isPostMember) { + if (isPostMember) { const newInvitation = new RuleInvitationEntity(); newInvitation.member = await UserEntity.findExistUser(updateMemberId); @@ -639,18 +694,4 @@ export class RuleService { throw new Error(e.message); } } - - - // [member] 초대 받은 멤버 리스트 생성 - async getInvitationList(ruleId: number) { - try { - const invitationEntity = await RuleInvitationEntity.find({ - where: { id : ruleId }, - relations: ['invited'], - }); - return invitationEntity; - } catch (error) { - console.log('Error on getInvitationList: ' + error); - } - } -} +} \ No newline at end of file From 5e7b05f8b313cc942db9fb011c9f75da32be36b8 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Tue, 13 Feb 2024 23:00:59 +0900 Subject: [PATCH 257/316] =?UTF-8?q?fix=20:=20sort=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit try-catch 수정 --- src/rule/rule.service.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index f2f4cc5..d2cbfec 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -82,7 +82,6 @@ export class RuleService { // [2] 여행 규칙 상세 페이지 조회 (게시글) async getDetail(userId: number, ruleId: number): Promise { const dto = new DetailRuleDto(); - const sortedDto = new DetailRuleDto(); const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ @@ -111,11 +110,11 @@ export class RuleService { if(!!invitation) { // -1) 제목 - sortedDto.id = ruleId; - sortedDto.mainTitle = main.mainTitle; + dto.id = ruleId; + dto.mainTitle = main.mainTitle; // -2) 규칙 - dto.rulePairs = await Promise.all(subs.map(async (sub): Promise => { + const rulePairs = await Promise.all(subs.map(async (sub): Promise => { const rulePair = new RulePairDto(); rulePair.id = sub.id; rulePair.ruleTitle = sub.ruleTitle; @@ -123,10 +122,13 @@ export class RuleService { return rulePair; })); - sortedDto.rulePairs = dto.rulePairs.sort((a, b) => a.id - b.id); + console.log('Before sorting : ', rulePairs); + rulePairs.sort((a, b) => a.id - b.id); + console.log('After sorting : ', rulePairs); + dto.rulePairs = rulePairs; // -3) 멤버 정보 - dto.detailMembers = await Promise.all(invitations.map(async (invitation): Promise => { + const detailMembers = await Promise.all(invitations.map(async (invitation): Promise => { const detailMember = new DetailMemberDto; const memberEntity = invitation.member; detailMember.id = memberEntity.id; @@ -141,9 +143,10 @@ export class RuleService { } return detailMember; })); - sortedDto.detailMembers = dto.detailMembers.sort((a, b) => a.id - b.id); + detailMembers.sort((a, b) => a.id - b.id); + dto.detailMembers = detailMembers; - return sortedDto; + return dto; } else { throw new Error('여행 규칙에 참여하는 사용자가 아닙니다'); } From cc10b80ad586de3fc04b5051b1325343a97ef24b Mon Sep 17 00:00:00 2001 From: kaaang Date: Wed, 14 Feb 2024 00:32:32 +0900 Subject: [PATCH 258/316] =?UTF-8?q?fix:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=EB=A1=9C=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 32 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 2c0600c..8e859e4 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -6,7 +6,7 @@ import { Injectable, NotFoundException, } from '@nestjs/common'; -import { CreateSignatureDto } from './dto/create-signature.dto'; +import { CreateSignatureDto } from './dto/signature/create-signature.dto'; import { SignatureEntity } from './domain/signature.entity'; import { HomeSignatureDto } from './dto/signature/home-signature.dto'; import { UserEntity } from 'src/user/user.entity'; @@ -17,10 +17,10 @@ import { AuthorSignatureDto } from './dto/signature/author-signature.dto'; import { HeaderSignatureDto } from './dto/signature/header-signature.dto'; import { UserService } from '../user/user.service'; import { SignatureLikeEntity } from './domain/signature.like.entity'; -import { GetLikeListDto } from './dto/get-like-list.dto'; -import { LikeProfileDto } from './dto/like-profile.dto'; +import { GetLikeListDto } from './dto/like/get-like-list.dto'; +import { LikeProfileDto } from './dto/like/like-profile.dto'; import { S3UtilService } from '../utils/S3.service'; -import { ResponsePageSignatureDto } from './dto/response-page-signature.dto'; +import { ResponsePageSignatureDto } from './dto/signature/response-page-signature.dto'; import { NotificationEntity } from '../notification/notification.entity'; import { NotificationService } from '../notification/notification.service'; @@ -353,21 +353,29 @@ export class SignatureService { originalPage.location = patchedPage.location; // base64로 들어온 이미지면 디코딩해서 새롭게 저장 / 아니면 그대로 두기 - if(patchedPage.image.startsWith("https://hereyou-cdn.kaaang.dev/signature/")){ + if ( + patchedPage.image.startsWith( + 'https://hereyou-cdn.kaaang.dev/signature/', + ) + ) { // 이미지 그대로 들어왔다면 이미지를 수정할 필요 없음 - console.log(patchedPage._id,": original Image - 수정할 필요 없음"); - - } - else{ + console.log(patchedPage._id, ': original Image - 수정할 필요 없음'); + } else { // 새로운 이미지가 인코딩돼서 들어왔다면 해당 이미지를 새로 저장해야 - console.log(patchedPage._id,": patched Image - 이미지키 수정 진행"); + console.log( + patchedPage._id, + ': patched Image - 이미지키 수정 진행', + ); // 랜덤 이미지 키 생성 - const key = `signature/${this.s3Service.generateRandomImageKey('signaturePage.png')}`; + const key = `signature/${this.s3Service.generateRandomImageKey( + 'signaturePage.png', + )}`; // Base64 이미지 업로드 const uploadedImage = await this.s3Service.putObjectFromBase64( - key, patchedPage.image + key, + patchedPage.image, ); // 이미지 키 저장 From caf19c6ed14d958b614364b9888aedb42381cd32 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 14 Feb 2024 02:57:10 +0900 Subject: [PATCH 259/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=82=AD=EC=A0=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.controller.ts | 39 +++++++- src/signature/signature.comment.service.ts | 91 +++++++++++++++++-- 2 files changed, 122 insertions(+), 8 deletions(-) diff --git a/src/signature/signature.comment.controller.ts b/src/signature/signature.comment.controller.ts index d7a07ad..651d5ca 100644 --- a/src/signature/signature.comment.controller.ts +++ b/src/signature/signature.comment.controller.ts @@ -2,7 +2,7 @@ import { Body, - Controller, + Controller, Delete, ForbiddenException, Get, NotFoundException, @@ -144,7 +144,44 @@ export class SignatureCommentController{ errorMessage, null ); + } + } + + + @Delete('/:commentId') + @UseGuards(UserGuard) + async deleteSignatureComment( // 시그니처 수정하기 + @Param('signatureId') signatureId: number, + @Param('commentId') commentId: number, + @Req() req: Request, + ){ + try{ + const result = await this.signatureCommentService.deleteSignatureComment(req.user.id,signatureId,commentId); + + return new ResponseDto( + ResponseCode.COMMENT_DELETE_SUCCESS, + true, + "시그니처 댓글 삭제하기 성공", + result + ); + } + catch(error){ + console.log("Err on DeleteSigComment: "+ error); + let errorMessage = ""; + + if(error instanceof NotFoundException) errorMessage = error.message; + else if(error instanceof ForbiddenException) errorMessage = error.message; + else errorMessage = "시그니처 댓글 삭제하기 실패"; + + return new ResponseDto( + ResponseCode.COMMENT_DELETE_FAIL, + false, + errorMessage, + null + ); } } + + } \ No newline at end of file diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 54686f4..0aee5eb 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -87,7 +87,10 @@ export class SignatureCommentService{ }, relations: { user: { profileImage: true }, - parentComment: true + parentComment: true, + signature:{ + user: true, + } }, order: { parentComment: { id: "ASC" as any,}, @@ -103,8 +106,8 @@ export class SignatureCommentService{ writerProfile._id = comment.user.id; writerProfile.name = comment.user.nickname; - // 로그인한 사용자가 댓글 작성자인지 확인 - if( userId == comment.user.id ) writerProfile.is_writer = true; + // 로그인한 사용자가 댓글 작성자 혹은 시그니처 작성자인지 확인 + if( userId == comment.user.id || userId == comment.signature.user.id ) writerProfile.is_writer = true; else writerProfile.is_writer = false; // 작성자 프로필 이미지 @@ -160,19 +163,33 @@ export class SignatureCommentService{ patchedComment: CreateCommentDto) { // 시그니처 유효한지 확인 - const signature = await SignatureEntity.findOne({ where:{ id: signatureId }}); + const signature = await SignatureEntity.findOne({ + where:{ id: signatureId }, + relations: { user: true } + }); if(!signature) throw new NotFoundException('존재하지 않는 시그니처입니다'); // 댓글 데이터 유효한지 확인 const comment = await SignatureCommentEntity.findOne({ where:{ id: commentId }, - relations: ['user'] + relations: { user: true } }, ); if(!comment) throw new NotFoundException('존재하지 않는 댓글입니다'); - // 댓글 작성자가 로그인한 사용자 본인이 맞는지 확인 - if(comment.user.id != userId ) throw new ForbiddenException('댓글 수정 권한이 없습니다'); + + let forbiddenUser = true; + // 댓글 작성자가 로그인한 사용자 본인 혹은 시그니처 작성자가 맞는지 확인 + if(signature.user){ // 시그니처 작성자가 존재한다면 시그니처 작성자와 로그인한 사용자가 일치하는지 확인 + if( signature.user.id == userId ) forbiddenUser = false; + } + + if(comment.user.id){ // 댓글 작성자가 존재한다면 댓글 작성자와 로그인한 사용자가 일치하는지 확인 + if(comment.user.id == userId ) forbiddenUser = false; + } + + if(forbiddenUser) throw new ForbiddenException('댓글 수정 권한이 없습니다'); + // 댓글 수정하기 comment.content = patchedComment.content; @@ -180,4 +197,64 @@ export class SignatureCommentService{ return comment.id; } + + async deleteSignatureComment(userId: number, signatureId: number, commentId: number) { + try { + // 시그니처 유효한지 확인 + const signature = await SignatureEntity.findOne({ + where: { id: signatureId }, + relations: { user: true } + }); + if (!signature) throw new NotFoundException('존재하지 않는 시그니처입니다'); + + // 댓글 데이터 유효한지 확인 + const comment = await SignatureCommentEntity.findOne({ + where: { id: commentId }, + relations: ['user', 'parentComment', 'signature'] + }, + ); + if (!comment) throw new NotFoundException('존재하지 않는 댓글입니다'); + + + let forbiddenUser = true; + // 댓글 작성자가 로그인한 사용자 본인 혹은 시그니처 작성자가 맞는지 확인 + if(signature.user){ // 시그니처 작성자가 존재한다면 시그니처 작성자와 로그인한 사용자가 일치하는지 확인 + if( signature.user.id == userId ) forbiddenUser = false; + } + + if(comment.user.id){ // 댓글 작성자가 존재한다면 댓글 작성자와 로그인한 사용자가 일치하는지 확인 + if(comment.user.id == userId ) forbiddenUser = false; + } + + if(forbiddenUser) throw new ForbiddenException('댓글 삭제 권한이 없습니다'); + + + // 해당 댓글이 부모 댓글인 경우 자식 댓글 모두 삭제 + if (commentId == comment.parentComment.id) { + + // 자식 댓글 모두 찾아오기 + const replyComments: SignatureCommentEntity[] = await SignatureCommentEntity.find({ + where: { parentComment: { id: commentId } } + }); + + // 자식 댓글 모두 삭제 + for (const reply of replyComments) { + await reply.softRemove(); + } + + // 자식 모두 삭제했으면 부모 댓글 삭제 + await comment.softRemove(); + + } + else{ // 자식 댓글 없는 경우 본인만 삭제 + await comment.softRemove(); + } + + return commentId; + + } catch (error) { + console.log(error); + throw error; + } + } } \ No newline at end of file From e71987ebac135ab0eb95bcd866c67a8a49be967c Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 14 Feb 2024 03:04:23 +0900 Subject: [PATCH 260/316] =?UTF-8?q?Refactor:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B6=8C=ED=95=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 0aee5eb..c9aa7c6 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -126,8 +126,14 @@ export class SignatureCommentService{ getCommentDto.date = comment.updated; // 댓글 수정 여부 구하기 - if(comment.created.getTime() === comment.updated.getTime()) getCommentDto.is_edited = false; - else getCommentDto.is_edited = true; + const createdTime = comment.created.getTime(); + const updatedTime = comment.updated.getTime(); + + if (Math.abs(createdTime - updatedTime) <= 2000) { // 두 시간 차가 2초 이하면 수정 안함 + getCommentDto.is_edited = false; + } else { + getCommentDto.is_edited = true; + } return getCommentDto; From e1cbbb8d91db917b360ee8b496f554b1f0e51254 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 14 Feb 2024 03:31:23 +0900 Subject: [PATCH 261/316] =?UTF-8?q?fix:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=88=98=EC=A0=95=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=EC=9B=90=EC=83=81=EB=B3=B5=EA=B7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 54686f4..167f310 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -103,7 +103,9 @@ export class SignatureCommentService{ writerProfile._id = comment.user.id; writerProfile.name = comment.user.nickname; - // 로그인한 사용자가 댓글 작성자인지 확인 + + // 로그인한 사용자가 댓글 작성자(혹은 시그니처 작성자-> 보류)인지 확인 + //if( userId == comment.user.id || userId == comment.signature.user.id ) writerProfile.is_writer = true; if( userId == comment.user.id ) writerProfile.is_writer = true; else writerProfile.is_writer = false; From 31625230e55c0333b7bfd1522741737c6782631d Mon Sep 17 00:00:00 2001 From: kaaang Date: Wed, 14 Feb 2024 10:59:48 +0900 Subject: [PATCH 262/316] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=A1=B0=ED=9A=8C=20API=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.controller.ts | 17 ++++++++++++++++- src/user/user.service.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index e74979e..077a424 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,4 +1,13 @@ -import { Body, Controller, Delete, Get, Post, Query, Req, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + Post, + Query, + Req, + UseGuards, +} from '@nestjs/common'; import { UserService } from './user.service'; import { IUserProfile } from './user.dto'; import { UserGuard } from './user.guard'; @@ -28,6 +37,12 @@ export class UserController { return this.userService.updateUserProfile(req.user.id, body); } + @Get('/profile') + @UseGuards(UserGuard) + GetUserProfile(@Req() req: Request) { + return this.userService.GetUserProfile(req.user.id); + } + @Post('/profile/nickname') @UseGuards(UserGuard) UpdateNickname(@Body('nickname') nickname: string, @Req() req: Request) { diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 7a29d92..42d3658 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -498,4 +498,39 @@ export class UserService { }, ); } + + async GetUserProfile(userId: number) { + const user = await UserEntity.findOne({ + where: { + id: userId, + }, + relations: { + profileImage: true, + follower: true, + following: true, + }, + }); + + const profileImage = user.profileImage + ? await this.s3Service.getImageUrl(user.profileImage.imageKey) + : null; + + return new ResponseDto( + ResponseCode.GET_USER_PROFILE_SUCCESS, + true, + '유저 프로필 조회 성공', + { + user: { + id: user.id, + email: user.email, + nickname: user.nickname, + introduction: user.introduction, + visibility: user.visibility, + profileImage: profileImage, + followers: user.follower.length, + followings: user.following.length, + }, + }, + ); + } } From 499e0d828ca7705f830518f137b7773284ea9a63 Mon Sep 17 00:00:00 2001 From: kaaang Date: Wed, 14 Feb 2024 11:15:03 +0900 Subject: [PATCH 263/316] =?UTF-8?q?feat:=20=EA=B7=9C=EC=B9=99=EC=97=90=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EC=9E=91=EC=84=B1=EC=8B=9C=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=EC=9D=98=20=EB=AA=A8=EB=93=A0=20=EC=B0=B8=EC=97=AC?= =?UTF-8?q?=EC=9E=90=EB=93=A4=EC=97=90=EA=B2=8C=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=EC=9D=84=20=EC=A0=84=EC=86=A1=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 2f1e288..139f6b7 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -3,6 +3,8 @@ import { CreateCommentDto } from './dto/create-comment.dto'; import { CommentEntity } from './domain/comment.entity'; import {RuleMainEntity} from "../rule/domain/rule.main.entity"; import {UserEntity} from "../user/user.entity"; +import { NotificationEntity } from '../notification/notification.entity'; +import { NotificationService } from '../notification/notification.service'; @Injectable() export class CommentService { @@ -13,7 +15,7 @@ export class CommentService { const comment = new CommentEntity(); const user = await UserEntity.findOneOrFail({ where: { id: userId } }); - const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId } }); + const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId }, relations: { invitations: { member: true } } }); if(!user || !rule){ throw new Error('Data not found'); @@ -25,6 +27,22 @@ export class CommentService { comment.rule = rule; comment.content = dto.content; await comment.save(); + + // 댓글 알림 + for (const invitation of rule.invitations) { + const receiver = invitation.member; + if (receiver.id === user.id) { + continue; + } + + const notification = new NotificationEntity(); + notification.notificationReceiver = invitation.member; + notification.notificationType = 'COMMENT'; + notification.notificationContent = + NotificationService.createNotificationContent('LIKE', user.nickname); + notification.notificationItemId = rule.id; + await notification.save(); + } } return comment.id; } From 4fec58ec3523c0b048d94ba86b8042b053d0baab Mon Sep 17 00:00:00 2001 From: kaaang Date: Wed, 14 Feb 2024 15:34:49 +0900 Subject: [PATCH 264/316] =?UTF-8?q?feat:=20Google=20oAuth=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 86cb9fa..607cb68 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -71,6 +71,19 @@ export class UserService { return await response.json(); } + private async getGoogleInformation(accessToken: string) { + const response = await fetch( + 'https://www.googleapis.com/oauth2/v3/userinfo', + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); + + return await response.json(); + } + async Login(email: string, password: string) { console.log(email, password); const user = await UserEntity.findOne({ @@ -156,7 +169,52 @@ export class UserService { register_required: isNewUser, }; } else if (type === 'GOOGLE') { - // Todo + // 사용자 정보 받기 + const googleInfo = await this.getKakaoInformation(code); + + const userId = googleInfo.sub; + const userEmail = googleInfo.email; + + // 사용자 정보로 DB 조회 + let userEntity = await UserEntity.findOne({ + where: { + oauthType: 'GOOGLE', + oauthToken: userId.toString(), + }, + relations: { + profileImage: true, + }, + }); + + let isNewUser = false; + if (!userEntity) { + isNewUser = true; + userEntity = new UserEntity(); + userEntity.oauthType = 'GOOGLE'; + userEntity.oauthToken = userId.toString(); + + userEntity.introduction = ''; + userEntity.visibility = 'PUBLIC'; + userEntity.name = ''; + userEntity.age = 0; + } + + userEntity.email = userEmail; + userEntity.password = ''; + userEntity.nickname = googleInfo.name; + + // Todo: 프로필 이미지 저장하기 + await userEntity.save(); + + return { + status: 200, + success: true, + message: '로그인 성공', + token: this._generateToken({ + id: userEntity.id, + }), + register_required: isNewUser, + }; } else { return new ResponseDto( ResponseCode.INTERNAL_SERVEr_ERROR, From 0df958f4557ab14350899447abf81936cb5594ba Mon Sep 17 00:00:00 2001 From: kaaang Date: Wed, 14 Feb 2024 15:38:08 +0900 Subject: [PATCH 265/316] =?UTF-8?q?fix:=20Google=20oAuth=EA=B0=80=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 607cb68..04c8408 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -170,7 +170,7 @@ export class UserService { }; } else if (type === 'GOOGLE') { // 사용자 정보 받기 - const googleInfo = await this.getKakaoInformation(code); + const googleInfo = await this.getGoogleInformation(code); const userId = googleInfo.sub; const userEmail = googleInfo.email; From d6e927385f42ae19be06154cfa276056fae30c81 Mon Sep 17 00:00:00 2001 From: kaaang Date: Wed, 14 Feb 2024 15:56:57 +0900 Subject: [PATCH 266/316] =?UTF-8?q?feat:=20SNS=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=8B=9C=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EB=B6=88=EB=9F=AC=EC=98=AC=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 39 +++++++++++++++++++++++ package.json | 2 ++ src/user/user.entity.ts | 4 ++- src/user/user.module.ts | 2 ++ src/user/user.service.ts | 67 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 111 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f6553b..e6e8ec6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", + "md5": "^2.3.0", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "mysql2": "^3.9.0", @@ -41,6 +42,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsonwebtoken": "^9.0.5", + "@types/md5": "^2.3.5", "@types/multer-s3": "^3.0.3", "@types/node": "^20.3.1", "@types/node-fetch": "^2.6.11", @@ -3793,6 +3795,12 @@ "@types/node": "*" } }, + "node_modules/@types/md5": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.5.tgz", + "integrity": "sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==", + "dev": true + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -5059,6 +5067,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -5538,6 +5554,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/dayjs": { "version": "1.11.10", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", @@ -7215,6 +7239,11 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -8426,6 +8455,16 @@ "tmpl": "1.0.5" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/package.json b/package.json index 10da63d..13dcd25 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "jsonwebtoken": "^9.0.2", + "md5": "^2.3.0", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "mysql2": "^3.9.0", @@ -52,6 +53,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsonwebtoken": "^9.0.5", + "@types/md5": "^2.3.5", "@types/multer-s3": "^3.0.3", "@types/node": "^20.3.1", "@types/node-fetch": "^2.6.11", diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 3fc3fe0..b26a057 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -54,7 +54,9 @@ export class UserEntity extends BaseEntity { @Column() oauthToken: string; - @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user) + @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user, { + cascade: true, + }) profileImage: UserProfileImageEntity; @OneToMany(() => UserFollowingEntity, (following) => following.user) diff --git a/src/user/user.module.ts b/src/user/user.module.ts index e21d51f..4c741a9 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { UserService } from './user.service'; import { UserController } from './user.controller'; +import { S3Module } from '../utils/S3.module'; @Module({ + imports: [S3Module], controllers: [UserController], providers: [UserService], }) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 04c8408..eeeb908 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -9,11 +9,15 @@ import { ResponseDto } from '../response/response.dto'; import { ResponseCode } from '../response/response-code.enum'; import { UserFollowingEntity } from './user.following.entity'; import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; +import * as md5 from 'md5'; +import { S3UtilService } from '../utils/S3.service'; @Injectable() export class UserService { private readonly logger: Logger = new Logger(UserService.name); + constructor(private s3Service: S3UtilService) {} + private _hashPassword(password: string): string { return bcrypt.hashSync(password, 10); } @@ -84,6 +88,12 @@ export class UserService { return await response.json(); } + private async downloadImage(url: string) { + const response = await fetch(url); + + return await response.buffer(); + } + async Login(email: string, password: string) { console.log(email, password); const user = await UserEntity.findOne({ @@ -156,7 +166,35 @@ export class UserService { userEntity.password = ''; userEntity.nickname = userProfile?.nickname; - // Todo: 프로필 이미지 저장하기 + if (userProfile?.profile_image_url) { + const urlHash = md5(userProfile.profile_image_url); + const extension = userProfile.profile_image_url.split('.').pop(); + const profileFilename = `profile/kakao_${urlHash}.${extension}`; + + if ( + !userEntity.profileImage || + userEntity.profileImage.imageKey !== profileFilename + ) { + const profileImageEntity = new UserProfileImageEntity(); + profileImageEntity.imageKey = urlHash; + + const profileImageFile = await this.downloadImage( + userProfile.profile_image_url, + ); + await this.s3Service.putObject(profileFilename, profileImageFile); + + profileImageEntity.imageKey = profileFilename; + if (userEntity.profileImage) { + userEntity.profileImage = null; + await userEntity.save(); + } + + await profileImageEntity.save(); + + userEntity.profileImage = profileImageEntity; + } + } + await userEntity.save(); return { @@ -203,7 +241,32 @@ export class UserService { userEntity.password = ''; userEntity.nickname = googleInfo.name; - // Todo: 프로필 이미지 저장하기 + if (googleInfo.picture) { + const urlHash = md5(googleInfo.picture); + const profileFilename = `profile/google_${urlHash}`; + + if ( + !userEntity.profileImage || + userEntity.profileImage.imageKey !== profileFilename + ) { + const profileImageEntity = new UserProfileImageEntity(); + profileImageEntity.imageKey = urlHash; + + const profileImageFile = await this.downloadImage(googleInfo.picture); + await this.s3Service.putObject(profileFilename, profileImageFile); + + profileImageEntity.imageKey = profileFilename; + if (userEntity.profileImage) { + userEntity.profileImage = null; + await userEntity.save(); + } + + await profileImageEntity.save(); + + userEntity.profileImage = profileImageEntity; + } + } + await userEntity.save(); return { From 77b4e1cb6da52571c25e61f367e8268162057203 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:28:02 +0900 Subject: [PATCH 267/316] =?UTF-8?q?Refactor:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=82=AD=EC=A0=9C=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/dto/comment/get-signature-comment.dto.ts | 5 +++-- src/signature/signature.comment.service.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/signature/dto/comment/get-signature-comment.dto.ts b/src/signature/dto/comment/get-signature-comment.dto.ts index db5a53c..af862d3 100644 --- a/src/signature/dto/comment/get-signature-comment.dto.ts +++ b/src/signature/dto/comment/get-signature-comment.dto.ts @@ -7,6 +7,7 @@ export class GetSignatureCommentDto{ parentId: number; content: string; writer: GetCommentWriterDto; - date: Date; // 생성 | 수정일 - is_edited: boolean; // 댓글 수정 여부 + date: Date; // 생성 | 수정일 + is_edited: boolean; // 댓글 수정 여부 + can_delete: boolean; // 로그인한 사용자의 댓글 삭제 권한 여부: 시그니처 작성자면 true } \ No newline at end of file diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index a8be82f..45b0202 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -137,6 +137,15 @@ export class SignatureCommentService{ getCommentDto.is_edited = true; } + // 로그인한 사용자가 시그니처 작성하면 can_delete = true + let can_delete = false; + if(comment.signature.user){ // 시그니처 작성자가 존재할 경우 + if(comment.signature.user.id == userId){ // 로그인한 사용자가 시그니처 작성자일 경우 댓글 삭제 가능 + can_delete = true; + } + } + getCommentDto.can_delete = can_delete; + return getCommentDto; })); From 09828606e14b9d2baf7689e51de8c052bec2b446 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 14 Feb 2024 17:23:17 +0900 Subject: [PATCH 268/316] =?UTF-8?q?fix=20:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=ED=91=9C=EC=8B=9C=20name=20->=20nickname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 - src/member/member.controller.ts | 65 ---------------------- src/member/member.module.ts | 12 ---- src/member/member.service.ts | 97 --------------------------------- src/rule/rule.service.ts | 12 ++-- 5 files changed, 6 insertions(+), 182 deletions(-) delete mode 100644 src/member/member.controller.ts delete mode 100644 src/member/member.module.ts delete mode 100644 src/member/member.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 606fbaf..9f42da9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -16,7 +16,6 @@ import { CommentModule } from './comment/comment.module'; import { DetailScheduleModule } from './detail-schedule/detail-schedule.module'; import { S3Module } from './utils/S3.module'; import { FollowModule } from './follow/follow.module'; -import { MemberModule } from './member/member.module'; import { SearchModule } from './search/search.module'; import { MapModule } from './map/map.module'; import { MateModule } from './mate/mate.module'; @@ -40,7 +39,6 @@ import { NotificationModule } from './notification/notification.module'; CommentModule, S3Module, FollowModule, - MemberModule, SearchModule, MapModule, MateModule, diff --git a/src/member/member.controller.ts b/src/member/member.controller.ts deleted file mode 100644 index f6499d7..0000000 --- a/src/member/member.controller.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Controller, Post, Req, UseGuards, Param, Delete, Get } from '@nestjs/common'; -import { ResponseCode } from '../response/response-code.enum'; -import { ResponseDto } from '../response/response.dto'; -import { MemberService } from './member.service'; -// import {UserSearchDto} from "../follow/dto/follow.search.dto"; -import { UserService} from "../user/user.service"; -import { UserGuard } from '../user/user.guard'; -import { Request } from 'express'; -import {RuleInvitationEntity} from "../rule/domain/rule.invitation.entity"; -import {UserEntity} from "../user/user.entity"; - -// @UseGuards(UserGuard) -@Controller('mate/rule/member') -export class MemberController { - constructor( - private readonly memberService: MemberService, - private readonly userService: UserService, - ) {} - - // [2] 여행 규칙 멤버 초대 - @Post('/invite/:ruleId/:invitedId') - async createInvitation(@Param('ruleId') ruleId : number, @Param('invitedId') invitedId : number) : Promise> { - const result = await this.memberService.createInvitation(ruleId, invitedId); - - return result; - } - - // [3] 여행 규칙 멤버 삭제 - @Delete('/delete/:ruleId/:memberId') - async deleteMember(@Param('ruleId') ruleId : number, @Param('memberId') memberId : number) : Promise> { - const result = await this.memberService.deleteMember(ruleId, memberId); - - return result; - } - - // [4] 초대할 여행 규칙 멤버 검색 - /* - @Get('/search/:searchTerm') - @UseGuards(UserGuard) - async getSearchResult( - @Req() req: Request, - @Param('searchTerm') searchTerm: string): Promise> { - // 현재 로그인한 사용자 ID - // const userId = req.user.id; - // const userId = 1; - - try { - const userSearchDto : UserSearchDto[] = await this.userService.getSearchResult(req.user.id, searchTerm) - return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_SUCCESS, - true, - "검색 결과 리스트 불러오기 성공", - userSearchDto - ); - } catch (error) { - return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_FAIL, - false, - "검색 결과 리스트 불러오기 실패", - null - ); - } - } - */ -} \ No newline at end of file diff --git a/src/member/member.module.ts b/src/member/member.module.ts deleted file mode 100644 index 14bf82c..0000000 --- a/src/member/member.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { MemberService } from './member.service'; -import { RuleService } from 'src/rule/rule.service'; -import { MemberController } from './member.controller'; -import { UserService } from 'src/user/user.service'; -import { S3UtilService } from '../utils/S3.service'; - -@Module({ - controllers: [MemberController], - providers: [MemberService, RuleService, UserService, S3UtilService], -}) -export class MemberModule {} diff --git a/src/member/member.service.ts b/src/member/member.service.ts deleted file mode 100644 index 1a3fc48..0000000 --- a/src/member/member.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import {Injectable, NotFoundException} from '@nestjs/common'; -import { RuleInvitationEntity } from 'src/rule/domain/rule.invitation.entity'; -import { UserEntity } from "../user/user.entity"; -import { UserService} from "../user/user.service"; -import {RuleMainEntity} from "../rule/domain/rule.main.entity"; -import { S3UtilService } from "../utils/S3.service"; -import {ResponseDto} from "../response/response.dto"; -import {ResponseCode} from "../response/response-code.enum"; - -@Injectable() -export class MemberService { - constructor( - private userService: UserService, - private readonly s3Service: S3UtilService, - ) {} - - // [2] 여행 규칙 멤버 초대 - async createInvitation(ruleId: number, invitedId: number): Promise> { - // (1) 존재하는 회원인지 확인 - const invitedEntity = await this.userService.findUserById(invitedId); - if(!invitedEntity) { - return new ResponseDto( - ResponseCode.USER_NOT_FOUND, - false, - "존재하지 않는 회원 입니다", - null - ); - } - - // (2) 이미 초대된 멤버인지 확인 - const isAlreadyMember = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, invitedId); - console.log('isAlreadyMember', isAlreadyMember); - - if (!isAlreadyMember) { - const invitationEntity : RuleInvitationEntity = new RuleInvitationEntity(); - const ruleEntity : RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); - const invitedUserEntity : UserEntity = await this.userService.findUserById(invitedId); - - invitationEntity.rule = ruleEntity; - invitationEntity.member = invitedUserEntity; - - await invitationEntity.save(); - return new ResponseDto( - ResponseCode.INVITATION_CREATED, - true, - "여행 규칙 멤버 초대 성공", - null - ); - } else { - return new ResponseDto( - ResponseCode.IS_ALREADY_MEMBER, - false, - "이미 초대된 멤버 입니다", - null - ); - } - } - - // [3] 여행 규칙 멤버 삭제 - async deleteMember(ruleId: number, memberId: number) :Promise> { - // (1) 존재하는 회원인지 확인 - const invitedEntity = await this.userService.findUserById(memberId); - if(!invitedEntity) { - return new ResponseDto( - ResponseCode.USER_NOT_FOUND, - false, - "존재하지 않는 회원 입니다", - null - ); - } - - // (2) 이미 초대된 멤버인지 확인 - const isAlreadyMember = await RuleInvitationEntity.findInvitationByRuleAndUser(ruleId, memberId); - console.log('isAlreadyMember', isAlreadyMember); - - if (!!isAlreadyMember) { - const invitation = await RuleInvitationEntity.findOne({ - where: { rule: { id: ruleId }, member: { id: memberId } } - }); - await invitation.softRemove(); - - return new ResponseDto( - ResponseCode.DELETE_MEMBER_SUCCESS, - true, - "여행 규칙 멤버 삭제 성공", - null - ); - } else { - return new ResponseDto( - ResponseCode.IS_NOT_MEMBER, - false, - "초대된 회원이 아닙니다", - null - ); - } - } -} \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index d2cbfec..655452f 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -132,7 +132,7 @@ export class RuleService { const detailMember = new DetailMemberDto; const memberEntity = invitation.member; detailMember.id = memberEntity.id; - detailMember.name = memberEntity.name; + detailMember.name = memberEntity.nickname; // 사용자 프로필 이미지 const image = memberEntity.profileImage; @@ -200,7 +200,7 @@ export class RuleService { getCommentDto.id = comment.id; getCommentDto.writerId = comment.user.id; - getCommentDto.name = comment.user.name; + getCommentDto.name = comment.user.nickname; getCommentDto.content = comment.content; getCommentDto.updated = comment.updated; @@ -281,7 +281,7 @@ export class RuleService { console.log('memberEntity : ', memberEntity); memberDto.id = memberEntity.id; - memberDto.name = memberEntity.name; + memberDto.name = memberEntity.nickname; memberDto.email = memberEntity.email; memberDto.introduction = memberEntity.introduction; @@ -363,7 +363,7 @@ export class RuleService { console.log('user.id : ', user.id); memberPair.id = user.id; - memberPair.name = user.name; + memberPair.name = user.nickname; // 사용자 프로필 이미지 const image = user.profileImage; @@ -434,7 +434,7 @@ export class RuleService { const dtoAtCreate: GetSearchMemberAtCreateDto = new GetSearchMemberAtCreateDto(); dtoAtCreate.id = user.id; - dtoAtCreate.name = user.name; + dtoAtCreate.name = user.nickname; dtoAtCreate.email = user.email; dtoAtCreate.introduction = user.introduction; @@ -517,7 +517,7 @@ export class RuleService { const dto: GetSearchMemberDto = new GetSearchMemberDto(); dto.id = user.id; - dto.name = user.name; + dto.name = user.nickname; dto.email = user.email; dto.introduction = user.introduction; From 728963c70c4c6ec59fda2ff4b9fe9a5180b97a97 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Wed, 14 Feb 2024 22:57:38 +0900 Subject: [PATCH 269/316] =?UTF-8?q?fix=20:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=A9=A4=EB=B2=84=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rule/rule.controller.ts | 19 +++++++---- src/rule/rule.service.ts | 65 ++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index 13153b3..e288269 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -45,9 +45,11 @@ export class RuleController { // [2] 여행 규칙 멤버 리스트 조회 @Get('/detail/member/:ruleId') - async getMemberList(@Param('ruleId') ruleId : number) : Promise> { + @UseGuards(UserGuard) + async getMemberList(@Req() req: Request, + @Param('ruleId') ruleId : number) : Promise> { try { - const memberList = await this.ruleService.getMemberList(ruleId); + const memberList = await this.ruleService.getMemberList(req.user.id, ruleId); return new ResponseDto( ResponseCode.GET_MEMBER_LIST_SUCCESS, true, @@ -119,7 +121,8 @@ export class RuleController { // [4] 여행 규칙 상세 페이지 조회 (게시글) @Get('/detail/:ruleId') @UseGuards(UserGuard) - async getDetail(@Req() req: Request, @Param('ruleId') ruleId: number): Promise> { + async getDetail(@Req() req: Request, + @Param('ruleId') ruleId: number): Promise> { const result = await this.ruleService.getDetail(req.user.id, ruleId); @@ -144,7 +147,9 @@ export class RuleController { // [5] 여행 규칙 수정 @Patch('/detail/:ruleId') @UseGuards(UserGuard) - async updateRule(@Body() updateRuleDto: UpdateRuleDto, @Req() req: Request, @Param('ruleId') ruleId: number): Promise> { + async updateRule(@Body() updateRuleDto: UpdateRuleDto, + @Req() req: Request, + @Param('ruleId') ruleId: number): Promise> { try { const result = await this.ruleService.updateRule(updateRuleDto, req.user.id, ruleId); @@ -167,7 +172,8 @@ export class RuleController { // [6] 여행 규칙 생성 @Post('/detail') @UseGuards(UserGuard) - async createRule(@Req() req: Request, @Body() createRuleDto: CreateRuleDto): Promise> { + async createRule(@Req() req: Request, + @Body() createRuleDto: CreateRuleDto): Promise> { try { const result = await this.ruleService.createRule(createRuleDto, req.user.id); return new ResponseDto( @@ -189,7 +195,8 @@ export class RuleController { // [7] 여행 규칙 나가기 @Delete('/:ruleId') @UseGuards(UserGuard) - async deleteInvitation(@Req() req: Request, @Param('ruleId') ruleId: number){ + async deleteInvitation(@Req() req: Request, + @Param('ruleId') ruleId: number){ try { await this.ruleService.deleteInvitation(ruleId, req.user.id); return new ResponseDto( diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 655452f..7d20946 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -268,34 +268,53 @@ export class RuleService { } // [4] 여행 규칙 멤버 리스트 조회 - async getMemberList(ruleId: number): Promise { + async getMemberList(userId: number, ruleId: number): Promise { try { - const invitationsList: RuleInvitationEntity[] = await RuleInvitationEntity.find({ - where: {rule: {id: ruleId}}, - relations: {member: true} + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id: userId}, }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); - const membersList: GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation): Promise => { - const memberEntity: UserEntity = invitation.member; - const memberDto: GetMemberListDto = new GetMemberListDto(); + // 검증2) 규칙이 존재하지 않는 경우 + const ruleMain = await RuleMainEntity.findOne({ + where: {id: ruleId}, + }); + if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); - console.log('memberEntity : ', memberEntity); - memberDto.id = memberEntity.id; - memberDto.name = memberEntity.nickname; - memberDto.email = memberEntity.email; - memberDto.introduction = memberEntity.introduction; + // 검증3) 규칙에 참여하는 사용자가 아닌 경우 + const invitation = await RuleInvitationEntity.findOne({ + where: {member: {id: userId}, rule: {id: ruleId}}, + }) - // 사용자 프로필 이미지 - const image = await this.userService.getProfileImage(memberEntity.id); - if (image == null) memberDto.image = null; - else { - const userImageKey = image.imageKey; - memberDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return memberDto; - })); - const sortedList = membersList.sort((a, b) => a.id - b.id); - return sortedList; + if(!!invitation) { + const invitationsList: RuleInvitationEntity[] = await RuleInvitationEntity.find({ + where: {rule: {id: ruleId}}, + relations: {member: true} + }); + + const membersList: GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation): Promise => { + const memberEntity: UserEntity = invitation.member; + const memberDto: GetMemberListDto = new GetMemberListDto(); + + console.log('memberEntity : ', memberEntity); + memberDto.id = memberEntity.id; + memberDto.name = memberEntity.nickname; + memberDto.email = memberEntity.email; + memberDto.introduction = memberEntity.introduction; + + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage(memberEntity.id); + if (image == null) memberDto.image = null; + else { + const userImageKey = image.imageKey; + memberDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return memberDto; + })); + const sortedList = membersList.sort((a, b) => a.id - b.id); + return sortedList; + } else throw new Error('사용자가 참여하는 규칙이 아닙니다'); } catch (e) { throw new Error(e.message); } From a71d4a5b19c7b68cf95f7101593fbf95d016aed2 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 02:15:56 +0900 Subject: [PATCH 270/316] =?UTF-8?q?fix=20:=20=EC=86=8C=EC=85=9C=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=95=88=EB=90=98=EB=8A=94=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [초대할 멤버 검색 결과] - 본인은 안뜨도록 - 팔로우하는 유저들만 검색 결과로 나오도록 [소셜 계정 사용자 규칙 상세 페이지 조회 안되는 에러] - subRule 검색 조건 수정 (id가 null값으로 전송돼서 에러 발생) --- src/rule/rule.service.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 7d20946..31379e6 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -9,7 +9,7 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import { LessThan, Like, MoreThan } from 'typeorm'; +import {Equal, LessThan, Like, MoreThan, Not} from 'typeorm'; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; @@ -82,12 +82,6 @@ export class RuleService { // [2] 여행 규칙 상세 페이지 조회 (게시글) async getDetail(userId: number, ruleId: number): Promise { const dto = new DetailRuleDto(); - const main: RuleMainEntity = await RuleMainEntity.findRuleById(ruleId); - const subs: RuleSubEntity[] = await RuleSubEntity.findSubById(ruleId); - const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ - where: {rule: {id: ruleId}}, - relations: {member: {profileImage: true}} - }); try { // 검증1) 사용자가 존재하지 않는 경우 @@ -97,21 +91,29 @@ export class RuleService { if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 - const rule = await RuleMainEntity.findOne({ + const ruleMain = await RuleMainEntity.findOne({ where: {id: ruleId}, relations: {rules: true, invitations: {member: true}} }) - if (!rule) throw new Error('규칙을 찾을 수 없습니다'); + if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자인지 체크 const invitation = await RuleInvitationEntity.findOne({ where: {member: {id: userId}, rule: {id: ruleId}}, }) + const subs: RuleSubEntity[] = await RuleSubEntity.find({ + where: {main: {id: ruleId}} + }) + const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ + where: {rule: {id: ruleId}}, + relations: {member: {profileImage: true}} + }); + if(!!invitation) { // -1) 제목 dto.id = ruleId; - dto.mainTitle = main.mainTitle; + dto.mainTitle = ruleMain.mainTitle; // -2) 규칙 const rulePairs = await Promise.all(subs.map(async (sub): Promise => { @@ -122,9 +124,9 @@ export class RuleService { return rulePair; })); - console.log('Before sorting : ', rulePairs); + console.log('Before sorting rulePairs : ', rulePairs); rulePairs.sort((a, b) => a.id - b.id); - console.log('After sorting : ', rulePairs); + console.log('After sorting rulePairs : ', rulePairs); dto.rulePairs = rulePairs; // -3) 멤버 정보 @@ -435,12 +437,14 @@ export class RuleService { take: cursorPageOptionsDto.take, where: [ { - id: cursorId ? LessThan(cursorId) : null, + following : {id: cursorId ? LessThan(cursorId) : null}, name: Like(`%${searchTerm}%`), + id: Not(Equal(userId)) }, { - id: cursorId ? LessThan(cursorId) : null, + following : {id: cursorId ? LessThan(cursorId) : null}, nickname: Like(`%${searchTerm}%`), + id: Not(Equal(userId)) } ], relations: {profileImage: true, ruleParticipate: {rule: true}}, From 910c1b09e65a0b35fe6c454a1ed601d110c26625 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 03:00:39 +0900 Subject: [PATCH 271/316] =?UTF-8?q?fix=20:=20=EC=B4=88=EB=8C=80=ED=95=A0?= =?UTF-8?q?=20=EB=A9=A4=EB=B2=84=20=EA=B2=80=EC=83=89=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 본인이 팔로우하는 사용자만 검색 결과에 뜨도록 검색 조건 설정하면서, 본인은 자동으로 안 뜸 -> 본인 안나오도록 설정하는 검색 조건 삭제 --- src/rule/rule.service.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 31379e6..d8bb44e 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -404,10 +404,17 @@ export class RuleService { async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { try { - let cursorId: number = 0; + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id: userId}, + }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); + // (1) cursorId 설정 + let cursorId: number = 0; console.log('cursorId : ', cursorPageOptionsDto); - // (1) 처음 요청인 경우 cursorId 설정 + + // -1) 처음 요청인 경우 if (cursorPageOptionsDto.cursorId == 0) { const newUser = await UserEntity.find({ order: { @@ -419,6 +426,7 @@ export class RuleService { console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); console.log('cursor: ', cursorId); + // -2) 처음 요청이 아닌 경우 } else { cursorId = cursorPageOptionsDto.cursorId; console.log('cursorPageOptionsDto.cursorId != 0 로 인식') @@ -427,9 +435,9 @@ export class RuleService { // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 + // [검색 조건] // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - // { id: Not(Equal(userId))} // 사용자 본인은 검색결과에 뜨지 않도록 - + // 본인이 팔로우 하는 사용자 중에서만 검색이 가능하도록 (본인은 자동으로 검색 결과에서 제외) console.log('검색 값: ', searchTerm); @@ -438,13 +446,11 @@ export class RuleService { where: [ { following : {id: cursorId ? LessThan(cursorId) : null}, - name: Like(`%${searchTerm}%`), - id: Not(Equal(userId)) + name: Like(`%${searchTerm}%`) }, { following : {id: cursorId ? LessThan(cursorId) : null}, - nickname: Like(`%${searchTerm}%`), - id: Not(Equal(userId)) + nickname: Like(`%${searchTerm}%`) } ], relations: {profileImage: true, ruleParticipate: {rule: true}}, From 74de2a44ee5fb0ff1e00ef608380aa6fcf48ee0e Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 04:48:46 +0900 Subject: [PATCH 272/316] =?UTF-8?q?fix=20:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=97=90=EB=9F=AC,=20=EC=97=AC=ED=96=89?= =?UTF-8?q?=20=EA=B7=9C=EC=B9=99=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [규칙에 초대할 멤버 검색] : UserEntity의 find 조건을, 검색 결과로 나와야하는 사용자를 대상으로 [여행 규칙 상세 조회] : id에 null값이 담겨가는 에러 (relations 사용 과정에서 발생) --- src/rule/rule.service.ts | 62 +++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index d8bb44e..02c3b7b 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -18,6 +18,7 @@ import {GetCommentDto } from "./dto/get-comment.dto"; import {CursorPageDto} from "./dto/cursor-page.dto"; import {CursorPageMetaDto} from "./dto/cursor-page.meta.dto"; import {GetSearchMemberAtCreateDto} from "./dto/get-search-member-at-create.dto"; +import {UserFollowingEntity} from "../user/user.following.entity"; @Injectable() export class RuleService { @@ -114,6 +115,7 @@ export class RuleService { // -1) 제목 dto.id = ruleId; dto.mainTitle = ruleMain.mainTitle; + console.log('dto.id : ', dto.id); // -2) 규칙 const rulePairs = await Promise.all(subs.map(async (sub): Promise => { @@ -121,13 +123,11 @@ export class RuleService { rulePair.id = sub.id; rulePair.ruleTitle = sub.ruleTitle; rulePair.ruleDetail = sub.ruleDetail; + console.log('rulePair.id', rulePair.id); return rulePair; })); - console.log('Before sorting rulePairs : ', rulePairs); - rulePairs.sort((a, b) => a.id - b.id); - console.log('After sorting rulePairs : ', rulePairs); - dto.rulePairs = rulePairs; + dto.rulePairs = rulePairs.sort((a, b) => a.id - b.id); // -3) 멤버 정보 const detailMembers = await Promise.all(invitations.map(async (invitation): Promise => { @@ -135,6 +135,7 @@ export class RuleService { const memberEntity = invitation.member; detailMember.id = memberEntity.id; detailMember.name = memberEntity.nickname; + console.log('detailMember.id : ', detailMember.id); // 사용자 프로필 이미지 const image = memberEntity.profileImage; @@ -143,10 +144,10 @@ export class RuleService { const userImageKey = image.imageKey; detailMember.image = await this.s3Service.getImageUrl(userImageKey); } + return detailMember; })); - detailMembers.sort((a, b) => a.id - b.id); - dto.detailMembers = detailMembers; + dto.detailMembers = detailMembers.sort((a, b) => a.id - b.id); return dto; } else { @@ -346,28 +347,33 @@ export class RuleService { console.log('현재 로그인한 사용자 : ', user.id); if (!user) throw new Error('사용자를 찾을 수 없습니다'); - const invitationEntities = user.ruleParticipate; - // 수정한 날짜 updated 내림차순으로 정렬 - invitationEntities.sort((a, b) => new Date(b.rule.updated).getTime() - new Date(a.rule.updated).getTime()); - console.log('invitationEntities 출력 : ', invitationEntities); + const invitationEntities = await RuleInvitationEntity.find({ + where: {member: {id: userId}}, + relations: { + rule: { + invitations: true + } + } + }); if (!!invitationEntities) { - const ruleMains = await Promise.all(invitationEntities.map(async (invitation: RuleInvitationEntity): Promise => { - console.log('참여하는 규칙 ID : ', invitation.rule.id); - const ruleMain: RuleMainEntity = invitation.rule; + const getRuleListDtos = await Promise.all(invitationEntities.map(async (invitation: RuleInvitationEntity): Promise => { const ruleListDto: GetRuleListDto = new GetRuleListDto; - - console.log('ruleMain.id : ', ruleMain.id); + const ruleId = invitation.rule.id; + const ruleMain = invitation.rule; ruleListDto.id = ruleMain.id; ruleListDto.title = ruleMain.mainTitle; ruleListDto.updated = ruleMain.updated; ruleListDto.memberCnt = ruleMain.invitations.length; - ruleListDto.memberPairs = await this.getMemberPairs(ruleMain); + ruleListDto.memberPairs = await this.getMemberPairs(ruleId); return ruleListDto; })); - return ruleMains; + + const sortedGetRuleListDtos = getRuleListDtos.sort((a, b) => new Date(b.updated).getTime() - new Date(a.updated).getTime()); + + return sortedGetRuleListDtos; } } catch (e) { console.log('참여하는 여행 규칙이 없습니다'); @@ -375,8 +381,15 @@ export class RuleService { } } - async getMemberPairs(ruleMain: RuleMainEntity): Promise { - const invitations: RuleInvitationEntity[] = ruleMain.invitations; + async getMemberPairs(ruleId: number): Promise { + const invitations = await RuleInvitationEntity.find({ + where: {rule: {id: ruleId}}, + relations: { + member: { + profileImage: true + } + } + }) const result: MemberPairDto[] = await Promise.all(invitations.map(async (invitation): Promise => { const memberPair = new MemberPairDto; @@ -436,6 +449,7 @@ export class RuleService { // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // [검색 조건] + // 검색 기준 UserEntity : 검색 결과로 나와야 하는 사용자 (searchTerm 에 해당하는) // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 // 본인이 팔로우 하는 사용자 중에서만 검색이 가능하도록 (본인은 자동으로 검색 결과에서 제외) @@ -445,12 +459,14 @@ export class RuleService { take: cursorPageOptionsDto.take, where: [ { - following : {id: cursorId ? LessThan(cursorId) : null}, - name: Like(`%${searchTerm}%`) + name: Like(`%${searchTerm}%`), + follower: {id: userId}, + id: cursorId ? LessThan(cursorId) : null }, { - following : {id: cursorId ? LessThan(cursorId) : null}, - nickname: Like(`%${searchTerm}%`) + nickname: Like(`%${searchTerm}%`), + follower: {id: userId}, + id: cursorId ? LessThan(cursorId) : null } ], relations: {profileImage: true, ruleParticipate: {rule: true}}, From cde508db6f920bff528b83aac92cd74fbc413934 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 12:46:44 +0900 Subject: [PATCH 273/316] =?UTF-8?q?feat:=20=EC=9D=BD=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=80=20=EC=95=8C=EB=A6=BC=20=EA=B0=9C=EC=88=98=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20API=EB=A5=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notification/notification.controller.ts | 6 ++++++ src/notification/notification.service.ts | 20 ++++++++++++++++++++ src/response/response-code.enum.ts | 1 + 3 files changed, 27 insertions(+) diff --git a/src/notification/notification.controller.ts b/src/notification/notification.controller.ts index 409fd03..0736848 100644 --- a/src/notification/notification.controller.ts +++ b/src/notification/notification.controller.ts @@ -12,4 +12,10 @@ export class NotificationController { ListNotification(@Req() req: Request) { return this.notificationService.listNotifications(req.user.id); } + + @Get('/unread') + @UseGuards(UserGuard) + CountUnreadNotification(@Req() req: Request) { + return this.notificationService.countUnreadNotification(req.user.id); + } } diff --git a/src/notification/notification.service.ts b/src/notification/notification.service.ts index 4a3e89d..c5a61c3 100644 --- a/src/notification/notification.service.ts +++ b/src/notification/notification.service.ts @@ -62,4 +62,24 @@ export class NotificationService { ); } } + + async countUnreadNotification(userId: number) { + const unreadCount = await NotificationEntity.count({ + where: { + notificationReceiver: { + id: userId, + }, + notificationRead: false, + }, + }); + + return new ResponseDto( + ResponseCode.GET_NOTIFICATION_COUNT_SUCCESS, + true, + '읽지 않은 알림 개수 조회 성공', + { + unreadCount, + }, + ); + } } diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index fbb1e84..683dc4a 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -21,6 +21,7 @@ export enum ResponseCode { GET_RULE_LIST_SUCCESS = 'OK', PATCH_RULE_SUCCESS = 'OK', GET_NOTIFICATION_SUCCESS = 'OK', + GET_NOTIFICATION_COUNT_SUCCESS = 'OK', GET_DIARY_SUCCESS = 'OK', From 9beb090032cbdad9b9a1936a87d099fe26598ec0 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 13:25:48 +0900 Subject: [PATCH 274/316] =?UTF-8?q?fix:=20=ED=83=88=ED=87=B4=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EC=9D=B4=20UserGuard=EB=A5=BC=20=ED=86=B5=EA=B3=BC?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=AC=B8=EC=A0=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.guard.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/user/user.guard.ts b/src/user/user.guard.ts index 6abfd5a..6501dc7 100644 --- a/src/user/user.guard.ts +++ b/src/user/user.guard.ts @@ -1,18 +1,20 @@ import Express from 'express'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import { Observable } from 'rxjs'; import * as jsonwebtoken from 'jsonwebtoken'; import { IReqUser } from './user.dto'; +import { UserEntity } from './user.entity'; @Injectable() export class UserGuard implements CanActivate { - canActivate( - context: ExecutionContext, - ): boolean | Promise | Observable { + async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const authorization = request.headers['authorization']?.split(' '); - if (authorization.length === 2 && authorization[0] === 'Bearer') { + if ( + authorization && + authorization.length === 2 && + authorization[0] === 'Bearer' + ) { const token = authorization[1]; try { @@ -20,6 +22,17 @@ export class UserGuard implements CanActivate { token, process.env.JWT_SECRET, ) as IReqUser; + + // 사용자 검증 + const isValidUser = await UserEntity.findOne({ + where: { + id: request.user.id, + }, + }); + + if (!isValidUser) { + return false; + } } catch (error) { return false; } From 438d1fc0f97c4b73641ddb0021d662649c4e580d Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 14:08:06 +0900 Subject: [PATCH 275/316] =?UTF-8?q?update:=20SNS=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C=20=EC=A4=91=EB=B3=B5=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=EC=9D=B4=20=EC=9E=88=EB=8B=A4=EB=A9=B4=20=EB=82=9C?= =?UTF-8?q?=EC=88=98=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 50 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 92e18dd..d67bdc2 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -166,7 +166,30 @@ export class UserService { userEntity.email = userEmail; userEntity.password = ''; - userEntity.nickname = userProfile?.nickname; + + if (userEntity.nickname !== userProfile?.nickname) { + // 중복 닉네임 확인 + const existingNickname = await UserEntity.count({ + where: { + nickname: userProfile?.nickname, + }, + }); + if (existingNickname > 0) { + // 난수 추가 + const availableChars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + const randomStringLength = 5; + let randomString = ''; + for (let i = 0; i < randomStringLength; i++) { + randomString += availableChars.charAt( + Math.floor(Math.random() * availableChars.length), + ); + } + + userEntity.nickname = `${userProfile?.nickname}_${randomString}`; + } else { + userEntity.nickname = userProfile?.nickname; + } + } if (userProfile?.profile_image_url) { const urlHash = md5(userProfile.profile_image_url); @@ -241,7 +264,30 @@ export class UserService { userEntity.email = userEmail; userEntity.password = ''; - userEntity.nickname = googleInfo.name; + + if (userEntity.nickname !== googleInfo.name) { + // 중복 닉네임 확인 + const existingNickname = await UserEntity.count({ + where: { + nickname: googleInfo.name, + }, + }); + if (existingNickname > 0) { + // 난수 추가 + const availableChars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + const randomStringLength = 5; + let randomString = ''; + for (let i = 0; i < randomStringLength; i++) { + randomString += availableChars.charAt( + Math.floor(Math.random() * availableChars.length), + ); + } + + userEntity.nickname = `${googleInfo.name}_${randomString}`; + } else { + userEntity.nickname = googleInfo.name; + } + } if (googleInfo.picture) { const urlHash = md5(googleInfo.picture); From 3dc858dc6de21f703daa6c06d86271a2e38d2255 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 15:41:26 +0900 Subject: [PATCH 276/316] =?UTF-8?q?fix=20:=20=EA=B7=9C=EC=B9=99=EC=97=90?= =?UTF-8?q?=20=EC=B4=88=EB=8C=80=ED=95=A0=20=EB=A9=A4=EB=B2=84=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [규칙에 초대할 멤버 검색 결과] : readOnly, import 오류 : where 조건 id 중복 사용 불가 -> cursorId 적용하는 부분 분리 후, filter, slice 이용 --- src/follow/follow.service.ts | 3 +- .../cursor-page/cursor-page-option.dto.ts | 2 +- src/rule/dto/cursor-page.options.dto.ts | 8 +- .../dto/get-search-member-at-create.dto.ts | 4 - src/rule/rule.service.ts | 98 ++++++++++++------- 5 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 7b6101c..eda58a3 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -47,8 +47,7 @@ export class FollowService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`)}, - {id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`)}, + {id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)} ], relations: {profileImage : true, follower : true, following : true}, order: { diff --git a/src/mate/cursor-page/cursor-page-option.dto.ts b/src/mate/cursor-page/cursor-page-option.dto.ts index 5148d04..8319054 100644 --- a/src/mate/cursor-page/cursor-page-option.dto.ts +++ b/src/mate/cursor-page/cursor-page-option.dto.ts @@ -15,7 +15,7 @@ export class CursorPageOptionsDto { @IsOptional() readonly take?: number = 5; - @Type(() => String) + @Type(() => Number) @IsOptional() readonly cursorId?: number = "" as any; } \ No newline at end of file diff --git a/src/rule/dto/cursor-page.options.dto.ts b/src/rule/dto/cursor-page.options.dto.ts index d2b2a6e..207fc85 100644 --- a/src/rule/dto/cursor-page.options.dto.ts +++ b/src/rule/dto/cursor-page.options.dto.ts @@ -7,13 +7,13 @@ export class CursorPageOptionsDto { @Type(() => String) @IsEnum(Order) @IsOptional() - readonly sort?: Order = Order.DESC; + sort?: Order = Order.DESC; @Type(() => Number) @IsOptional() - readonly take?: number = 5; + take?: number = 5; - @Type(() => String) + @Type(() => Number) @IsOptional() - readonly cursorId?: number = "" as any; + cursorId?: number = "" as any; } \ No newline at end of file diff --git a/src/rule/dto/get-search-member-at-create.dto.ts b/src/rule/dto/get-search-member-at-create.dto.ts index 3c135d1..392ef18 100644 --- a/src/rule/dto/get-search-member-at-create.dto.ts +++ b/src/rule/dto/get-search-member-at-create.dto.ts @@ -20,8 +20,4 @@ export class GetSearchMemberAtCreateDto { @IsOptional() @IsString() image: string; - - @IsOptional() - @IsBoolean() - isInvited: boolean; } \ No newline at end of file diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 02c3b7b..a1e4f7d 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -9,10 +9,10 @@ import { S3UtilService} from "../utils/S3.service"; import { GetMemberListDto} from "./dto/get-member-list.dto"; import {UserService} from "../user/user.service"; import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import {Equal, LessThan, Like, MoreThan, Not} from 'typeorm'; +import {Equal, In, LessThan, Like, MoreThan, Not} from 'typeorm'; import {GetSearchMemberDto} from "./dto/get-search-member.dto"; import {UpdateRuleDto} from "./dto/update-rule.dto"; -import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; +import {CursorPageOptionsDto} from "./dto/cursor-page.options.dto"; import {CommentEntity} from "../comment/domain/comment.entity"; import {GetCommentDto } from "./dto/get-comment.dto"; import {CursorPageDto} from "./dto/cursor-page.dto"; @@ -425,7 +425,7 @@ export class RuleService { // (1) cursorId 설정 let cursorId: number = 0; - console.log('cursorId : ', cursorPageOptionsDto); + console.log('cursorPageOptionsDto : ', cursorPageOptionsDto); // -1) 처음 요청인 경우 if (cursorPageOptionsDto.cursorId == 0) { @@ -435,7 +435,7 @@ export class RuleService { }, take: 1 }); - const cursorId = newUser[0].id + 1; + cursorId = newUser[0].id + 1; console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); console.log('cursor: ', cursorId); @@ -449,46 +449,66 @@ export class RuleService { // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 // [검색 조건] - // 검색 기준 UserEntity : 검색 결과로 나와야 하는 사용자 (searchTerm 에 해당하는) - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + // 검색 기준 UserFollowingEntity : 검색 결과로 나와야 하는 사용자 (searchTerm 에 해당하는) + // 해당 결과값을 nickName 에 포함하고 있는 사용자 찾기 // 본인이 팔로우 하는 사용자 중에서만 검색이 가능하도록 (본인은 자동으로 검색 결과에서 제외) console.log('검색 값: ', searchTerm); - const [resultUsers, total] = await UserEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: [ - { - name: Like(`%${searchTerm}%`), - follower: {id: userId}, - id: cursorId ? LessThan(cursorId) : null - }, - { - nickname: Like(`%${searchTerm}%`), - follower: {id: userId}, - id: cursorId ? LessThan(cursorId) : null - } - ], - relations: {profileImage: true, ruleParticipate: {rule: true}}, - order: { - id: "DESC" as any, + // 조건에 맞는 유저 검색해서 [] 에 담고 + // 해당 리스트에서 UserEntity 의 id, cursorId 이용해서 가져오기 + + // 1번 검색 조건) searchTerm 을 name 이나 nickname 에 포함하고 있는 + // 2번 검색 조건) 유저가 팔로우하는 유저 + // userFollowingEntity) user: 로그인한 유저, followUser: 유저가 팔로우하는 유저 + let resultFollowingEntities = await UserFollowingEntity.find({ + where: { + user: {id: userId}, + followUser: {nickname: Like(`%${searchTerm}%`)} }, + relations: { + followUser: { profileImage : true } + }, + order: { + followUser: {id: 'DESC'} + } }); + console.log('resultFollowingEntities', resultFollowingEntities); - const searchResult = await Promise.all(resultUsers.map(async (user) => { + const total = resultFollowingEntities.length; + + // 3번 검색 조건) id 가 cursorId 보다 작은 + // 해당 요소보다 작은 요소들만 필터링 + for(const userFollowingEntity of resultFollowingEntities) { + console.log('userFollowingEntity.followUser.id : ', userFollowingEntity.followUser.id); + } + + resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.id < cursorId); + + // take 초기값 설정 + console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); + if (cursorPageOptionsDto.take == 0) { + cursorPageOptionsDto.take = 5; + } + const results = resultFollowingEntities.slice(0, cursorPageOptionsDto.take); + + console.log('results (UserFollowingEntity[]) : ', results); + + const searchResult = await Promise.all(results.map(async (result) => { const dtoAtCreate: GetSearchMemberAtCreateDto = new GetSearchMemberAtCreateDto(); + const follower = result.followUser; - dtoAtCreate.id = user.id; - dtoAtCreate.name = user.nickname; - dtoAtCreate.email = user.email; - dtoAtCreate.introduction = user.introduction; + dtoAtCreate.id = follower.id; + dtoAtCreate.name = follower.nickname; + dtoAtCreate.email = follower.email; + dtoAtCreate.introduction = follower.introduction; // 사용자 프로필 이미지 - const image = user.profileImage; + const image = follower.profileImage; if (image == null) dtoAtCreate.image = null; else { - const userImageKey = image.imageKey; - dtoAtCreate.image = await this.s3Service.getImageUrl(userImageKey); + const followerImageKey = image.imageKey; + dtoAtCreate.image = await this.s3Service.getImageUrl(followerImageKey); } return dtoAtCreate; })); @@ -504,7 +524,7 @@ export class RuleService { const isLastScroll = total <= takePerScroll; console.log('isLastScroll : ', isLastScroll); console.log('total : ', total) - const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + const lastDataPerScroll = searchResult[searchResult.length - 1]; if (isLastScroll) { hasNextData = false; @@ -517,7 +537,7 @@ export class RuleService { return new CursorPageDto(searchResult, cursorPageMetaDto); } catch (e) { - throw new Error(e.message()); + throw new Error(e.message); } } @@ -549,8 +569,16 @@ export class RuleService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorId ? LessThan(cursorId) : null, name: Like(`%${searchTerm}%`)}, - {id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)}, + { + nickname: Like(`%${searchTerm}%`), + follower: In([userId]), + id: cursorId ? LessThan(cursorId) : null + }, + { + name: Like(`%${searchTerm}%`), + follower: In([userId]), + id: cursorId ? LessThan(cursorId) : null + } ], relations: {profileImage: true, ruleParticipate: {rule: true}}, order: { From 7845aa4831a558b825a4401b80ed01adccdf5523 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 15:57:53 +0900 Subject: [PATCH 277/316] =?UTF-8?q?update:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=ED=83=88=ED=87=B4=ED=95=B4=EB=8F=84=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B3=A0=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=A7=8C=20=EB=A7=88=EC=8A=A4=ED=82=B9?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.entity.ts | 8 +++++++- src/user/user.service.ts | 12 +++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index cce748f..ebc1627 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -55,6 +55,9 @@ export class UserEntity extends BaseEntity { @Column() oauthToken: string; + @Column({ default: false }) + isQuit: boolean; + @OneToOne(() => UserProfileImageEntity, (profileImage) => profileImage.user, { cascade: true, }) @@ -84,7 +87,10 @@ export class UserEntity extends BaseEntity { @OneToMany(() => JourneyEntity, (journey) => journey.user) journeys: JourneyEntity[]; - @OneToMany(() => SignatureCommentEntity, (signatureComment) => signatureComment.user) + @OneToMany( + () => SignatureCommentEntity, + (signatureComment) => signatureComment.user, + ) signatureComments: SignatureCommentEntity[]; @CreateDateColumn() diff --git a/src/user/user.service.ts b/src/user/user.service.ts index d67bdc2..68d1a7b 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -497,7 +497,17 @@ export class UserService { }, }); - await user.softRemove(); + user.name = '탈퇴한 사용자'; + user.email = ''; + user.password = ''; + user.nickname = '탈퇴한 사용자'; + user.introduction = '탈퇴한 사용자입니다.'; + user.age = 0; + user.gender = 'UNKNOWN'; + user.profileImage = null; + user.oauthToken = ''; + user.isQuit = true; + await user.save(); return new ResponseDto( ResponseCode.DELETE_ACCOUNT_SUCCESS, From bffb7644590188168e7f2529cdf992f4f3087700 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 16:04:18 +0900 Subject: [PATCH 278/316] =?UTF-8?q?fix:=20=ED=83=88=ED=87=B4=ED=95=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EB=8A=94=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=ED=95=A0=20=EC=88=98=20=EC=97=86=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 68d1a7b..52ba1ba 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -101,6 +101,7 @@ export class UserService { const user = await UserEntity.findOne({ where: { email: email.toString() ?? '', + isQuit: false, }, }); @@ -145,6 +146,7 @@ export class UserService { where: { oauthType: 'KAKAO', oauthToken: userId.toString(), + isQuit: false, }, relations: { profileImage: true, @@ -243,6 +245,7 @@ export class UserService { where: { oauthType: 'GOOGLE', oauthToken: userId.toString(), + isQuit: false, }, relations: { profileImage: true, From 19c816beaa82e42a9abc4dbdf8b681927c25079a Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 16:35:15 +0900 Subject: [PATCH 279/316] =?UTF-8?q?fix:=20=ED=83=88=ED=87=B4=EB=90=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EC=9D=98=20API=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=9D=84=20=EC=B0=A8=EB=8B=A8=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.guard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/user/user.guard.ts b/src/user/user.guard.ts index 6501dc7..6ecb9b0 100644 --- a/src/user/user.guard.ts +++ b/src/user/user.guard.ts @@ -27,6 +27,7 @@ export class UserGuard implements CanActivate { const isValidUser = await UserEntity.findOne({ where: { id: request.user.id, + isQuit: false, }, }); From 0c3ecfd415515107890aa1905da318f78613a28e Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 17:17:05 +0900 Subject: [PATCH 280/316] =?UTF-8?q?fix=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9B=8C=20=EB=AA=A9=EB=A1=9D,=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EB=A9=A4=EB=B2=84=20=EC=88=98=EC=A0=95=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 158 +++++++++++++++++++---------------- src/rule/rule.service.ts | 109 +++++++++++++++++------- 2 files changed, 167 insertions(+), 100 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index eda58a3..0b9c1ce 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -4,11 +4,9 @@ import { FollowDto } from './dto/follow.dto'; import { UserEntity } from "../user/user.entity"; import { UserService } from "../user/user.service"; import { S3UtilService } from "../utils/S3.service"; -import {SignatureEntity} from "../signature/domain/signature.entity"; import {LessThan, Like} from "typeorm"; import {FollowSearchDto} from "./dto/follow.search.dto"; -import {CursorPageOptionsDto} from "../mate/cursor-page/cursor-page-option.dto"; -import {CommentEntity} from "../comment/domain/comment.entity"; +import {CursorPageOptionsDto} from "../rule/dto/cursor-page.options.dto"; import {CursorPageMetaDto} from "../rule/dto/cursor-page.meta.dto"; import {CursorPageDto} from "../rule/dto/cursor-page.dto"; @@ -22,83 +20,103 @@ export class FollowService { // [1] 메이트 검색 async getSearchResult(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string) { - let cursorId: number = 0; - - // (1) 처음 요청인 경우 cursorId 설정 - if(cursorPageOptionsDto.cursorId == 0){ - const newUser = await UserEntity.find({ - order: { - id: 'DESC' // 가장 최근에 가입한 유저 - }, - take: 1 + try { + // 검증1) 사용자가 존재하지 않는 경우 + const userEntity = await UserEntity.findOne({ + where: {id: userId}, }); - const cursorId = newUser[0].id + 1; - - console.log('random cursor: ', cursorId); - } - else { - cursorId = cursorPageOptionsDto.cursorId; - } - - // (2) 데이터 조회 - // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 - console.log('검색 값: ', searchTerm); - const [resultUsers, total] = await UserEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: [ - {id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)} - ], - relations: {profileImage : true, follower : true, following : true}, - order: { - id: "DESC" as any, - }, - }); - - const userEntity = await UserEntity.findExistUser(userId); + if (!userEntity) throw new Error('사용자를 찾을 수 없습니다'); + + let cursorId: number = 0; + + // (1) cursorId 설정 + // -1) 처음 요청인 경우 + if (cursorPageOptionsDto.cursorId == 0) { + const newUser = await UserEntity.find({ + order: { + id: 'DESC' // 가장 최근에 가입한 유저 + }, + take: 1 + }); + cursorId = newUser[0].id + 1; + + console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); + console.log('cursor: ', cursorId); + // -2) 처음 요청이 아닌 경우 + } else { + cursorId = cursorPageOptionsDto.cursorId; + console.log('cursorPageOptionsDto.cursorId != 0 로 인식') + } + console.log('cursor: ', cursorId); - const searchResult = await Promise.all(resultUsers.map(async (user) => { - const followSearchDto = new FollowSearchDto(); + // (2) 데이터 조회 + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 nickName 에 포함하고 있는 사용자 찾기 - console.log('현재의 유저 : ', user.id); - followSearchDto.id = user.id; - followSearchDto.nickName = user.nickname; - followSearchDto.introduction = user.introduction; + console.log('검색 값: ', searchTerm); - followSearchDto.followerCnt = user.follower.length; - followSearchDto.followingCnt = user.following.length; + // take 초기값 설정 + console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); + if (cursorPageOptionsDto.take == 0) { + cursorPageOptionsDto.take = 5; + } - // 팔로우 여부 - followSearchDto.isFollowing = await this.userService.checkIfFollowing(userEntity, followSearchDto.id); + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: [ + {id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)} + ], + relations: {profileImage : true, follower : true, following : true}, + order: { + id: "DESC" as any, + }, + }); - // 사용자 프로필 이미지 - const image = user.profileImage; - if(image == null) followSearchDto.image = null; - else{ - const userImageKey = image.imageKey; - followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); + const searchResult = await Promise.all(resultUsers.map(async (user) => { + const followSearchDto = new FollowSearchDto(); + + console.log('현재의 유저 : ', user.id); + followSearchDto.id = user.id; + followSearchDto.nickName = user.nickname; + followSearchDto.introduction = user.introduction; + + followSearchDto.followerCnt = user.follower.length; + followSearchDto.followingCnt = user.following.length; + + // 팔로우 여부 + followSearchDto.isFollowing = await this.userService.checkIfFollowing(userEntity, followSearchDto.id); + + // 사용자 프로필 이미지 + const image = user.profileImage; + if(image == null) followSearchDto.image = null; + else{ + const userImageKey = image.imageKey; + followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return followSearchDto; + })); + + // (3) 페이징 및 정렬 기준 설정 + let hasNextData = true; + let cursor: number; + + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; } - return followSearchDto; - })); - - // (3) 페이징 및 정렬 기준 설정 - let hasNextData = true; - let cursor: number; - const takePerScroll = cursorPageOptionsDto.take; - const isLastScroll = total <= takePerScroll; - const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); - if (isLastScroll) { - hasNextData = false; - cursor = null; - } else { - cursor = lastDataPerScroll.id; + return new CursorPageDto(searchResult, cursorPageMetaDto); + } catch (e) { + throw new Error(e.message); } - - const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); - - return new CursorPageDto(searchResult, cursorPageMetaDto); } // [2] 팔로우 리스트 조회 diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index a1e4f7d..045137e 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -545,9 +545,29 @@ export class RuleService { async getSearchMemberAtUpdate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { try { + // 검증1) 사용자가 존재하지 않는 경우 + const user = await UserEntity.findOne({ + where: {id: userId}, + }); + if (!user) throw new Error('사용자를 찾을 수 없습니다'); + // 검증2) 규칙이 존재하지 않는 경우 + const rule = await RuleMainEntity.findOne({ + where: {id: ruleId}, + relations: {rules: true, invitations: {member: true}} + }) + if (!rule) throw new Error('규칙을 찾을 수 없습니다'); + // 검증3) 규칙에 참여하는 사용자인지 체크 + const invitation = await RuleInvitationEntity.findOne({ + where: {member: {id: userId}, rule: {id: ruleId}}, + }) + + if(!invitation) throw new Error('규칙에 참여하지 않는 사용자 입니다'); + + // (1) cursorId 설정 let cursorId: number = 0; + console.log('cursorPageOptionsDto : ', cursorPageOptionsDto); - // (1) 처음 요청인 경우 cursorId 설정 + // -1) 처음 요청인 경우 if (cursorPageOptionsDto.cursorId == 0) { const newUser = await UserEntity.find({ order: { @@ -555,54 +575,83 @@ export class RuleService { }, take: 1 }); - const cursorId = newUser[0].id + 1; + cursorId = newUser[0].id + 1; + console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); console.log('cursor: ', cursorId); + // -2) 처음 요청이 아닌 경우 } else { cursorId = cursorPageOptionsDto.cursorId; + console.log('cursorPageOptionsDto.cursorId != 0 로 인식') } + console.log('cursor: ', cursorId); // (2) 데이터 조회 // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 name 혹은 nickName 에 포함하고 있는 사용자 찾기 + // [검색 조건] + // 검색 기준 UserFollowingEntity : 검색 결과로 나와야 하는 사용자 (searchTerm 에 해당하는) + // 해당 결과값을 nickName 에 포함하고 있는 사용자 찾기 + // 본인이 팔로우 하는 사용자 중에서만 검색이 가능하도록 (본인은 자동으로 검색 결과에서 제외) + console.log('검색 값: ', searchTerm); - const [resultUsers, total] = await UserEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: [ - { - nickname: Like(`%${searchTerm}%`), - follower: In([userId]), - id: cursorId ? LessThan(cursorId) : null - }, - { - name: Like(`%${searchTerm}%`), - follower: In([userId]), - id: cursorId ? LessThan(cursorId) : null - } - ], - relations: {profileImage: true, ruleParticipate: {rule: true}}, - order: { - id: "DESC" as any, + + // 조건에 맞는 유저 검색해서 [] 에 담고 + // 해당 리스트에서 UserEntity 의 id, cursorId 이용해서 가져오기 + + // 1번 검색 조건) searchTerm 을 name 이나 nickname 에 포함하고 있는 + // 2번 검색 조건) 유저가 팔로우하는 유저 + // userFollowingEntity) user: 로그인한 유저, followUser: 유저가 팔로우하는 유저 + let resultFollowingEntities = await UserFollowingEntity.find({ + where: { + user: {id: userId}, + followUser: {nickname: Like(`%${searchTerm}%`)} }, + relations: { + followUser: {profileImage : true, ruleParticipate: {rule: true} } + }, + order: { + followUser: {id: 'DESC'} + } }); + console.log('resultFollowingEntities', resultFollowingEntities); - const searchResult = await Promise.all(resultUsers.map(async (user) => { - const dto: GetSearchMemberDto = new GetSearchMemberDto(); + const total = resultFollowingEntities.length; + + // 3번 검색 조건) id 가 cursorId 보다 작은 + // 해당 요소보다 작은 요소들만 필터링 + for(const userFollowingEntity of resultFollowingEntities) { + console.log('userFollowingEntity.followUser.id : ', userFollowingEntity.followUser.id); + } - dto.id = user.id; - dto.name = user.nickname; - dto.email = user.email; - dto.introduction = user.introduction; + resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.id < cursorId); + + // take 초기값 설정 + console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); + if (cursorPageOptionsDto.take == 0) { + cursorPageOptionsDto.take = 5; + } + const results = resultFollowingEntities.slice(0, cursorPageOptionsDto.take); + console.log('results (UserFollowingEntity[]) : ', results); + + // dto 데이터 넣기 + const searchResult = await Promise.all(results.map(async (result) => { + const dto: GetSearchMemberDto = new GetSearchMemberDto(); + const follower = result.followUser; + + dto.id = follower.id; + dto.name = follower.nickname; + dto.email = follower.email; + dto.introduction = follower.introduction; // 이미 여행 규칙에 참여하는 멤버인지 여부 - dto.isInvited = await this.userService.checkAlreadyMember(user.id, ruleId); + dto.isInvited = await this.userService.checkAlreadyMember(follower.id, ruleId); // 사용자 프로필 이미지 - const image = user.profileImage; + const image = follower.profileImage; if (image == null) dto.image = null; else { - const userImageKey = image.imageKey; - dto.image = await this.s3Service.getImageUrl(userImageKey); + const followerImageKey = image.imageKey; + dto.image = await this.s3Service.getImageUrl(followerImageKey); } return dto; })); From e0be67bd61b6d52a0a28fd41041ddd2b73781691 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 18:25:45 +0900 Subject: [PATCH 281/316] =?UTF-8?q?fix=20:=20=ED=83=88=ED=87=B4=ED=95=9C?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EC=9D=80=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EC=97=90=EC=84=9C=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 탈퇴 여부 확인 추가 --- src/follow/follow.service.ts | 5 ++++- src/rule/rule.service.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 0b9c1ce..53a1026 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -23,7 +23,10 @@ export class FollowService { try { // 검증1) 사용자가 존재하지 않는 경우 const userEntity = await UserEntity.findOne({ - where: {id: userId}, + where: { + id: userId, + isQuit: false, + }, }); if (!userEntity) throw new Error('사용자를 찾을 수 없습니다'); diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 045137e..848694f 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -419,7 +419,10 @@ export class RuleService { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { + id: userId, + isQuit: false, + }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); @@ -547,7 +550,10 @@ export class RuleService { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { + id: userId, + isQuit: false, + }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 From 7d8b0dca309e29388940ce939d67f6bd65505b19 Mon Sep 17 00:00:00 2001 From: minjunkaaang Date: Thu, 15 Feb 2024 18:26:37 +0900 Subject: [PATCH 282/316] =?UTF-8?q?update:=20=EC=B5=9C=EC=B4=88=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=EC=97=90=EB=A7=8C=20SNS?= =?UTF-8?q?=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=9B=EC=95=84=EC=98=A4=EB=8F=84=EB=A1=9D=20=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.service.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 52ba1ba..8697348 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -164,12 +164,7 @@ export class UserService { userEntity.visibility = 'PUBLIC'; userEntity.name = ''; userEntity.age = 0; - } - - userEntity.email = userEmail; - userEntity.password = ''; - if (userEntity.nickname !== userProfile?.nickname) { // 중복 닉네임 확인 const existingNickname = await UserEntity.count({ where: { @@ -193,6 +188,9 @@ export class UserService { } } + userEntity.email = userEmail; + userEntity.password = ''; + if (userProfile?.profile_image_url) { const urlHash = md5(userProfile.profile_image_url); const extension = userProfile.profile_image_url.split('.').pop(); @@ -263,12 +261,7 @@ export class UserService { userEntity.visibility = 'PUBLIC'; userEntity.name = ''; userEntity.age = 0; - } - - userEntity.email = userEmail; - userEntity.password = ''; - if (userEntity.nickname !== googleInfo.name) { // 중복 닉네임 확인 const existingNickname = await UserEntity.count({ where: { @@ -292,6 +285,9 @@ export class UserService { } } + userEntity.email = userEmail; + userEntity.password = ''; + if (googleInfo.picture) { const urlHash = md5(googleInfo.picture); const profileFilename = `profile/google_${urlHash}`; From bf79270135db531179627a8ae399611425ac1e49 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 18:30:07 +0900 Subject: [PATCH 283/316] =?UTF-8?q?fix=20:=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EB=B0=98=EB=8C=80=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 2 +- src/rule/rule.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 53a1026..0ea2bfd 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -25,7 +25,7 @@ export class FollowService { const userEntity = await UserEntity.findOne({ where: { id: userId, - isQuit: false, + isQuit: true, }, }); if (!userEntity) throw new Error('사용자를 찾을 수 없습니다'); diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 848694f..8e225f9 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -421,7 +421,7 @@ export class RuleService { const user = await UserEntity.findOne({ where: { id: userId, - isQuit: false, + isQuit: true, }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); @@ -552,7 +552,7 @@ export class RuleService { const user = await UserEntity.findOne({ where: { id: userId, - isQuit: false, + isQuit: true, }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); From 7b55664ff96d992180166963fae037d909a075ab Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 18:46:10 +0900 Subject: [PATCH 284/316] =?UTF-8?q?fix=20:=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 6 +++++- src/rule/rule.service.ts | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 0ea2bfd..b237d56 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -67,7 +67,11 @@ export class FollowService { const [resultUsers, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: [ - {id: cursorId ? LessThan(cursorId) : null, nickname: Like(`%${searchTerm}%`)} + { + id: cursorId ? LessThan(cursorId) : null, + nickname: Like(`%${searchTerm}%`), + isQuit: false, + } ], relations: {profileImage : true, follower : true, following : true}, order: { diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 8e225f9..6e072eb 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -421,7 +421,6 @@ export class RuleService { const user = await UserEntity.findOne({ where: { id: userId, - isQuit: true, }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); @@ -478,9 +477,12 @@ export class RuleService { }); console.log('resultFollowingEntities', resultFollowingEntities); + // 3번 검색 조건) 탈퇴 여부 확인 + resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.isQuit == false); + const total = resultFollowingEntities.length; - // 3번 검색 조건) id 가 cursorId 보다 작은 + // 4번 검색 조건) id 가 cursorId 보다 작은 // 해당 요소보다 작은 요소들만 필터링 for(const userFollowingEntity of resultFollowingEntities) { console.log('userFollowingEntity.followUser.id : ', userFollowingEntity.followUser.id); @@ -552,7 +554,6 @@ export class RuleService { const user = await UserEntity.findOne({ where: { id: userId, - isQuit: true, }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); @@ -621,9 +622,12 @@ export class RuleService { }); console.log('resultFollowingEntities', resultFollowingEntities); + // 3번 검색 조건) 탈퇴 여부 확인 + resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.isQuit == false); + const total = resultFollowingEntities.length; - // 3번 검색 조건) id 가 cursorId 보다 작은 + // 4번 검색 조건) id 가 cursorId 보다 작은 // 해당 요소보다 작은 요소들만 필터링 for(const userFollowingEntity of resultFollowingEntities) { console.log('userFollowingEntity.followUser.id : ', userFollowingEntity.followUser.id); From cea4bb46faaa0d4b04eaface35e4569a1900510c Mon Sep 17 00:00:00 2001 From: yewonahn Date: Thu, 15 Feb 2024 19:06:08 +0900 Subject: [PATCH 285/316] =?UTF-8?q?fix=20:=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EA=B2=80=EC=A6=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 3 +-- src/rule/rule.service.ts | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index b237d56..49f02f7 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -25,7 +25,6 @@ export class FollowService { const userEntity = await UserEntity.findOne({ where: { id: userId, - isQuit: true, }, }); if (!userEntity) throw new Error('사용자를 찾을 수 없습니다'); @@ -69,8 +68,8 @@ export class FollowService { where: [ { id: cursorId ? LessThan(cursorId) : null, - nickname: Like(`%${searchTerm}%`), isQuit: false, + nickname: Like(`%${searchTerm}%`), } ], relations: {profileImage : true, follower : true, following : true}, diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 6e072eb..4f24574 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -479,6 +479,9 @@ export class RuleService { // 3번 검색 조건) 탈퇴 여부 확인 resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.isQuit == false); + for(const userFollowingEntity of resultFollowingEntities) { + console.log('isQuit == false : ', userFollowingEntity.followUser.isQuit); + } const total = resultFollowingEntities.length; @@ -624,6 +627,9 @@ export class RuleService { // 3번 검색 조건) 탈퇴 여부 확인 resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.isQuit == false); + for(const userFollowingEntity of resultFollowingEntities) { + console.log('isQuit == false : ', userFollowingEntity.followUser.isQuit); + } const total = resultFollowingEntities.length; From e1e9f95eb77922f3a02fb5c18d006c576e6de6ed Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 00:26:21 +0900 Subject: [PATCH 286/316] =?UTF-8?q?Refactor:=20=ED=83=90=EC=83=89=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20api=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=20=EB=88=84=EB=9D=BD=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/dto/get-search-main.dto.ts | 3 +- src/search/search.controller.ts | 44 ++++++++++++++++++++------- src/search/search.service.ts | 2 +- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/search/dto/get-search-main.dto.ts b/src/search/dto/get-search-main.dto.ts index 51e1d84..07b2da3 100644 --- a/src/search/dto/get-search-main.dto.ts +++ b/src/search/dto/get-search-main.dto.ts @@ -3,6 +3,5 @@ import { SignatureCoverDto } from './signature-cover.dto'; export class GetSearchMainDto{ - hot: SignatureCoverDto[]; - new: SignatureCoverDto[]; + covers: SignatureCoverDto[]; } \ No newline at end of file diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index 18229fd..3d2d871 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -14,27 +14,48 @@ export class SearchController{ constructor(private readonly searchService: SearchService) {} - @Get('/') // 팀색탭 메인: 인기 급상승, 메이트의 최신 시그니처 + @Get('/hot') // 팀색탭 메인: 인기 급상승 시그니처 + async getSearchHotSignatures( + ): Promise>{ + try{ + const getHotSignaturesDto:GetSearchMainDto = new GetSearchMainDto(); + + // 인기 급상승 시그니처 가져오기 + getHotSignaturesDto.covers = await this.searchService.findHotSignatures(); + + return new ResponseDto( + ResponseCode.GET_SEARCH_MAIN_SUCCESS, + true, + "탐색탭 메인 화면 가져오기 성공", + getHotSignaturesDto + ); + }catch (error){ + console.log("탐색탭 메인 가져오기 실패: ", error); + return new ResponseDto( + ResponseCode.GET_SEARCH_MAIN_FAIL, + false, + "탐색탭 메인 화면 가져오기 실패", + null + ); + } + } + + @Get('/new') // 팀색탭 메인: 인기 급상승, 메이트의 최신 시그니처 @UseGuards(UserGuard) - async getSearchMain( + async getSearchNewSignatures( @Req() req: Request, ): Promise>{ try{ - const getSearchMainDto:GetSearchMainDto = new GetSearchMainDto(); + const getMatesNewSignatureDto:GetSearchMainDto = new GetSearchMainDto(); - // [1] 인기 급상승 시그니처 가져오기 - const hotSignatures:SignatureCoverDto[] = await this.searchService.findHotSignatures(); - getSearchMainDto.hot = hotSignatures; - - // [2] 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 - const newSignatures:SignatureCoverDto[] = await this.searchService.findMatesNewSignatures(req.user.id); - getSearchMainDto.new = newSignatures; + // 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 + getMatesNewSignatureDto.covers = await this.searchService.findMatesNewSignatures(req.user.id); return new ResponseDto( ResponseCode.GET_SEARCH_MAIN_SUCCESS, true, "탐색탭 메인 화면 가져오기 성공", - getSearchMainDto + getMatesNewSignatureDto ); }catch (error){ console.log("탐색탭 메인 가져오기 실패: ", error); @@ -47,6 +68,7 @@ export class SearchController{ } } + @Get('/find') // 탑색탭 검색: 키워드로 시그니처 검색하기 async search(@Query('keyword') keyword: string): Promise> { try{ diff --git a/src/search/search.service.ts b/src/search/search.service.ts index b0dbcc7..0c5564b 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -121,7 +121,7 @@ export class SearchService{ signatureCover._id = signature.id; signatureCover.title = signature.title; signatureCover.liked = signature.liked; - signatureCover.userName = signature.user.name; + signatureCover.userName = signature.user.nickname; // 시그니처 썸네일 이미지 가져오기 signatureCover.date = await SignatureEntity.formatDateString(signature.created); From 5f92480924c50df61bbbfed6551691f5cbaaf88a Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 00:56:24 +0900 Subject: [PATCH 287/316] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.controller.ts | 12 ++++-- src/search/search.service.ts | 2 +- src/signature/domain/signature.entity.ts | 12 +++--- src/user/optional.user.guard.ts | 47 ++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 src/user/optional.user.guard.ts diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index 3d2d871..f465104 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -8,6 +8,7 @@ import { SignatureCoverDto } from './dto/signature-cover.dto'; import { SearchService } from './search.service'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; +import { OptionalUserGuard } from '../user/optional.user.guard'; @Controller('search') export class SearchController{ @@ -41,15 +42,18 @@ export class SearchController{ } @Get('/new') // 팀색탭 메인: 인기 급상승, 메이트의 최신 시그니처 - @UseGuards(UserGuard) + @UseGuards(OptionalUserGuard) async getSearchNewSignatures( - @Req() req: Request, + @Req() req?: Request, ): Promise>{ try{ const getMatesNewSignatureDto:GetSearchMainDto = new GetSearchMainDto(); - // 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 - getMatesNewSignatureDto.covers = await this.searchService.findMatesNewSignatures(req.user.id); + // 로그인 했을 경우 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 + if(req.user != null) getMatesNewSignatureDto.covers = await this.searchService.findMatesNewSignatures(req.user.id); + + // 로그인 안했으면 빈 배열 + else getMatesNewSignatureDto.covers = null; return new ResponseDto( ResponseCode.GET_SEARCH_MAIN_SUCCESS, diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 0c5564b..b17b4a3 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -48,7 +48,7 @@ export class SearchService{ /******************************************************** 내 메이트 최신 시그니처 로직: [1] 내가 팔로우하고 있는 메이트 목록 가져오기 - [2] 각 메이트가 작성한 시그니처 중 일주일 안으로 작성된 것만 가져오기 + [2] 각 메이트가 작성한 시그니처 중 20일 안으로 작성된 것 가져오기 [3] 최신순으로 정렬해서 20개만 리턴 ********************************************************/ diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index f873a84..d12714a 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -137,14 +137,14 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter } static async findNewSignaturesByUser(userId: number) { - // [1] 기준이 되는 일주일 전 날짜 - const sevenDaysAgo: Date = new Date(); - sevenDaysAgo.setDate(sevenDaysAgo.getDate()-7); - console.log(sevenDaysAgo); + // [1] 기준이 되는 20일 전 날짜 + const twentyDaysAgo: Date = new Date(); + twentyDaysAgo.setDate(twentyDaysAgo.getDate()-20); + console.log(twentyDaysAgo); - // [2] 일주일 전에 쓰인 메이트의 최신 시그니처 가져오기 + // [2] 20일 전에 쓰인 메이트의 최신 시그니처 가져오기 const signatures = await SignatureEntity.find({ - where:{user:{id: userId}, created: MoreThan(sevenDaysAgo)}, + where:{user:{id: userId}, created: MoreThan(twentyDaysAgo)}, relations: ['user'] // user 포함 }) return signatures; diff --git a/src/user/optional.user.guard.ts b/src/user/optional.user.guard.ts new file mode 100644 index 0000000..2b47c05 --- /dev/null +++ b/src/user/optional.user.guard.ts @@ -0,0 +1,47 @@ +import Express from 'express'; +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import * as jsonwebtoken from 'jsonwebtoken'; +import { IReqUser } from './user.dto'; +import { UserEntity } from './user.entity'; + +@Injectable() +export class OptionalUserGuard implements CanActivate { + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const authorization = request.headers['authorization']?.split(' '); + + if ( + authorization && + authorization.length === 2 && + authorization[0] === 'Bearer' + ) { + const token = authorization[1]; + + try { + request.user = jsonwebtoken.verify( + token, + process.env.JWT_SECRET, + ) as IReqUser; + + // 사용자 검증 + const isValidUser = await UserEntity.findOne({ + where: { + id: request.user.id, + isQuit: false, + }, + }); + + if (!isValidUser) { + return false; + } + } catch (error) { + return false; + } + } + else{ // 토큰이 없는 경우 + request.user = null; + } + + return true; + } +} From 8d86e3c3f214d72e877a99a38a6c6d9291742f57 Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Fri, 16 Feb 2024 14:31:00 +0900 Subject: [PATCH 288/316] =?UTF-8?q?fix=20:=20journey,=20schedule=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/map/map.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/map/map.service.ts b/src/map/map.service.ts index a2ed2e4..ea4be7d 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -24,15 +24,22 @@ export class MapService { ) { const user = await UserEntity.findExistUser(userId); const journey = await JourneyEntity.findExistJourneyByDate(user.id, date); + if (!journey) { + return errResponse(BaseResponse.JOURNEY_NOT_FOUND); + } const schedules = await ScheduleEntity.findExistSchedulesByJourneyId( journey.id, ); + if (!schedules) { + return errResponse(BaseResponse.SCHEDULE_NOT_FOUND); + } const journeyInfo = { userId: user.id, journeyId: journey.id, startDate: journey.startDate, endDate: journey.endDate, }; + const scheduleList = await Promise.all( schedules.map(async (schedule) => { const locations = await this.getLocationList([schedule]); // getLocationList에 schedule 배열을 전달 From d3c0ff774eefed883709ab428651105adb1f7a1a Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 16 Feb 2024 14:34:10 +0900 Subject: [PATCH 289/316] =?UTF-8?q?fix=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=83=88=ED=87=B4=ED=95=9C=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [메이트탐색 : 팔로우, 팔로잉 리스트] : 탈퇴한 회원 제외 --- src/user/user.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 8697348..aec01b4 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -435,7 +435,7 @@ export class UserService { async getFollowingList(userId: number) { try { return await UserFollowingEntity.find({ - where: { user: { id: userId } }, + where: { user: { id: userId, isQuit: false } }, relations: { followUser: { profileImage: true } }, }); } catch (error) { @@ -446,7 +446,7 @@ export class UserService { async getFollowerList(userId: number) { try { return await UserFollowingEntity.find({ - where: { followUser: { id: userId } }, + where: { followUser: { id: userId, isQuit: false } }, relations: { user: { profileImage: true } }, }); } catch (error) { From 20c1050e625e5dbf57b5e6299be3386b0639bdfd Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:20:24 +0900 Subject: [PATCH 290/316] =?UTF-8?q?Refactor:=20=ED=83=88=ED=87=B4=ED=95=9C?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/mate.service.ts | 15 +++++++++++---- src/search/search.service.ts | 2 +- src/signature/domain/signature.entity.ts | 5 ++++- src/signature/domain/signature.like.entity.ts | 9 +++++---- src/signature/signature.service.ts | 4 ++-- src/user/user.service.ts | 1 + 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index c279123..1d5e868 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -34,7 +34,12 @@ export class MateService{ // 1. 메이트 탐색의 기준이 될 장소 가져오기 = 사용자의 가장 최신 시그니처의 첫 번째 페이지 장소 const mySignaturePageEntity = await SignaturePageEntity.findOne({ where: { - signature:{ user:{ id: userId } }, + signature:{ + user:{ + id: userId, + isQuit: false // 탈퇴한 사용자 필터링 + }, + }, page: 1 }, order: { @@ -117,6 +122,7 @@ export class MateService{ // [0] 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 if(cursorPageOptionsDto.cursorId == 0){ const newUser = await UserEntity.find({ + where: { isQuit: false }, // 탈퇴 필터링 order: { id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 가입한 유저 가져오기 }, @@ -139,9 +145,10 @@ export class MateService{ // [1] 무한 스크롤: take만큼 cursorId보다 id값이 작은 유저들 불러오기 const [mates, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, - where: cursorId ? { - id: LessThan(cursorId) - }: null, + where: { + id: LessThan(cursorId), + isQuit: false + }, order: { id: "DESC" as any, }, diff --git a/src/search/search.service.ts b/src/search/search.service.ts index b17b4a3..8f9930a 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -93,7 +93,7 @@ export class SearchService{ return signatureCovers; } - async searchByKeyword(keyword: string) { // 키워드로 검색하기 + async searchByKeyword(keyword: string) { // 키워드로 검색하기: 탈퇴한 메이트의 시그니처도 반환 try{ const resultSignatures = await SignatureEntity.find({ where:{ title: Like(`%${keyword}%`) }, diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index d12714a..fdf1bc4 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -129,7 +129,10 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter // [2] 오늘로부터 일주일 안으로 쓰여진 시그니처 가져오기 const recentSignatures = await SignatureEntity.find({ - where:{ created: MoreThan(sevenDaysAgo) }, + where:{ + created: MoreThan(sevenDaysAgo), + user: { isQuit: false }, // 탈퇴한 사용자의 시그니처는 추천에서 제외 + }, relations: ['user'] // user 포함 }); diff --git a/src/signature/domain/signature.like.entity.ts b/src/signature/domain/signature.like.entity.ts index a14c0b5..0ba8ba3 100644 --- a/src/signature/domain/signature.like.entity.ts +++ b/src/signature/domain/signature.like.entity.ts @@ -50,11 +50,12 @@ export class SignatureLikeEntity extends BaseEntity { } static async findSignatureLikes(signatureId: number) { - const signatureLikeEntities = await SignatureLikeEntity.find({ - where:{ signature:{id: signatureId} }, + return await SignatureLikeEntity.find({ + where:{ + signature:{id: signatureId}, + user: { isQuit: false } // 탈퇴한 유저의 좋아요는 가져오지 않음 + }, relations: ['user', 'signature'], }) - - return signatureLikeEntities; } } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 8e859e4..0980893 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -159,7 +159,7 @@ export class SignatureService { // [2] 시그니처 작성자 정보 가져오기 const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); - if (signature.user) { + if (!signature.user.isQuit) { // 유저가 탈퇴하지 않았다면 authorDto._id = signature.user.id; authorDto.name = signature.user.nickname; @@ -179,7 +179,7 @@ export class SignatureService { } detailSignatureDto.author = authorDto; } else { - // 해당 시그니처를 작성한 유저가 존재하지 않는 경우(탈퇴한 경우) + // 해당 시그니처를 작성한 유저가 탈퇴한 경우 console.log('작성자 유저가 존재하지 않습니다.'); authorDto._id = null; authorDto.name = null; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 8697348..c6342ec 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -535,6 +535,7 @@ export class UserService { const followingMates = await UserEntity.find({ where: { follower: { user: { id: userId } }, + isQuit: false, // 탈퇴한 메이트는 팔로잉 목록에서 제외 }, }); return followingMates; From f566994183cbd3ae54b037c5daba2c202e284972 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:53:53 +0900 Subject: [PATCH 291/316] =?UTF-8?q?fix:=20=ED=83=90=EC=83=89=20-=20?= =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20=EC=8D=B8=EB=84=A4?= =?UTF-8?q?=EC=9D=BC=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.service.ts | 5 ++++- src/signature/domain/signature.page.entity.ts | 9 +++++++-- src/signature/signature.service.ts | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 8f9930a..41b1c37 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -125,8 +125,11 @@ export class SearchService{ // 시그니처 썸네일 이미지 가져오기 signatureCover.date = await SignatureEntity.formatDateString(signature.created); + const signatureImageKey = await SignaturePageEntity.findThumbnail(signature.id); - signatureCover.image = await this.s3Service.getImageUrl(signatureImageKey); + if(signatureImageKey != null ){ + signatureCover.image = await this.s3Service.getImageUrl(signatureImageKey); + } // 시그니처 작성자 프로필 이미지 가져오기 const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 193c4d2..7a9183a 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -55,8 +55,13 @@ export class SignaturePageEntity extends BaseEntity { }, }); - console.log("썸네일 이미지: ",firstPage.image); - return firstPage.image; + console.log("첫번째 페이지: ",firstPage); + + if(firstPage == null) return null; + else { + console.log("썸네일 이미지: ",firstPage.image); + return firstPage.image; + } } catch (error) { console.log('Error on findThumbnail: ', error); diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 0980893..88215fa 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -94,10 +94,10 @@ export class SignatureService { async homeSignature(userId: number): Promise { try { console.log('userId; ', userId); - const homeSignatureList: HomeSignatureDto[] = await this.findMySignature( + return this.findMySignature( userId, ); - return homeSignatureList; + } catch (error) { // 예외 처리 console.error('Error on HomeSignature: ', error); From 8f1d890deaac507a274c261e9a9379a89de4d0a6 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 16 Feb 2024 15:55:32 +0900 Subject: [PATCH 292/316] =?UTF-8?q?fix=20:=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=ED=83=88=ED=87=B4?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [팔로우, 언팔로우] [메이트 탐색 : 메이트 검색] [팔로잉, 팔로워 리스트] : 대상 사용자가 탈퇴한 회원인지 검증 --- src/follow/follow.service.ts | 14 +++++++++++--- src/user/user.service.ts | 9 +++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 49f02f7..9cbc780 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -197,9 +197,17 @@ export class FollowService { async checkFollow(userId : number, followingId : number): Promise { try { // case1) 유효한 유저인지 검증 - const userEntity : UserEntity = await this.userService.findUserById(userId); - const followingUser = await UserEntity.findExistUser(followingId); - if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); + const userEntity : UserEntity = await UserEntity.findOne({ + where: {id: userId} + }); + if(!userEntity) throw new NotFoundException('요청을 보낸 사용자를 찾을 수 없습니다') + const followingUser = await UserEntity.findOne({ + where: { + id: followingId, + isQuit: false + } + }); + if (!followingUser) throw new NotFoundException('대상 사용자를 찾을 수 없습니다'); console.log('현재 로그인한 유저 : ', userEntity); console.log('팔로우 대상 유저 : ', followingUser); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index aec01b4..8b9fef4 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -435,7 +435,10 @@ export class UserService { async getFollowingList(userId: number) { try { return await UserFollowingEntity.find({ - where: { user: { id: userId, isQuit: false } }, + where: { + user: { id: userId }, + followUser: {isQuit: false}, + }, relations: { followUser: { profileImage: true } }, }); } catch (error) { @@ -446,7 +449,9 @@ export class UserService { async getFollowerList(userId: number) { try { return await UserFollowingEntity.find({ - where: { followUser: { id: userId, isQuit: false } }, + where: { + followUser: { id: userId, isQuit: false } + }, relations: { user: { profileImage: true } }, }); } catch (error) { From 31748ffea4c2e292216524d5cbee8b3c5606a1c0 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:56:58 +0900 Subject: [PATCH 293/316] =?UTF-8?q?fix:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=ED=83=90=EC=83=89=20-=20=EC=B5=9C=EC=8B=A0=20?= =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 41b1c37..d392dd9 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -86,6 +86,7 @@ export class SearchService{ for (let i = 0; i < signatureEntities.length && i < 20; i++) { const signature = signatureEntities[i]; const signatureCover = await this.getSignatureCover(signature); + if(signatureCover) signatureCovers.push(signatureCover); signatureCovers.push(signatureCover); } @@ -103,7 +104,7 @@ export class SearchService{ const resultCovers = []; for(const signature of resultSignatures){ const signatureCover = await this.getSignatureCover(signature); - resultCovers.push(signatureCover); + if(signatureCover) resultCovers.push(signatureCover); } return resultCovers; @@ -130,6 +131,7 @@ export class SearchService{ if(signatureImageKey != null ){ signatureCover.image = await this.s3Service.getImageUrl(signatureImageKey); } + else return null; // 시그니처 작성자 프로필 이미지 가져오기 const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); From a0fdc911f5bb35aa3f993e2b78e9f42d34a8da44 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:00:41 +0900 Subject: [PATCH 294/316] =?UTF-8?q?[hotfix]=20=EC=B5=9C=EC=8B=A0=20?= =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20=EC=A4=91=EB=B3=B5=20pus?= =?UTF-8?q?h=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/search/search.service.ts b/src/search/search.service.ts index d392dd9..29d286c 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -87,8 +87,6 @@ export class SearchService{ const signature = signatureEntities[i]; const signatureCover = await this.getSignatureCover(signature); if(signatureCover) signatureCovers.push(signatureCover); - - signatureCovers.push(signatureCover); } return signatureCovers; From 7eaa6552a71d446df29b117b0e7388e07f3a7be8 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:07:49 +0900 Subject: [PATCH 295/316] =?UTF-8?q?refactor:=20=ED=99=88=20=EC=8B=9C?= =?UTF-8?q?=EA=B7=B8=EB=8B=88=EC=B2=98=20=EC=B5=9C=EC=8B=A0=EC=88=9C?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B0=98=ED=99=98=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 88215fa..b708d36 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -109,6 +109,7 @@ export class SignatureService { const mySignatureList: HomeSignatureDto[] = []; const signatures = await SignatureEntity.find({ where: { user: { id: user_id } }, + order: { created: 'DESC' } // 내가 작성한 시그니처 최신 순으로 보여주도록 }); for (const signature of signatures) { From 6cf76c8ffca535b974c91970721c25406455fa7b Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 16 Feb 2024 16:56:57 +0900 Subject: [PATCH 296/316] =?UTF-8?q?fix=20:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EC=8B=9C,=20=ED=8C=94=EB=A1=9C=EC=9E=89,?= =?UTF-8?q?=20=EA=B7=9C=EC=B9=99=20=EC=B4=88=EB=8C=80=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [회원 탈퇴] : 해당 회원의 userFollowingEntity, ruleInvitationEntity softRemove 처리 --- src/rule/dto/detail.rule.dto.ts | 2 - src/rule/dto/get-comment.dto.ts | 11 +++--- src/rule/rule.service.ts | 28 ++++++++++---- src/user/user.service.ts | 67 +++++++++++---------------------- 4 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/rule/dto/detail.rule.dto.ts b/src/rule/dto/detail.rule.dto.ts index 5b03ffe..c5f0990 100644 --- a/src/rule/dto/detail.rule.dto.ts +++ b/src/rule/dto/detail.rule.dto.ts @@ -16,11 +16,9 @@ export class RulePairDto { } export class DetailMemberDto { - @IsNotEmpty() @IsNumber() id: number; - @IsNotEmpty() @IsString() name: string; diff --git a/src/rule/dto/get-comment.dto.ts b/src/rule/dto/get-comment.dto.ts index 72189bd..e967cd9 100644 --- a/src/rule/dto/get-comment.dto.ts +++ b/src/rule/dto/get-comment.dto.ts @@ -5,10 +5,6 @@ export class GetCommentDto { @IsNumber() id: number; - @IsNotEmpty() - @IsNumber() - writerId: number; - @IsNotEmpty() @IsString() content: string; @@ -17,7 +13,12 @@ export class GetCommentDto { @IsDate() updated: Date; - @IsNotEmpty() + // 탈퇴한 회원이 작성한 댓글도 표시 -> null 허용 + @IsOptional() + @IsNumber() + writerId: number; + + @IsOptional() @IsString() name: string; diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 4f24574..d98f0fd 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -202,17 +202,31 @@ export class RuleService { const getCommentDto = new GetCommentDto(); getCommentDto.id = comment.id; - getCommentDto.writerId = comment.user.id; - getCommentDto.name = comment.user.nickname; getCommentDto.content = comment.content; getCommentDto.updated = comment.updated; - // 사용자 프로필 이미지 - const image = comment.user.profileImage; - if (image == null) getCommentDto.image = null; + // 댓글 작성자 정보 + // 탈퇴한 사용자가 작성한 댓글도 표시 + // -> 댓글 작성자 (user) 존재 여부 확인 + const writerEntity = comment.user; + if (!!writerEntity) { + getCommentDto.writerId = comment.user.id; + getCommentDto.name = comment.user.nickname; + + // 사용자 프로필 이미지 + const image = comment.user.profileImage; + if (image == null) getCommentDto.image = null; + else { + const userImageKey = image.imageKey; + getCommentDto.image = await this.s3Service.getImageUrl(userImageKey); + } + } + // 댓글 작성자가 탈퇴한 사용자인 경우 else { - const userImageKey = image.imageKey; - getCommentDto.image = await this.s3Service.getImageUrl(userImageKey); + console.log('탈퇴한 회원이 작성한 댓글 입니다'); + getCommentDto.writerId = null; + getCommentDto.name = null; + getCommentDto.image = null; } return getCommentDto; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 8b9fef4..1d481d1 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -13,6 +13,7 @@ import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import * as md5 from 'md5'; import { DiaryEntity } from '../diary/models/diary.entity'; import { S3UtilService } from '../utils/S3.service'; +import {CommentEntity} from "../comment/domain/comment.entity"; @Injectable() export class UserService { @@ -513,6 +514,28 @@ export class UserService { user.isQuit = true; await user.save(); + const followings = await UserFollowingEntity.find({ + where: [ + {user: {id: userId}}, + {followUser: {id: userId}} + ] + }); + + for(const following of followings) { + console.log('삭제될 팔로잉 테이블 ID : ', following.id); + await following.softRemove(); + } + + const ruleInvitations = await RuleInvitationEntity.find({ + where: {member: {id: userId}} + }); + + for(const invitation of ruleInvitations) { + console.log('삭제될 규칙 초대 테이블 ID : ', invitation.id); + await invitation.softRemove(); + } + + return new ResponseDto( ResponseCode.DELETE_ACCOUNT_SUCCESS, true, @@ -549,50 +572,6 @@ export class UserService { } } - /* - async getSearchResult(userId: number, searchTerm: string) : Promise { - // 현재 로그인한 유저 객체 - const user = await this.findUserById(userId); - console.log('현재 로그인한 유저 아이디 : ', user.id) - - console.log(searchTerm); - - // 검색 결과로 보여줄 유저 객체 리스트 - const mates = await UserEntity.find({ - where: [ - {name: Like(`%${searchTerm}%`)}, - {nickname: Like(`%${searchTerm}%`)}, - ], - relations : { - profileImage: true, - following: true, - follower: true, - } - }); - console.log(mates); - - - // dto 리스트 생성 - const results : UserSearchDto[] = await Promise.all(mates.map(async (mate) => { - const userSearchDto = new UserSearchDto(); - userSearchDto.mateId = mate.id; - userSearchDto.nickName = mate.nickname; - userSearchDto.introduction = mate.introduction; - userSearchDto.followerCnt = mate.follower.length; - userSearchDto.followingCnt = mate.following.length; - userSearchDto.image = mate.profileImage.imageKey; - userSearchDto.isFollowing = await this.checkIfFollowing(user, mate.id); - - return userSearchDto; - })); - - console.log('검색 결과 : ', results); - - return results; - } - - */ - async isAlreadyFollowing(userId: number, followingId: number) { const userEntity = await this.findUserById(userId); const followingEntity = await this.findUserById(followingId); From f4f88338ce021d72f67854a73846b28b7349bbf7 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:23:45 +0900 Subject: [PATCH 297/316] =?UTF-8?q?[Refactor]=20=EB=A9=94=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=83=90=EC=83=89=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=B5=9C=EC=8B=A0=EC=88=9C=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/search.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search/search.service.ts b/src/search/search.service.ts index 29d286c..f61933a 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -100,6 +100,10 @@ export class SearchService{ }); const resultCovers = []; + + // 검색 결과 최신 순으로 정렬 + resultSignatures.sort((a, b) => b.created.getTime() - a.created.getTime()); + for(const signature of resultSignatures){ const signatureCover = await this.getSignatureCover(signature); if(signatureCover) resultCovers.push(signatureCover); From 834a4fb3d59656b58e754a5a13dbdbe30cac06a3 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Fri, 16 Feb 2024 17:36:17 +0900 Subject: [PATCH 298/316] =?UTF-8?q?fix=20:=20=ED=83=88=ED=87=B4=ED=95=9C?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EC=9D=84=20=ED=8C=94=EB=A1=9C=EC=9A=B0?= =?UTF-8?q?=ED=95=9C=20=EA=B2=BD=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 에러 처리 --- src/follow/follow.service.ts | 4 ++-- src/rule/rule.service.ts | 40 ++++++++++++++++++++++++------------ src/user/user.service.ts | 1 - 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 9cbc780..bc53232 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -203,10 +203,10 @@ export class FollowService { if(!userEntity) throw new NotFoundException('요청을 보낸 사용자를 찾을 수 없습니다') const followingUser = await UserEntity.findOne({ where: { - id: followingId, - isQuit: false + id: followingId } }); + if (followingUser.isQuit == true) throw new BadRequestException('탈퇴한 회원 입니다'); if (!followingUser) throw new NotFoundException('대상 사용자를 찾을 수 없습니다'); console.log('현재 로그인한 유저 : ', userEntity); console.log('팔로우 대상 유저 : ', followingUser); diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index d98f0fd..029fd0d 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,4 +1,4 @@ -import {Injectable } from '@nestjs/common'; +import {BadRequestException, Injectable, NotFoundException} from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; @@ -59,7 +59,11 @@ export class RuleService { const members = await Promise.all(dto.membersId.map(async (memberId): Promise => { const ruleInvitationEntity = new RuleInvitationEntity(); - const userEntity = await UserEntity.findOneOrFail({where: {id: memberId}}); + const userEntity = await UserEntity.findOne({ + where: {id: memberId } + }); + if(!userEntity) throw new NotFoundException('멤버로 초대한 회원을 찾을 수 없습니다'); + if(userEntity.isQuit == true) throw new BadRequestException('탈퇴한 회원은 멤버로 초대할 수 없습니다'); ruleInvitationEntity.rule = main; ruleInvitationEntity.member = userEntity; @@ -133,16 +137,27 @@ export class RuleService { const detailMembers = await Promise.all(invitations.map(async (invitation): Promise => { const detailMember = new DetailMemberDto; const memberEntity = invitation.member; - detailMember.id = memberEntity.id; - detailMember.name = memberEntity.nickname; - console.log('detailMember.id : ', detailMember.id); - - // 사용자 프로필 이미지 - const image = memberEntity.profileImage; - if (image == null) detailMember.image = null; + if (memberEntity.isQuit == false) { + detailMember.id = memberEntity.id; + detailMember.name = memberEntity.nickname; + console.log('detailMember.id : ', detailMember.id); + + // 사용자 프로필 이미지 + const image = memberEntity.profileImage; + if (image == null) detailMember.image = null; + else { + const userImageKey = image.imageKey; + detailMember.image = await this.s3Service.getImageUrl(userImageKey); + } + } + // 탈퇴한 회원인데 ruleInvitationEntity 삭제 안된 경우) else { - const userImageKey = image.imageKey; - detailMember.image = await this.s3Service.getImageUrl(userImageKey); + console.log('탈퇴한 회원의 ruleInvitationEntity 가 삭제되지 않았습니다'); + console.log('탈퇴한 회원의 ID : ', memberEntity.id); + console.log('해당 ruleInvitationEntity ID : ', invitation.id); + detailMember.id = null; + detailMember.name = null; + detailMember.image = null; } return detailMember; @@ -209,7 +224,7 @@ export class RuleService { // 탈퇴한 사용자가 작성한 댓글도 표시 // -> 댓글 작성자 (user) 존재 여부 확인 const writerEntity = comment.user; - if (!!writerEntity) { + if (writerEntity.isQuit == false) { getCommentDto.writerId = comment.user.id; getCommentDto.name = comment.user.nickname; @@ -256,7 +271,6 @@ export class RuleService { } // [4] 여행 규칙 나가기 - // -1) 초대 받은 팀원 -> 초대 삭제 async deleteInvitation(ruleId: number, userId: number): Promise { try { // 검증1) 사용자가 존재하지 않는 경우 diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 1d481d1..6041262 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -535,7 +535,6 @@ export class UserService { await invitation.softRemove(); } - return new ResponseDto( ResponseCode.DELETE_ACCOUNT_SUCCESS, true, From 33309d7a0b4701b00e4f46cef87619467a726da2 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:58:52 +0900 Subject: [PATCH 299/316] =?UTF-8?q?[BugFix]=20=ED=8C=94=EB=A1=9C=EC=9B=8C?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=EC=97=90=EC=84=9C=20isFollow=20=3D=20true?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/follow/follow.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 49f02f7..1491fe2 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -178,7 +178,7 @@ export class FollowService { followDto.mateId = mateEntity.id; followDto.email = mateEntity.email; followDto.introduction = mateEntity.introduction; - followDto.isFollowing = !!follow.id; + followDto.isFollowing = await this.userService.checkIfFollowing(user,mateEntity.id); // 사용자 프로필 이미지 const image = await this.userService.getProfileImage(mateEntity.id); From 313bbaa8d37d9313878185ed3e0e6bb188d7c05c Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:08:37 +0900 Subject: [PATCH 300/316] =?UTF-8?q?hotfix:=20=ED=8C=94=EB=A1=9C=EC=9A=B0,?= =?UTF-8?q?=20=ED=8C=94=EB=A1=9C=EC=9E=89=20=EB=AA=A9=EB=A1=9D=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=ED=95=9C=20=EC=9C=A0=EC=A0=80=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user/user.entity.ts | 5 ++++- src/user/user.service.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index ebc1627..b03832d 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -104,7 +104,10 @@ export class UserEntity extends BaseEntity { static async findExistUser(userId) { const user = await UserEntity.findOne({ - where: { id: userId }, + where: { + id: userId, + isQuit: false, + }, }); if (!user) { throw new NotFoundException(BaseResponse.USER_NOT_FOUND); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index ea5a254..cb6d4fe 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -435,7 +435,10 @@ export class UserService { async getFollowingList(userId: number) { try { return await UserFollowingEntity.find({ - where: { user: { id: userId, isQuit: false } }, + where: { + user: { id: userId, isQuit: false }, + followUser: { isQuit: false }, + }, relations: { followUser: { profileImage: true } }, }); } catch (error) { @@ -446,7 +449,10 @@ export class UserService { async getFollowerList(userId: number) { try { return await UserFollowingEntity.find({ - where: { followUser: { id: userId, isQuit: false } }, + where: { + followUser: { id: userId, isQuit: false }, + user: { isQuit: false }, + }, relations: { user: { profileImage: true } }, }); } catch (error) { From cf93026540fa671d0356f1ac42c80bc84436af88 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:23:54 +0900 Subject: [PATCH 301/316] =?UTF-8?q?[Refactor]=20=EB=A9=94=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=94=84=EB=A1=9C=ED=95=84=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mate/dto/mate-profile-response.dto.ts | 1 + src/mate/mate.service.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/mate/dto/mate-profile-response.dto.ts b/src/mate/dto/mate-profile-response.dto.ts index aeb8aa2..526600f 100644 --- a/src/mate/dto/mate-profile-response.dto.ts +++ b/src/mate/dto/mate-profile-response.dto.ts @@ -10,4 +10,5 @@ export class MateProfileResponseDto { signatures: number; // 시그니처 개수 follower: number; // 팔로워 수 following: number; // 팔로잉 수 + isQuit: boolean; // 탈퇴 여부 } \ No newline at end of file diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 1d5e868..68e152a 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -268,6 +268,7 @@ export class MateService{ mateProfileResponseDto._id = targetUserEntity.id; mateProfileResponseDto.nickname = targetUserEntity.nickname; mateProfileResponseDto.introduction = targetUserEntity.introduction; + mateProfileResponseDto.isQuit = targetUserEntity.isQuit; // 타겟 유저 프로필 이미지 가져오기 const userProfileImageEntity = await this.userService.getProfileImage(targetUserId); From f3db73d5a0d1c21aeff82781e38e9c9797e18b8c Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sat, 17 Feb 2024 05:16:20 +0900 Subject: [PATCH 302/316] =?UTF-8?q?fix=20:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EA=B8=80=EC=9E=90=20=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 댓글 글자 수 200자로 변경 --- src/comment/domain/comment.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/comment/domain/comment.entity.ts b/src/comment/domain/comment.entity.ts index 9bf2c3e..112d4ab 100644 --- a/src/comment/domain/comment.entity.ts +++ b/src/comment/domain/comment.entity.ts @@ -17,7 +17,7 @@ export class CommentEntity extends BaseEntity { @PrimaryGeneratedColumn({ type: 'bigint' }) id: number; - @Column({ type: 'varchar', length: 255 }) + @Column({ type: 'varchar', length: 200 }) content: string; @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.comments) From ffbad0bde7b82da2d31fe84caacfa4b87ca80ae9 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sat, 17 Feb 2024 05:31:49 +0900 Subject: [PATCH 303/316] =?UTF-8?q?fix=20:=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9=20=EA=B8=80=EC=9E=90=20=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 규칙 전체 제목, 개별 제목 글자 수 제한 수정 (200자) --- src/rule/domain/detail.rule.entity.ts | 0 src/rule/domain/rule.main.entity.ts | 2 +- src/rule/domain/rule.sub.entity.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 src/rule/domain/detail.rule.entity.ts diff --git a/src/rule/domain/detail.rule.entity.ts b/src/rule/domain/detail.rule.entity.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index a402d3b..a811548 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -16,7 +16,7 @@ export class RuleMainEntity extends BaseEntity { @PrimaryGeneratedColumn({ type: 'bigint' }) id: number; - @Column({ type: 'varchar', length: 255 }) + @Column({ type: 'varchar', length: 200 }) mainTitle: string; @CreateDateColumn() diff --git a/src/rule/domain/rule.sub.entity.ts b/src/rule/domain/rule.sub.entity.ts index f0bf1ef..f1c8849 100644 --- a/src/rule/domain/rule.sub.entity.ts +++ b/src/rule/domain/rule.sub.entity.ts @@ -15,7 +15,7 @@ export class RuleSubEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column({ type: 'varchar', length: 255 }) + @Column({ type: 'varchar', length: 200 }) ruleTitle: string; @Column({ type: 'text' }) From 96e5527c12a786ddd2a2821bac176617e71c28f7 Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 18 Feb 2024 08:53:05 +0900 Subject: [PATCH 304/316] =?UTF-8?q?fix=20:=20=EB=8C=80=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EB=AC=B4=ED=95=9C=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 80 ++++++++++++++++++---- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 45b0202..e98752e 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -78,26 +78,82 @@ export class SignatureCommentService{ signatureId: number, ) { - // 1. 'cursorId'부터 오름차순 정령된 댓글 'take'만큼 가져오기 - const [comments, total] = await SignatureCommentEntity.findAndCount({ - take: cursorPageOptionsDto.take, + // cursorId 이용해서 마지막으로 보낸 댓글 정보 알아내기 + const lastComment = await SignatureCommentEntity.findOne({ where: { signature: { id: signatureId }, - parentComment: { id: cursorPageOptionsDto.cursorId ? MoreThan(cursorPageOptionsDto.cursorId) : null }, + id: cursorPageOptionsDto.cursorId }, - relations: { - user: { profileImage: true }, - parentComment: true, - signature:{ - user: true, - } + relations: {parentComment: true} + }); + + let comments = []; + let total: number = 0; + + // lastComment 가 자신과 부모가 같으면서 자신보다 depth 가 깊은 대댓글이 있는, 대댓글인지 확인 + let sendData = new SignatureCommentEntity(); + + const check = await SignatureCommentEntity.find({ + where: { + signature: { id: signatureId }, + parentComment: {id: lastComment.parentComment.id}, + id: MoreThan(lastComment.id) }, order: { parentComment: { id: "ASC" as any,}, }, }); - // 2. 댓글 response dto에 담기 + // -1) 있는 경우 + if(!!check) { + sendData = check[0]; + + const [commentEntities, totalNumber] = await SignatureCommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + signature: { id: signatureId }, + id: MoreThan(sendData.id), + }, + relations: { + user: { profileImage: true }, + parentComment: true, + signature:{ + user: true, + } + }, + order: { + parentComment: { id: "ASC" as any,}, + }, + }); + comments = commentEntities; + total = totalNumber; + } + + else { + // -2) 없는 경우 + // 'cursorId' 부터 오름차순 정렬된 댓글 'take' 만큼 가져오기 + const [commentEntities, totalNumber] = await SignatureCommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + signature: { id: signatureId }, + parentComment: { id: cursorPageOptionsDto.cursorId ? MoreThan(cursorPageOptionsDto.cursorId) : null }, + }, + relations: { + user: { profileImage: true }, + parentComment: true, + signature:{ + user: true, + } + }, + order: { + parentComment: { id: "ASC" as any,}, + }, + }); + comments = commentEntities; + total = totalNumber; + } + + // 2. 댓글 response dto 에 담기 const result = await Promise.all(comments.map(async (comment) => { const writerProfile = new GetCommentWriterDto(); const getCommentDto = new GetSignatureCommentDto(); @@ -169,8 +225,6 @@ export class SignatureCommentService{ { cursorPageOptionsDto, total, hasNextData, cursor }); return new CursorPageDto( result, cursorPageMetaDto ); - - } async patchSignatureComment( // 댓글 수정하기 From 95c1fc07c97a226af8d9b950d27fed0b22eef9aa Mon Sep 17 00:00:00 2001 From: yewonahn Date: Sun, 18 Feb 2024 09:06:49 +0900 Subject: [PATCH 305/316] =?UTF-8?q?fix=20:=20=EB=8C=80=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 80 ++++------------------ 1 file changed, 13 insertions(+), 67 deletions(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index e98752e..45b0202 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -78,82 +78,26 @@ export class SignatureCommentService{ signatureId: number, ) { - // cursorId 이용해서 마지막으로 보낸 댓글 정보 알아내기 - const lastComment = await SignatureCommentEntity.findOne({ + // 1. 'cursorId'부터 오름차순 정령된 댓글 'take'만큼 가져오기 + const [comments, total] = await SignatureCommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, where: { signature: { id: signatureId }, - id: cursorPageOptionsDto.cursorId + parentComment: { id: cursorPageOptionsDto.cursorId ? MoreThan(cursorPageOptionsDto.cursorId) : null }, }, - relations: {parentComment: true} - }); - - let comments = []; - let total: number = 0; - - // lastComment 가 자신과 부모가 같으면서 자신보다 depth 가 깊은 대댓글이 있는, 대댓글인지 확인 - let sendData = new SignatureCommentEntity(); - - const check = await SignatureCommentEntity.find({ - where: { - signature: { id: signatureId }, - parentComment: {id: lastComment.parentComment.id}, - id: MoreThan(lastComment.id) + relations: { + user: { profileImage: true }, + parentComment: true, + signature:{ + user: true, + } }, order: { parentComment: { id: "ASC" as any,}, }, }); - // -1) 있는 경우 - if(!!check) { - sendData = check[0]; - - const [commentEntities, totalNumber] = await SignatureCommentEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: { - signature: { id: signatureId }, - id: MoreThan(sendData.id), - }, - relations: { - user: { profileImage: true }, - parentComment: true, - signature:{ - user: true, - } - }, - order: { - parentComment: { id: "ASC" as any,}, - }, - }); - comments = commentEntities; - total = totalNumber; - } - - else { - // -2) 없는 경우 - // 'cursorId' 부터 오름차순 정렬된 댓글 'take' 만큼 가져오기 - const [commentEntities, totalNumber] = await SignatureCommentEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: { - signature: { id: signatureId }, - parentComment: { id: cursorPageOptionsDto.cursorId ? MoreThan(cursorPageOptionsDto.cursorId) : null }, - }, - relations: { - user: { profileImage: true }, - parentComment: true, - signature:{ - user: true, - } - }, - order: { - parentComment: { id: "ASC" as any,}, - }, - }); - comments = commentEntities; - total = totalNumber; - } - - // 2. 댓글 response dto 에 담기 + // 2. 댓글 response dto에 담기 const result = await Promise.all(comments.map(async (comment) => { const writerProfile = new GetCommentWriterDto(); const getCommentDto = new GetSignatureCommentDto(); @@ -225,6 +169,8 @@ export class SignatureCommentService{ { cursorPageOptionsDto, total, hasNextData, cursor }); return new CursorPageDto( result, cursorPageMetaDto ); + + } async patchSignatureComment( // 댓글 수정하기 From 91aa743a96480edf78faf674167a54e7ea1ca0dc Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:01:54 +0900 Subject: [PATCH 306/316] =?UTF-8?q?[BugFix]=20=EB=8B=B5=EA=B8=80=EC=9D=B4?= =?UTF-8?q?=20=EB=B6=80=EB=AA=A8=20=EB=8C=93=EA=B8=80=EB=B3=B4=EB=8B=A4=20?= =?UTF-8?q?=EB=A8=BC=EC=A0=80=20=EB=9C=A8=EB=8A=94=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 45b0202..1de372c 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -78,27 +78,27 @@ export class SignatureCommentService{ signatureId: number, ) { - // 1. 'cursorId'부터 오름차순 정령된 댓글 'take'만큼 가져오기 + // 1. 'cursorId'부터 오름차순 정렬된 댓글 'take'만큼 가져오기 const [comments, total] = await SignatureCommentEntity.findAndCount({ take: cursorPageOptionsDto.take, where: { signature: { id: signatureId }, - parentComment: { id: cursorPageOptionsDto.cursorId ? MoreThan(cursorPageOptionsDto.cursorId) : null }, + parentComment: { id: MoreThan(cursorPageOptionsDto.cursorId) }, }, relations: { user: { profileImage: true }, parentComment: true, - signature:{ - user: true, - } + signature:{ user: true, } }, order: { parentComment: { id: "ASC" as any,}, + created: 'ASC' }, }); // 2. 댓글 response dto에 담기 const result = await Promise.all(comments.map(async (comment) => { + const writerProfile = new GetCommentWriterDto(); const getCommentDto = new GetSignatureCommentDto(); @@ -106,8 +106,7 @@ export class SignatureCommentService{ writerProfile._id = comment.user.id; writerProfile.name = comment.user.nickname; - // 로그인한 사용자가 댓글 작성자(혹은 시그니처 작성자-> 보류)인지 확인 - //if( userId == comment.user.id || userId == comment.signature.user.id ) writerProfile.is_writer = true; + // 로그인한 사용자가 댓글 작성자인지 확인 if( userId == comment.user.id ) writerProfile.is_writer = true; else writerProfile.is_writer = false; From 9caf111ebd3f8108d37bb2024bbeb3cf7b04bbca Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:08:01 +0900 Subject: [PATCH 307/316] =?UTF-8?q?[BugFix]=EB=8C=93=EA=B8=80=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 1de372c..3c53e88 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -161,7 +161,7 @@ export class SignatureCommentService{ hasNextData = false; cursor = null; } else { - cursor = lastDataPerScroll.id; + cursor = lastDataPerScroll.parentComment.id; } const cursorPageMetaDto = new CursorPageMetaDto( From 39043a14813948d6718f854b09f7f58681adf169 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:50:04 +0900 Subject: [PATCH 308/316] Update signature.comment.service.ts --- src/signature/signature.comment.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 3c53e88..4e1b2b9 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -161,7 +161,7 @@ export class SignatureCommentService{ hasNextData = false; cursor = null; } else { - cursor = lastDataPerScroll.parentComment.id; + cursor = lastDataPerScroll.parentComment.id - 1; } const cursorPageMetaDto = new CursorPageMetaDto( From 0ca5d599ed5cbadfd12e12e825f29807433382c8 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Sun, 18 Feb 2024 14:57:18 +0900 Subject: [PATCH 309/316] =?UTF-8?q?[BugFix]=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 182 ++++++++++++--------- 1 file changed, 106 insertions(+), 76 deletions(-) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 4e1b2b9..611ef69 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -9,12 +9,9 @@ import { SignatureCommentEntity } from './domain/signature.comment.entity'; import { UserEntity } from '../user/user.entity'; import { SignatureEntity } from './domain/signature.entity'; import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; -import { CommentEntity } from '../comment/domain/comment.entity'; -import { MoreThan } from 'typeorm'; -import { GetCommentDto } from '../rule/dto/get-comment.dto'; +import { MoreThan, Not, Raw } from 'typeorm'; import { GetSignatureCommentDto } from './dto/comment/get-signature-comment.dto'; import { GetCommentWriterDto } from './dto/comment/get-comment-writer.dto'; -import * as querystring from 'querystring'; import { CursorPageMetaDto } from '../mate/cursor-page/cursor-page.meta.dto'; import { CursorPageDto } from '../mate/cursor-page/cursor-page.dto'; @@ -77,98 +74,131 @@ export class SignatureCommentService{ userId: number, signatureId: number, ) { + try{ + + // 1. 'cursorId'부터 오름차순 정렬된 댓글 'take'만큼 가져오기 + const [comments, total] = await SignatureCommentEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + id: MoreThan(cursorPageOptionsDto.cursorId), + signature: { id: signatureId }, + parentComment: { id: Raw("SignatureCommentEntity.id") }, // 부모 댓글이 자기 자신인 댓글들만 가져오기 + }, + relations: { + user: { profileImage: true }, + parentComment: true, + signature:{ user: true, } + }, + order: { + parentComment: { id: "ASC" as any,}, + created: 'ASC' + }, + }); - // 1. 'cursorId'부터 오름차순 정렬된 댓글 'take'만큼 가져오기 - const [comments, total] = await SignatureCommentEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: { - signature: { id: signatureId }, - parentComment: { id: MoreThan(cursorPageOptionsDto.cursorId) }, - }, - relations: { - user: { profileImage: true }, - parentComment: true, - signature:{ user: true, } - }, - order: { - parentComment: { id: "ASC" as any,}, - created: 'ASC' - }, - }); - - // 2. 댓글 response dto에 담기 - const result = await Promise.all(comments.map(async (comment) => { - const writerProfile = new GetCommentWriterDto(); - const getCommentDto = new GetSignatureCommentDto(); + const result: GetSignatureCommentDto[] = []; + + // 2. 각 부모 댓글의 답글들 찾아오기 + for(const comment of comments){ + console.log(comment); + result.push(await this.createSignatureCommentDto(comment,userId)); + + const childrenComments = await SignatureCommentEntity.find({ // 답글 찾아오기 + where: { + parentComment: { id: comment.id }, + id: Not(Raw("SignatureCommentEntity.parentComment.id")) + }, + relations: { + user: { profileImage: true }, + parentComment: true, + signature:{ user: true, } + }, + order: { + created: 'ASC' + }, + }); - // 2-[1] 댓글 작성자 정보 담기 - writerProfile._id = comment.user.id; - writerProfile.name = comment.user.nickname; + for(const childComment of childrenComments){ + console.log(childComment); + result.push(await this.createSignatureCommentDto(childComment,userId)) + } + } - // 로그인한 사용자가 댓글 작성자인지 확인 - if( userId == comment.user.id ) writerProfile.is_writer = true; + // 3. 스크롤 설정 + let hasNextData = true; + let cursor: number; - else writerProfile.is_writer = false; + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = comments[comments.length - 1]; - // 작성자 프로필 이미지 - const image = comment.user.profileImage; - if(image == null) writerProfile.image = null; - else { - const userImageKey = image.imageKey; - writerProfile.image = await this.s3Service.getImageUrl(userImageKey); + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; } - // 2-[2] 댓글 정보 담기 - getCommentDto._id = comment.id; - getCommentDto.content = comment.content; - getCommentDto.parentId = comment.parentComment.id; - getCommentDto.writer = writerProfile; - getCommentDto.date = comment.updated; + const cursorPageMetaDto = new CursorPageMetaDto( + { cursorPageOptionsDto, total, hasNextData, cursor }); - // 댓글 수정 여부 구하기 - const createdTime = comment.created.getTime(); - const updatedTime = comment.updated.getTime(); + return new CursorPageDto( result, cursorPageMetaDto ); - if (Math.abs(createdTime - updatedTime) <= 2000) { // 두 시간 차가 2초 이하면 수정 안함 - getCommentDto.is_edited = false; - } else { - getCommentDto.is_edited = true; - } + } + catch(e){ + console.log("Error on GetSignature: ",e); + throw e; + } + } - // 로그인한 사용자가 시그니처 작성하면 can_delete = true - let can_delete = false; - if(comment.signature.user){ // 시그니처 작성자가 존재할 경우 - if(comment.signature.user.id == userId){ // 로그인한 사용자가 시그니처 작성자일 경우 댓글 삭제 가능 - can_delete = true; - } - } - getCommentDto.can_delete = can_delete; + async createSignatureCommentDto(comment: SignatureCommentEntity, userId: number){ // 댓글 DTO 만들기 + const writerProfile = new GetCommentWriterDto(); + const getCommentDto = new GetSignatureCommentDto(); - return getCommentDto; + // 2-[1] 댓글 작성자 정보 담기 + writerProfile._id = comment.user.id; + writerProfile.name = comment.user.nickname; - })); + // 로그인한 사용자가 댓글 작성자인지 확인 + if( userId == comment.user.id ) writerProfile.is_writer = true; - // 3. 스크롤 설정 - let hasNextData = true; - let cursor: number; + else writerProfile.is_writer = false; - const takePerScroll = cursorPageOptionsDto.take; - const isLastScroll = total <= takePerScroll; - const lastDataPerScroll = comments[comments.length - 1]; + // 작성자 프로필 이미지 + const image = comment.user.profileImage; + if(image == null) writerProfile.image = null; + else { + const userImageKey = image.imageKey; + writerProfile.image = await this.s3Service.getImageUrl(userImageKey); + } + + // 2-[2] 댓글 정보 담기 + getCommentDto._id = comment.id; + getCommentDto.content = comment.content; + getCommentDto.parentId = comment.parentComment.id; + getCommentDto.writer = writerProfile; + getCommentDto.date = comment.updated; + + // 댓글 수정 여부 구하기 + const createdTime = comment.created.getTime(); + const updatedTime = comment.updated.getTime(); - if (isLastScroll) { - hasNextData = false; - cursor = null; + if (Math.abs(createdTime - updatedTime) <= 2000) { // 두 시간 차가 2초 이하면 수정 안함 + getCommentDto.is_edited = false; } else { - cursor = lastDataPerScroll.parentComment.id - 1; + getCommentDto.is_edited = true; } - const cursorPageMetaDto = new CursorPageMetaDto( - { cursorPageOptionsDto, total, hasNextData, cursor }); - - return new CursorPageDto( result, cursorPageMetaDto ); + // 로그인한 사용자가 시그니처 작성하면 can_delete = true + let can_delete = false; + if(comment.signature.user){ // 시그니처 작성자가 존재할 경우 + if(comment.signature.user.id == userId){ // 로그인한 사용자가 시그니처 작성자일 경우 댓글 삭제 가능 + can_delete = true; + } + } + getCommentDto.can_delete = can_delete; + return getCommentDto; } From 9a0823331c268eae7731581e7e3dbacad1c00e4f Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 18 Feb 2024 18:52:52 +0900 Subject: [PATCH 310/316] =?UTF-8?q?update:=20=EC=95=8C=EB=A6=BC=EC=9D=98?= =?UTF-8?q?=20=EB=82=B4=EC=9A=A9=EC=9D=84=20=EB=B6=84=EB=A6=AC=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20=EC=A0=80=EC=9E=A5=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notification/notification.entity.ts | 14 +++++++++----- src/notification/notification.service.ts | 18 +++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/notification/notification.entity.ts b/src/notification/notification.entity.ts index a0395ae..90fa3d7 100644 --- a/src/notification/notification.entity.ts +++ b/src/notification/notification.entity.ts @@ -20,14 +20,18 @@ export class NotificationEntity extends BaseEntity { @ManyToOne(() => UserEntity) notificationReceiver: UserEntity; - @Column({ type: 'enum', enum: ['LIKE', 'COMMENT'] }) - notificationType: 'LIKE' | 'COMMENT'; + @JoinColumn() + @ManyToOne(() => UserEntity) + notificationSender: UserEntity; - @Column() - notificationContent: string; + @Column({ type: 'enum', enum: ['SIGNATURE', 'RULE'] }) + notificationTargetType: 'SIGNATURE' | 'RULE'; @Column() - notificationItemId: number; + notificationTargetId: number; + + @Column({ type: 'enum', enum: ['LIKE', 'COMMENT'] }) + notificationAction: 'LIKE' | 'COMMENT'; @Column({ default: false }) notificationRead: boolean; diff --git a/src/notification/notification.service.ts b/src/notification/notification.service.ts index c5a61c3..8e97115 100644 --- a/src/notification/notification.service.ts +++ b/src/notification/notification.service.ts @@ -7,12 +7,6 @@ import { ResponseCode } from '../response/response-code.enum'; export class NotificationService { private readonly logger = new Logger(NotificationService.name); - static createNotificationContent(type: 'LIKE' | 'COMMENT', nickname: string) { - return `${nickname}님이 내 시그니처에 ${ - type === 'LIKE' ? '좋아요' : '댓글' - }을 남겼습니다.`; - } - async listNotifications(userId: number) { try { const notifications = await NotificationEntity.find({ @@ -21,6 +15,9 @@ export class NotificationService { id: userId, }, }, + relations: { + notificationSender: true, + }, order: { created: 'DESC', }, @@ -45,9 +42,12 @@ export class NotificationService { '알림 조회 성공', notifications.map((notification) => ({ id: notification.id, - type: notification.notificationType, - content: notification.notificationContent, - itemId: notification.notificationItemId, + content: { + actionUserNickname: notification.notificationSender.nickname, + type: notification.notificationTargetType, + action: notification.notificationAction, + }, + itemId: notification.notificationTargetId, isRead: notification.notificationRead, created: notification.created, })), From 560b212c223c342aaba78361bcd330f770109390 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 18 Feb 2024 18:55:06 +0900 Subject: [PATCH 311/316] =?UTF-8?q?update:=20=EB=B3=80=EA=B2=BD=EB=90=9C?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=20entity=EC=97=90=20=EB=A7=9E=EC=B6=94?= =?UTF-8?q?=EC=96=B4=20=EC=95=8C=EB=A6=BC=20=ED=8A=B8=EB=A6=AC=EA=B1=B0?= =?UTF-8?q?=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 8 ++++---- src/signature/signature.service.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 139f6b7..0ed863f 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -37,10 +37,10 @@ export class CommentService { const notification = new NotificationEntity(); notification.notificationReceiver = invitation.member; - notification.notificationType = 'COMMENT'; - notification.notificationContent = - NotificationService.createNotificationContent('LIKE', user.nickname); - notification.notificationItemId = rule.id; + notification.notificationSender = user; + notification.notificationTargetType = 'RULE'; + notification.notificationTargetId = rule.id; + notification.notificationAction = 'COMMENT'; await notification.save(); } } diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 8e859e4..24196a0 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -274,10 +274,10 @@ export class SignatureService { // Todo: 좋아요를 했다가 해제한 경우에 알림을 어떻게 처리할 것인가? const notification = new NotificationEntity(); notification.notificationReceiver = signature.user; - notification.notificationType = 'LIKE'; - notification.notificationContent = - NotificationService.createNotificationContent('LIKE', loginUser.nickname); - notification.notificationItemId = signature.id; + notification.notificationSender = loginUser; + notification.notificationTargetType = 'SIGNATURE'; + notification.notificationTargetId = signature.id; + notification.notificationAction = 'LIKE'; await notification.save(); return signature; From a7c68e586748a19a0fe47dfdb0aa4b47dcd713d1 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 18 Feb 2024 18:57:15 +0900 Subject: [PATCH 312/316] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EB=8C=93=EA=B8=80=20=EC=9E=91=EC=84=B1=EC=8B=9C=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=EC=9D=84=20=EB=B0=9C=EC=86=A1=ED=95=98?= =?UTF-8?q?=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 45b0202..9beff7f 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -17,6 +17,7 @@ import { GetCommentWriterDto } from './dto/comment/get-comment-writer.dto'; import * as querystring from 'querystring'; import { CursorPageMetaDto } from '../mate/cursor-page/cursor-page.meta.dto'; import { CursorPageDto } from '../mate/cursor-page/cursor-page.dto'; +import { NotificationEntity } from '../notification/notification.entity'; @Injectable() export class SignatureCommentService{ @@ -48,6 +49,9 @@ export class SignatureCommentService{ comment.signature = signature; comment.content = createCommentDto.content; + // 알림 생성 + const notification = new NotificationEntity(); + // parentCommentId가 존재할 경우 -> 답글 / 존재하지 않을 경우 -> 댓글 if(parentCommentId){ // 대댓글: parentId는 파라미터로 받은 parentCommentId로 설정 @@ -61,13 +65,22 @@ export class SignatureCommentService{ await comment.save(); } + notification.notificationReceiver = parentComment.user; } else{ // 댓글: parentId는 본인으로 설정 const savedComment = await comment.save(); savedComment.parentComment = savedComment; await savedComment.save(); + + notification.notificationReceiver = signature.user; } + notification.notificationSender = user; + notification.notificationTargetType = 'SIGNATURE'; + notification.notificationTargetId = signature.id; + notification.notificationAction = 'COMMENT'; + await notification.save(); + return comment.id; } } From 0627ed3625485da4a2f8d9cb10fc5d1006b4cad2 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 18 Feb 2024 19:07:53 +0900 Subject: [PATCH 313/316] =?UTF-8?q?chore:=20lint=EB=A5=BC=20=EC=88=98?= =?UTF-8?q?=ED=96=89=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.controller.ts | 1 - src/comment/comment.controller.ts | 110 ++- src/comment/comment.service.ts | 64 +- src/comment/domain/comment.entity.ts | 12 +- src/comment/dto/create-comment.dto.ts | 8 +- .../detail-schedule.controller.ts | 5 +- src/detail-schedule/detail-schedule.entity.ts | 6 +- .../detail-schedule.service.ts | 8 +- src/diary/models/diary.image.entity.ts | 1 - src/follow/dto/follow.dto.ts | 46 +- src/follow/dto/follow.search.dto.ts | 52 +- src/follow/follow.controller.ts | 214 +++--- src/follow/follow.service.ts | 572 +++++++------- src/journey/journey.controller.ts | 5 +- src/journey/journey.service.ts | 15 +- src/journey/model/journey.entity.ts | 3 +- src/location/location.entity.ts | 7 - src/map/map.service.ts | 1 - .../cursor-page/cursor-page-option.dto.ts | 9 +- ...cursor-page-options-parameter.interface.ts | 2 +- .../cursor-page/cursor-page-order.enum.ts | 6 +- src/mate/cursor-page/cursor-page.dto.ts | 5 +- src/mate/cursor-page/cursor-page.meta.dto.ts | 10 +- src/mate/dto/mate-profile-response.dto.ts | 12 +- src/mate/dto/mate-profile-signature.dto.ts | 4 +- src/mate/dto/mate-recommend-profile.dto.ts | 12 +- src/mate/dto/mate-signature-cover.dto.ts | 8 +- .../mate-with-common-location-response.dto.ts | 6 +- src/mate/mate.controller.ts | 137 ++-- src/mate/mate.module.ts | 6 +- src/mate/mate.service.ts | 357 +++++---- src/response/response-code.enum.ts | 13 - src/response/response.dto.ts | 24 +- src/rule/domain/rule.invitation.entity.ts | 56 +- src/rule/domain/rule.main.entity.ts | 28 +- src/rule/domain/rule.sub.entity.ts | 19 +- src/rule/dto/create-rule.dto.ts | 8 +- ...cursor-page-options-parameter.interface.ts | 12 +- src/rule/dto/cursor-page-order.enum.ts | 6 +- src/rule/dto/cursor-page.dto.ts | 21 +- src/rule/dto/cursor-page.meta.dto.ts | 30 +- src/rule/dto/cursor-page.options.dto.ts | 29 +- src/rule/dto/detail.rule.dto.ts | 74 +- src/rule/dto/get-comment.dto.ts | 48 +- src/rule/dto/get-member-list.dto.ts | 32 +- src/rule/dto/get-rule-list.dto.ts | 64 +- .../dto/get-search-member-at-create.dto.ts | 34 +- src/rule/dto/get-search-member.dto.ts | 46 +- src/rule/dto/update-rule.dto.ts | 55 +- src/rule/rule.controller.ts | 268 ++++--- src/rule/rule.module.ts | 4 +- src/rule/rule.service.ts | 715 ++++++++++-------- src/schedule/schedule.entity.ts | 4 - src/schedule/schedule.service.ts | 5 +- src/search/dto/get-search-main.dto.ts | 4 +- src/search/dto/signature-cover.dto.ts | 8 +- src/search/search.controller.ts | 73 +- src/search/search.module.ts | 6 +- src/search/search.service.ts | 106 +-- .../domain/signature.comment.entity.ts | 28 +- src/signature/domain/signature.entity.ts | 78 +- src/signature/domain/signature.like.entity.ts | 29 +- src/signature/domain/signature.page.entity.ts | 9 +- .../dto/comment/create-comment.dto.ts | 7 +- .../dto/comment/get-comment-writer.dto.ts | 8 +- .../dto/comment/get-signature-comment.dto.ts | 10 +- src/signature/dto/like/get-like-list.dto.ts | 7 +- src/signature/dto/like/like-profile.dto.ts | 4 +- src/signature/dto/like/like-signature.dto.ts | 2 +- .../dto/signature/author-signature.dto.ts | 11 +- .../dto/signature/detail-signature.dto.ts | 12 +- .../dto/signature/header-signature.dto.ts | 14 +- .../dto/signature/home-signature.dto.ts | 8 +- .../dto/signature/page-signature.dto.ts | 2 +- src/signature/signature.comment.controller.ts | 163 ++-- src/signature/signature.comment.service.ts | 202 ++--- src/signature/signature.controller.ts | 214 +++--- src/signature/signature.module.ts | 11 +- src/signature/signature.service.ts | 20 +- src/user/optional.user.guard.ts | 4 +- src/user/user.following.entity.ts | 7 +- src/user/user.profile.image.entity.ts | 6 +- src/user/user.service.ts | 16 +- src/utils/S3.controller.ts | 21 +- src/utils/S3.presignedUrl.dto.ts | 4 +- src/utils/S3.service.ts | 7 +- 86 files changed, 2410 insertions(+), 1990 deletions(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index 08e392f..24fa193 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,5 +1,4 @@ import { Controller } from '@nestjs/common'; -import { AppService } from './app.service'; @Controller() export class AppController {} diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts index 3ff18f1..6aa42a2 100644 --- a/src/comment/comment.controller.ts +++ b/src/comment/comment.controller.ts @@ -1,79 +1,115 @@ -import {Controller, Post, Body, Req, UseGuards, Param, Patch, Delete} from '@nestjs/common'; +import { + Controller, + Post, + Body, + Req, + UseGuards, + Param, + Patch, + Delete, +} from '@nestjs/common'; import { CommentService } from './comment.service'; import { CreateCommentDto } from './dto/create-comment.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; -import {UserEntity} from "../user/user.entity"; -import {RuleMainEntity} from "../rule/domain/rule.main.entity"; @Controller('mate/rule/detail/comment') export class CommentController { - constructor( - private readonly commentService: CommentService, - ) {} + constructor(private readonly commentService: CommentService) {} // [1] 댓글 작성 @Post('/:ruleId') @UseGuards(UserGuard) - async createComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Req() req: Request): Promise> { - const result = await this.commentService.createComment(dto, ruleId, req.user.id); + async createComment( + @Body() dto: CreateCommentDto, + @Param('ruleId') ruleId: number, + @Req() req: Request, + ): Promise> { + const result = await this.commentService.createComment( + dto, + ruleId, + req.user.id, + ); - console.log('controller 진입') - if(!result){ + console.log('controller 진입'); + if (!result) { return new ResponseDto( ResponseCode.COMMENT_CREATION_FAIL, false, - "여행 규칙 코멘트 생성 실패", - null); - } - else{ + '여행 규칙 코멘트 생성 실패', + null, + ); + } else { return new ResponseDto( ResponseCode.COMMENT_CREATED, true, - "여행 규칙 코멘트 생성 성공", - result); + '여행 규칙 코멘트 생성 성공', + result, + ); } } // [2] 댓글 수정 @Patch('/:ruleId/:commentId') @UseGuards(UserGuard) - async updateComment(@Body() dto: CreateCommentDto, @Param('ruleId') ruleId: number, @Param('commentId') commentId: number,@Req() req: Request): Promise> { + async updateComment( + @Body() dto: CreateCommentDto, + @Param('ruleId') ruleId: number, + @Param('commentId') commentId: number, + @Req() req: Request, + ): Promise> { try { - const result = await this.commentService.updateComment(dto, ruleId, req.user.id, commentId); + const result = await this.commentService.updateComment( + dto, + ruleId, + req.user.id, + commentId, + ); return new ResponseDto( - ResponseCode.COMMENT_UPDATE_SUCCESS, - true, - "여행 규칙 코멘트 수정 성공", - result); + ResponseCode.COMMENT_UPDATE_SUCCESS, + true, + '여행 규칙 코멘트 수정 성공', + result, + ); } catch (e) { return new ResponseDto( - ResponseCode.COMMENT_UPDATE_FAIL, - false, - e.message, - null); + ResponseCode.COMMENT_UPDATE_FAIL, + false, + e.message, + null, + ); } } // [3] 댓글 삭제 @Delete('/:ruleId/:commentId') @UseGuards(UserGuard) - async deleteComment(@Param('ruleId') ruleId: number, @Param('commentId') commentId: number,@Req() req: Request): Promise> { + async deleteComment( + @Param('ruleId') ruleId: number, + @Param('commentId') commentId: number, + @Req() req: Request, + ): Promise> { try { - const result = await this.commentService.deleteComment(ruleId, req.user.id, commentId); + const result = await this.commentService.deleteComment( + ruleId, + req.user.id, + commentId, + ); return new ResponseDto( - ResponseCode.COMMENT_DELETE_SUCCESS, - true, - "여행 규칙 코멘트 삭제 성공", - result); + ResponseCode.COMMENT_DELETE_SUCCESS, + true, + '여행 규칙 코멘트 삭제 성공', + result, + ); } catch (e) { return new ResponseDto( - ResponseCode.COMMENT_DELETE_FAIL, - false, - e.message, - null); + ResponseCode.COMMENT_DELETE_FAIL, + false, + e.message, + null, + ); } } -} \ No newline at end of file +} diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index 0ed863f..acb25f2 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -1,29 +1,32 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { CreateCommentDto } from './dto/create-comment.dto'; import { CommentEntity } from './domain/comment.entity'; -import {RuleMainEntity} from "../rule/domain/rule.main.entity"; -import {UserEntity} from "../user/user.entity"; +import { RuleMainEntity } from '../rule/domain/rule.main.entity'; +import { UserEntity } from '../user/user.entity'; import { NotificationEntity } from '../notification/notification.entity'; -import { NotificationService } from '../notification/notification.service'; @Injectable() export class CommentService { - // [1] 댓글 작성 - async createComment(dto: CreateCommentDto, ruleId: number, userId: number): Promise { - + async createComment( + dto: CreateCommentDto, + ruleId: number, + userId: number, + ): Promise { const comment = new CommentEntity(); const user = await UserEntity.findOneOrFail({ where: { id: userId } }); - const rule = await RuleMainEntity.findOneOrFail({ where: { id: ruleId }, relations: { invitations: { member: true } } }); + const rule = await RuleMainEntity.findOneOrFail({ + where: { id: ruleId }, + relations: { invitations: { member: true } }, + }); - if(!user || !rule){ + if (!user || !rule) { throw new Error('Data not found'); - } - else{ - console.log("user name: "+ user.name); + } else { + console.log('user name: ' + user.name); comment.user = user; - console.log("rule id: "+ rule.id); + console.log('rule id: ' + rule.id); comment.rule = rule; comment.content = dto.content; await comment.save(); @@ -48,25 +51,30 @@ export class CommentService { } // [2] 댓글 수정 - async updateComment(dto: CreateCommentDto, ruleId: number, userId: number, commentId: number) : Promise { + async updateComment( + dto: CreateCommentDto, + ruleId: number, + userId: number, + commentId: number, + ): Promise { try { // 사용자, 규칙, 댓글 검증 const user = await UserEntity.findOne({ - where: {id : userId}, + where: { id: userId }, }); if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); const rule = await RuleMainEntity.findOne({ - where: {id: ruleId}, - }) + where: { id: ruleId }, + }); if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); const comment = await CommentEntity.findOne({ - where: {id: commentId} + where: { id: commentId }, }); if (!comment) throw new NotFoundException('존재하지 않는 댓글 입니다'); const checkValidateUser = await CommentEntity.findOne({ - where: {id: commentId, user: {id: userId}, rule: {id: ruleId}}} - ) + where: { id: commentId, user: { id: userId }, rule: { id: ruleId } }, + }); if (!!checkValidateUser) { comment.content = dto.content; await CommentEntity.save(comment); @@ -81,26 +89,30 @@ export class CommentService { } // [3] 댓글 삭제 - async deleteComment(ruleId: number, userId: number, commentId: number) : Promise { + async deleteComment( + ruleId: number, + userId: number, + commentId: number, + ): Promise { try { // 사용자, 규칙, 댓글 검증 const user = await UserEntity.findOne({ - where: {id : userId}, + where: { id: userId }, }); if (!user) throw new NotFoundException('사용자를 찾을 수 없습니다'); const rule = await RuleMainEntity.findOne({ - where: {id: ruleId}, - }) + where: { id: ruleId }, + }); if (!rule) throw new NotFoundException('존재하지 않는 규칙입니다'); const comment = await CommentEntity.findOne({ - where: {id: commentId} + where: { id: commentId }, }); if (!comment) throw new NotFoundException('존재하지 않는 댓글 입니다'); // 해당 규칙에, 해당 사용자가 작성한, 해당 댓글 ID를 가진 댓글이 있는지 검증 const checkValidateUser = await CommentEntity.findOne({ - where: {id: commentId, user: {id: userId}, rule: {id: ruleId}}} - ) + where: { id: commentId, user: { id: userId }, rule: { id: ruleId } }, + }); if (!!checkValidateUser) { await comment.softRemove(); console.log('댓글 삭제 성공'); diff --git a/src/comment/domain/comment.entity.ts b/src/comment/domain/comment.entity.ts index 112d4ab..800ed26 100644 --- a/src/comment/domain/comment.entity.ts +++ b/src/comment/domain/comment.entity.ts @@ -7,7 +7,7 @@ import { JoinColumn, CreateDateColumn, UpdateDateColumn, - DeleteDateColumn + DeleteDateColumn, } from 'typeorm'; import { RuleMainEntity } from 'src/rule/domain/rule.main.entity'; import { UserEntity } from 'src/user/user.entity'; @@ -20,12 +20,12 @@ export class CommentEntity extends BaseEntity { @Column({ type: 'varchar', length: 200 }) content: string; - @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.comments) - @JoinColumn({ name: 'rule_id'}) + @ManyToOne(() => RuleMainEntity, (ruleMain) => ruleMain.comments) + @JoinColumn({ name: 'rule_id' }) rule: RuleMainEntity; - @ManyToOne(() => UserEntity, user => user.comments) - @JoinColumn({ name: 'user_id'}) + @ManyToOne(() => UserEntity, (user) => user.comments) + @JoinColumn({ name: 'user_id' }) user: UserEntity; @CreateDateColumn() @@ -36,4 +36,4 @@ export class CommentEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/comment/dto/create-comment.dto.ts b/src/comment/dto/create-comment.dto.ts index 8fd8ba2..f9b0981 100644 --- a/src/comment/dto/create-comment.dto.ts +++ b/src/comment/dto/create-comment.dto.ts @@ -1,7 +1,7 @@ import { IsNotEmpty, IsString } from 'class-validator'; export class CreateCommentDto { - @IsNotEmpty() - @IsString() - content: string; -} \ No newline at end of file + @IsNotEmpty() + @IsString() + content: string; +} diff --git a/src/detail-schedule/detail-schedule.controller.ts b/src/detail-schedule/detail-schedule.controller.ts index 024606b..58516cb 100644 --- a/src/detail-schedule/detail-schedule.controller.ts +++ b/src/detail-schedule/detail-schedule.controller.ts @@ -12,10 +12,7 @@ import { import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; import { Request } from 'express'; import { DetailScheduleService } from './detail-schedule.service'; -import { - DetailContentDto, - DetailScheduleInfoDto, -} from './detail-schedule-info.dto'; +import { DetailContentDto } from './detail-schedule-info.dto'; import { UserGuard } from 'src/user/user.guard'; @Controller('detail-schedule') diff --git a/src/detail-schedule/detail-schedule.entity.ts b/src/detail-schedule/detail-schedule.entity.ts index ccccfeb..44d7c38 100644 --- a/src/detail-schedule/detail-schedule.entity.ts +++ b/src/detail-schedule/detail-schedule.entity.ts @@ -4,7 +4,6 @@ import { CreateDateColumn, DeleteDateColumn, Entity, - JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, @@ -12,10 +11,7 @@ import { import { NotFoundException } from '@nestjs/common'; import { BaseResponse } from 'src/response/response.status'; import { ScheduleEntity } from '../schedule/schedule.entity'; -import { - DetailContentDto, - DetailScheduleInfoDto, -} from './detail-schedule-info.dto'; +import { DetailContentDto } from './detail-schedule-info.dto'; @Entity() export class DetailScheduleEntity extends BaseEntity { diff --git a/src/detail-schedule/detail-schedule.service.ts b/src/detail-schedule/detail-schedule.service.ts index 50a3eae..241ff4e 100644 --- a/src/detail-schedule/detail-schedule.service.ts +++ b/src/detail-schedule/detail-schedule.service.ts @@ -11,10 +11,7 @@ export class DetailScheduleService { async createDetailSchedule(scheduleId: number, content: DetailContentDto) { const schedule = await ScheduleEntity.findExistSchedule(scheduleId); console.log(schedule.id); - const detailSchedule = await DetailScheduleEntity.createDetailSchedule( - schedule, - content, - ); + await DetailScheduleEntity.createDetailSchedule(schedule, content); return response(BaseResponse.DETAIL_SCHEDULE_CREATED); } @@ -45,8 +42,7 @@ export class DetailScheduleService { //세부일정 삭제하기 async deleteDetailSchedule(detailId: number) { const detailSchedule = await DetailScheduleEntity.findExistDetail(detailId); - const deleteDetailSchedule = - await DetailScheduleEntity.deleteDetailSchedule(detailSchedule); + await DetailScheduleEntity.deleteDetailSchedule(detailSchedule); return response(BaseResponse.DELETE_DETAIL_SCHEDULE_SUCCESS); } } diff --git a/src/diary/models/diary.image.entity.ts b/src/diary/models/diary.image.entity.ts index c9ab3f6..b39a49c 100644 --- a/src/diary/models/diary.image.entity.ts +++ b/src/diary/models/diary.image.entity.ts @@ -10,7 +10,6 @@ import { UpdateDateColumn, } from 'typeorm'; import { DiaryEntity } from './diary.entity'; -import { S3UtilService } from 'src/utils/S3.service'; @Entity() export class DiaryImageEntity extends BaseEntity { diff --git a/src/follow/dto/follow.dto.ts b/src/follow/dto/follow.dto.ts index 9c154b6..82a206f 100644 --- a/src/follow/dto/follow.dto.ts +++ b/src/follow/dto/follow.dto.ts @@ -1,27 +1,33 @@ -import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; +import { + IsBoolean, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from 'class-validator'; export class FollowDto { - @IsNotEmpty() - @IsNumber() - mateId: number; + @IsNotEmpty() + @IsNumber() + mateId: number; - @IsNotEmpty() - @IsString() - nickName: string; + @IsNotEmpty() + @IsString() + nickName: string; - @IsNotEmpty() - @IsString() - email: string; + @IsNotEmpty() + @IsString() + email: string; - @IsOptional() - @IsString() - image: string; + @IsOptional() + @IsString() + image: string; - @IsOptional() - @IsString() - introduction: string; + @IsOptional() + @IsString() + introduction: string; - @IsNotEmpty() - @IsBoolean() - isFollowing: boolean; -} \ No newline at end of file + @IsNotEmpty() + @IsBoolean() + isFollowing: boolean; +} diff --git a/src/follow/dto/follow.search.dto.ts b/src/follow/dto/follow.search.dto.ts index 06cbe00..108e28f 100644 --- a/src/follow/dto/follow.search.dto.ts +++ b/src/follow/dto/follow.search.dto.ts @@ -1,31 +1,37 @@ -import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; +import { + IsBoolean, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from 'class-validator'; export class FollowSearchDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - nickName: string; + @IsNotEmpty() + @IsString() + nickName: string; - @IsOptional() - @IsString() - introduction: string; + @IsOptional() + @IsString() + introduction: string; - @IsNotEmpty() - @IsString() - followerCnt: number; + @IsNotEmpty() + @IsString() + followerCnt: number; - @IsOptional() - @IsString() - followingCnt: number; + @IsOptional() + @IsString() + followingCnt: number; - @IsOptional() - @IsString() - image: string; + @IsOptional() + @IsString() + image: string; - @IsOptional() - @IsBoolean() - isFollowing: boolean; -} \ No newline at end of file + @IsOptional() + @IsBoolean() + isFollowing: boolean; +} diff --git a/src/follow/follow.controller.ts b/src/follow/follow.controller.ts index d3010da..923ddba 100644 --- a/src/follow/follow.controller.ts +++ b/src/follow/follow.controller.ts @@ -1,115 +1,129 @@ -import {Controller, Req, UseGuards, Param, Get, Patch, Query} from '@nestjs/common'; +import { + Controller, + Req, + UseGuards, + Param, + Get, + Patch, + Query, +} from '@nestjs/common'; import { FollowService } from './follow.service'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserGuard } from '../user/user.guard'; -import { Request} from 'express'; -import {CursorPageOptionsDto} from "../rule/dto/cursor-page.options.dto"; +import { Request } from 'express'; +import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; @Controller('mate/follow') export class FollowController { - constructor( - private readonly followService: FollowService, - ) {} + constructor(private readonly followService: FollowService) {} - // [1] 메이트 검색 - 무한 스크롤 적용 - @Get('/search') - @UseGuards(UserGuard) - async getSearchResult( - @Query('searchTerm')searchTerm : string, - @Query() cursorPageOptionsDto: CursorPageOptionsDto, - @Req() req: Request): Promise> { - try { - const result = await this.followService.getSearchResult(cursorPageOptionsDto, req.user.id, searchTerm); - return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_SUCCESS, - true, - "검색 결과 리스트 불러오기 성공", - result - ); - } catch (error) { - return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_FAIL, - false, - "검색 결과 리스트 불러오기 실패", - null - ); - } + // [1] 메이트 검색 - 무한 스크롤 적용 + @Get('/search') + @UseGuards(UserGuard) + async getSearchResult( + @Query('searchTerm') searchTerm: string, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, + @Req() req: Request, + ): Promise> { + try { + const result = await this.followService.getSearchResult( + cursorPageOptionsDto, + req.user.id, + searchTerm, + ); + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + '검색 결과 리스트 불러오기 성공', + result, + ); + } catch (error) { + return new ResponseDto( + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + '검색 결과 리스트 불러오기 실패', + null, + ); } + } - // [2] 팔로워 리스트 조회 - @Get('/followerList') - @UseGuards(UserGuard) - async getFollowerList(@Req() req: Request): Promise> { - try { - const followerList = await this.followService.getFollowerList(req.user.id); - return new ResponseDto( - ResponseCode.GET_FOLLOWER_LIST_SUCCESS, - true, - "팔로워 리스트 불러오기 성공", - followerList - ); - } catch (e) { - return new ResponseDto( - ResponseCode.GET_FOLLOWER_LIST_FAIL, - false, - e.message, - null - ); - } + // [2] 팔로워 리스트 조회 + @Get('/followerList') + @UseGuards(UserGuard) + async getFollowerList(@Req() req: Request): Promise> { + try { + const followerList = await this.followService.getFollowerList( + req.user.id, + ); + return new ResponseDto( + ResponseCode.GET_FOLLOWER_LIST_SUCCESS, + true, + '팔로워 리스트 불러오기 성공', + followerList, + ); + } catch (e) { + return new ResponseDto( + ResponseCode.GET_FOLLOWER_LIST_FAIL, + false, + e.message, + null, + ); } + } - // [3] 팔로우 리스트 조회 - @Get('/followList') - @UseGuards(UserGuard) - async getFollowList(@Req() req: Request): Promise> { - console.log('controller'); - try { - const followList = await this.followService.getFollowList(req.user.id); - return new ResponseDto( - ResponseCode.GET_FOLLOWING_LIST_SUCCESS, - true, - "팔로우 리스트 불러오기 성공", - followList - ); - } catch (e) { - return new ResponseDto( - ResponseCode.GET_FOLLOWING_LIST_FAIL, - false, - e.message, - null - ); - } + // [3] 팔로우 리스트 조회 + @Get('/followList') + @UseGuards(UserGuard) + async getFollowList(@Req() req: Request): Promise> { + console.log('controller'); + try { + const followList = await this.followService.getFollowList(req.user.id); + return new ResponseDto( + ResponseCode.GET_FOLLOWING_LIST_SUCCESS, + true, + '팔로우 리스트 불러오기 성공', + followList, + ); + } catch (e) { + return new ResponseDto( + ResponseCode.GET_FOLLOWING_LIST_FAIL, + false, + e.message, + null, + ); } + } - // [4] 팔로우 - @Patch('/:followingId') - @UseGuards(UserGuard) - async createFollow(@Req() req: Request, @Param('followingId') followingId : number): Promise> { - try { - const result = await this.followService.checkFollow(req.user.id, followingId); - if (!!result.deleted) { - return new ResponseDto( - ResponseCode.FOLLOW_SUCCESS, - true, - "언팔로우 성공", - result.id - ); - } else { - return new ResponseDto( - ResponseCode.FOLLOW_SUCCESS, - true, - "팔로우 성공", - result.id - ); - } - } catch (e) { - return new ResponseDto( - ResponseCode.FOLLOW_FAIL, - false, - e.message, - null - ); - } + // [4] 팔로우 + @Patch('/:followingId') + @UseGuards(UserGuard) + async createFollow( + @Req() req: Request, + @Param('followingId') followingId: number, + ): Promise> { + try { + const result = await this.followService.checkFollow( + req.user.id, + followingId, + ); + if (!!result.deleted) { + return new ResponseDto( + ResponseCode.FOLLOW_SUCCESS, + true, + '언팔로우 성공', + result.id, + ); + } else { + return new ResponseDto( + ResponseCode.FOLLOW_SUCCESS, + true, + '팔로우 성공', + result.id, + ); + } + } catch (e) { + return new ResponseDto(ResponseCode.FOLLOW_FAIL, false, e.message, null); } -} \ No newline at end of file + } +} diff --git a/src/follow/follow.service.ts b/src/follow/follow.service.ts index 4468d94..afd4f5e 100644 --- a/src/follow/follow.service.ts +++ b/src/follow/follow.service.ts @@ -1,274 +1,322 @@ -import {Injectable, HttpException, NotFoundException, BadRequestException} from '@nestjs/common'; +import { + Injectable, + NotFoundException, + BadRequestException, +} from '@nestjs/common'; import { UserFollowingEntity } from 'src/user/user.following.entity'; import { FollowDto } from './dto/follow.dto'; -import { UserEntity } from "../user/user.entity"; -import { UserService } from "../user/user.service"; -import { S3UtilService } from "../utils/S3.service"; -import {LessThan, Like} from "typeorm"; -import {FollowSearchDto} from "./dto/follow.search.dto"; -import {CursorPageOptionsDto} from "../rule/dto/cursor-page.options.dto"; -import {CursorPageMetaDto} from "../rule/dto/cursor-page.meta.dto"; -import {CursorPageDto} from "../rule/dto/cursor-page.dto"; +import { UserEntity } from '../user/user.entity'; +import { UserService } from '../user/user.service'; +import { S3UtilService } from '../utils/S3.service'; +import { LessThan, Like } from 'typeorm'; +import { FollowSearchDto } from './dto/follow.search.dto'; +import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; +import { CursorPageMetaDto } from '../rule/dto/cursor-page.meta.dto'; +import { CursorPageDto } from '../rule/dto/cursor-page.dto'; @Injectable() export class FollowService { - constructor( - private readonly userService: UserService, - private readonly s3Service: S3UtilService, - ) {} - - // [1] 메이트 검색 - async getSearchResult(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string) { - - try { - // 검증1) 사용자가 존재하지 않는 경우 - const userEntity = await UserEntity.findOne({ - where: { - id: userId, - }, - }); - if (!userEntity) throw new Error('사용자를 찾을 수 없습니다'); - - let cursorId: number = 0; - - // (1) cursorId 설정 - // -1) 처음 요청인 경우 - if (cursorPageOptionsDto.cursorId == 0) { - const newUser = await UserEntity.find({ - order: { - id: 'DESC' // 가장 최근에 가입한 유저 - }, - take: 1 - }); - cursorId = newUser[0].id + 1; - - console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); - console.log('cursor: ', cursorId); - // -2) 처음 요청이 아닌 경우 - } else { - cursorId = cursorPageOptionsDto.cursorId; - console.log('cursorPageOptionsDto.cursorId != 0 로 인식') - } - console.log('cursor: ', cursorId); - - // (2) 데이터 조회 - // 검색 결과에 해당하는 값 찾기 - // 해당 결과값을 nickName 에 포함하고 있는 사용자 찾기 - - console.log('검색 값: ', searchTerm); - - // take 초기값 설정 - console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); - if (cursorPageOptionsDto.take == 0) { - cursorPageOptionsDto.take = 5; - } - - const [resultUsers, total] = await UserEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: [ - { - id: cursorId ? LessThan(cursorId) : null, - isQuit: false, - nickname: Like(`%${searchTerm}%`), - } - ], - relations: {profileImage : true, follower : true, following : true}, - order: { - id: "DESC" as any, - }, - }); - - const searchResult = await Promise.all(resultUsers.map(async (user) => { - const followSearchDto = new FollowSearchDto(); - - console.log('현재의 유저 : ', user.id); - followSearchDto.id = user.id; - followSearchDto.nickName = user.nickname; - followSearchDto.introduction = user.introduction; - - followSearchDto.followerCnt = user.follower.length; - followSearchDto.followingCnt = user.following.length; - - // 팔로우 여부 - followSearchDto.isFollowing = await this.userService.checkIfFollowing(userEntity, followSearchDto.id); - - // 사용자 프로필 이미지 - const image = user.profileImage; - if(image == null) followSearchDto.image = null; - else{ - const userImageKey = image.imageKey; - followSearchDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return followSearchDto; - })); - - // (3) 페이징 및 정렬 기준 설정 - let hasNextData = true; - let cursor: number; - - const takePerScroll = cursorPageOptionsDto.take; - const isLastScroll = total <= takePerScroll; - const lastDataPerScroll = resultUsers[resultUsers.length - 1]; - - if (isLastScroll) { - hasNextData = false; - cursor = null; - } else { - cursor = lastDataPerScroll.id; - } - - const cursorPageMetaDto = new CursorPageMetaDto({ cursorPageOptionsDto, total, hasNextData, cursor }); - - return new CursorPageDto(searchResult, cursorPageMetaDto); - } catch (e) { - throw new Error(e.message); - } - } - - // [2] 팔로우 리스트 조회 - async getFollowList(userId: number): Promise { - // 현재 로그인한 사용자 - const user : UserEntity = await this.userService.findUserById(userId); - console.log('현재 로그인한 사용자 : ',user.id); - - // 로그인한 사용자 = 팔로우하는 user - const follows : UserFollowingEntity[] = await this.userService.getFollowingList(userId); - - // 팔로우 사용자들 정보 리스트 - const informs = await Promise.all(follows.map(async (follow) => { - const followDto : FollowDto = new FollowDto(); - const mateEntity : UserEntity = follow.followUser; - console.log('팔로우 사용자의 ID : ', mateEntity.id); - - followDto.nickName = mateEntity.nickname; - followDto.mateId = mateEntity.id; - followDto.email = mateEntity.email; - followDto.introduction = mateEntity.introduction; - followDto.isFollowing = !!follow.id; - - // 사용자 프로필 이미지 - const image = await this.userService.getProfileImage(mateEntity.id); - if(image == null) followDto.image = null; - else{ - const userImageKey = image.imageKey; - followDto.image = await this.s3Service.getImageUrl(userImageKey); - } - - return followDto; - })); - return informs; - } - - // [3] 팔로워 리스트 조회 - async getFollowerList(userId: number): Promise { - // 현재 로그인한 사용자 - const user : UserEntity = await this.userService.findUserById(userId); - console.log('현재 로그인한 사용자 : ',user.id); - - // 로그인한 사용자 = 팔로워 - const follows : UserFollowingEntity[] = await this.userService.getFollowerList(userId); - - // 팔로워 사용자들 정보 리스트 - const informs = await Promise.all(follows.map(async (follow) => { - const followDto : FollowDto = new FollowDto(); - const mateEntity : UserEntity = follow.user; - console.log('팔로워 사용자 ID : ', mateEntity.id); - - followDto.nickName = mateEntity.nickname; - followDto.mateId = mateEntity.id; - followDto.email = mateEntity.email; - followDto.introduction = mateEntity.introduction; - followDto.isFollowing = await this.userService.checkIfFollowing(user,mateEntity.id); - - // 사용자 프로필 이미지 - const image = await this.userService.getProfileImage(mateEntity.id); - if(image == null) followDto.image = null; - else{ - const userImageKey = image.imageKey; - followDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return followDto; - })); - - return informs; + constructor( + private readonly userService: UserService, + private readonly s3Service: S3UtilService, + ) {} + + // [1] 메이트 검색 + async getSearchResult( + cursorPageOptionsDto: CursorPageOptionsDto, + userId: number, + searchTerm: string, + ) { + try { + // 검증1) 사용자가 존재하지 않는 경우 + const userEntity = await UserEntity.findOne({ + where: { + id: userId, + }, + }); + if (!userEntity) throw new Error('사용자를 찾을 수 없습니다'); + + let cursorId = 0; + + // (1) cursorId 설정 + // -1) 처음 요청인 경우 + if (cursorPageOptionsDto.cursorId == 0) { + const newUser = await UserEntity.find({ + order: { + id: 'DESC', // 가장 최근에 가입한 유저 + }, + take: 1, + }); + cursorId = newUser[0].id + 1; + + console.log('cursorPageOptionsDto.cursorId == 0 로 인식'); + console.log('cursor: ', cursorId); + // -2) 처음 요청이 아닌 경우 + } else { + cursorId = cursorPageOptionsDto.cursorId; + console.log('cursorPageOptionsDto.cursorId != 0 로 인식'); + } + console.log('cursor: ', cursorId); + + // (2) 데이터 조회 + // 검색 결과에 해당하는 값 찾기 + // 해당 결과값을 nickName 에 포함하고 있는 사용자 찾기 + + console.log('검색 값: ', searchTerm); + + // take 초기값 설정 + console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); + if (cursorPageOptionsDto.take == 0) { + cursorPageOptionsDto.take = 5; + } + + const [resultUsers, total] = await UserEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: [ + { + id: cursorId ? LessThan(cursorId) : null, + isQuit: false, + nickname: Like(`%${searchTerm}%`), + }, + ], + relations: { profileImage: true, follower: true, following: true }, + order: { + id: 'DESC' as any, + }, + }); + + const searchResult = await Promise.all( + resultUsers.map(async (user) => { + const followSearchDto = new FollowSearchDto(); + + console.log('현재의 유저 : ', user.id); + followSearchDto.id = user.id; + followSearchDto.nickName = user.nickname; + followSearchDto.introduction = user.introduction; + + followSearchDto.followerCnt = user.follower.length; + followSearchDto.followingCnt = user.following.length; + + // 팔로우 여부 + followSearchDto.isFollowing = await this.userService.checkIfFollowing( + userEntity, + followSearchDto.id, + ); + + // 사용자 프로필 이미지 + const image = user.profileImage; + if (image == null) followSearchDto.image = null; + else { + const userImageKey = image.imageKey; + followSearchDto.image = await this.s3Service.getImageUrl( + userImageKey, + ); + } + return followSearchDto; + }), + ); + + // (3) 페이징 및 정렬 기준 설정 + let hasNextData = true; + let cursor: number; + + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = resultUsers[resultUsers.length - 1]; + + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } + + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); + + return new CursorPageDto(searchResult, cursorPageMetaDto); + } catch (e) { + throw new Error(e.message); } - - // [4] 팔로우 가능한 사이인지 검증 - async checkFollow(userId : number, followingId : number): Promise { - try { - // case1) 유효한 유저인지 검증 - const userEntity : UserEntity = await UserEntity.findOne({ - where: {id: userId} - }); - if(!userEntity) throw new NotFoundException('요청을 보낸 사용자를 찾을 수 없습니다') - const followingUser = await UserEntity.findOne({ - where: { - id: followingId - } - }); - if (followingUser.isQuit == true) throw new BadRequestException('탈퇴한 회원 입니다'); - if (!followingUser) throw new NotFoundException('대상 사용자를 찾을 수 없습니다'); - console.log('현재 로그인한 유저 : ', userEntity); - console.log('팔로우 대상 유저 : ', followingUser); - - // case2) 본인을 팔로우한 경우 - if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); - - // case3) 팔로우 관계 확인 - const isAlreadyFollowing = await this.userService.isAlreadyFollowing(userId, followingId); - console.log('Is already following? : ', isAlreadyFollowing); - - // [2] 이미 팔로우 한 사이, 팔로우 취소 - if (isAlreadyFollowing) { - console.log('언팔로우 service 호출'); - return this.deleteFollow(userId, followingId); - } else { - // [1] 팔로우 - console.log('팔로우 service 호출'); - return this.createFollow(userId, followingId); - } - } catch (e) { - console.log('팔로우 요청에 실패하였습니다'); - throw new Error(e.message); + } + + // [2] 팔로우 리스트 조회 + async getFollowList(userId: number): Promise { + // 현재 로그인한 사용자 + const user: UserEntity = await this.userService.findUserById(userId); + console.log('현재 로그인한 사용자 : ', user.id); + + // 로그인한 사용자 = 팔로우하는 user + const follows: UserFollowingEntity[] = + await this.userService.getFollowingList(userId); + + // 팔로우 사용자들 정보 리스트 + const informs = await Promise.all( + follows.map(async (follow) => { + const followDto: FollowDto = new FollowDto(); + const mateEntity: UserEntity = follow.followUser; + console.log('팔로우 사용자의 ID : ', mateEntity.id); + + followDto.nickName = mateEntity.nickname; + followDto.mateId = mateEntity.id; + followDto.email = mateEntity.email; + followDto.introduction = mateEntity.introduction; + followDto.isFollowing = !!follow.id; + + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage(mateEntity.id); + if (image == null) followDto.image = null; + else { + const userImageKey = image.imageKey; + followDto.image = await this.s3Service.getImageUrl(userImageKey); } - } - // [4-1] 팔로우 - async createFollow(userId : number, followingId : number): Promise { - - try { - const userEntity : UserEntity = await this.userService.findUserById(userId); - const followingUser = await UserEntity.findExistUser(followingId); - if (!followingUser) throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); - console.log('현재 로그인한 유저 : ', userEntity); - console.log('팔로우 대상 유저 : ', followingUser); - if (userId == followingId) throw new BadRequestException('본인을 팔로우 할 수 없습니다'); - - const userFollowingEntity = new UserFollowingEntity(); - userFollowingEntity.user = userEntity; - userFollowingEntity.followUser = followingUser; - - await userFollowingEntity.save(); - return userFollowingEntity; - } catch (e) { - console.log('팔로우 요청에 실패하였습니다'); - throw new Error(e.message); + return followDto; + }), + ); + return informs; + } + + // [3] 팔로워 리스트 조회 + async getFollowerList(userId: number): Promise { + // 현재 로그인한 사용자 + const user: UserEntity = await this.userService.findUserById(userId); + console.log('현재 로그인한 사용자 : ', user.id); + + // 로그인한 사용자 = 팔로워 + const follows: UserFollowingEntity[] = + await this.userService.getFollowerList(userId); + + // 팔로워 사용자들 정보 리스트 + const informs = await Promise.all( + follows.map(async (follow) => { + const followDto: FollowDto = new FollowDto(); + const mateEntity: UserEntity = follow.user; + console.log('팔로워 사용자 ID : ', mateEntity.id); + + followDto.nickName = mateEntity.nickname; + followDto.mateId = mateEntity.id; + followDto.email = mateEntity.email; + followDto.introduction = mateEntity.introduction; + followDto.isFollowing = await this.userService.checkIfFollowing( + user, + mateEntity.id, + ); + + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage(mateEntity.id); + if (image == null) followDto.image = null; + else { + const userImageKey = image.imageKey; + followDto.image = await this.s3Service.getImageUrl(userImageKey); } + return followDto; + }), + ); + + return informs; + } + + // [4] 팔로우 가능한 사이인지 검증 + async checkFollow( + userId: number, + followingId: number, + ): Promise { + try { + // case1) 유효한 유저인지 검증 + const userEntity: UserEntity = await UserEntity.findOne({ + where: { id: userId }, + }); + if (!userEntity) + throw new NotFoundException('요청을 보낸 사용자를 찾을 수 없습니다'); + const followingUser = await UserEntity.findOne({ + where: { + id: followingId, + }, + }); + if (followingUser.isQuit == true) + throw new BadRequestException('탈퇴한 회원 입니다'); + if (!followingUser) + throw new NotFoundException('대상 사용자를 찾을 수 없습니다'); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUser); + + // case2) 본인을 팔로우한 경우 + if (userId == followingId) + throw new BadRequestException('본인을 팔로우 할 수 없습니다'); + + // case3) 팔로우 관계 확인 + const isAlreadyFollowing = await this.userService.isAlreadyFollowing( + userId, + followingId, + ); + console.log('Is already following? : ', isAlreadyFollowing); + + // [2] 이미 팔로우 한 사이, 팔로우 취소 + if (isAlreadyFollowing) { + console.log('언팔로우 service 호출'); + return this.deleteFollow(userId, followingId); + } else { + // [1] 팔로우 + console.log('팔로우 service 호출'); + return this.createFollow(userId, followingId); + } + } catch (e) { + console.log('팔로우 요청에 실패하였습니다'); + throw new Error(e.message); } - - // [4-2] 언팔로우 - async deleteFollow(userId: number, followingId:number): Promise { - console.log('언팔로우 서비스 호출'); - const followEntity : UserFollowingEntity = await UserFollowingEntity.findOneOrFail({ where: - { user : {id : userId}, followUser : {id : followingId}} - }); - - try{ - await followEntity.softRemove(); - return followEntity; - }catch(e){ - console.error('언팔로우 요청에 실패하였습니다: '); - throw new Error(e.message); - } + } + + // [4-1] 팔로우 + async createFollow( + userId: number, + followingId: number, + ): Promise { + try { + const userEntity: UserEntity = await this.userService.findUserById( + userId, + ); + const followingUser = await UserEntity.findExistUser(followingId); + if (!followingUser) + throw new NotFoundException('해당 사용자를 찾을 수 없습니다'); + console.log('현재 로그인한 유저 : ', userEntity); + console.log('팔로우 대상 유저 : ', followingUser); + if (userId == followingId) + throw new BadRequestException('본인을 팔로우 할 수 없습니다'); + + const userFollowingEntity = new UserFollowingEntity(); + userFollowingEntity.user = userEntity; + userFollowingEntity.followUser = followingUser; + + await userFollowingEntity.save(); + return userFollowingEntity; + } catch (e) { + console.log('팔로우 요청에 실패하였습니다'); + throw new Error(e.message); + } + } + + // [4-2] 언팔로우 + async deleteFollow( + userId: number, + followingId: number, + ): Promise { + console.log('언팔로우 서비스 호출'); + const followEntity: UserFollowingEntity = + await UserFollowingEntity.findOneOrFail({ + where: { user: { id: userId }, followUser: { id: followingId } }, + }); + + try { + await followEntity.softRemove(); + return followEntity; + } catch (e) { + console.error('언팔로우 요청에 실패하였습니다: '); + throw new Error(e.message); } + } } diff --git a/src/journey/journey.controller.ts b/src/journey/journey.controller.ts index 80aea7e..6ffb133 100644 --- a/src/journey/journey.controller.ts +++ b/src/journey/journey.controller.ts @@ -72,10 +72,7 @@ export class JourneyController { }) @UseGuards(UserGuard) @Delete('delete/:journeyId') - async deleteJourney( - @Param('journeyId') journeyId: number, - @Req() req: Request, - ) { + async deleteJourney(@Param('journeyId') journeyId: number) { const result = await this.journeyService.deleteJourney(journeyId); return result; } diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index b1811f3..715d6f6 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -1,9 +1,5 @@ // journey.service.ts -import { - ConflictException, - Injectable, - NotFoundException, -} from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { addDays, isAfter, isEqual } from 'date-fns'; import { JourneyEntity } from './model/journey.entity'; import { errResponse, response } from 'src/response/response'; @@ -34,7 +30,7 @@ export class JourneyService { const startDate = new Date(createJourneyDto.startDate); const endDate = new Date(createJourneyDto.endDate); - const schedules = await this.createSchedules(journey, startDate, endDate); + await this.createSchedules(journey, startDate, endDate); return errResponse(BaseResponse.JOURNEY_CREATED); } @@ -43,7 +39,6 @@ export class JourneyService { startDate: Date, endDate: Date, ) { - let currentDate = startDate; const schedules = []; // startDate와 endDate가 같은 경우 하나의 schedule 생성 @@ -78,10 +73,10 @@ export class JourneyService { return errResponse(BaseResponse.JOURNEY_NOT_FOUND); } if (title === null) { - const updateJourney = await JourneyEntity.updateJourney(journey, ''); + await JourneyEntity.updateJourney(journey, ''); return response(BaseResponse.UPDATE_JOURNEY_TITLE_SUCCESS); } - const updateJourney = await JourneyEntity.updateJourney(journey, title); + await JourneyEntity.updateJourney(journey, title); return response(BaseResponse.UPDATE_JOURNEY_TITLE_SUCCESS); } @@ -96,7 +91,7 @@ export class JourneyService { await this.deleteScheduleRelations(schedule); } - const deleteJourney = await JourneyEntity.deleteJourney(journey); + await JourneyEntity.deleteJourney(journey); return response(BaseResponse.DELETE_JOURNEY_SUCCESS); } async deleteScheduleRelations(schedule) { diff --git a/src/journey/model/journey.entity.ts b/src/journey/model/journey.entity.ts index 1c546fb..1d0217f 100644 --- a/src/journey/model/journey.entity.ts +++ b/src/journey/model/journey.entity.ts @@ -14,11 +14,10 @@ import { LessThanOrEqual, MoreThanOrEqual, } from 'typeorm'; -import { startOfMonth, endOfMonth, isWithinInterval } from 'date-fns'; +import { isWithinInterval } from 'date-fns'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { MonthInfoDto } from 'src/map/month-info.dto'; -import { CreateJourneyDto } from '../dtos/create-journey.dto'; @Entity() export class JourneyEntity extends BaseEntity { diff --git a/src/location/location.entity.ts b/src/location/location.entity.ts index 300f9ed..3ff79ca 100644 --- a/src/location/location.entity.ts +++ b/src/location/location.entity.ts @@ -83,11 +83,4 @@ export class LocationEntity extends BaseEntity { }); return location; } - - static async findExistLocationById(schedule) { - const existLocation = await LocationEntity.findOne({ - where: { schedules: {} }, - }); - return existLocation; - } } diff --git a/src/map/map.service.ts b/src/map/map.service.ts index ea4be7d..4cc52c8 100644 --- a/src/map/map.service.ts +++ b/src/map/map.service.ts @@ -6,7 +6,6 @@ import { UserEntity } from 'src/user/user.entity'; import { DiaryEntity } from 'src/diary/models/diary.entity'; import { ScheduleEntity } from 'src/schedule/schedule.entity'; import { MonthInfoDto } from './month-info.dto'; -import { LocationEntity } from 'src/location/location.entity'; import { DiaryImageEntity } from 'src/diary/models/diary.image.entity'; import { DetailScheduleEntity } from 'src/detail-schedule/detail-schedule.entity'; import { CursorBasedPaginationRequestDto } from './cursor-based-pagination-request.dto.ts'; diff --git a/src/mate/cursor-page/cursor-page-option.dto.ts b/src/mate/cursor-page/cursor-page-option.dto.ts index 8319054..821cc11 100644 --- a/src/mate/cursor-page/cursor-page-option.dto.ts +++ b/src/mate/cursor-page/cursor-page-option.dto.ts @@ -1,11 +1,10 @@ // cursor-page.options.dto.ts -import { Type } from "class-transformer"; -import { IsEnum, IsOptional } from "class-validator"; +import { Type } from 'class-transformer'; +import { IsEnum, IsOptional } from 'class-validator'; import { Order } from './cursor-page-order.enum'; export class CursorPageOptionsDto { - @Type(() => String) @IsEnum(Order) @IsOptional() @@ -17,5 +16,5 @@ export class CursorPageOptionsDto { @Type(() => Number) @IsOptional() - readonly cursorId?: number = "" as any; -} \ No newline at end of file + readonly cursorId?: number = '' as any; +} diff --git a/src/mate/cursor-page/cursor-page-options-parameter.interface.ts b/src/mate/cursor-page/cursor-page-options-parameter.interface.ts index 3baa0fd..596de7a 100644 --- a/src/mate/cursor-page/cursor-page-options-parameter.interface.ts +++ b/src/mate/cursor-page/cursor-page-options-parameter.interface.ts @@ -5,4 +5,4 @@ export interface CursorPageMetaDtoParameters { total: number; hasNextData: boolean; cursor: number; -} \ No newline at end of file +} diff --git a/src/mate/cursor-page/cursor-page-order.enum.ts b/src/mate/cursor-page/cursor-page-order.enum.ts index 2ff68ab..47e0e41 100644 --- a/src/mate/cursor-page/cursor-page-order.enum.ts +++ b/src/mate/cursor-page/cursor-page-order.enum.ts @@ -1,5 +1,5 @@ // cursor-page-order.enum.ts export enum Order { - ASC = "asc", - DESC = "desc" -} \ No newline at end of file + ASC = 'asc', + DESC = 'desc', +} diff --git a/src/mate/cursor-page/cursor-page.dto.ts b/src/mate/cursor-page/cursor-page.dto.ts index 33f1dc3..d53b792 100644 --- a/src/mate/cursor-page/cursor-page.dto.ts +++ b/src/mate/cursor-page/cursor-page.dto.ts @@ -1,8 +1,7 @@ -import { IsArray } from "class-validator"; +import { IsArray } from 'class-validator'; import { CursorPageMetaDto } from './cursor-page.meta.dto'; export class CursorPageDto { - @IsArray() readonly data: T[]; @@ -12,4 +11,4 @@ export class CursorPageDto { this.data = data; this.meta = meta; } -} \ No newline at end of file +} diff --git a/src/mate/cursor-page/cursor-page.meta.dto.ts b/src/mate/cursor-page/cursor-page.meta.dto.ts index 041c915..62198fa 100644 --- a/src/mate/cursor-page/cursor-page.meta.dto.ts +++ b/src/mate/cursor-page/cursor-page.meta.dto.ts @@ -3,16 +3,20 @@ import { CursorPageMetaDtoParameters } from './cursor-page-options-parameter.interface'; export class CursorPageMetaDto { - readonly total: number; readonly take: number; readonly hasNextData: boolean; readonly cursor: number; - constructor({cursorPageOptionsDto, total, hasNextData, cursor}: CursorPageMetaDtoParameters) { + constructor({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }: CursorPageMetaDtoParameters) { this.take = cursorPageOptionsDto.take; this.total = total; this.hasNextData = hasNextData; this.cursor = cursor; } -} \ No newline at end of file +} diff --git a/src/mate/dto/mate-profile-response.dto.ts b/src/mate/dto/mate-profile-response.dto.ts index 526600f..ae8b4c6 100644 --- a/src/mate/dto/mate-profile-response.dto.ts +++ b/src/mate/dto/mate-profile-response.dto.ts @@ -1,14 +1,14 @@ // mate-profile-response.dto.ts export class MateProfileResponseDto { - _id : number; + _id: number; image: string; nickname: string; introduction: string; is_followed: boolean; - signatures: number; // 시그니처 개수 - follower: number; // 팔로워 수 - following: number; // 팔로잉 수 - isQuit: boolean; // 탈퇴 여부 -} \ No newline at end of file + signatures: number; // 시그니처 개수 + follower: number; // 팔로워 수 + following: number; // 팔로잉 수 + isQuit: boolean; // 탈퇴 여부 +} diff --git a/src/mate/dto/mate-profile-signature.dto.ts b/src/mate/dto/mate-profile-signature.dto.ts index 36fc858..ae3edd7 100644 --- a/src/mate/dto/mate-profile-signature.dto.ts +++ b/src/mate/dto/mate-profile-signature.dto.ts @@ -1,6 +1,6 @@ // mate-profile-signature.dto.ts -export class MateProfileSignatureDto{ +export class MateProfileSignatureDto { _id: number; image: string; -} \ No newline at end of file +} diff --git a/src/mate/dto/mate-recommend-profile.dto.ts b/src/mate/dto/mate-recommend-profile.dto.ts index 905b116..10297c2 100644 --- a/src/mate/dto/mate-recommend-profile.dto.ts +++ b/src/mate/dto/mate-recommend-profile.dto.ts @@ -2,11 +2,11 @@ import { MateSignatureCoverDto } from './mate-signature-cover.dto'; -export class MateRecommendProfileDto{ +export class MateRecommendProfileDto { _id: number; - mateImage: string; // 유저 사진 - mateName: string; // 유저 별명 - is_followed: boolean; // 팔로우 여부 - introduction: string; // 한 줄 소개 + mateImage: string; // 유저 사진 + mateName: string; // 유저 별명 + is_followed: boolean; // 팔로우 여부 + introduction: string; // 한 줄 소개 signatures: MateSignatureCoverDto[]; -} \ No newline at end of file +} diff --git a/src/mate/dto/mate-signature-cover.dto.ts b/src/mate/dto/mate-signature-cover.dto.ts index 8e0db99..16a93fb 100644 --- a/src/mate/dto/mate-signature-cover.dto.ts +++ b/src/mate/dto/mate-signature-cover.dto.ts @@ -1,8 +1,8 @@ // mate-signature-cover.dto.ts -export class MateSignatureCoverDto{ +export class MateSignatureCoverDto { _id: number; - image: string - title: string + image: string; + title: string; liked: number; -} \ No newline at end of file +} diff --git a/src/mate/dto/mate-with-common-location-response.dto.ts b/src/mate/dto/mate-with-common-location-response.dto.ts index f7f772a..f98e151 100644 --- a/src/mate/dto/mate-with-common-location-response.dto.ts +++ b/src/mate/dto/mate-with-common-location-response.dto.ts @@ -2,8 +2,8 @@ import { MateRecommendProfileDto } from './mate-recommend-profile.dto'; -export class MateWithCommonLocationResponseDto{ +export class MateWithCommonLocationResponseDto { location: string; - userName: string; // #112 로그인한 사용자 닉네임 추가 + userName: string; // #112 로그인한 사용자 닉네임 추가 mateProfiles: MateRecommendProfileDto[]; -} \ No newline at end of file +} diff --git a/src/mate/mate.controller.ts b/src/mate/mate.controller.ts index 26d7f10..e485c4f 100644 --- a/src/mate/mate.controller.ts +++ b/src/mate/mate.controller.ts @@ -6,40 +6,40 @@ import { Request } from 'express'; import { CursorPageOptionsDto } from './cursor-page/cursor-page-option.dto'; import { MateService } from './mate.service'; import { ResponseDto } from '../response/response.dto'; -import { MateRecommendProfileDto } from './dto/mate-recommend-profile.dto'; import { ResponseCode } from '../response/response-code.enum'; import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; import { MateProfileResponseDto } from './dto/mate-profile-response.dto'; @Controller('/mate') -export class MateController{ - - constructor(private readonly mateService:MateService) {} - +export class MateController { + constructor(private readonly mateService: MateService) {} @Get('/random') // 메이트 탐색 첫째 줄: 랜덤으로 메이트 추천 @UseGuards(UserGuard) async getRandomMateProfileWithInfiniteCursor( @Req() req: Request, - @Query() cursorPageOptionDto: CursorPageOptionsDto - ){ - try{ - const result = await this.mateService.recommendRandomMateWithInfiniteScroll(cursorPageOptionDto, req.user.id); + @Query() cursorPageOptionDto: CursorPageOptionsDto, + ) { + try { + const result = + await this.mateService.recommendRandomMateWithInfiniteScroll( + cursorPageOptionDto, + req.user.id, + ); return new ResponseDto( ResponseCode.GET_RANDOM_MATE_PROFILE_SUCCESS, true, - "랜덤 메이트 추천 데이터 생성 성공", - result + '랜덤 메이트 추천 데이터 생성 성공', + result, ); - } - catch(e){ + } catch (e) { console.log(e); return new ResponseDto( ResponseCode.GET_RANDOM_MATE_PROFILE_FAIL, false, - "랜덤 메이트 추천 데이터 생성 실패", - null + '랜덤 메이트 추천 데이터 생성 실패', + null, ); } } @@ -49,94 +49,99 @@ export class MateController{ async getMateProfileWithMyFirstLocation( @Req() req: Request, ): Promise> { + try { + const result = await this.mateService.getMateProfileWithMyFirstLocation( + req.user.id, + ); - try{ - const result = await this.mateService.getMateProfileWithMyFirstLocation(req.user.id); - - if(!result){ - return new ResponseDto( - ResponseCode.GET_MATE_WITH_COMMON_LOCATION_SUCCESS, - true, - "공통 메이트가 없거나 내 시그니처가 없습니다.", - null - ) - } + if (!result) { return new ResponseDto( ResponseCode.GET_MATE_WITH_COMMON_LOCATION_SUCCESS, true, - "장소 기반 메이트 추천 리스트 가져오기 성공", - result - ); - - } - catch (e){ - console.log(e); - return new ResponseDto( - ResponseCode.GET_MATE_WITH_COMMON_LOCATION_FAIL, - false, - "장소 기반 메이트 추천 실패", - null + '공통 메이트가 없거나 내 시그니처가 없습니다.', + null, ); } + return new ResponseDto( + ResponseCode.GET_MATE_WITH_COMMON_LOCATION_SUCCESS, + true, + '장소 기반 메이트 추천 리스트 가져오기 성공', + result, + ); + } catch (e) { + console.log(e); + return new ResponseDto( + ResponseCode.GET_MATE_WITH_COMMON_LOCATION_FAIL, + false, + '장소 기반 메이트 추천 실패', + null, + ); + } } @Get(':mateId') @UseGuards(UserGuard) async getUserProfile( @Req() req: Request, - @Param('mateId') mateId: number - ): Promise>{ - try{ - const result = await this.mateService.findProfileWithUserId(req.user.id, mateId); + @Param('mateId') mateId: number, + ): Promise> { + try { + const result = await this.mateService.findProfileWithUserId( + req.user.id, + mateId, + ); - if(!result){ + if (!result) { return new ResponseDto( ResponseCode.GET_USER_PROFILE_FAIL, false, - "유저 프로필 정보 가져오기 실패", - null); - } - else{ + '유저 프로필 정보 가져오기 실패', + null, + ); + } else { return new ResponseDto( ResponseCode.GET_USER_PROFILE_SUCCESS, true, - "유저 프로필 정보 가져오기 성공", - result); + '유저 프로필 정보 가져오기 성공', + result, + ); } - - }catch (error) { - console.log("Error at GetUserProfile: ", error); + } catch (error) { + console.log('Error at GetUserProfile: ', error); return new ResponseDto( ResponseCode.GET_USER_PROFILE_FAIL, false, - "유저 프로필 정보 가져오기 실패", - null); + '유저 프로필 정보 가져오기 실패', + null, + ); } } @Get('/signature/:mateId') async getSignaturesWithInfiniteCursor( @Param('mateId') mateId: number, - @Query() cursorPageOptionDto: CursorPageOptionsDto - ){ - try{ - const result = await this.mateService.getSignaturesWithInfiniteCursor(cursorPageOptionDto, mateId); + @Query() cursorPageOptionDto: CursorPageOptionsDto, + ) { + try { + const result = await this.mateService.getSignaturesWithInfiniteCursor( + cursorPageOptionDto, + mateId, + ); return new ResponseDto( ResponseCode.GET_USER_SIGNATURES_SUCCESS, true, - "메이트의 시그니처 가져오기 성공", - result + '메이트의 시그니처 가져오기 성공', + result, ); - - }catch(error){ - console.log("Err on getUserSignatures: ", error); + } catch (error) { + console.log('Err on getUserSignatures: ', error); return new ResponseDto( ResponseCode.GET_USER_SIGNATURES_FAIL, false, - "메이트의 시그니처 가져오기 실패", - null + '메이트의 시그니처 가져오기 실패', + null, ); } } -} \ No newline at end of file +} diff --git a/src/mate/mate.module.ts b/src/mate/mate.module.ts index be8ad90..068204e 100644 --- a/src/mate/mate.module.ts +++ b/src/mate/mate.module.ts @@ -6,7 +6,7 @@ import { S3UtilService } from '../utils/S3.service'; import { SignatureService } from '../signature/signature.service'; @Module({ - controllers: [MateController], - providers: [MateService, UserService, S3UtilService, SignatureService] + controllers: [MateController], + providers: [MateService, UserService, S3UtilService, SignatureService], }) -export class MateModule{} +export class MateModule {} diff --git a/src/mate/mate.service.ts b/src/mate/mate.service.ts index 68e152a..ee45d4e 100644 --- a/src/mate/mate.service.ts +++ b/src/mate/mate.service.ts @@ -7,58 +7,58 @@ import { CursorPageOptionsDto } from './cursor-page/cursor-page-option.dto'; import { CursorPageDto } from './cursor-page/cursor-page.dto'; import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureService } from '../signature/signature.service'; -import { LessThan, Like, MoreThan, Not } from 'typeorm'; +import { LessThan, Like } from 'typeorm'; import { CursorPageMetaDto } from './cursor-page/cursor-page.meta.dto'; import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; import { UserEntity } from '../user/user.entity'; import { MateRecommendProfileDto } from './dto/mate-recommend-profile.dto'; -import { MateController } from './mate.controller'; import { MateSignatureCoverDto } from './dto/mate-signature-cover.dto'; import { MateWithCommonLocationResponseDto } from './dto/mate-with-common-location-response.dto'; import { MateProfileResponseDto } from './dto/mate-profile-response.dto'; - @Injectable() -export class MateService{ +export class MateService { constructor( private readonly userService: UserService, private readonly s3Service: S3UtilService, private readonly signatureService: SignatureService, ) {} - async getMateProfileWithMyFirstLocation(userId: number): Promise { - try{ - - const mateWithCommonLocationResponseDto = new MateWithCommonLocationResponseDto(); + async getMateProfileWithMyFirstLocation( + userId: number, + ): Promise { + try { + const mateWithCommonLocationResponseDto = + new MateWithCommonLocationResponseDto(); // 1. 메이트 탐색의 기준이 될 장소 가져오기 = 사용자의 가장 최신 시그니처의 첫 번째 페이지 장소 const mySignaturePageEntity = await SignaturePageEntity.findOne({ where: { - signature:{ - user:{ + signature: { + user: { id: userId, - isQuit: false // 탈퇴한 사용자 필터링 + isQuit: false, // 탈퇴한 사용자 필터링 }, }, - page: 1 + page: 1, }, order: { - created: 'DESC' // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 + created: 'DESC', // 'created'를 내림차순으로 정렬해서 가장 최근꺼 가져오기 }, }); - if(!mySignaturePageEntity){ // 로그인한 사용자가 아직 한번도 시그니처를 작성한 적이 없을 경우 + if (!mySignaturePageEntity) { + // 로그인한 사용자가 아직 한번도 시그니처를 작성한 적이 없을 경우 return null; } const longLocation = mySignaturePageEntity.location; - console.log("*longLocation: ",longLocation); - + console.log('*longLocation: ', longLocation); // 2. 쉼표로 구분된 현재 'longLocation'에서 핵심 부분인 마지막 부분을 가져오기 const locationBlocks = longLocation.split(','); - const myLocation = locationBlocks[locationBlocks.length-1].trim(); - console.log("*firstLocation: ", myLocation); + const myLocation = locationBlocks[locationBlocks.length - 1].trim(); + console.log('*firstLocation: ', myLocation); const loginUser = await this.userService.findUserById(userId); mateWithCommonLocationResponseDto.location = myLocation; @@ -67,14 +67,14 @@ export class MateService{ // 3. 이제 내 기준 로케이션이 사용된 모든 페이지 가져오기 const commonLocationSignaturePages = await SignaturePageEntity.find({ where: { location: Like(`%${myLocation}%`) }, - relations: ['signature'] + relations: ['signature'], }); // 4. 3번에서 찾아온 페이지의 시그니처 가져오기 const commonLocationSignatures = []; - for(const page of commonLocationSignaturePages){ + for (const page of commonLocationSignaturePages) { const signature = await SignatureEntity.findOne({ - where: { id: page.signature.id}, + where: { id: page.signature.id }, relations: ['user'], }); commonLocationSignatures.push(signature); @@ -82,85 +82,82 @@ export class MateService{ // 5. 시그니처 작성자 기준으로 분류: 중복된 작성자를 또 찾아오지 않기 위해 const signatureGroups = {}; - for(const signature of commonLocationSignatures){ - if(!signatureGroups[signature.user.id]){ // 새로운 유저일 경우 새 리스트 생성, 시그니처 삽입 + for (const signature of commonLocationSignatures) { + if (!signatureGroups[signature.user.id]) { + // 새로운 유저일 경우 새 리스트 생성, 시그니처 삽입 signatureGroups[signature.user.id] = []; signatureGroups[signature.user.id].push(signature); } } - // 6. 유저 아이디 순회하면서 한명씩 찾아서 메이트 프로필 생성하기 - const mateProfiles : MateRecommendProfileDto[] = []; + const mateProfiles: MateRecommendProfileDto[] = []; - for(const authorUserId of Object.keys(signatureGroups)){ - const authorId:number = Number(authorUserId); + for (const authorUserId of Object.keys(signatureGroups)) { + const authorId = Number(authorUserId); const mate = await this.userService.findUserById(authorId); - if(userId == authorId) continue; // 본인은 제외 - const locationSignature:SignatureEntity = signatureGroups[authorId][0]; - const mateProfile: MateRecommendProfileDto = await this.generateMateProfile(mate, userId, locationSignature); + if (userId == authorId) continue; // 본인은 제외 + const locationSignature: SignatureEntity = signatureGroups[authorId][0]; + const mateProfile: MateRecommendProfileDto = + await this.generateMateProfile(mate, userId, locationSignature); mateProfiles.push(mateProfile); - } mateWithCommonLocationResponseDto.mateProfiles = mateProfiles; return mateWithCommonLocationResponseDto; - - } - catch(error){ - console.log("Err: ", error); + } catch (error) { + console.log('Err: ', error); throw error; } } - async recommendRandomMateWithInfiniteScroll(cursorPageOptionsDto: CursorPageOptionsDto, userId: number) { - - let cursorId: number = 0; + async recommendRandomMateWithInfiniteScroll( + cursorPageOptionsDto: CursorPageOptionsDto, + userId: number, + ) { + let cursorId = 0; // [0] 맨 처음 요청일 경우 랜덤 숫자 생성해서 cursorId에 할당 - if(cursorPageOptionsDto.cursorId == 0){ + if (cursorPageOptionsDto.cursorId == 0) { const newUser = await UserEntity.find({ - where: { isQuit: false }, // 탈퇴 필터링 + where: { isQuit: false }, // 탈퇴 필터링 order: { - id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 가입한 유저 가져오기 + id: 'DESC', // id를 내림차순으로 정렬해서 가장 최근에 가입한 유저 가져오기 }, - take: 1 + take: 1, }); const max = newUser[0].id + 1; // 랜덤 숫자 생성의 max 값 - console.log('max id: ',max); + console.log('max id: ', max); - const min = 5; // 랜덤 숫자 생성의 min 값 + const min = 5; // 랜덤 숫자 생성의 min 값 // TODO 사용자 늘어나면 min 값 늘리기 cursorId = Math.floor(Math.random() * (max - min + 1)) + min; console.log('random cursor: ', cursorId); - - } - else { + } else { cursorId = cursorPageOptionsDto.cursorId; } - // [1] 무한 스크롤: take만큼 cursorId보다 id값이 작은 유저들 불러오기 const [mates, total] = await UserEntity.findAndCount({ take: cursorPageOptionsDto.take, where: { id: LessThan(cursorId), - isQuit: false + isQuit: false, }, order: { - id: "DESC" as any, + id: 'DESC' as any, }, }); - console.log("mates: ", mates); + console.log('mates: ', mates); // [2] 가져온 메이트들 프로필 커버 만들기 - const mateProfiles:MateRecommendProfileDto[] = []; + const mateProfiles: MateRecommendProfileDto[] = []; - for(const mate of mates){ - if(userId == mate.id) continue; // 본인은 제외 + for (const mate of mates) { + if (userId == mate.id) continue; // 본인은 제외 const mateProfile = await this.generateMateProfile(mate, userId, null); mateProfiles.push(mateProfile); } @@ -180,14 +177,21 @@ export class MateService{ cursor = lastDataPerScroll.id; } - const cursorPageMetaDto = new CursorPageMetaDto( - { cursorPageOptionsDto, total, hasNextData, cursor }); + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); return new CursorPageDto(mateProfiles, cursorPageMetaDto); - } - async generateMateProfile(mate:UserEntity, userId:number, locationSignature:SignatureEntity){ + async generateMateProfile( + mate: UserEntity, + userId: number, + locationSignature: SignatureEntity, + ) { const mateProfile = new MateRecommendProfileDto(); // 1. 메이트의 기본 정보 담기 @@ -197,13 +201,16 @@ export class MateService{ // 2. 로그인한 유저가 메이트를 팔로우하는지 팔로우 여부 체크 const myEntity = await this.userService.findUserById(userId); - mateProfile.is_followed = await this.userService.checkIfFollowing(myEntity, mate.id); + mateProfile.is_followed = await this.userService.checkIfFollowing( + myEntity, + mate.id, + ); // 3. 메이트 프로필 사진 가져오기 - let userProfileImage = await this.userService.getProfileImage(mate.id); - if(!userProfileImage) mateProfile.mateImage = null; - else{ - let userImageKey = userProfileImage.imageKey; + const userProfileImage = await this.userService.getProfileImage(mate.id); + if (!userProfileImage) mateProfile.mateImage = null; + else { + const userImageKey = userProfileImage.imageKey; mateProfile.mateImage = await this.s3Service.getImageUrl(userImageKey); } @@ -212,27 +219,33 @@ export class MateService{ [1] 랜덤 메이트 추천: 가장 최신 시그니처 두 개 [2] 장소 기반 추천: 장소 관련 시그니처 1개, 최신 시그니처 1개 ****************************************************/ - console.log("locationSig: ", locationSignature); + console.log('locationSig: ', locationSignature); let recommendSignatures = []; - if(locationSignature == null){ // [1] 랜덤 추천이면 가장 최신 시그니처 두 개 가져오기 - recommendSignatures = await this.signatureService.getMyRecentSignatures(mate.id, 2); - } - else{ // [2] 장소 기반 추천이면 장소 관련 하나, 최신 시그니처 하나 + if (locationSignature == null) { + // [1] 랜덤 추천이면 가장 최신 시그니처 두 개 가져오기 + recommendSignatures = await this.signatureService.getMyRecentSignatures( + mate.id, + 2, + ); + } else { + // [2] 장소 기반 추천이면 장소 관련 하나, 최신 시그니처 하나 recommendSignatures.push(locationSignature); - console.log("recommendSignatures: ",recommendSignatures); + console.log('recommendSignatures: ', recommendSignatures); // ㄱ. 삽입할 최신 시그니처 후보 두 개 가져오기 (두 개 중에 이미 삽입된 시그니처와 다른 것을 추가할 것임) - const recentSignatures = await this.signatureService.getMyRecentSignatures(mate.id, 2); + const recentSignatures = + await this.signatureService.getMyRecentSignatures(mate.id, 2); // ㄴ. 이미 들어있는 시그니처와 id 비교해서 다르면 삽입 - for(const recentSignature of recentSignatures){ - console.log("recentSignature.id: ",recentSignature.id); - console.log("locationSignature.id: ",locationSignature.id); + for (const recentSignature of recentSignatures) { + console.log('recentSignature.id: ', recentSignature.id); + console.log('locationSignature.id: ', locationSignature.id); - if(recentSignature.id != locationSignature.id) { // 이미 들어있는 시그니처와 다른 경우에만 push - console.log("push! : ", recentSignature.id) + if (recentSignature.id != locationSignature.id) { + // 이미 들어있는 시그니처와 다른 경우에만 push + console.log('push! : ', recentSignature.id); recommendSignatures.push(recentSignature); break; } @@ -241,53 +254,76 @@ export class MateService{ const signatureCovers = []; //TODO 작성자가 작성한 시그니처가 하나일 경우에는 리스트에 하나만 담겨있음 프론트에 알리기 -> 완료 - for(const signature of recommendSignatures) { + for (const signature of recommendSignatures) { const signatureCover: MateSignatureCoverDto = new MateSignatureCoverDto(); - console.log("signature.id: ",signature.id); + console.log('signature.id: ', signature.id); signatureCover._id = signature.id; signatureCover.title = signature.title; - let thumbnailImageKey = await SignaturePageEntity.findThumbnail(signature.id); - signatureCover.image = await this.s3Service.getImageUrl(thumbnailImageKey); - - console.log("signatureCover: ", signatureCover); + const thumbnailImageKey = await SignaturePageEntity.findThumbnail( + signature.id, + ); + signatureCover.image = await this.s3Service.getImageUrl( + thumbnailImageKey, + ); + + console.log('signatureCover: ', signatureCover); signatureCovers.push(signatureCover); } mateProfile.signatures = signatureCovers; return mateProfile; - } - async findProfileWithUserId(loginUserId: number, targetUserId): Promise { // 유저 정보 가져오기 - try{ - const targetUserEntity = await this.userService.findUserById(targetUserId); + async findProfileWithUserId( + loginUserId: number, + targetUserId, + ): Promise { + // 유저 정보 가져오기 + try { + const targetUserEntity = await this.userService.findUserById( + targetUserId, + ); console.log(targetUserEntity); // 타겟 유저 프로필 가져오기 - const mateProfileResponseDto:MateProfileResponseDto = new MateProfileResponseDto(); + const mateProfileResponseDto: MateProfileResponseDto = + new MateProfileResponseDto(); mateProfileResponseDto._id = targetUserEntity.id; mateProfileResponseDto.nickname = targetUserEntity.nickname; mateProfileResponseDto.introduction = targetUserEntity.introduction; mateProfileResponseDto.isQuit = targetUserEntity.isQuit; // 타겟 유저 프로필 이미지 가져오기 - const userProfileImageEntity = await this.userService.getProfileImage(targetUserId); - if(userProfileImageEntity == null) mateProfileResponseDto.image = null; - else{ + const userProfileImageEntity = await this.userService.getProfileImage( + targetUserId, + ); + if (userProfileImageEntity == null) mateProfileResponseDto.image = null; + else { const userProfileImageKey = userProfileImageEntity.imageKey; - mateProfileResponseDto.image = await this.s3Service.getImageUrl(userProfileImageKey); + mateProfileResponseDto.image = await this.s3Service.getImageUrl( + userProfileImageKey, + ); } // 현재 로그인한 유저가 타켓 유저를 팔로우하는지 여부 가져오기 - if(loginUserId == targetUserId){ // 현재 로그인 유저와 타겟 유저가 같다면 is_followed = null + if (loginUserId == targetUserId) { + // 현재 로그인 유저와 타겟 유저가 같다면 is_followed = null mateProfileResponseDto.is_followed = null; - }else{ - const loginUserEntity = await this.userService.findUserById(loginUserId); - mateProfileResponseDto.is_followed = await this.userService.checkIfFollowing( loginUserEntity, targetUserId); + } else { + const loginUserEntity = await this.userService.findUserById( + loginUserId, + ); + mateProfileResponseDto.is_followed = + await this.userService.checkIfFollowing( + loginUserEntity, + targetUserId, + ); } // 팔로잉 수 - const followingList = await this.userService.getFollowingList(targetUserId); + const followingList = await this.userService.getFollowingList( + targetUserId, + ); mateProfileResponseDto.following = followingList.length; // 팔로워 수 @@ -295,89 +331,92 @@ export class MateService{ mateProfileResponseDto.follower = followerList.length; // 시그니처 개수 - mateProfileResponseDto.signatures = await this.signatureService.getSignatureCnt(targetUserId); + mateProfileResponseDto.signatures = + await this.signatureService.getSignatureCnt(targetUserId); return mateProfileResponseDto; - - } - catch(error){ - console.log("Err on findProfileWithId Service: ",error); + } catch (error) { + console.log('Err on findProfileWithId Service: ', error); throw error; } } - async getSignaturesWithInfiniteCursor(cursorPageOptionsDto: CursorPageOptionsDto, mateId: number) { - try{ - - let cursorId: number = 0; - - // 1. 맨 처음 요청일 경우 해당 유저의 시그니처 중 가장 최근 시그니처 id 가져오기 - if(cursorPageOptionsDto.cursorId == 0) { - const recentSignature = await SignatureEntity.findOne({ - where: { user: {id: mateId} }, - order: { - id: 'DESC' // id를 내림차순으로 정렬해서 가장 최근에 작성한 시그니처 - } - }); - - cursorId = recentSignature.id + 1; - - } - else cursorId = cursorPageOptionsDto.cursorId; - - - // 2. 무한 스크롤: take만큼 cursorId보다 id값이 작은 시그니처들 불러오기 - const [ signatureEntities, total] = await SignatureEntity.findAndCount({ - take: cursorPageOptionsDto.take, - where: { - id: cursorId ? LessThan(cursorId) : null, - user: { id: mateId } - }, + async getSignaturesWithInfiniteCursor( + cursorPageOptionsDto: CursorPageOptionsDto, + mateId: number, + ) { + try { + let cursorId = 0; + + // 1. 맨 처음 요청일 경우 해당 유저의 시그니처 중 가장 최근 시그니처 id 가져오기 + if (cursorPageOptionsDto.cursorId == 0) { + const recentSignature = await SignatureEntity.findOne({ + where: { user: { id: mateId } }, order: { - id: "DESC" as any, + id: 'DESC', // id를 내림차순으로 정렬해서 가장 최근에 작성한 시그니처 }, }); + cursorId = recentSignature.id + 1; + } else cursorId = cursorPageOptionsDto.cursorId; - // 3. 가져온 시그니처들로 커버 만들기 - const signatureCoverDtos: MateSignatureCoverDto[] = []; + // 2. 무한 스크롤: take만큼 cursorId보다 id값이 작은 시그니처들 불러오기 + const [signatureEntities, total] = await SignatureEntity.findAndCount({ + take: cursorPageOptionsDto.take, + where: { + id: cursorId ? LessThan(cursorId) : null, + user: { id: mateId }, + }, + order: { + id: 'DESC' as any, + }, + }); - for( const signatureEntity of signatureEntities){ - const signatureCover: MateSignatureCoverDto = new MateSignatureCoverDto(); + // 3. 가져온 시그니처들로 커버 만들기 + const signatureCoverDtos: MateSignatureCoverDto[] = []; - signatureCover._id = signatureEntity.id; - signatureCover.title = signatureEntity.title; - signatureCover.liked = signatureEntity.liked; + for (const signatureEntity of signatureEntities) { + const signatureCover: MateSignatureCoverDto = + new MateSignatureCoverDto(); - // 시그니처 썸네일 가져오기 - const imageKey = await SignaturePageEntity.findThumbnail(signatureEntity.id); - signatureCover.image = await this.s3Service.getImageUrl(imageKey); + signatureCover._id = signatureEntity.id; + signatureCover.title = signatureEntity.title; + signatureCover.liked = signatureEntity.liked; - signatureCoverDtos.push(signatureCover); - } + // 시그니처 썸네일 가져오기 + const imageKey = await SignaturePageEntity.findThumbnail( + signatureEntity.id, + ); + signatureCover.image = await this.s3Service.getImageUrl(imageKey); - // 4. 스크롤 설정 - let hasNextData = true; - let cursor: number; + signatureCoverDtos.push(signatureCover); + } - const takePerScroll = cursorPageOptionsDto.take; - const isLastScroll = total <= takePerScroll; - const lastDataPerScroll = signatureEntities[signatureEntities.length - 1]; + // 4. 스크롤 설정 + let hasNextData = true; + let cursor: number; - if (isLastScroll) { - hasNextData = false; - cursor = null; - } else { - cursor = lastDataPerScroll.id; - } + const takePerScroll = cursorPageOptionsDto.take; + const isLastScroll = total <= takePerScroll; + const lastDataPerScroll = signatureEntities[signatureEntities.length - 1]; - const cursorPageMetaDto = new CursorPageMetaDto( - { cursorPageOptionsDto, total, hasNextData, cursor }); + if (isLastScroll) { + hasNextData = false; + cursor = null; + } else { + cursor = lastDataPerScroll.id; + } - return new CursorPageDto(signatureCoverDtos, cursorPageMetaDto); + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); - }catch(error){ - throw error; - } + return new CursorPageDto(signatureCoverDtos, cursorPageMetaDto); + } catch (error) { + throw error; + } } -} \ No newline at end of file +} diff --git a/src/response/response-code.enum.ts b/src/response/response-code.enum.ts index 683dc4a..3cc7a57 100644 --- a/src/response/response-code.enum.ts +++ b/src/response/response-code.enum.ts @@ -1,5 +1,3 @@ -import { HttpStatus } from '@nestjs/common'; - export enum ResponseCode { /* 200 OK : 요청 성공 */ SIGNIN_SUCCESS = 'OK', @@ -24,7 +22,6 @@ export enum ResponseCode { GET_NOTIFICATION_COUNT_SUCCESS = 'OK', GET_DIARY_SUCCESS = 'OK', - GET_USER_PROFILE_SUCCESS = 'OK', GET_USER_SIGNATURES_SUCCESS = 'OK', @@ -33,7 +30,6 @@ export enum ResponseCode { FOLLOW_SUCCESS = 'OK', GET_COMMENT_DETAIL_SUCCESS = 'OK', - DELETE_SIGNATURE_SUCCESS = 'OK', PATCH_SIGNATURE_SUCCESS = 'OK', GET_LIKE_SIGNATURE_PROFILES_SUCCESS = 'OK', @@ -43,8 +39,6 @@ export enum ResponseCode { GET_MATE_WITH_COMMON_LOCATION_SUCCESS = 'OK', GET_RANDOM_MATE_PROFILE_SUCCESS = 'OK', - - /* 201 CREATED : 요청 성공, 자원 생성 */ SIGNUP_SUCCESS = 'CREATED', SIGNATURE_CREATED = 'CREATED', @@ -54,9 +48,6 @@ export enum ResponseCode { INVITATION_CREATED = 'CREATED', CREATE_SIGNATURE_COMMENT_SUCCESS = 'CREATED', - - - /* 400 BAD_REQUEST : 잘못된 요청 */ AUTH_NUMBER_INCORRECT = 'BAD_REQUEST', RESET_PASSWORD_FAIL_MATCH = 'BAD_REQUEST', @@ -80,7 +71,6 @@ export enum ResponseCode { DELETE_INVITATION_FAIL = 'BAD_REQUEST', PATCH_RULE_FAIL = 'BAD_REQUEST', - GET_MATE_WITH_COMMON_LOCATION_FAIL = 'BAD_REQUEST', GET_RANDOM_MATE_PROFILE_FAIL = 'BAD_REQUEST', GET_RULE_LIST_FAIL = 'BAD_REQUEST', @@ -89,15 +79,12 @@ export enum ResponseCode { COMMENT_DELETE_FAIL = 'BAD_REQUEST', GET_COMMENT_DETAIL_FAIL = 'BAD_REQUEST', - - SIGNATURE_DELETE_FAIL = 'BAD_REQUEST', SIGNATURE_PATCH_FAIL = 'BAD_REQUEST', GET_LIKE_SIGNATURE_PROFILES_FAIL = 'BAD_REQUEST', GET_SEARCH_MAIN_FAIL = 'BAD_REQUEST', SEARCH_BY_KEYWORD_FAIL = 'BAD_REQUEST', GET_USER_SIGNATURES_FAIL = 'BAD_REQUEST', - /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */ INVALID_AUTH_TOKEN = 'UNAUTHORIZED', diff --git a/src/response/response.dto.ts b/src/response/response.dto.ts index 0045117..49ad25e 100644 --- a/src/response/response.dto.ts +++ b/src/response/response.dto.ts @@ -1,24 +1,32 @@ -import { HttpStatus } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; -import {ResponseCode} from './response-code.enum'; +import { ResponseCode } from './response-code.enum'; export class ResponseDto { - @ApiProperty({ description: '응답 시간'}) + @ApiProperty({ description: '응답 시간' }) timestamp: Date = new Date(); - @ApiProperty({ description: 'http status code'}) + @ApiProperty({ description: 'http status code' }) code: ResponseCode; @ApiProperty() - success: boolean + success: boolean; - @ApiProperty({ example: '데이터 불러오기 성공', description: '응답 메시지'}) + @ApiProperty({ example: '데이터 불러오기 성공', description: '응답 메시지' }) message: string; - @ApiProperty({ required: false, nullable: true , description: 'Response Body'}) + @ApiProperty({ + required: false, + nullable: true, + description: 'Response Body', + }) data?: T; - public constructor(code: ResponseCode, success:boolean, message: string, data: T) { + public constructor( + code: ResponseCode, + success: boolean, + message: string, + data: T, + ) { this.code = code; this.success = success; this.message = message; diff --git a/src/rule/domain/rule.invitation.entity.ts b/src/rule/domain/rule.invitation.entity.ts index fec931b..63e5036 100644 --- a/src/rule/domain/rule.invitation.entity.ts +++ b/src/rule/domain/rule.invitation.entity.ts @@ -1,26 +1,27 @@ -import { BaseEntity, - Entity, - ManyToOne, - PrimaryGeneratedColumn, +import { + BaseEntity, + Entity, + ManyToOne, + PrimaryGeneratedColumn, JoinColumn, CreateDateColumn, UpdateDateColumn, - DeleteDateColumn, + DeleteDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; -import { RuleMainEntity } from './rule.main.entity' +import { RuleMainEntity } from './rule.main.entity'; @Entity() export class RuleInvitationEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.invitations) - @JoinColumn({name: 'rule_id'}) + @ManyToOne(() => RuleMainEntity, (ruleMain) => ruleMain.invitations) + @JoinColumn({ name: 'rule_id' }) rule: RuleMainEntity; - @ManyToOne(() => UserEntity, user => user.ruleParticipate) - @JoinColumn({name: 'member_id'}) + @ManyToOne(() => UserEntity, (user) => user.ruleParticipate) + @JoinColumn({ name: 'member_id' }) member: UserEntity; @CreateDateColumn() @@ -32,9 +33,11 @@ export class RuleInvitationEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async findNameById(inviterId: number): Promise<{ memberId : number, name : string }> { - const userEntity : UserEntity = await UserEntity.findOne({ - where: { id: inviterId } + static async findNameById( + inviterId: number, + ): Promise<{ memberId: number; name: string }> { + const userEntity: UserEntity = await UserEntity.findOne({ + where: { id: inviterId }, }); const memberId = inviterId; const name = userEntity.name; @@ -42,11 +45,13 @@ export class RuleInvitationEntity extends BaseEntity { return { memberId, name }; } - static async findInvitationByRuleId(ruleId: number): Promise { + static async findInvitationByRuleId( + ruleId: number, + ): Promise { try { const invitation = await RuleInvitationEntity.find({ - where: {rule: {id : ruleId}}, - relations: {member:true}, + where: { rule: { id: ruleId } }, + relations: { member: true }, }); console.log('invitation 조회 결과 : ', invitation); return invitation; @@ -56,11 +61,13 @@ export class RuleInvitationEntity extends BaseEntity { } } - static async findInvitationByRuleAndUser(ruleId: number, userId: number) : Promise { + static async findInvitationByRuleAndUser( + ruleId: number, + userId: number, + ): Promise { try { const invitation = await RuleInvitationEntity.findOne({ - where: [{rule: {id : ruleId}}, - {member: {id : userId}}] + where: [{ rule: { id: ruleId } }, { member: { id: userId } }], }); console.log('invitation 조회 결과 : ', invitation); return invitation; @@ -71,13 +78,16 @@ export class RuleInvitationEntity extends BaseEntity { } // [member] 멤버인지 확인 - static async isAlreadyMember(ruleId: number, targetUserId: number) :Promise { + static async isAlreadyMember( + ruleId: number, + targetUserId: number, + ): Promise { const isAlreadyMember = await RuleInvitationEntity.findOne({ - where : {member: {id : targetUserId}, rule: {id : ruleId}} - }) + where: { member: { id: targetUserId }, rule: { id: ruleId } }, + }); console.log(isAlreadyMember); if (!!isAlreadyMember) return true; else return false; } -} \ No newline at end of file +} diff --git a/src/rule/domain/rule.main.entity.ts b/src/rule/domain/rule.main.entity.ts index a811548..92f8cf4 100644 --- a/src/rule/domain/rule.main.entity.ts +++ b/src/rule/domain/rule.main.entity.ts @@ -1,14 +1,15 @@ -import { BaseEntity, - Column, - Entity, - PrimaryGeneratedColumn, +import { + BaseEntity, + Column, + Entity, + PrimaryGeneratedColumn, OneToMany, CreateDateColumn, UpdateDateColumn, - DeleteDateColumn, + DeleteDateColumn, } from 'typeorm'; import { RuleSubEntity } from './rule.sub.entity'; -import { RuleInvitationEntity } from './rule.invitation.entity' +import { RuleInvitationEntity } from './rule.invitation.entity'; import { CommentEntity } from 'src/comment/domain/comment.entity'; @Entity() @@ -28,19 +29,22 @@ export class RuleMainEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - @OneToMany(() => RuleSubEntity, ruleSub => ruleSub.main) + @OneToMany(() => RuleSubEntity, (ruleSub) => ruleSub.main) rules: RuleSubEntity[]; - @OneToMany(() => RuleInvitationEntity, ruleInvitation => ruleInvitation.rule) + @OneToMany( + () => RuleInvitationEntity, + (ruleInvitation) => ruleInvitation.rule, + ) invitations: RuleInvitationEntity[]; - @OneToMany(() => CommentEntity, comment => comment.rule) + @OneToMany(() => CommentEntity, (comment) => comment.rule) comments: CommentEntity[]; static async findMainById(ruleId: number): Promise { - const ruleMain : RuleMainEntity = await RuleMainEntity.findOne({ + const ruleMain: RuleMainEntity = await RuleMainEntity.findOne({ where: { id: ruleId }, - relations: ['rules', 'invitations', 'comments'] + relations: ['rules', 'invitations', 'comments'], }); return ruleMain; @@ -57,4 +61,4 @@ export class RuleMainEntity extends BaseEntity { throw error; } } -} \ No newline at end of file +} diff --git a/src/rule/domain/rule.sub.entity.ts b/src/rule/domain/rule.sub.entity.ts index f1c8849..bdc49cc 100644 --- a/src/rule/domain/rule.sub.entity.ts +++ b/src/rule/domain/rule.sub.entity.ts @@ -1,8 +1,9 @@ -import { BaseEntity, - Column, - Entity, - ManyToOne, - PrimaryGeneratedColumn, +import { + BaseEntity, + Column, + Entity, + ManyToOne, + PrimaryGeneratedColumn, JoinColumn, CreateDateColumn, UpdateDateColumn, @@ -21,8 +22,8 @@ export class RuleSubEntity extends BaseEntity { @Column({ type: 'text' }) ruleDetail: string; - @ManyToOne(() => RuleMainEntity, ruleMain => ruleMain.rules) - @JoinColumn({ name: 'rule_id'}) + @ManyToOne(() => RuleMainEntity, (ruleMain) => ruleMain.rules) + @JoinColumn({ name: 'rule_id' }) main: RuleMainEntity; @CreateDateColumn() @@ -37,7 +38,7 @@ export class RuleSubEntity extends BaseEntity { static async findSubById(ruleId: number): Promise { try { const rule: RuleSubEntity[] = await RuleSubEntity.find({ - where: {main: {id : ruleId}}, + where: { main: { id: ruleId } }, }); return rule; } catch (error) { @@ -45,4 +46,4 @@ export class RuleSubEntity extends BaseEntity { throw error; } } -} \ No newline at end of file +} diff --git a/src/rule/dto/create-rule.dto.ts b/src/rule/dto/create-rule.dto.ts index 80b3592..3d0f299 100644 --- a/src/rule/dto/create-rule.dto.ts +++ b/src/rule/dto/create-rule.dto.ts @@ -1,4 +1,10 @@ -import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import { + IsNotEmpty, + IsNumber, + IsString, + IsArray, + ValidateNested, +} from 'class-validator'; import { Type } from 'class-transformer'; class RulePairDto { @IsNotEmpty() diff --git a/src/rule/dto/cursor-page-options-parameter.interface.ts b/src/rule/dto/cursor-page-options-parameter.interface.ts index de61566..403b32e 100644 --- a/src/rule/dto/cursor-page-options-parameter.interface.ts +++ b/src/rule/dto/cursor-page-options-parameter.interface.ts @@ -1,8 +1,8 @@ -import { CursorPageOptionsDto } from "./cursor-page.options.dto"; +import { CursorPageOptionsDto } from './cursor-page.options.dto'; export interface CursorPageMetaDtoParameters { - cursorPageOptionsDto: CursorPageOptionsDto; - total: number; - hasNextData: boolean; - cursor: number; -} \ No newline at end of file + cursorPageOptionsDto: CursorPageOptionsDto; + total: number; + hasNextData: boolean; + cursor: number; +} diff --git a/src/rule/dto/cursor-page-order.enum.ts b/src/rule/dto/cursor-page-order.enum.ts index 0c83c3f..9b0a631 100644 --- a/src/rule/dto/cursor-page-order.enum.ts +++ b/src/rule/dto/cursor-page-order.enum.ts @@ -1,4 +1,4 @@ export enum Order { - ASC = "asc", - DESC = "desc" -} \ No newline at end of file + ASC = 'asc', + DESC = 'desc', +} diff --git a/src/rule/dto/cursor-page.dto.ts b/src/rule/dto/cursor-page.dto.ts index 08c2417..d53b792 100644 --- a/src/rule/dto/cursor-page.dto.ts +++ b/src/rule/dto/cursor-page.dto.ts @@ -1,15 +1,14 @@ -import { IsArray } from "class-validator"; -import { CursorPageMetaDto } from "./cursor-page.meta.dto"; +import { IsArray } from 'class-validator'; +import { CursorPageMetaDto } from './cursor-page.meta.dto'; export class CursorPageDto { + @IsArray() + readonly data: T[]; - @IsArray() - readonly data: T[]; + readonly meta: CursorPageMetaDto; - readonly meta: CursorPageMetaDto; - - constructor(data: T[], meta: CursorPageMetaDto) { - this.data = data; - this.meta = meta; - } -} \ No newline at end of file + constructor(data: T[], meta: CursorPageMetaDto) { + this.data = data; + this.meta = meta; + } +} diff --git a/src/rule/dto/cursor-page.meta.dto.ts b/src/rule/dto/cursor-page.meta.dto.ts index 4fc9fac..a57b55d 100644 --- a/src/rule/dto/cursor-page.meta.dto.ts +++ b/src/rule/dto/cursor-page.meta.dto.ts @@ -1,16 +1,20 @@ -import { CursorPageMetaDtoParameters } from "./cursor-page-options-parameter.interface"; +import { CursorPageMetaDtoParameters } from './cursor-page-options-parameter.interface'; export class CursorPageMetaDto { + readonly total: number; + readonly take: number; + readonly hasNextData: boolean; + readonly cursor: number; - readonly total: number; - readonly take: number; - readonly hasNextData: boolean; - readonly cursor: number; - - constructor({cursorPageOptionsDto, total, hasNextData, cursor}: CursorPageMetaDtoParameters) { - this.take = cursorPageOptionsDto.take; - this.total = total; - this.hasNextData = hasNextData; - this.cursor = cursor; - } -} \ No newline at end of file + constructor({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }: CursorPageMetaDtoParameters) { + this.take = cursorPageOptionsDto.take; + this.total = total; + this.hasNextData = hasNextData; + this.cursor = cursor; + } +} diff --git a/src/rule/dto/cursor-page.options.dto.ts b/src/rule/dto/cursor-page.options.dto.ts index 207fc85..2a71402 100644 --- a/src/rule/dto/cursor-page.options.dto.ts +++ b/src/rule/dto/cursor-page.options.dto.ts @@ -1,19 +1,18 @@ -import { Type } from "class-transformer"; -import { IsEnum, IsOptional } from "class-validator"; -import { Order } from "./cursor-page-order.enum"; +import { Type } from 'class-transformer'; +import { IsEnum, IsOptional } from 'class-validator'; +import { Order } from './cursor-page-order.enum'; export class CursorPageOptionsDto { + @Type(() => String) + @IsEnum(Order) + @IsOptional() + sort?: Order = Order.DESC; - @Type(() => String) - @IsEnum(Order) - @IsOptional() - sort?: Order = Order.DESC; + @Type(() => Number) + @IsOptional() + take?: number = 5; - @Type(() => Number) - @IsOptional() - take?: number = 5; - - @Type(() => Number) - @IsOptional() - cursorId?: number = "" as any; -} \ No newline at end of file + @Type(() => Number) + @IsOptional() + cursorId?: number = '' as any; +} diff --git a/src/rule/dto/detail.rule.dto.ts b/src/rule/dto/detail.rule.dto.ts index c5f0990..6d956ae 100644 --- a/src/rule/dto/detail.rule.dto.ts +++ b/src/rule/dto/detail.rule.dto.ts @@ -1,47 +1,53 @@ -import { IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested } from 'class-validator'; +import { + IsNotEmpty, + IsNumber, + IsString, + IsArray, + ValidateNested, +} from 'class-validator'; import { Type } from 'class-transformer'; export class RulePairDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - ruleTitle: string; + @IsNotEmpty() + @IsString() + ruleTitle: string; - @IsNotEmpty() - @IsString() - ruleDetail: string; + @IsNotEmpty() + @IsString() + ruleDetail: string; } export class DetailMemberDto { - @IsNumber() - id: number; + @IsNumber() + id: number; - @IsString() - name: string; + @IsString() + name: string; - @IsString() - image: string; + @IsString() + image: string; } export class DetailRuleDto { - @IsNotEmpty() - @IsNumber() - id: number; - - @IsNotEmpty() - @IsString() - mainTitle: string; - - @IsArray() - @ValidateNested({each: true}) - @Type(() => RulePairDto) - rulePairs: RulePairDto[]; - - @IsArray() - @ValidateNested({each: true}) - @Type(() => DetailMemberDto) - detailMembers: DetailMemberDto[]; -} \ No newline at end of file + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNotEmpty() + @IsString() + mainTitle: string; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => RulePairDto) + rulePairs: RulePairDto[]; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => DetailMemberDto) + detailMembers: DetailMemberDto[]; +} diff --git a/src/rule/dto/get-comment.dto.ts b/src/rule/dto/get-comment.dto.ts index e967cd9..5fdec99 100644 --- a/src/rule/dto/get-comment.dto.ts +++ b/src/rule/dto/get-comment.dto.ts @@ -1,28 +1,34 @@ -import {IsDate, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; +import { + IsDate, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from 'class-validator'; export class GetCommentDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - content: string; + @IsNotEmpty() + @IsString() + content: string; - @IsNotEmpty() - @IsDate() - updated: Date; + @IsNotEmpty() + @IsDate() + updated: Date; - // 탈퇴한 회원이 작성한 댓글도 표시 -> null 허용 - @IsOptional() - @IsNumber() - writerId: number; + // 탈퇴한 회원이 작성한 댓글도 표시 -> null 허용 + @IsOptional() + @IsNumber() + writerId: number; - @IsOptional() - @IsString() - name: string; + @IsOptional() + @IsString() + name: string; - @IsOptional() - @IsString() - image: string; -} \ No newline at end of file + @IsOptional() + @IsString() + image: string; +} diff --git a/src/rule/dto/get-member-list.dto.ts b/src/rule/dto/get-member-list.dto.ts index 964960c..ccd8ccb 100644 --- a/src/rule/dto/get-member-list.dto.ts +++ b/src/rule/dto/get-member-list.dto.ts @@ -1,23 +1,23 @@ import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; export class GetMemberListDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - name: string; + @IsNotEmpty() + @IsString() + name: string; - @IsNotEmpty() - @IsString() - email: string; + @IsNotEmpty() + @IsString() + email: string; - @IsOptional() - @IsString() - introduction: string; + @IsOptional() + @IsString() + introduction: string; - @IsOptional() - @IsString() - image: string; -} \ No newline at end of file + @IsOptional() + @IsString() + image: string; +} diff --git a/src/rule/dto/get-rule-list.dto.ts b/src/rule/dto/get-rule-list.dto.ts index 37c6dc7..e59e85d 100644 --- a/src/rule/dto/get-rule-list.dto.ts +++ b/src/rule/dto/get-rule-list.dto.ts @@ -1,38 +1,46 @@ -import {IsArray, IsDate, IsNotEmpty, IsNumber, IsOptional, IsString, ValidateNested} from 'class-validator'; -import {Type} from "class-transformer"; +import { + IsArray, + IsDate, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator'; +import { Type } from 'class-transformer'; export class MemberPairDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - name: string; + @IsNotEmpty() + @IsString() + name: string; - @IsOptional() - @IsString() - image: string; + @IsOptional() + @IsString() + image: string; } export class GetRuleListDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - title: string; + @IsNotEmpty() + @IsString() + title: string; - @IsNotEmpty() - @IsDate() - updated: Date; + @IsNotEmpty() + @IsDate() + updated: Date; - @IsNotEmpty() - @IsNumber() - memberCnt: number; + @IsNotEmpty() + @IsNumber() + memberCnt: number; - @IsArray() - @ValidateNested({ each: true }) - @Type(() => MemberPairDto) - memberPairs: MemberPairDto[]; -} \ No newline at end of file + @IsArray() + @ValidateNested({ each: true }) + @Type(() => MemberPairDto) + memberPairs: MemberPairDto[]; +} diff --git a/src/rule/dto/get-search-member-at-create.dto.ts b/src/rule/dto/get-search-member-at-create.dto.ts index 392ef18..3f78b27 100644 --- a/src/rule/dto/get-search-member-at-create.dto.ts +++ b/src/rule/dto/get-search-member-at-create.dto.ts @@ -1,23 +1,23 @@ -import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; +import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; export class GetSearchMemberAtCreateDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - name: string; + @IsNotEmpty() + @IsString() + name: string; - @IsNotEmpty() - @IsString() - email: string; + @IsNotEmpty() + @IsString() + email: string; - @IsOptional() - @IsString() - introduction: string; + @IsOptional() + @IsString() + introduction: string; - @IsOptional() - @IsString() - image: string; -} \ No newline at end of file + @IsOptional() + @IsString() + image: string; +} diff --git a/src/rule/dto/get-search-member.dto.ts b/src/rule/dto/get-search-member.dto.ts index a6fd99b..ee826b6 100644 --- a/src/rule/dto/get-search-member.dto.ts +++ b/src/rule/dto/get-search-member.dto.ts @@ -1,27 +1,33 @@ -import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'; +import { + IsBoolean, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from 'class-validator'; export class GetSearchMemberDto { - @IsNotEmpty() - @IsNumber() - id: number; + @IsNotEmpty() + @IsNumber() + id: number; - @IsNotEmpty() - @IsString() - name: string; + @IsNotEmpty() + @IsString() + name: string; - @IsNotEmpty() - @IsString() - email: string; + @IsNotEmpty() + @IsString() + email: string; - @IsOptional() - @IsString() - introduction: string; + @IsOptional() + @IsString() + introduction: string; - @IsOptional() - @IsString() - image: string; + @IsOptional() + @IsString() + image: string; - @IsOptional() - @IsBoolean() - isInvited: boolean; -} \ No newline at end of file + @IsOptional() + @IsBoolean() + isInvited: boolean; +} diff --git a/src/rule/dto/update-rule.dto.ts b/src/rule/dto/update-rule.dto.ts index 7101689..1347f89 100644 --- a/src/rule/dto/update-rule.dto.ts +++ b/src/rule/dto/update-rule.dto.ts @@ -1,35 +1,42 @@ -import {IsNotEmpty, IsNumber, IsString, IsArray, ValidateNested, IsOptional} from 'class-validator'; +import { + IsNotEmpty, + IsNumber, + IsString, + IsArray, + ValidateNested, + IsOptional, +} from 'class-validator'; import { Type } from 'class-transformer'; export class UpdateRulePairDto { - @IsOptional() - @IsNumber() - id: number; + @IsOptional() + @IsNumber() + id: number; - @IsNotEmpty() - @IsNumber() - ruleNumber: number; + @IsNotEmpty() + @IsNumber() + ruleNumber: number; - @IsNotEmpty() - @IsString() - ruleTitle: string; + @IsNotEmpty() + @IsString() + ruleTitle: string; - @IsNotEmpty() - @IsString() - ruleDetail: string; + @IsNotEmpty() + @IsString() + ruleDetail: string; } export class UpdateRuleDto { - @IsNotEmpty() - @IsString() - mainTitle: string; + @IsNotEmpty() + @IsString() + mainTitle: string; - @IsArray() - @ValidateNested({ each: true }) - @Type(() => UpdateRulePairDto) - rulePairs: UpdateRulePairDto[]; + @IsArray() + @ValidateNested({ each: true }) + @Type(() => UpdateRulePairDto) + rulePairs: UpdateRulePairDto[]; - @IsArray() - @IsNumber({}, { each: true }) - membersId: number[]; -} \ No newline at end of file + @IsArray() + @IsNumber({}, { each: true }) + membersId: number[]; +} diff --git a/src/rule/rule.controller.ts b/src/rule/rule.controller.ts index e288269..cbd83fb 100644 --- a/src/rule/rule.controller.ts +++ b/src/rule/rule.controller.ts @@ -1,44 +1,58 @@ -import {Controller, Post, Body, Get, Param, Delete, UseGuards, Req, Query, Patch} from '@nestjs/common'; +import { + Controller, + Post, + Body, + Get, + Param, + Delete, + UseGuards, + Req, + Query, + Patch, +} from '@nestjs/common'; import { RuleService } from './rule.service'; import { CreateRuleDto } from './dto/create-rule.dto'; import { ResponseCode } from '../response/response-code.enum'; import { ResponseDto } from '../response/response.dto'; import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; -import {GetSearchMemberDto} from "./dto/get-search-member.dto"; -import { UpdateRuleDto } from "./dto/update-rule.dto"; -import {CursorPageOptionsDto} from "./dto/cursor-page.options.dto"; -import {CursorPageDto} from "./dto/cursor-page.dto"; -import {GetSearchMemberAtCreateDto} from "./dto/get-search-member-at-create.dto"; +import { GetSearchMemberDto } from './dto/get-search-member.dto'; +import { UpdateRuleDto } from './dto/update-rule.dto'; +import { CursorPageOptionsDto } from './dto/cursor-page.options.dto'; +import { CursorPageDto } from './dto/cursor-page.dto'; +import { GetSearchMemberAtCreateDto } from './dto/get-search-member-at-create.dto'; @Controller('mate/rule') export class RuleController { - constructor( - private readonly ruleService: RuleService, - ) {} + constructor(private readonly ruleService: RuleService) {} // [1] 여행 규칙 상세 페이지 조회 (댓글) - 무한 스크롤 적용 @Get('/detail/comment/:ruleId') @UseGuards(UserGuard) - async getComment(@Req() req: Request, - @Param('ruleId') ruleId: number, - @Query() cursorPageOptionsDto: CursorPageOptionsDto + async getComment( + @Req() req: Request, + @Param('ruleId') ruleId: number, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, ): Promise> { try { - const result = await this.ruleService.getComment(cursorPageOptionsDto, ruleId, req.user.id); + const result = await this.ruleService.getComment( + cursorPageOptionsDto, + ruleId, + req.user.id, + ); return new ResponseDto( - ResponseCode.GET_COMMENT_DETAIL_SUCCESS, - true, - "여행 규칙 상세 페이지 (댓글) 조회 성공", - result + ResponseCode.GET_COMMENT_DETAIL_SUCCESS, + true, + '여행 규칙 상세 페이지 (댓글) 조회 성공', + result, ); } catch (e) { return new ResponseDto( - ResponseCode.GET_COMMENT_DETAIL_FAIL, - false, - e.message, - null + ResponseCode.GET_COMMENT_DETAIL_FAIL, + false, + e.message, + null, ); } } @@ -46,22 +60,27 @@ export class RuleController { // [2] 여행 규칙 멤버 리스트 조회 @Get('/detail/member/:ruleId') @UseGuards(UserGuard) - async getMemberList(@Req() req: Request, - @Param('ruleId') ruleId : number) : Promise> { + async getMemberList( + @Req() req: Request, + @Param('ruleId') ruleId: number, + ): Promise> { try { - const memberList = await this.ruleService.getMemberList(req.user.id, ruleId); + const memberList = await this.ruleService.getMemberList( + req.user.id, + ruleId, + ); return new ResponseDto( - ResponseCode.GET_MEMBER_LIST_SUCCESS, - true, - "여행 규칙 멤버 리스트 불러오기 성공", - memberList + ResponseCode.GET_MEMBER_LIST_SUCCESS, + true, + '여행 규칙 멤버 리스트 불러오기 성공', + memberList, ); } catch (e) { return new ResponseDto( - ResponseCode.GET_MEMBER_LIST_FAIL, - false, - e.message, - null + ResponseCode.GET_MEMBER_LIST_FAIL, + false, + e.message, + null, ); } } @@ -71,23 +90,29 @@ export class RuleController { @Get('/detail/search') @UseGuards(UserGuard) async getSearchMemberAtCreate( - @Query('searchTerm')searchTerm : string, - @Query() cursorPageOptionsDto: CursorPageOptionsDto, - @Req() req: Request): Promise> { + @Query('searchTerm') searchTerm: string, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, + @Req() req: Request, + ): Promise> { try { - const result : CursorPageDto = await this.ruleService.getSearchMemberAtCreate(cursorPageOptionsDto, req.user.id, searchTerm) + const result: CursorPageDto = + await this.ruleService.getSearchMemberAtCreate( + cursorPageOptionsDto, + req.user.id, + searchTerm, + ); return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_SUCCESS, - true, - "초대할 메이트 검색 결과 리스트 불러오기 성공", - result + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + '초대할 메이트 검색 결과 리스트 불러오기 성공', + result, ); } catch (e) { return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_FAIL, - false, - e.message, - null + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + e.message, + null, ); } } @@ -96,24 +121,31 @@ export class RuleController { @Get('/detail/search/:ruleId') @UseGuards(UserGuard) async getSearchMemberAtUpdate( - @Query('searchTerm')searchTerm : string, - @Query() cursorPageOptionsDto: CursorPageOptionsDto, - @Param('ruleId') ruleId: number, - @Req() req: Request): Promise> { + @Query('searchTerm') searchTerm: string, + @Query() cursorPageOptionsDto: CursorPageOptionsDto, + @Param('ruleId') ruleId: number, + @Req() req: Request, + ): Promise> { try { - const result : CursorPageDto = await this.ruleService.getSearchMemberAtUpdate(cursorPageOptionsDto, req.user.id, ruleId, searchTerm) + const result: CursorPageDto = + await this.ruleService.getSearchMemberAtUpdate( + cursorPageOptionsDto, + req.user.id, + ruleId, + searchTerm, + ); return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_SUCCESS, - true, - "초대할 메이트 검색 결과 리스트 불러오기 성공", - result + ResponseCode.GET_SEARCH_RESULT_SUCCESS, + true, + '초대할 메이트 검색 결과 리스트 불러오기 성공', + result, ); } catch (e) { return new ResponseDto( - ResponseCode.GET_SEARCH_RESULT_FAIL, - false, - e.message, - null + ResponseCode.GET_SEARCH_RESULT_FAIL, + false, + e.message, + null, ); } } @@ -121,25 +153,26 @@ export class RuleController { // [4] 여행 규칙 상세 페이지 조회 (게시글) @Get('/detail/:ruleId') @UseGuards(UserGuard) - async getDetail(@Req() req: Request, - @Param('ruleId') ruleId: number): Promise> { - - const result = await this.ruleService.getDetail(req.user.id, ruleId); + async getDetail( + @Req() req: Request, + @Param('ruleId') ruleId: number, + ): Promise> { + await this.ruleService.getDetail(req.user.id, ruleId); try { const result = await this.ruleService.getDetail(req.user.id, ruleId); return new ResponseDto( - ResponseCode.GET_RULE_DETAIL_SUCCESS, - true, - "여행 규칙 상세 페이지 (게시글) 조회 성공", - result + ResponseCode.GET_RULE_DETAIL_SUCCESS, + true, + '여행 규칙 상세 페이지 (게시글) 조회 성공', + result, ); } catch (e) { return new ResponseDto( - ResponseCode.GET_RULE_DETAIL_FAIL, - false, - e.message, - null + ResponseCode.GET_RULE_DETAIL_FAIL, + false, + e.message, + null, ); } } @@ -147,24 +180,29 @@ export class RuleController { // [5] 여행 규칙 수정 @Patch('/detail/:ruleId') @UseGuards(UserGuard) - async updateRule(@Body() updateRuleDto: UpdateRuleDto, - @Req() req: Request, - @Param('ruleId') ruleId: number): Promise> { - + async updateRule( + @Body() updateRuleDto: UpdateRuleDto, + @Req() req: Request, + @Param('ruleId') ruleId: number, + ): Promise> { try { - const result = await this.ruleService.updateRule(updateRuleDto, req.user.id, ruleId); + const result = await this.ruleService.updateRule( + updateRuleDto, + req.user.id, + ruleId, + ); return new ResponseDto( - ResponseCode.PATCH_RULE_SUCCESS, - true, - "여행 규칙 수정 성공", - result + ResponseCode.PATCH_RULE_SUCCESS, + true, + '여행 규칙 수정 성공', + result, ); } catch (e) { return new ResponseDto( - ResponseCode.PATCH_RULE_FAIL, - false, - e.message, - null + ResponseCode.PATCH_RULE_FAIL, + false, + e.message, + null, ); } } @@ -172,22 +210,27 @@ export class RuleController { // [6] 여행 규칙 생성 @Post('/detail') @UseGuards(UserGuard) - async createRule(@Req() req: Request, - @Body() createRuleDto: CreateRuleDto): Promise> { + async createRule( + @Req() req: Request, + @Body() createRuleDto: CreateRuleDto, + ): Promise> { try { - const result = await this.ruleService.createRule(createRuleDto, req.user.id); + const result = await this.ruleService.createRule( + createRuleDto, + req.user.id, + ); return new ResponseDto( - ResponseCode.RULE_CREATED, - true, - "여행 규칙 생성 성공", - result + ResponseCode.RULE_CREATED, + true, + '여행 규칙 생성 성공', + result, ); } catch (e) { return new ResponseDto( - ResponseCode.RULE_CREATION_FAIL, - false, - e.message, - null + ResponseCode.RULE_CREATION_FAIL, + false, + e.message, + null, ); } } @@ -195,22 +238,21 @@ export class RuleController { // [7] 여행 규칙 나가기 @Delete('/:ruleId') @UseGuards(UserGuard) - async deleteInvitation(@Req() req: Request, - @Param('ruleId') ruleId: number){ + async deleteInvitation(@Req() req: Request, @Param('ruleId') ruleId: number) { try { await this.ruleService.deleteInvitation(ruleId, req.user.id); return new ResponseDto( - ResponseCode.DELETE_INVITATION_SUCCESS, - true, - "여행 규칙 나가기 성공", - null + ResponseCode.DELETE_INVITATION_SUCCESS, + true, + '여행 규칙 나가기 성공', + null, ); } catch (e) { return new ResponseDto( - ResponseCode.DELETE_INVITATION_FAIL, - false, - e.message, - null + ResponseCode.DELETE_INVITATION_FAIL, + false, + e.message, + null, ); } } @@ -222,16 +264,18 @@ export class RuleController { try { const result = await this.ruleService.getRuleList(req.user.id); return new ResponseDto( - ResponseCode.GET_RULE_LIST_SUCCESS, - true, - "여행 규칙 전체 리스트 조회 성공", - result); + ResponseCode.GET_RULE_LIST_SUCCESS, + true, + '여행 규칙 전체 리스트 조회 성공', + result, + ); } catch (e) { return new ResponseDto( - ResponseCode.GET_RULE_LIST_FAIL, - false, - e.message, - null); + ResponseCode.GET_RULE_LIST_FAIL, + false, + e.message, + null, + ); } } -} \ No newline at end of file +} diff --git a/src/rule/rule.module.ts b/src/rule/rule.module.ts index b30a405..24c3d93 100644 --- a/src/rule/rule.module.ts +++ b/src/rule/rule.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { RuleService } from './rule.service'; import { RuleController } from './rule.controller'; -import { S3UtilService } from "../utils/S3.service"; -import { UserService } from "../user/user.service"; +import { S3UtilService } from '../utils/S3.service'; +import { UserService } from '../user/user.service'; @Module({ controllers: [RuleController], diff --git a/src/rule/rule.service.ts b/src/rule/rule.service.ts index 029fd0d..96d016f 100644 --- a/src/rule/rule.service.ts +++ b/src/rule/rule.service.ts @@ -1,39 +1,47 @@ -import {BadRequestException, Injectable, NotFoundException} from '@nestjs/common'; +import { + BadRequestException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { CreateRuleDto } from './dto/create-rule.dto'; import { RuleMainEntity } from './domain/rule.main.entity'; import { RuleSubEntity } from './domain/rule.sub.entity'; import { RuleInvitationEntity } from './domain/rule.invitation.entity'; -import { UserEntity} from "../user/user.entity"; -import {DetailMemberDto, DetailRuleDto, RulePairDto} from "./dto/detail.rule.dto"; -import { S3UtilService} from "../utils/S3.service"; -import { GetMemberListDto} from "./dto/get-member-list.dto"; -import {UserService} from "../user/user.service"; -import {GetRuleListDto, MemberPairDto} from "./dto/get-rule-list.dto"; -import {Equal, In, LessThan, Like, MoreThan, Not} from 'typeorm'; -import {GetSearchMemberDto} from "./dto/get-search-member.dto"; -import {UpdateRuleDto} from "./dto/update-rule.dto"; -import {CursorPageOptionsDto} from "./dto/cursor-page.options.dto"; -import {CommentEntity} from "../comment/domain/comment.entity"; -import {GetCommentDto } from "./dto/get-comment.dto"; -import {CursorPageDto} from "./dto/cursor-page.dto"; -import {CursorPageMetaDto} from "./dto/cursor-page.meta.dto"; -import {GetSearchMemberAtCreateDto} from "./dto/get-search-member-at-create.dto"; -import {UserFollowingEntity} from "../user/user.following.entity"; +import { UserEntity } from '../user/user.entity'; +import { + DetailMemberDto, + DetailRuleDto, + RulePairDto, +} from './dto/detail.rule.dto'; +import { S3UtilService } from '../utils/S3.service'; +import { GetMemberListDto } from './dto/get-member-list.dto'; +import { UserService } from '../user/user.service'; +import { GetRuleListDto, MemberPairDto } from './dto/get-rule-list.dto'; +import { Like, MoreThan } from 'typeorm'; +import { GetSearchMemberDto } from './dto/get-search-member.dto'; +import { UpdateRuleDto } from './dto/update-rule.dto'; +import { CursorPageOptionsDto } from './dto/cursor-page.options.dto'; +import { CommentEntity } from '../comment/domain/comment.entity'; +import { GetCommentDto } from './dto/get-comment.dto'; +import { CursorPageDto } from './dto/cursor-page.dto'; +import { CursorPageMetaDto } from './dto/cursor-page.meta.dto'; +import { GetSearchMemberAtCreateDto } from './dto/get-search-member-at-create.dto'; +import { UserFollowingEntity } from '../user/user.following.entity'; @Injectable() export class RuleService { constructor( - private readonly s3Service: S3UtilService, - private readonly userService: UserService, - ) { - } + private readonly s3Service: S3UtilService, + private readonly userService: UserService, + ) {} // [1] 여행 규칙 생성 async createRule(dto: CreateRuleDto, userId: number): Promise { - try { // 사용자 검증 - const inviterEntity = await UserEntity.findOneOrFail({where: {id: userId}}); + const inviterEntity = await UserEntity.findOneOrFail({ + where: { id: userId }, + }); if (!inviterEntity) throw new Error('사용자를 찾을 수 없습니다'); // -1) main 저장 @@ -56,20 +64,28 @@ export class RuleService { } // -3) invitation 저장 - const members = await Promise.all(dto.membersId.map(async (memberId): Promise => { - const ruleInvitationEntity = new RuleInvitationEntity(); - - const userEntity = await UserEntity.findOne({ - where: {id: memberId } - }); - if(!userEntity) throw new NotFoundException('멤버로 초대한 회원을 찾을 수 없습니다'); - if(userEntity.isQuit == true) throw new BadRequestException('탈퇴한 회원은 멤버로 초대할 수 없습니다'); - ruleInvitationEntity.rule = main; - ruleInvitationEntity.member = userEntity; - - await ruleInvitationEntity.save(); - return ruleInvitationEntity; - })); + await Promise.all( + dto.membersId.map(async (memberId): Promise => { + const ruleInvitationEntity = new RuleInvitationEntity(); + + const userEntity = await UserEntity.findOne({ + where: { id: memberId }, + }); + if (!userEntity) + throw new NotFoundException( + '멤버로 초대한 회원을 찾을 수 없습니다', + ); + if (userEntity.isQuit == true) + throw new BadRequestException( + '탈퇴한 회원은 멤버로 초대할 수 없습니다', + ); + ruleInvitationEntity.rule = main; + ruleInvitationEntity.member = userEntity; + + await ruleInvitationEntity.save(); + return ruleInvitationEntity; + }), + ); // -4) 여행 규칙 글 작성자 정보 저장 const writerEntity = new RuleInvitationEntity(); @@ -91,77 +107,86 @@ export class RuleService { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { id: userId }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const ruleMain = await RuleMainEntity.findOne({ - where: {id: ruleId}, - relations: {rules: true, invitations: {member: true}} - }) + where: { id: ruleId }, + relations: { rules: true, invitations: { member: true } }, + }); if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자인지 체크 const invitation = await RuleInvitationEntity.findOne({ - where: {member: {id: userId}, rule: {id: ruleId}}, - }) + where: { member: { id: userId }, rule: { id: ruleId } }, + }); const subs: RuleSubEntity[] = await RuleSubEntity.find({ - where: {main: {id: ruleId}} - }) - const invitations: RuleInvitationEntity[] = await RuleInvitationEntity.find({ - where: {rule: {id: ruleId}}, - relations: {member: {profileImage: true}} + where: { main: { id: ruleId } }, }); + const invitations: RuleInvitationEntity[] = + await RuleInvitationEntity.find({ + where: { rule: { id: ruleId } }, + relations: { member: { profileImage: true } }, + }); - if(!!invitation) { + if (!!invitation) { // -1) 제목 dto.id = ruleId; dto.mainTitle = ruleMain.mainTitle; console.log('dto.id : ', dto.id); // -2) 규칙 - const rulePairs = await Promise.all(subs.map(async (sub): Promise => { - const rulePair = new RulePairDto(); - rulePair.id = sub.id; - rulePair.ruleTitle = sub.ruleTitle; - rulePair.ruleDetail = sub.ruleDetail; - console.log('rulePair.id', rulePair.id); - - return rulePair; - })); + const rulePairs = await Promise.all( + subs.map(async (sub): Promise => { + const rulePair = new RulePairDto(); + rulePair.id = sub.id; + rulePair.ruleTitle = sub.ruleTitle; + rulePair.ruleDetail = sub.ruleDetail; + console.log('rulePair.id', rulePair.id); + + return rulePair; + }), + ); dto.rulePairs = rulePairs.sort((a, b) => a.id - b.id); // -3) 멤버 정보 - const detailMembers = await Promise.all(invitations.map(async (invitation): Promise => { - const detailMember = new DetailMemberDto; - const memberEntity = invitation.member; - if (memberEntity.isQuit == false) { - detailMember.id = memberEntity.id; - detailMember.name = memberEntity.nickname; - console.log('detailMember.id : ', detailMember.id); - - // 사용자 프로필 이미지 - const image = memberEntity.profileImage; - if (image == null) detailMember.image = null; + const detailMembers = await Promise.all( + invitations.map(async (invitation): Promise => { + const detailMember = new DetailMemberDto(); + const memberEntity = invitation.member; + if (memberEntity.isQuit == false) { + detailMember.id = memberEntity.id; + detailMember.name = memberEntity.nickname; + console.log('detailMember.id : ', detailMember.id); + + // 사용자 프로필 이미지 + const image = memberEntity.profileImage; + if (image == null) detailMember.image = null; + else { + const userImageKey = image.imageKey; + detailMember.image = await this.s3Service.getImageUrl( + userImageKey, + ); + } + } + // 탈퇴한 회원인데 ruleInvitationEntity 삭제 안된 경우) else { - const userImageKey = image.imageKey; - detailMember.image = await this.s3Service.getImageUrl(userImageKey); + console.log( + '탈퇴한 회원의 ruleInvitationEntity 가 삭제되지 않았습니다', + ); + console.log('탈퇴한 회원의 ID : ', memberEntity.id); + console.log('해당 ruleInvitationEntity ID : ', invitation.id); + detailMember.id = null; + detailMember.name = null; + detailMember.image = null; } - } - // 탈퇴한 회원인데 ruleInvitationEntity 삭제 안된 경우) - else { - console.log('탈퇴한 회원의 ruleInvitationEntity 가 삭제되지 않았습니다'); - console.log('탈퇴한 회원의 ID : ', memberEntity.id); - console.log('해당 ruleInvitationEntity ID : ', invitation.id); - detailMember.id = null; - detailMember.name = null; - detailMember.image = null; - } - return detailMember; - })); + return detailMember; + }), + ); dto.detailMembers = detailMembers.sort((a, b) => a.id - b.id); return dto; @@ -172,31 +197,34 @@ export class RuleService { console.log('게시글 조회에 실패하였습니다'); throw new Error(e.message); } - }; + } // [3] 여행 규칙 상세 페이지 조회 (댓글) - 페이지네이션 - async getComment(cursorPageOptionsDto: CursorPageOptionsDto, ruleId: number, userId: number): Promise> { - + async getComment( + cursorPageOptionsDto: CursorPageOptionsDto, + ruleId: number, + userId: number, + ): Promise> { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { id: userId }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const ruleMain = await RuleMainEntity.findOne({ - where: {id: ruleId}, + where: { id: ruleId }, }); if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자가 아닌 경우 const invitation = await RuleInvitationEntity.findOne({ - where: {member: {id: userId}, rule: {id: ruleId}}, - }) + where: { member: { id: userId }, rule: { id: ruleId } }, + }); if (!invitation) throw new Error('사용자가 참여하는 규칙이 아닙니다'); - console.log('--- 검증 완료 ---') + console.log('--- 검증 완료 ---'); // (1) 데이터 조회 const cursorId: number = cursorPageOptionsDto.cursorId; @@ -204,48 +232,52 @@ export class RuleService { const [comments, total] = await CommentEntity.findAndCount({ take: cursorPageOptionsDto.take, where: { - rule: {id: ruleId}, + rule: { id: ruleId }, id: cursorId ? MoreThan(cursorId) : null, }, - relations: {user: {profileImage: true}}, + relations: { user: { profileImage: true } }, order: { - id: "ASC" as any, + id: 'ASC' as any, }, }); - const result = await Promise.all(comments.map(async (comment) => { - const getCommentDto = new GetCommentDto(); + const result = await Promise.all( + comments.map(async (comment) => { + const getCommentDto = new GetCommentDto(); - getCommentDto.id = comment.id; - getCommentDto.content = comment.content; - getCommentDto.updated = comment.updated; + getCommentDto.id = comment.id; + getCommentDto.content = comment.content; + getCommentDto.updated = comment.updated; - // 댓글 작성자 정보 - // 탈퇴한 사용자가 작성한 댓글도 표시 - // -> 댓글 작성자 (user) 존재 여부 확인 - const writerEntity = comment.user; - if (writerEntity.isQuit == false) { - getCommentDto.writerId = comment.user.id; - getCommentDto.name = comment.user.nickname; + // 댓글 작성자 정보 + // 탈퇴한 사용자가 작성한 댓글도 표시 + // -> 댓글 작성자 (user) 존재 여부 확인 + const writerEntity = comment.user; + if (writerEntity.isQuit == false) { + getCommentDto.writerId = comment.user.id; + getCommentDto.name = comment.user.nickname; - // 사용자 프로필 이미지 - const image = comment.user.profileImage; - if (image == null) getCommentDto.image = null; + // 사용자 프로필 이미지 + const image = comment.user.profileImage; + if (image == null) getCommentDto.image = null; + else { + const userImageKey = image.imageKey; + getCommentDto.image = await this.s3Service.getImageUrl( + userImageKey, + ); + } + } + // 댓글 작성자가 탈퇴한 사용자인 경우 else { - const userImageKey = image.imageKey; - getCommentDto.image = await this.s3Service.getImageUrl(userImageKey); + console.log('탈퇴한 회원이 작성한 댓글 입니다'); + getCommentDto.writerId = null; + getCommentDto.name = null; + getCommentDto.image = null; } - } - // 댓글 작성자가 탈퇴한 사용자인 경우 - else { - console.log('탈퇴한 회원이 작성한 댓글 입니다'); - getCommentDto.writerId = null; - getCommentDto.name = null; - getCommentDto.image = null; - } - return getCommentDto; - })); + return getCommentDto; + }), + ); // (2) 페이징 및 정렬 기준 설정 let hasNextData = true; @@ -262,7 +294,12 @@ export class RuleService { cursor = lastDataPerScroll.id; } - const cursorPageMetaDto = new CursorPageMetaDto({cursorPageOptionsDto, total, hasNextData, cursor}); + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); return new CursorPageDto(result, cursorPageMetaDto); } catch (e) { @@ -271,24 +308,27 @@ export class RuleService { } // [4] 여행 규칙 나가기 - async deleteInvitation(ruleId: number, userId: number): Promise { + async deleteInvitation( + ruleId: number, + userId: number, + ): Promise { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { id: userId }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const ruleMain = await RuleMainEntity.findOne({ - where: {id: ruleId}, + where: { id: ruleId }, }); if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자가 아닌 경우 const invitation = await RuleInvitationEntity.findOne({ - where: {member: {id: userId}, rule: {id: ruleId}}, - }) + where: { member: { id: userId }, rule: { id: ruleId } }, + }); if (!!invitation) { return invitation.softRemove(); } else throw new Error('사용자가 참여하는 규칙이 아닙니다'); @@ -299,50 +339,58 @@ export class RuleService { } // [4] 여행 규칙 멤버 리스트 조회 - async getMemberList(userId: number, ruleId: number): Promise { + async getMemberList( + userId: number, + ruleId: number, + ): Promise { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { id: userId }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const ruleMain = await RuleMainEntity.findOne({ - where: {id: ruleId}, + where: { id: ruleId }, }); if (!ruleMain) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자가 아닌 경우 const invitation = await RuleInvitationEntity.findOne({ - where: {member: {id: userId}, rule: {id: ruleId}}, - }) - - if(!!invitation) { - const invitationsList: RuleInvitationEntity[] = await RuleInvitationEntity.find({ - where: {rule: {id: ruleId}}, - relations: {member: true} - }); - - const membersList: GetMemberListDto[] = await Promise.all(invitationsList.map(async (invitation): Promise => { - const memberEntity: UserEntity = invitation.member; - const memberDto: GetMemberListDto = new GetMemberListDto(); + where: { member: { id: userId }, rule: { id: ruleId } }, + }); - console.log('memberEntity : ', memberEntity); - memberDto.id = memberEntity.id; - memberDto.name = memberEntity.nickname; - memberDto.email = memberEntity.email; - memberDto.introduction = memberEntity.introduction; + if (!!invitation) { + const invitationsList: RuleInvitationEntity[] = + await RuleInvitationEntity.find({ + where: { rule: { id: ruleId } }, + relations: { member: true }, + }); + + const membersList: GetMemberListDto[] = await Promise.all( + invitationsList.map(async (invitation): Promise => { + const memberEntity: UserEntity = invitation.member; + const memberDto: GetMemberListDto = new GetMemberListDto(); + + console.log('memberEntity : ', memberEntity); + memberDto.id = memberEntity.id; + memberDto.name = memberEntity.nickname; + memberDto.email = memberEntity.email; + memberDto.introduction = memberEntity.introduction; - // 사용자 프로필 이미지 - const image = await this.userService.getProfileImage(memberEntity.id); - if (image == null) memberDto.image = null; - else { - const userImageKey = image.imageKey; - memberDto.image = await this.s3Service.getImageUrl(userImageKey); - } - return memberDto; - })); + // 사용자 프로필 이미지 + const image = await this.userService.getProfileImage( + memberEntity.id, + ); + if (image == null) memberDto.image = null; + else { + const userImageKey = image.imageKey; + memberDto.image = await this.s3Service.getImageUrl(userImageKey); + } + return memberDto; + }), + ); const sortedList = membersList.sort((a, b) => a.id - b.id); return sortedList; } else throw new Error('사용자가 참여하는 규칙이 아닙니다'); @@ -353,7 +401,6 @@ export class RuleService { // [5] 여행 규칙 전체 리스트 조회 async getRuleList(userId: number): Promise { - try { // 검증) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ @@ -376,30 +423,39 @@ export class RuleService { if (!user) throw new Error('사용자를 찾을 수 없습니다'); const invitationEntities = await RuleInvitationEntity.find({ - where: {member: {id: userId}}, + where: { member: { id: userId } }, relations: { rule: { - invitations: true - } - } + invitations: true, + }, + }, }); if (!!invitationEntities) { - const getRuleListDtos = await Promise.all(invitationEntities.map(async (invitation: RuleInvitationEntity): Promise => { - const ruleListDto: GetRuleListDto = new GetRuleListDto; - const ruleId = invitation.rule.id; - const ruleMain = invitation.rule; - - ruleListDto.id = ruleMain.id; - ruleListDto.title = ruleMain.mainTitle; - ruleListDto.updated = ruleMain.updated; - ruleListDto.memberCnt = ruleMain.invitations.length; - ruleListDto.memberPairs = await this.getMemberPairs(ruleId); - - return ruleListDto; - })); + const getRuleListDtos = await Promise.all( + invitationEntities.map( + async ( + invitation: RuleInvitationEntity, + ): Promise => { + const ruleListDto: GetRuleListDto = new GetRuleListDto(); + const ruleId = invitation.rule.id; + const ruleMain = invitation.rule; + + ruleListDto.id = ruleMain.id; + ruleListDto.title = ruleMain.mainTitle; + ruleListDto.updated = ruleMain.updated; + ruleListDto.memberCnt = ruleMain.invitations.length; + ruleListDto.memberPairs = await this.getMemberPairs(ruleId); + + return ruleListDto; + }, + ), + ); - const sortedGetRuleListDtos = getRuleListDtos.sort((a, b) => new Date(b.updated).getTime() - new Date(a.updated).getTime()); + const sortedGetRuleListDtos = getRuleListDtos.sort( + (a, b) => + new Date(b.updated).getTime() - new Date(a.updated).getTime(), + ); return sortedGetRuleListDtos; } @@ -411,39 +467,44 @@ export class RuleService { async getMemberPairs(ruleId: number): Promise { const invitations = await RuleInvitationEntity.find({ - where: {rule: {id: ruleId}}, + where: { rule: { id: ruleId } }, relations: { member: { - profileImage: true + profileImage: true, + }, + }, + }); + + const result: MemberPairDto[] = await Promise.all( + invitations.map(async (invitation): Promise => { + const memberPair = new MemberPairDto(); + const user: UserEntity = invitation.member; + + console.log('user.id : ', user.id); + memberPair.id = user.id; + memberPair.name = user.nickname; + + // 사용자 프로필 이미지 + const image = user.profileImage; + if (image == null) memberPair.image = null; + else { + const userImageKey = image.imageKey; + memberPair.image = await this.s3Service.getImageUrl(userImageKey); } - } - }) - - const result: MemberPairDto[] = await Promise.all(invitations.map(async (invitation): Promise => { - const memberPair = new MemberPairDto; - const user: UserEntity = invitation.member; - - console.log('user.id : ', user.id); - memberPair.id = user.id; - memberPair.name = user.nickname; - - // 사용자 프로필 이미지 - const image = user.profileImage; - if (image == null) memberPair.image = null; - else { - const userImageKey = image.imageKey; - memberPair.image = await this.s3Service.getImageUrl(userImageKey); - } - return memberPair; - })); + return memberPair; + }), + ); return result; } // [6] 여행 규칙 참여 멤버로 초대할 메이트 검색 결과 - 무한 스크롤 // 여행 규칙 생성 / 여행 규칙 수정 분리 // case1. 여행 규칙 생성 - async getSearchMemberAtCreate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, searchTerm: string): Promise> { - + async getSearchMemberAtCreate( + cursorPageOptionsDto: CursorPageOptionsDto, + userId: number, + searchTerm: string, + ): Promise> { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ @@ -454,16 +515,16 @@ export class RuleService { if (!user) throw new Error('사용자를 찾을 수 없습니다'); // (1) cursorId 설정 - let cursorId: number = 0; + let cursorId = 0; console.log('cursorPageOptionsDto : ', cursorPageOptionsDto); // -1) 처음 요청인 경우 if (cursorPageOptionsDto.cursorId == 0) { const newUser = await UserEntity.find({ order: { - id: 'DESC' // 가장 최근에 가입한 유저 + id: 'DESC', // 가장 최근에 가입한 유저 }, - take: 1 + take: 1, }); cursorId = newUser[0].id + 1; @@ -472,7 +533,7 @@ export class RuleService { // -2) 처음 요청이 아닌 경우 } else { cursorId = cursorPageOptionsDto.cursorId; - console.log('cursorPageOptionsDto.cursorId != 0 로 인식') + console.log('cursorPageOptionsDto.cursorId != 0 로 인식'); } console.log('cursor: ', cursorId); @@ -493,61 +554,79 @@ export class RuleService { // userFollowingEntity) user: 로그인한 유저, followUser: 유저가 팔로우하는 유저 let resultFollowingEntities = await UserFollowingEntity.find({ where: { - user: {id: userId}, - followUser: {nickname: Like(`%${searchTerm}%`)} + user: { id: userId }, + followUser: { nickname: Like(`%${searchTerm}%`) }, }, relations: { - followUser: { profileImage : true } + followUser: { profileImage: true }, }, order: { - followUser: {id: 'DESC'} - } + followUser: { id: 'DESC' }, + }, }); console.log('resultFollowingEntities', resultFollowingEntities); // 3번 검색 조건) 탈퇴 여부 확인 - resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.isQuit == false); - for(const userFollowingEntity of resultFollowingEntities) { - console.log('isQuit == false : ', userFollowingEntity.followUser.isQuit); + resultFollowingEntities = resultFollowingEntities.filter( + (userFollowingEntity) => userFollowingEntity.followUser.isQuit == false, + ); + for (const userFollowingEntity of resultFollowingEntities) { + console.log( + 'isQuit == false : ', + userFollowingEntity.followUser.isQuit, + ); } const total = resultFollowingEntities.length; // 4번 검색 조건) id 가 cursorId 보다 작은 // 해당 요소보다 작은 요소들만 필터링 - for(const userFollowingEntity of resultFollowingEntities) { - console.log('userFollowingEntity.followUser.id : ', userFollowingEntity.followUser.id); + for (const userFollowingEntity of resultFollowingEntities) { + console.log( + 'userFollowingEntity.followUser.id : ', + userFollowingEntity.followUser.id, + ); } - resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.id < cursorId); + resultFollowingEntities = resultFollowingEntities.filter( + (userFollowingEntity) => userFollowingEntity.followUser.id < cursorId, + ); // take 초기값 설정 console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); if (cursorPageOptionsDto.take == 0) { cursorPageOptionsDto.take = 5; } - const results = resultFollowingEntities.slice(0, cursorPageOptionsDto.take); + const results = resultFollowingEntities.slice( + 0, + cursorPageOptionsDto.take, + ); console.log('results (UserFollowingEntity[]) : ', results); - const searchResult = await Promise.all(results.map(async (result) => { - const dtoAtCreate: GetSearchMemberAtCreateDto = new GetSearchMemberAtCreateDto(); - const follower = result.followUser; + const searchResult = await Promise.all( + results.map(async (result) => { + const dtoAtCreate: GetSearchMemberAtCreateDto = + new GetSearchMemberAtCreateDto(); + const follower = result.followUser; - dtoAtCreate.id = follower.id; - dtoAtCreate.name = follower.nickname; - dtoAtCreate.email = follower.email; - dtoAtCreate.introduction = follower.introduction; + dtoAtCreate.id = follower.id; + dtoAtCreate.name = follower.nickname; + dtoAtCreate.email = follower.email; + dtoAtCreate.introduction = follower.introduction; - // 사용자 프로필 이미지 - const image = follower.profileImage; - if (image == null) dtoAtCreate.image = null; - else { - const followerImageKey = image.imageKey; - dtoAtCreate.image = await this.s3Service.getImageUrl(followerImageKey); - } - return dtoAtCreate; - })); + // 사용자 프로필 이미지 + const image = follower.profileImage; + if (image == null) dtoAtCreate.image = null; + else { + const followerImageKey = image.imageKey; + dtoAtCreate.image = await this.s3Service.getImageUrl( + followerImageKey, + ); + } + return dtoAtCreate; + }), + ); console.log('searchResult : ', searchResult); @@ -559,7 +638,7 @@ export class RuleService { console.log('takePerScroll : ', takePerScroll); const isLastScroll = total <= takePerScroll; console.log('isLastScroll : ', isLastScroll); - console.log('total : ', total) + console.log('total : ', total); const lastDataPerScroll = searchResult[searchResult.length - 1]; if (isLastScroll) { @@ -569,7 +648,12 @@ export class RuleService { cursor = lastDataPerScroll.id; } - const cursorPageMetaDto = new CursorPageMetaDto({cursorPageOptionsDto, total, hasNextData, cursor}); + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); return new CursorPageDto(searchResult, cursorPageMetaDto); } catch (e) { @@ -578,8 +662,12 @@ export class RuleService { } // [6-2] case2. 여행 규칙 수정 - async getSearchMemberAtUpdate(cursorPageOptionsDto: CursorPageOptionsDto, userId: number, ruleId: number, searchTerm: string): Promise> { - + async getSearchMemberAtUpdate( + cursorPageOptionsDto: CursorPageOptionsDto, + userId: number, + ruleId: number, + searchTerm: string, + ): Promise> { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ @@ -590,28 +678,28 @@ export class RuleService { if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const rule = await RuleMainEntity.findOne({ - where: {id: ruleId}, - relations: {rules: true, invitations: {member: true}} - }) + where: { id: ruleId }, + relations: { rules: true, invitations: { member: true } }, + }); if (!rule) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자인지 체크 const invitation = await RuleInvitationEntity.findOne({ - where: {member: {id: userId}, rule: {id: ruleId}}, - }) + where: { member: { id: userId }, rule: { id: ruleId } }, + }); - if(!invitation) throw new Error('규칙에 참여하지 않는 사용자 입니다'); + if (!invitation) throw new Error('규칙에 참여하지 않는 사용자 입니다'); // (1) cursorId 설정 - let cursorId: number = 0; + let cursorId = 0; console.log('cursorPageOptionsDto : ', cursorPageOptionsDto); // -1) 처음 요청인 경우 if (cursorPageOptionsDto.cursorId == 0) { const newUser = await UserEntity.find({ order: { - id: 'DESC' // 가장 최근에 가입한 유저 + id: 'DESC', // 가장 최근에 가입한 유저 }, - take: 1 + take: 1, }); cursorId = newUser[0].id + 1; @@ -620,7 +708,7 @@ export class RuleService { // -2) 처음 요청이 아닌 경우 } else { cursorId = cursorPageOptionsDto.cursorId; - console.log('cursorPageOptionsDto.cursorId != 0 로 인식') + console.log('cursorPageOptionsDto.cursorId != 0 로 인식'); } console.log('cursor: ', cursorId); @@ -641,64 +729,82 @@ export class RuleService { // userFollowingEntity) user: 로그인한 유저, followUser: 유저가 팔로우하는 유저 let resultFollowingEntities = await UserFollowingEntity.find({ where: { - user: {id: userId}, - followUser: {nickname: Like(`%${searchTerm}%`)} + user: { id: userId }, + followUser: { nickname: Like(`%${searchTerm}%`) }, }, relations: { - followUser: {profileImage : true, ruleParticipate: {rule: true} } + followUser: { profileImage: true, ruleParticipate: { rule: true } }, }, order: { - followUser: {id: 'DESC'} - } + followUser: { id: 'DESC' }, + }, }); console.log('resultFollowingEntities', resultFollowingEntities); // 3번 검색 조건) 탈퇴 여부 확인 - resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.isQuit == false); - for(const userFollowingEntity of resultFollowingEntities) { - console.log('isQuit == false : ', userFollowingEntity.followUser.isQuit); + resultFollowingEntities = resultFollowingEntities.filter( + (userFollowingEntity) => userFollowingEntity.followUser.isQuit == false, + ); + for (const userFollowingEntity of resultFollowingEntities) { + console.log( + 'isQuit == false : ', + userFollowingEntity.followUser.isQuit, + ); } const total = resultFollowingEntities.length; // 4번 검색 조건) id 가 cursorId 보다 작은 // 해당 요소보다 작은 요소들만 필터링 - for(const userFollowingEntity of resultFollowingEntities) { - console.log('userFollowingEntity.followUser.id : ', userFollowingEntity.followUser.id); + for (const userFollowingEntity of resultFollowingEntities) { + console.log( + 'userFollowingEntity.followUser.id : ', + userFollowingEntity.followUser.id, + ); } - resultFollowingEntities = resultFollowingEntities.filter(userFollowingEntity => userFollowingEntity.followUser.id < cursorId); + resultFollowingEntities = resultFollowingEntities.filter( + (userFollowingEntity) => userFollowingEntity.followUser.id < cursorId, + ); // take 초기값 설정 console.log('cursorPageOptionsDto.take : ', cursorPageOptionsDto.take); if (cursorPageOptionsDto.take == 0) { cursorPageOptionsDto.take = 5; } - const results = resultFollowingEntities.slice(0, cursorPageOptionsDto.take); + const results = resultFollowingEntities.slice( + 0, + cursorPageOptionsDto.take, + ); console.log('results (UserFollowingEntity[]) : ', results); // dto 데이터 넣기 - const searchResult = await Promise.all(results.map(async (result) => { - const dto: GetSearchMemberDto = new GetSearchMemberDto(); - const follower = result.followUser; - - dto.id = follower.id; - dto.name = follower.nickname; - dto.email = follower.email; - dto.introduction = follower.introduction; - // 이미 여행 규칙에 참여하는 멤버인지 여부 - dto.isInvited = await this.userService.checkAlreadyMember(follower.id, ruleId); + const searchResult = await Promise.all( + results.map(async (result) => { + const dto: GetSearchMemberDto = new GetSearchMemberDto(); + const follower = result.followUser; + + dto.id = follower.id; + dto.name = follower.nickname; + dto.email = follower.email; + dto.introduction = follower.introduction; + // 이미 여행 규칙에 참여하는 멤버인지 여부 + dto.isInvited = await this.userService.checkAlreadyMember( + follower.id, + ruleId, + ); - // 사용자 프로필 이미지 - const image = follower.profileImage; - if (image == null) dto.image = null; - else { - const followerImageKey = image.imageKey; - dto.image = await this.s3Service.getImageUrl(followerImageKey); - } - return dto; - })); + // 사용자 프로필 이미지 + const image = follower.profileImage; + if (image == null) dto.image = null; + else { + const followerImageKey = image.imageKey; + dto.image = await this.s3Service.getImageUrl(followerImageKey); + } + return dto; + }), + ); console.log('searchResult : ', searchResult); @@ -717,7 +823,12 @@ export class RuleService { cursor = lastDataPerScroll.id; } - const cursorPageMetaDto = new CursorPageMetaDto({cursorPageOptionsDto, total, hasNextData, cursor}); + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); return new CursorPageDto(searchResult, cursorPageMetaDto); } catch (e) { @@ -726,31 +837,34 @@ export class RuleService { } // [7] 여행 규칙 수정 - async updateRule(updateRuleDto: UpdateRuleDto, userId: number, ruleId: number): Promise { - + async updateRule( + updateRuleDto: UpdateRuleDto, + userId: number, + ruleId: number, + ): Promise { try { // 검증1) 사용자가 존재하지 않는 경우 const user = await UserEntity.findOne({ - where: {id: userId}, + where: { id: userId }, }); if (!user) throw new Error('사용자를 찾을 수 없습니다'); // 검증2) 규칙이 존재하지 않는 경우 const rule = await RuleMainEntity.findOne({ - where: {id: ruleId}, - relations: {rules: true, invitations: {member: true}} - }) + where: { id: ruleId }, + relations: { rules: true, invitations: { member: true } }, + }); if (!rule) throw new Error('규칙을 찾을 수 없습니다'); // 검증3) 규칙에 참여하는 사용자인지 체크 const invitation = await RuleInvitationEntity.findOne({ - where: {member: {id: userId}, rule: {id: ruleId}}, - }) + where: { member: { id: userId }, rule: { id: ruleId } }, + }); // -> 규칙에 참여하는 사용자인 경우 if (!!invitation) { updateRuleDto.rulePairs.sort((a, b) => a.ruleNumber - b.ruleNumber); - rule.mainTitle = updateRuleDto.mainTitle + rule.mainTitle = updateRuleDto.mainTitle; await rule.save(); // (1) [상세 규칙 수정] @@ -763,7 +877,7 @@ export class RuleService { // case1) 규칙 삭제 for (const sub of subs) { - let isDeleteSub: boolean = true; + let isDeleteSub = true; for (const updateSub of updateSubsList) { if (sub.id == updateSub.id) { isDeleteSub = false; @@ -780,7 +894,7 @@ export class RuleService { for (const updateSub of updateSubsList) { // case1) 새로운 규칙 if (!updateSub.id) { - const newSub = new RuleSubEntity() + const newSub = new RuleSubEntity(); newSub.main = rule; newSub.ruleTitle = updateSub.ruleTitle; newSub.ruleDetail = updateSub.ruleDetail; @@ -791,8 +905,8 @@ export class RuleService { // case2) 수정 규칙 else { const oldSub = await RuleSubEntity.findOne({ - where: {id: updateSub.id} - }) + where: { id: updateSub.id }, + }); oldSub.ruleTitle = updateSub.ruleTitle; oldSub.ruleDetail = updateSub.ruleDetail; @@ -804,16 +918,16 @@ export class RuleService { // (2) [여행 규칙 멤버 수정] // 기존 멤버 초대 리스트 const oldInvitations = await RuleInvitationEntity.find({ - where: {rule: {id: ruleId}}, - relations: {member: true} - }) + where: { rule: { id: ruleId } }, + relations: { member: true }, + }); // 수정된 멤버 ID 리스트 const updateMemberIds = updateRuleDto.membersId; // case1) 멤버 삭제 for (const invitation of oldInvitations) { const member = invitation.member; - let isDeleteMember: boolean = true; + let isDeleteMember = true; // (예외 상황) 현재 로그인한 사용자 if (member.id == userId) break; @@ -832,9 +946,9 @@ export class RuleService { // case2) 멤버 추가 for (const updateMemberId of updateMemberIds) { - const member = await UserEntity.findExistUser(updateMemberId); + await UserEntity.findExistUser(updateMemberId); - let isPostMember: boolean = true; + let isPostMember = true; for (const oldInvitation of oldInvitations) { const oldMember = oldInvitation.member; @@ -847,20 +961,21 @@ export class RuleService { if (isPostMember) { const newInvitation = new RuleInvitationEntity(); - newInvitation.member = await UserEntity.findExistUser(updateMemberId); + newInvitation.member = await UserEntity.findExistUser( + updateMemberId, + ); newInvitation.rule = rule; await newInvitation.save(); console.log('새로 초대한 멤버 ID : ', updateMemberId); } } - console.log('--여행 규칙 수정이 완료되었습니다--') + console.log('--여행 규칙 수정이 완료되었습니다--'); return rule.id; } else throw new Error('사용자가 참여하는 규칙이 아닙니다'); // -> 여행 규칙에 참여하지 않는 경우 - } catch (e) { console.log('여행 규칙 수정 실패'); throw new Error(e.message); } } -} \ No newline at end of file +} diff --git a/src/schedule/schedule.entity.ts b/src/schedule/schedule.entity.ts index 9c9db63..473d247 100644 --- a/src/schedule/schedule.entity.ts +++ b/src/schedule/schedule.entity.ts @@ -12,7 +12,6 @@ import { Between, } from 'typeorm'; import { NotFoundException } from '@nestjs/common'; -import { startOfMonth, endOfMonth } from 'date-fns'; import { BaseResponse } from 'src/response/response.status'; import { DetailScheduleEntity } from '../detail-schedule/detail-schedule.entity'; import { LocationEntity } from 'src/location/location.entity'; @@ -138,9 +137,6 @@ export class ScheduleEntity extends BaseEntity { return schedules; } - static async findExistScheduleByOptions(journeyId, scheduleId) { - const schedule = await ScheduleEntity.find({}); - } // 월별 일정 조회하기 static async findMonthlySchedule( journeyId, diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index e078384..454ce80 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -1,11 +1,10 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { response } from 'src/response/response'; import { BaseResponse } from 'src/response/response.status'; import { LocationEntity } from 'src/location/location.entity'; import { ScheduleEntity } from './schedule.entity'; import { UserEntity } from 'src/user/user.entity'; import { UpdateScheduleDto } from './dtos/update-schedule-dto'; -import { JourneyEntity } from 'src/journey/model/journey.entity'; @Injectable() export class ScheduleService { @@ -45,7 +44,7 @@ export class ScheduleService { } async resetSchedule(user, scheduleId) { - const existUser = await UserEntity.findExistUser(user.id); + await UserEntity.findExistUser(user.id); const schedule = await ScheduleEntity.findExistSchedule(scheduleId); // 스케줄이 위치를 가지고 있는지 확인 diff --git a/src/search/dto/get-search-main.dto.ts b/src/search/dto/get-search-main.dto.ts index 07b2da3..9aa5e3e 100644 --- a/src/search/dto/get-search-main.dto.ts +++ b/src/search/dto/get-search-main.dto.ts @@ -2,6 +2,6 @@ import { SignatureCoverDto } from './signature-cover.dto'; -export class GetSearchMainDto{ +export class GetSearchMainDto { covers: SignatureCoverDto[]; -} \ No newline at end of file +} diff --git a/src/search/dto/signature-cover.dto.ts b/src/search/dto/signature-cover.dto.ts index 769e2d4..6112216 100644 --- a/src/search/dto/signature-cover.dto.ts +++ b/src/search/dto/signature-cover.dto.ts @@ -3,9 +3,9 @@ export class SignatureCoverDto { _id: number; title: string; - image: string; // 시그니처 첫 번째 페이지 사진 - userName: string; // 유저 닉네임 - userImage: string; // 유저 프로필 사진 + image: string; // 시그니처 첫 번째 페이지 사진 + userName: string; // 유저 닉네임 + userImage: string; // 유저 프로필 사진 date: string; liked: number; -} \ No newline at end of file +} diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index f465104..80e5074 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -1,25 +1,22 @@ // search.controller.ts -import { Body, Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; +import { Controller, Get, Query, Req, UseGuards } from '@nestjs/common'; import { ResponseDto } from '../response/response.dto'; import { GetSearchMainDto } from './dto/get-search-main.dto'; import { ResponseCode } from '../response/response-code.enum'; import { SignatureCoverDto } from './dto/signature-cover.dto'; import { SearchService } from './search.service'; -import { UserGuard } from '../user/user.guard'; import { Request } from 'express'; import { OptionalUserGuard } from '../user/optional.user.guard'; @Controller('search') -export class SearchController{ - +export class SearchController { constructor(private readonly searchService: SearchService) {} @Get('/hot') // 팀색탭 메인: 인기 급상승 시그니처 - async getSearchHotSignatures( - ): Promise>{ - try{ - const getHotSignaturesDto:GetSearchMainDto = new GetSearchMainDto(); + async getSearchHotSignatures(): Promise> { + try { + const getHotSignaturesDto: GetSearchMainDto = new GetSearchMainDto(); // 인기 급상승 시그니처 가져오기 getHotSignaturesDto.covers = await this.searchService.findHotSignatures(); @@ -27,16 +24,16 @@ export class SearchController{ return new ResponseDto( ResponseCode.GET_SEARCH_MAIN_SUCCESS, true, - "탐색탭 메인 화면 가져오기 성공", - getHotSignaturesDto + '탐색탭 메인 화면 가져오기 성공', + getHotSignaturesDto, ); - }catch (error){ - console.log("탐색탭 메인 가져오기 실패: ", error); + } catch (error) { + console.log('탐색탭 메인 가져오기 실패: ', error); return new ResponseDto( ResponseCode.GET_SEARCH_MAIN_FAIL, false, - "탐색탭 메인 화면 가져오기 실패", - null + '탐색탭 메인 화면 가져오기 실패', + null, ); } } @@ -45,57 +42,57 @@ export class SearchController{ @UseGuards(OptionalUserGuard) async getSearchNewSignatures( @Req() req?: Request, - ): Promise>{ - try{ - const getMatesNewSignatureDto:GetSearchMainDto = new GetSearchMainDto(); + ): Promise> { + try { + const getMatesNewSignatureDto: GetSearchMainDto = new GetSearchMainDto(); // 로그인 했을 경우 내가 팔로우하는 메이트들의 최신 시그니처 가져오기 - if(req.user != null) getMatesNewSignatureDto.covers = await this.searchService.findMatesNewSignatures(req.user.id); - + if (req.user != null) + getMatesNewSignatureDto.covers = + await this.searchService.findMatesNewSignatures(req.user.id); // 로그인 안했으면 빈 배열 else getMatesNewSignatureDto.covers = null; return new ResponseDto( ResponseCode.GET_SEARCH_MAIN_SUCCESS, true, - "탐색탭 메인 화면 가져오기 성공", - getMatesNewSignatureDto + '탐색탭 메인 화면 가져오기 성공', + getMatesNewSignatureDto, ); - }catch (error){ - console.log("탐색탭 메인 가져오기 실패: ", error); + } catch (error) { + console.log('탐색탭 메인 가져오기 실패: ', error); return new ResponseDto( ResponseCode.GET_SEARCH_MAIN_FAIL, false, - "탐색탭 메인 화면 가져오기 실패", - null + '탐색탭 메인 화면 가져오기 실패', + null, ); } } - @Get('/find') // 탑색탭 검색: 키워드로 시그니처 검색하기 - async search(@Query('keyword') keyword: string): Promise> { - try{ - - const searchResult: SignatureCoverDto[] = await this.searchService.searchByKeyword(keyword); + async search( + @Query('keyword') keyword: string, + ): Promise> { + try { + const searchResult: SignatureCoverDto[] = + await this.searchService.searchByKeyword(keyword); return new ResponseDto( ResponseCode.SEARCH_BY_KEYWORD_SUCCESS, true, - "키워드로 검색하기 성공", - searchResult + '키워드로 검색하기 성공', + searchResult, ); - - }catch(error){ - console.log("탑색- 키워드로 검색 실패: "+error); + } catch (error) { + console.log('탑색- 키워드로 검색 실패: ' + error); return new ResponseDto( ResponseCode.SEARCH_BY_KEYWORD_FAIL, false, - "키워드로 검색하기 실패", - null + '키워드로 검색하기 실패', + null, ); } } - } diff --git a/src/search/search.module.ts b/src/search/search.module.ts index c19bd01..99deb12 100644 --- a/src/search/search.module.ts +++ b/src/search/search.module.ts @@ -8,7 +8,7 @@ import { SignatureService } from '../signature/signature.service'; import { S3UtilService } from '../utils/S3.service'; @Module({ - controllers: [SearchController], - providers: [SearchService, UserService, SignatureService, S3UtilService], + controllers: [SearchController], + providers: [SearchService, UserService, SignatureService, S3UtilService], }) -export class SearchModule {} \ No newline at end of file +export class SearchModule {} diff --git a/src/search/search.service.ts b/src/search/search.service.ts index f61933a..2de42db 100644 --- a/src/search/search.service.ts +++ b/src/search/search.service.ts @@ -5,46 +5,42 @@ import { SignatureEntity } from '../signature/domain/signature.entity'; import { SignatureCoverDto } from './dto/signature-cover.dto'; import { SignaturePageEntity } from '../signature/domain/signature.page.entity'; import { UserService } from '../user/user.service'; -import { exit } from '@nestjs/cli/actions'; -import { SignatureService } from '../signature/signature.service'; import { Like } from 'typeorm'; import { S3UtilService } from '../utils/S3.service'; @Injectable() -export class SearchService{ - +export class SearchService { constructor( private readonly userService: UserService, private readonly s3Service: S3UtilService, ) {} async findHotSignatures(): Promise { - try{ + try { /***************************************** 인기 시그니처 알고리즘 로직: [1] 최근 일주일 안에 올라온 시그니처 모두 가져오기 [2] 그 중에서 좋아요 개수 상위 20개 리턴 *****************************************/ - // [1] 최근 일주일 안에 올라온 시그니처 가져오기 - const recentSignatures: SignatureEntity[] = await SignatureEntity.findRecentSignatures(); + // [1] 최근 일주일 안에 올라온 시그니처 가져오기 + const recentSignatures: SignatureEntity[] = + await SignatureEntity.findRecentSignatures(); // [2] 최근 시그니처들 리스트 좋아요 순으로 정렬 - recentSignatures.sort((a,b) => b.liked - a.liked ); + recentSignatures.sort((a, b) => b.liked - a.liked); console.log(recentSignatures); // [3] 그 중에서 20개만 리턴한다 return await this.getSignatureCoversForSearchMain(recentSignatures); - - }catch(error){ - console.log("Error on findHotSignatures: ", error); + } catch (error) { + console.log('Error on findHotSignatures: ', error); throw error; } - } async findMatesNewSignatures(userId: number) { - try{ + try { /******************************************************** 내 메이트 최신 시그니처 로직: [1] 내가 팔로우하고 있는 메이트 목록 가져오기 @@ -57,68 +53,70 @@ export class SearchService{ // [2] 각 메이트들이 작성한 시그니처 목록에 담기 const totalNewSignatures: SignatureEntity[] = []; - for(const mate of followingMates){ - const mateNewSignatures:SignatureEntity[] = await SignatureEntity.findNewSignaturesByUser(mate.id); + for (const mate of followingMates) { + const mateNewSignatures: SignatureEntity[] = + await SignatureEntity.findNewSignaturesByUser(mate.id); - for(const newSignature of mateNewSignatures){ + for (const newSignature of mateNewSignatures) { totalNewSignatures.push(newSignature); } } // [3] 최신 순으로 정렬 - totalNewSignatures.sort((a, b) => b.created.getTime() - a.created.getTime()); - + totalNewSignatures.sort( + (a, b) => b.created.getTime() - a.created.getTime(), + ); // [4] 20개만 리턴 return await this.getSignatureCoversForSearchMain(totalNewSignatures); - - }catch (error){ - console.log("Error on FindMatesNewSigs: "+error); + } catch (error) { + console.log('Error on FindMatesNewSigs: ' + error); throw error; } } - async getSignatureCoversForSearchMain(signatureEntities){ - + async getSignatureCoversForSearchMain(signatureEntities) { // 탐색 메인화면에 출력될 시그니처 커버 20개 만들기 const signatureCovers: SignatureCoverDto[] = []; for (let i = 0; i < signatureEntities.length && i < 20; i++) { const signature = signatureEntities[i]; const signatureCover = await this.getSignatureCover(signature); - if(signatureCover) signatureCovers.push(signatureCover); + if (signatureCover) signatureCovers.push(signatureCover); } return signatureCovers; } - async searchByKeyword(keyword: string) { // 키워드로 검색하기: 탈퇴한 메이트의 시그니처도 반환 - try{ + async searchByKeyword(keyword: string) { + // 키워드로 검색하기: 탈퇴한 메이트의 시그니처도 반환 + try { const resultSignatures = await SignatureEntity.find({ - where:{ title: Like(`%${keyword}%`) }, - relations: ['user'] // user 포함 + where: { title: Like(`%${keyword}%`) }, + relations: ['user'], // user 포함 }); const resultCovers = []; // 검색 결과 최신 순으로 정렬 - resultSignatures.sort((a, b) => b.created.getTime() - a.created.getTime()); - - for(const signature of resultSignatures){ + resultSignatures.sort( + (a, b) => b.created.getTime() - a.created.getTime(), + ); + + for (const signature of resultSignatures) { const signatureCover = await this.getSignatureCover(signature); - if(signatureCover) resultCovers.push(signatureCover); + if (signatureCover) resultCovers.push(signatureCover); } return resultCovers; - - }catch(error){ - console.log("검색 서비스 에러발생: "+error); + } catch (error) { + console.log('검색 서비스 에러발생: ' + error); throw error; } } - - async getSignatureCover(signature:SignatureEntity) // 시그니처 커버 만들기 - :Promise{ + async getSignatureCover( + signature: SignatureEntity, // 시그니처 커버 만들기 + ): Promise { const signatureCover = new SignatureCoverDto(); signatureCover._id = signature.id; @@ -127,22 +125,30 @@ export class SearchService{ signatureCover.userName = signature.user.nickname; // 시그니처 썸네일 이미지 가져오기 - signatureCover.date = await SignatureEntity.formatDateString(signature.created); - - const signatureImageKey = await SignaturePageEntity.findThumbnail(signature.id); - if(signatureImageKey != null ){ - signatureCover.image = await this.s3Service.getImageUrl(signatureImageKey); - } - else return null; + signatureCover.date = await SignatureEntity.formatDateString( + signature.created, + ); + + const signatureImageKey = await SignaturePageEntity.findThumbnail( + signature.id, + ); + if (signatureImageKey != null) { + signatureCover.image = await this.s3Service.getImageUrl( + signatureImageKey, + ); + } else return null; // 시그니처 작성자 프로필 이미지 가져오기 - const userProfileImageEntity = await this.userService.getProfileImage(signature.user.id); - if(userProfileImageEntity == null) signatureCover.userImage = null; - else{ + const userProfileImageEntity = await this.userService.getProfileImage( + signature.user.id, + ); + if (userProfileImageEntity == null) signatureCover.userImage = null; + else { const userProfileImageKey = userProfileImageEntity.imageKey; - signatureCover.userImage = await this.s3Service.getImageUrl(userProfileImageKey); + signatureCover.userImage = await this.s3Service.getImageUrl( + userProfileImageKey, + ); } return signatureCover; } } - diff --git a/src/signature/domain/signature.comment.entity.ts b/src/signature/domain/signature.comment.entity.ts index 277e0b1..438ef84 100644 --- a/src/signature/domain/signature.comment.entity.ts +++ b/src/signature/domain/signature.comment.entity.ts @@ -1,10 +1,14 @@ // signature.comment.entity.ts import { - BaseEntity, Column, + BaseEntity, + Column, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, OneToMany, + Entity, + JoinColumn, + ManyToOne, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -16,23 +20,25 @@ export class SignatureCommentEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => SignatureEntity, - (signature) => signature.comments) + @ManyToOne(() => SignatureEntity, (signature) => signature.comments) @JoinColumn() signature: SignatureEntity; - @ManyToOne(() => UserEntity, - (user) => user.signatureComments) + @ManyToOne(() => UserEntity, (user) => user.signatureComments) @JoinColumn() user: UserEntity; - @OneToMany(() => SignatureCommentEntity, - (childComment) => childComment.parentComment) + @OneToMany( + () => SignatureCommentEntity, + (childComment) => childComment.parentComment, + ) @JoinColumn() childComments: SignatureCommentEntity[]; - @ManyToOne(() => SignatureCommentEntity, - (parentComment) => parentComment.childComments) + @ManyToOne( + () => SignatureCommentEntity, + (parentComment) => parentComment.childComments, + ) @JoinColumn() parentComment: SignatureCommentEntity; @@ -47,4 +53,4 @@ export class SignatureCommentEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; -} \ No newline at end of file +} diff --git a/src/signature/domain/signature.entity.ts b/src/signature/domain/signature.entity.ts index fdf1bc4..7953244 100644 --- a/src/signature/domain/signature.entity.ts +++ b/src/signature/domain/signature.entity.ts @@ -5,20 +5,29 @@ import { Column, CreateDateColumn, DeleteDateColumn, - Entity, EntitySubscriberInterface, EventSubscriber, InsertEvent, JoinColumn, ManyToOne, MoreThan, + Entity, + EntitySubscriberInterface, + EventSubscriber, + InsertEvent, + JoinColumn, + ManyToOne, + MoreThan, OneToMany, - PrimaryGeneratedColumn, RemoveEvent, + PrimaryGeneratedColumn, + RemoveEvent, UpdateDateColumn, } from 'typeorm'; import { UserEntity } from 'src/user/user.entity'; -import { HomeSignatureDto } from '../dto/signature/home-signature.dto'; import { CreateSignatureDto } from '../dto/signature/create-signature.dto'; import { SignaturePageEntity } from './signature.page.entity'; import { SignatureLikeEntity } from './signature.like.entity'; import { SignatureCommentEntity } from './signature.comment.entity'; @Entity() @EventSubscriber() -export class SignatureEntity extends BaseEntity implements EntitySubscriberInterface{ +export class SignatureEntity + extends BaseEntity + implements EntitySubscriberInterface +{ @PrimaryGeneratedColumn() id: number; @@ -28,20 +37,26 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter @Column({ default: 0 }) liked: number; - @ManyToOne(() => UserEntity, - (user) => user.signatures) + @ManyToOne(() => UserEntity, (user) => user.signatures) @JoinColumn({ name: 'user_id' }) user: UserEntity; - @OneToMany(() => SignaturePageEntity, (signaturePage) => signaturePage.signature) + @OneToMany( + () => SignaturePageEntity, + (signaturePage) => signaturePage.signature, + ) signaturePages: SignaturePageEntity[]; - @OneToMany(() => SignatureLikeEntity, - (signatureLike) => signatureLike.signature) + @OneToMany( + () => SignatureLikeEntity, + (signatureLike) => signatureLike.signature, + ) likes: SignatureLikeEntity[]; - @OneToMany(() => SignatureCommentEntity, - (signatureComment) => signatureComment.signature) + @OneToMany( + () => SignatureCommentEntity, + (signatureComment) => signatureComment.signature, + ) comments: SignatureCommentEntity[]; listenTo() { @@ -64,7 +79,6 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter this.save(); // 업데이트된 liked 카운트를 데이터베이스에 저장 } - @CreateDateColumn() created: Date; @@ -83,25 +97,24 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter } static async createSignature( - createSignatureDto: CreateSignatureDto, userId: number + createSignatureDto: CreateSignatureDto, + userId: number, ): Promise { try { const signature: SignatureEntity = new SignatureEntity(); signature.title = createSignatureDto.title; const user: UserEntity = await UserEntity.findOne({ - where: { id: userId} + where: { id: userId }, }); - if(!user){ + if (!user) { throw new Error('User not found'); - } - else{ - console.log("user name: "+ user.name); + } else { + console.log('user name: ' + user.name); signature.user = user; return await signature.save(); - } } catch (error) { console.error('Error creating Signature:', error); @@ -109,31 +122,30 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter } } - - - static async findSignatureById(signatureId: number): Promise { - const signature:SignatureEntity = await SignatureEntity.findOne({ + static async findSignatureById( + signatureId: number, + ): Promise { + const signature: SignatureEntity = await SignatureEntity.findOne({ where: { id: signatureId }, - relations: ['user'] // user 포함 + relations: ['user'], // user 포함 }); return signature; } static async findRecentSignatures(): Promise { - // [1] 기준이 되는 일주일 전 날짜 const sevenDaysAgo: Date = new Date(); - sevenDaysAgo.setDate(sevenDaysAgo.getDate()-7); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); console.log(sevenDaysAgo); // [2] 오늘로부터 일주일 안으로 쓰여진 시그니처 가져오기 const recentSignatures = await SignatureEntity.find({ - where:{ + where: { created: MoreThan(sevenDaysAgo), - user: { isQuit: false }, // 탈퇴한 사용자의 시그니처는 추천에서 제외 + user: { isQuit: false }, // 탈퇴한 사용자의 시그니처는 추천에서 제외 }, - relations: ['user'] // user 포함 + relations: ['user'], // user 포함 }); return recentSignatures; @@ -142,14 +154,14 @@ export class SignatureEntity extends BaseEntity implements EntitySubscriberInter static async findNewSignaturesByUser(userId: number) { // [1] 기준이 되는 20일 전 날짜 const twentyDaysAgo: Date = new Date(); - twentyDaysAgo.setDate(twentyDaysAgo.getDate()-20); + twentyDaysAgo.setDate(twentyDaysAgo.getDate() - 20); console.log(twentyDaysAgo); // [2] 20일 전에 쓰인 메이트의 최신 시그니처 가져오기 const signatures = await SignatureEntity.find({ - where:{user:{id: userId}, created: MoreThan(twentyDaysAgo)}, - relations: ['user'] // user 포함 - }) + where: { user: { id: userId }, created: MoreThan(twentyDaysAgo) }, + relations: ['user'], // user 포함 + }); return signatures; } } diff --git a/src/signature/domain/signature.like.entity.ts b/src/signature/domain/signature.like.entity.ts index 0ba8ba3..42a8e0d 100644 --- a/src/signature/domain/signature.like.entity.ts +++ b/src/signature/domain/signature.like.entity.ts @@ -4,7 +4,9 @@ import { BaseEntity, CreateDateColumn, DeleteDateColumn, - Entity, JoinColumn, ManyToOne, + Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -16,14 +18,12 @@ export class SignatureLikeEntity extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => SignatureEntity, - (signature) => signature.likes) - @JoinColumn({name: 'signature_id'}) + @ManyToOne(() => SignatureEntity, (signature) => signature.likes) + @JoinColumn({ name: 'signature_id' }) signature: SignatureEntity; - @ManyToOne(() => UserEntity, - (user) => user.likes) - @JoinColumn({name: 'user_id'}) + @ManyToOne(() => UserEntity, (user) => user.likes) + @JoinColumn({ name: 'user_id' }) user: UserEntity; @CreateDateColumn() @@ -36,14 +36,13 @@ export class SignatureLikeEntity extends BaseEntity { deleted: Date; static async createLike(signature: SignatureEntity, loginUser: UserEntity) { - try{ + try { const signatureLike = new SignatureLikeEntity(); signatureLike.signature = signature; signatureLike.user = loginUser; const signatureLikeEntity = await signatureLike.save(); - console.log("sigLike created: ", signatureLikeEntity); - - }catch(error){ + console.log('sigLike created: ', signatureLikeEntity); + } catch (error) { console.error('Error on likeSignature: ', error); throw new Error('Failed to like Signature'); } @@ -51,11 +50,11 @@ export class SignatureLikeEntity extends BaseEntity { static async findSignatureLikes(signatureId: number) { return await SignatureLikeEntity.find({ - where:{ - signature:{id: signatureId}, - user: { isQuit: false } // 탈퇴한 유저의 좋아요는 가져오지 않음 + where: { + signature: { id: signatureId }, + user: { isQuit: false }, // 탈퇴한 유저의 좋아요는 가져오지 않음 }, relations: ['user', 'signature'], - }) + }); } } diff --git a/src/signature/domain/signature.page.entity.ts b/src/signature/domain/signature.page.entity.ts index 7a9183a..fc6321a 100644 --- a/src/signature/domain/signature.page.entity.ts +++ b/src/signature/domain/signature.page.entity.ts @@ -12,7 +12,6 @@ import { UpdateDateColumn, } from 'typeorm'; import { SignatureEntity } from './signature.entity'; -import { PageSignatureDto } from '../dto/signature/page-signature.dto'; @Entity() export class SignaturePageEntity extends BaseEntity { @@ -44,7 +43,6 @@ export class SignaturePageEntity extends BaseEntity { @DeleteDateColumn() deleted: Date; - static async findThumbnail(signatureId: number) { // 각 시그니처의 첫 번째 페이지의 이미지 가져오기 try { @@ -55,14 +53,13 @@ export class SignaturePageEntity extends BaseEntity { }, }); - console.log("첫번째 페이지: ",firstPage); + console.log('첫번째 페이지: ', firstPage); - if(firstPage == null) return null; + if (firstPage == null) return null; else { - console.log("썸네일 이미지: ",firstPage.image); + console.log('썸네일 이미지: ', firstPage.image); return firstPage.image; } - } catch (error) { console.log('Error on findThumbnail: ', error); throw error; diff --git a/src/signature/dto/comment/create-comment.dto.ts b/src/signature/dto/comment/create-comment.dto.ts index 72acced..986074e 100644 --- a/src/signature/dto/comment/create-comment.dto.ts +++ b/src/signature/dto/comment/create-comment.dto.ts @@ -1,6 +1,5 @@ // create-comment.dto.ts -export class CreateCommentDto{ - content: string; // 댓글 내용 - -} \ No newline at end of file +export class CreateCommentDto { + content: string; // 댓글 내용 +} diff --git a/src/signature/dto/comment/get-comment-writer.dto.ts b/src/signature/dto/comment/get-comment-writer.dto.ts index 77d2314..8607d71 100644 --- a/src/signature/dto/comment/get-comment-writer.dto.ts +++ b/src/signature/dto/comment/get-comment-writer.dto.ts @@ -1,8 +1,8 @@ // get-comment-writer.dto.ts -export class GetCommentWriterDto{ +export class GetCommentWriterDto { _id: number; name: string; - image: string; // 프로필 이미지 - is_writer: boolean; // 로그인 유저의 수정 삭제 가능 여부 -} \ No newline at end of file + image: string; // 프로필 이미지 + is_writer: boolean; // 로그인 유저의 수정 삭제 가능 여부 +} diff --git a/src/signature/dto/comment/get-signature-comment.dto.ts b/src/signature/dto/comment/get-signature-comment.dto.ts index af862d3..0949d80 100644 --- a/src/signature/dto/comment/get-signature-comment.dto.ts +++ b/src/signature/dto/comment/get-signature-comment.dto.ts @@ -2,12 +2,12 @@ import { GetCommentWriterDto } from './get-comment-writer.dto'; -export class GetSignatureCommentDto{ +export class GetSignatureCommentDto { _id: number; parentId: number; content: string; writer: GetCommentWriterDto; - date: Date; // 생성 | 수정일 - is_edited: boolean; // 댓글 수정 여부 - can_delete: boolean; // 로그인한 사용자의 댓글 삭제 권한 여부: 시그니처 작성자면 true -} \ No newline at end of file + date: Date; // 생성 | 수정일 + is_edited: boolean; // 댓글 수정 여부 + can_delete: boolean; // 로그인한 사용자의 댓글 삭제 권한 여부: 시그니처 작성자면 true +} diff --git a/src/signature/dto/like/get-like-list.dto.ts b/src/signature/dto/like/get-like-list.dto.ts index e118369..70de855 100644 --- a/src/signature/dto/like/get-like-list.dto.ts +++ b/src/signature/dto/like/get-like-list.dto.ts @@ -2,8 +2,7 @@ import { LikeProfileDto } from './like-profile.dto'; -export class GetLikeListDto{ - liked: number; // 좋아요 개수 +export class GetLikeListDto { + liked: number; // 좋아요 개수 profiles: LikeProfileDto[]; // 좋아요한 사용자 프로필 리스트 - -} \ No newline at end of file +} diff --git a/src/signature/dto/like/like-profile.dto.ts b/src/signature/dto/like/like-profile.dto.ts index ad76feb..409bd99 100644 --- a/src/signature/dto/like/like-profile.dto.ts +++ b/src/signature/dto/like/like-profile.dto.ts @@ -1,9 +1,9 @@ // like-profile.dto.ts -export class LikeProfileDto{ +export class LikeProfileDto { _id: number; nickname: string; introduction: string; is_followed: boolean; image: string; -} \ No newline at end of file +} diff --git a/src/signature/dto/like/like-signature.dto.ts b/src/signature/dto/like/like-signature.dto.ts index 1965a7f..e47d589 100644 --- a/src/signature/dto/like/like-signature.dto.ts +++ b/src/signature/dto/like/like-signature.dto.ts @@ -3,4 +3,4 @@ export class LikeSignatureDto { signatureId: number; liked: number; -} \ No newline at end of file +} diff --git a/src/signature/dto/signature/author-signature.dto.ts b/src/signature/dto/signature/author-signature.dto.ts index 0f1b4f0..5c8f728 100644 --- a/src/signature/dto/signature/author-signature.dto.ts +++ b/src/signature/dto/signature/author-signature.dto.ts @@ -1,8 +1,9 @@ // author-signature.dto.ts -export class AuthorSignatureDto { // 시그니처 작성자 정보 - _id: number; // 메이트 아이디 - name: string; // 메이트 닉네임 - image: string; // 메이트 프로필 이미지 +export class AuthorSignatureDto { + // 시그니처 작성자 정보 + _id: number; // 메이트 아이디 + name: string; // 메이트 닉네임 + image: string; // 메이트 프로필 이미지 is_followed: boolean; // 해당 메이트 팔로우 여부 -} \ No newline at end of file +} diff --git a/src/signature/dto/signature/detail-signature.dto.ts b/src/signature/dto/signature/detail-signature.dto.ts index f58240a..6615f09 100644 --- a/src/signature/dto/signature/detail-signature.dto.ts +++ b/src/signature/dto/signature/detail-signature.dto.ts @@ -2,11 +2,11 @@ import { AuthorSignatureDto } from './author-signature.dto'; import { HeaderSignatureDto } from './header-signature.dto'; -import { PageSignatureDto } from './page-signature.dto'; import { ResponsePageSignatureDto } from './response-page-signature.dto'; -export class DetailSignatureDto { // 시그니처 상세 보기 - author: AuthorSignatureDto; // 시그니처 작성자 정보 - header: HeaderSignatureDto; // 시그니처 제목 및 좋아요 정보 - pages: ResponsePageSignatureDto[]; // 시그니처 각 페이지 내용 -} \ No newline at end of file +export class DetailSignatureDto { + // 시그니처 상세 보기 + author: AuthorSignatureDto; // 시그니처 작성자 정보 + header: HeaderSignatureDto; // 시그니처 제목 및 좋아요 정보 + pages: ResponsePageSignatureDto[]; // 시그니처 각 페이지 내용 +} diff --git a/src/signature/dto/signature/header-signature.dto.ts b/src/signature/dto/signature/header-signature.dto.ts index d320d67..1a4db1d 100644 --- a/src/signature/dto/signature/header-signature.dto.ts +++ b/src/signature/dto/signature/header-signature.dto.ts @@ -1,9 +1,9 @@ // header-signature.dto.ts -export class HeaderSignatureDto{ - _id: number // 시그니처 아이디 - title: string; // 시그니처 제목 - is_liked: boolean; // 해당 시그니처 좋아요 여부 - like_cnt: number; // 좋아요 개수 - date: string; // 발행일 -} \ No newline at end of file +export class HeaderSignatureDto { + _id: number; // 시그니처 아이디 + title: string; // 시그니처 제목 + is_liked: boolean; // 해당 시그니처 좋아요 여부 + like_cnt: number; // 좋아요 개수 + date: string; // 발행일 +} diff --git a/src/signature/dto/signature/home-signature.dto.ts b/src/signature/dto/signature/home-signature.dto.ts index 1dbf511..1cbe72e 100644 --- a/src/signature/dto/signature/home-signature.dto.ts +++ b/src/signature/dto/signature/home-signature.dto.ts @@ -1,8 +1,8 @@ // home-signature.dto.ts export class HomeSignatureDto { - _id: number; // 시그니처 id - title: string; // 시그니처 제목 - date: Date; // 시그니처 발행일 - image: string; // 시그니처 첫번째 페이지의 이미지(썸네일) + _id: number; // 시그니처 id + title: string; // 시그니처 제목 + date: Date; // 시그니처 발행일 + image: string; // 시그니처 첫번째 페이지의 이미지(썸네일) } diff --git a/src/signature/dto/signature/page-signature.dto.ts b/src/signature/dto/signature/page-signature.dto.ts index d1e1aec..f942014 100644 --- a/src/signature/dto/signature/page-signature.dto.ts +++ b/src/signature/dto/signature/page-signature.dto.ts @@ -6,5 +6,5 @@ export class PageSignatureDto { content: string; location: string; //image: Buffer; // form-data 형식 - image: string // base-64 형식 + image: string; // base-64 형식 } diff --git a/src/signature/signature.comment.controller.ts b/src/signature/signature.comment.controller.ts index 651d5ca..4cb368b 100644 --- a/src/signature/signature.comment.controller.ts +++ b/src/signature/signature.comment.controller.ts @@ -2,7 +2,8 @@ import { Body, - Controller, Delete, + Controller, + Delete, ForbiddenException, Get, NotFoundException, @@ -22,166 +23,186 @@ import { ResponseCode } from '../response/response-code.enum'; import { CursorPageOptionsDto } from '../rule/dto/cursor-page.options.dto'; @Controller('signature/:signatureId/comment') -export class SignatureCommentController{ - constructor(private readonly signatureCommentService: SignatureCommentService) {} +export class SignatureCommentController { + constructor( + private readonly signatureCommentService: SignatureCommentService, + ) {} @Post('/') @UseGuards(UserGuard) - async createSignatureComment( // 시그니처 댓글 생성하기 + async createSignatureComment( + // 시그니처 댓글 생성하기 @Req() req: Request, @Param('signatureId') signatureId: number, @Body() newComment: CreateCommentDto, - ){ - try{ - const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId) + ) { + try { + const result = await this.signatureCommentService.createSignatureComment( + newComment, + req.user.id, + signatureId, + ); return new ResponseDto( ResponseCode.CREATE_SIGNATURE_COMMENT_SUCCESS, true, - "시그니처 댓글 생성 성공", - result + '시그니처 댓글 생성 성공', + result, ); - - } - catch(error){ - console.log('Error on createSigComment: ',error); + } catch (error) { + console.log('Error on createSigComment: ', error); return new ResponseDto( ResponseCode.COMMENT_CREATION_FAIL, false, - "시그니처 댓글 생성 실패", - null + '시그니처 댓글 생성 실패', + null, ); } } @Post('/:parentId') @UseGuards(UserGuard) - async createSignatureReplyComment( // 시그니처 답글 생성하기 + async createSignatureReplyComment( + // 시그니처 답글 생성하기 @Req() req: Request, @Param('signatureId') signatureId: number, @Param('parentId') parentId: number, @Body() newComment: CreateCommentDto, - ){ - try{ - const result = await this.signatureCommentService.createSignatureComment(newComment, req.user.id, signatureId, parentId) + ) { + try { + const result = await this.signatureCommentService.createSignatureComment( + newComment, + req.user.id, + signatureId, + parentId, + ); return new ResponseDto( ResponseCode.CREATE_SIGNATURE_COMMENT_SUCCESS, true, - "시그니처 답글 생성 성공", - result + '시그니처 답글 생성 성공', + result, ); - - } - catch(error){ - console.log('Error on createSigComment: ',error); + } catch (error) { + console.log('Error on createSigComment: ', error); return new ResponseDto( ResponseCode.COMMENT_CREATION_FAIL, false, - "시그니처 답글 생성 실패", - null + '시그니처 답글 생성 실패', + null, ); } } @Get('/') @UseGuards(UserGuard) - async getSignatureComment( // 시그니처 댓글 조회하기 (무한 스크롤) + async getSignatureComment( + // 시그니처 댓글 조회하기 (무한 스크롤) @Req() req: Request, @Param('signatureId') signatureId: number, @Query() cursorPageOptionsDto: CursorPageOptionsDto, - ){ - try{ - const result = await this.signatureCommentService.getSignatureComment(cursorPageOptionsDto, req.user.id, signatureId); + ) { + try { + const result = await this.signatureCommentService.getSignatureComment( + cursorPageOptionsDto, + req.user.id, + signatureId, + ); return new ResponseDto( ResponseCode.GET_COMMENT_DETAIL_SUCCESS, true, - "시그니처 댓글 가져오기 성공", - result + '시그니처 댓글 가져오기 성공', + result, ); - } - catch(error){ - console.log('Error on createSigChildComment: ',error); + } catch (error) { + console.log('Error on createSigChildComment: ', error); return new ResponseDto( ResponseCode.GET_COMMENT_DETAIL_FAIL, false, - "시그니처 댓글 가져오기 실패", - null + '시그니처 댓글 가져오기 실패', + null, ); } } @Patch('/:commentId') @UseGuards(UserGuard) - async patchSignatureComment( // 시그니처 수정하기 + async patchSignatureComment( + // 시그니처 수정하기 @Param('signatureId') signatureId: number, @Param('commentId') commentId: number, @Body() patchedComment: CreateCommentDto, @Req() req: Request, - ){ - try{ - const result = await this.signatureCommentService.patchSignatureComment(req.user.id,signatureId,commentId,patchedComment); + ) { + try { + const result = await this.signatureCommentService.patchSignatureComment( + req.user.id, + signatureId, + commentId, + patchedComment, + ); return new ResponseDto( ResponseCode.COMMENT_UPDATE_SUCCESS, true, - "시그니처 댓글 수정하기 성공", - result + '시그니처 댓글 수정하기 성공', + result, ); - } - catch(error){ - console.log("Err on PatchSigComment: "+ error); - let errorMessage = ""; + } catch (error) { + console.log('Err on PatchSigComment: ' + error); + let errorMessage = ''; - if(error instanceof NotFoundException) errorMessage = error.message; - else if(error instanceof ForbiddenException) errorMessage = error.message; - else errorMessage = "시그니처 댓글 수정하기 실패"; + if (error instanceof NotFoundException) errorMessage = error.message; + else if (error instanceof ForbiddenException) + errorMessage = error.message; + else errorMessage = '시그니처 댓글 수정하기 실패'; return new ResponseDto( ResponseCode.COMMENT_UPDATE_FAIL, false, errorMessage, - null + null, ); } } - @Delete('/:commentId') @UseGuards(UserGuard) - async deleteSignatureComment( // 시그니처 수정하기 + async deleteSignatureComment( + // 시그니처 수정하기 @Param('signatureId') signatureId: number, @Param('commentId') commentId: number, @Req() req: Request, - ){ - try{ - const result = await this.signatureCommentService.deleteSignatureComment(req.user.id,signatureId,commentId); + ) { + try { + const result = await this.signatureCommentService.deleteSignatureComment( + req.user.id, + signatureId, + commentId, + ); return new ResponseDto( ResponseCode.COMMENT_DELETE_SUCCESS, true, - "시그니처 댓글 삭제하기 성공", - result + '시그니처 댓글 삭제하기 성공', + result, ); - } - catch(error){ - console.log("Err on DeleteSigComment: "+ error); - let errorMessage = ""; + } catch (error) { + console.log('Err on DeleteSigComment: ' + error); + let errorMessage = ''; - if(error instanceof NotFoundException) errorMessage = error.message; - else if(error instanceof ForbiddenException) errorMessage = error.message; - else errorMessage = "시그니처 댓글 삭제하기 실패"; + if (error instanceof NotFoundException) errorMessage = error.message; + else if (error instanceof ForbiddenException) + errorMessage = error.message; + else errorMessage = '시그니처 댓글 삭제하기 실패'; return new ResponseDto( ResponseCode.COMMENT_DELETE_FAIL, false, errorMessage, - null + null, ); - } } - - -} \ No newline at end of file +} diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 1fdb89a..9f1b461 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -1,6 +1,10 @@ // signature.comment.service.ts -import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common'; +import { + ForbiddenException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { UserService } from '../user/user.service'; import { S3UtilService } from '../utils/S3.service'; import { SignatureService } from './signature.service'; @@ -17,31 +21,30 @@ import { CursorPageDto } from '../mate/cursor-page/cursor-page.dto'; import { NotificationEntity } from '../notification/notification.entity'; @Injectable() -export class SignatureCommentService{ - +export class SignatureCommentService { constructor( private readonly signatureService: SignatureService, private readonly userService: UserService, private readonly s3Service: S3UtilService, ) {} - async createSignatureComment( // 댓글, 답글 생성하기 + async createSignatureComment( + // 댓글, 답글 생성하기 createCommentDto: CreateCommentDto, userId: number, signatureId: number, - parentCommentId?: number){ - + parentCommentId?: number, + ) { const comment = new SignatureCommentEntity(); - const user = await UserEntity.findOneOrFail({ where: { id: userId }}); - const signature = await SignatureEntity.findOneOrFail( { where: { id: signatureId }}); - + const user = await UserEntity.findOneOrFail({ where: { id: userId } }); + const signature = await SignatureEntity.findOneOrFail({ + where: { id: signatureId }, + }); - if( !user || !signature ) { + if (!user || !signature) { throw new NotFoundException('404 Not Found'); - } - else { - + } else { comment.user = user; comment.signature = signature; comment.content = createCommentDto.content; @@ -50,21 +53,22 @@ export class SignatureCommentService{ const notification = new NotificationEntity(); // parentCommentId가 존재할 경우 -> 답글 / 존재하지 않을 경우 -> 댓글 - if(parentCommentId){ // 대댓글: parentId는 파라미터로 받은 parentCommentId로 설정 + if (parentCommentId) { + // 대댓글: parentId는 파라미터로 받은 parentCommentId로 설정 - const parentComment = await SignatureCommentEntity.findOneOrFail( { - where:{ id: parentCommentId - }}); + const parentComment = await SignatureCommentEntity.findOneOrFail({ + where: { id: parentCommentId }, + }); - if( !parentComment ) throw new NotFoundException('404 Not Found'); + if (!parentComment) throw new NotFoundException('404 Not Found'); else { comment.parentComment = parentComment; await comment.save(); } notification.notificationReceiver = parentComment.user; - } - else{ // 댓글: parentId는 본인으로 설정 + } else { + // 댓글: parentId는 본인으로 설정 const savedComment = await comment.save(); savedComment.parentComment = savedComment; await savedComment.save(); @@ -82,58 +86,60 @@ export class SignatureCommentService{ } } - async getSignatureComment( // 댓글 가져오기 + async getSignatureComment( + // 댓글 가져오기 cursorPageOptionsDto: CursorPageOptionsDto, userId: number, signatureId: number, ) { - try{ - + try { // 1. 'cursorId'부터 오름차순 정렬된 댓글 'take'만큼 가져오기 const [comments, total] = await SignatureCommentEntity.findAndCount({ take: cursorPageOptionsDto.take, where: { id: MoreThan(cursorPageOptionsDto.cursorId), signature: { id: signatureId }, - parentComment: { id: Raw("SignatureCommentEntity.id") }, // 부모 댓글이 자기 자신인 댓글들만 가져오기 + parentComment: { id: Raw('SignatureCommentEntity.id') }, // 부모 댓글이 자기 자신인 댓글들만 가져오기 }, relations: { user: { profileImage: true }, parentComment: true, - signature:{ user: true, } + signature: { user: true }, }, order: { - parentComment: { id: "ASC" as any,}, - created: 'ASC' + parentComment: { id: 'ASC' as any }, + created: 'ASC', }, }); - const result: GetSignatureCommentDto[] = []; // 2. 각 부모 댓글의 답글들 찾아오기 - for(const comment of comments){ + for (const comment of comments) { console.log(comment); - result.push(await this.createSignatureCommentDto(comment,userId)); + result.push(await this.createSignatureCommentDto(comment, userId)); - const childrenComments = await SignatureCommentEntity.find({ // 답글 찾아오기 + const childrenComments = await SignatureCommentEntity.find({ + // 답글 찾아오기 where: { parentComment: { id: comment.id }, - id: Not(Raw("SignatureCommentEntity.parentComment.id")) + id: Not(Raw('SignatureCommentEntity.parentComment.id')), }, relations: { user: { profileImage: true }, parentComment: true, - signature:{ user: true, } + signature: { user: true }, }, order: { - created: 'ASC' + created: 'ASC', }, }); - for(const childComment of childrenComments){ + for (const childComment of childrenComments) { console.log(childComment); - result.push(await this.createSignatureCommentDto(childComment,userId)) + result.push( + await this.createSignatureCommentDto(childComment, userId), + ); } } @@ -152,19 +158,25 @@ export class SignatureCommentService{ cursor = lastDataPerScroll.id; } - const cursorPageMetaDto = new CursorPageMetaDto( - { cursorPageOptionsDto, total, hasNextData, cursor }); - - return new CursorPageDto( result, cursorPageMetaDto ); + const cursorPageMetaDto = new CursorPageMetaDto({ + cursorPageOptionsDto, + total, + hasNextData, + cursor, + }); - } - catch(e){ - console.log("Error on GetSignature: ",e); + return new CursorPageDto(result, cursorPageMetaDto); + } catch (e) { + console.log('Error on GetSignature: ', e); throw e; } } - async createSignatureCommentDto(comment: SignatureCommentEntity, userId: number){ // 댓글 DTO 만들기 + async createSignatureCommentDto( + comment: SignatureCommentEntity, + userId: number, + ) { + // 댓글 DTO 만들기 const writerProfile = new GetCommentWriterDto(); const getCommentDto = new GetSignatureCommentDto(); @@ -173,13 +185,12 @@ export class SignatureCommentService{ writerProfile.name = comment.user.nickname; // 로그인한 사용자가 댓글 작성자인지 확인 - if( userId == comment.user.id ) writerProfile.is_writer = true; - + if (userId == comment.user.id) writerProfile.is_writer = true; else writerProfile.is_writer = false; // 작성자 프로필 이미지 const image = comment.user.profileImage; - if(image == null) writerProfile.image = null; + if (image == null) writerProfile.image = null; else { const userImageKey = image.imageKey; writerProfile.image = await this.s3Service.getImageUrl(userImageKey); @@ -196,7 +207,8 @@ export class SignatureCommentService{ const createdTime = comment.created.getTime(); const updatedTime = comment.updated.getTime(); - if (Math.abs(createdTime - updatedTime) <= 2000) { // 두 시간 차가 2초 이하면 수정 안함 + if (Math.abs(createdTime - updatedTime) <= 2000) { + // 두 시간 차가 2초 이하면 수정 안함 getCommentDto.is_edited = false; } else { getCommentDto.is_edited = true; @@ -204,97 +216,103 @@ export class SignatureCommentService{ // 로그인한 사용자가 시그니처 작성하면 can_delete = true let can_delete = false; - if(comment.signature.user){ // 시그니처 작성자가 존재할 경우 - if(comment.signature.user.id == userId){ // 로그인한 사용자가 시그니처 작성자일 경우 댓글 삭제 가능 + if (comment.signature.user) { + // 시그니처 작성자가 존재할 경우 + if (comment.signature.user.id == userId) { + // 로그인한 사용자가 시그니처 작성자일 경우 댓글 삭제 가능 can_delete = true; } } getCommentDto.can_delete = can_delete; return getCommentDto; - } - async patchSignatureComment( // 댓글 수정하기 + async patchSignatureComment( + // 댓글 수정하기 userId: number, signatureId: number, commentId: number, - patchedComment: CreateCommentDto) { - + patchedComment: CreateCommentDto, + ) { // 시그니처 유효한지 확인 const signature = await SignatureEntity.findOne({ - where:{ id: signatureId }, - relations: { user: true } + where: { id: signatureId }, + relations: { user: true }, }); - if(!signature) throw new NotFoundException('존재하지 않는 시그니처입니다'); + if (!signature) throw new NotFoundException('존재하지 않는 시그니처입니다'); // 댓글 데이터 유효한지 확인 const comment = await SignatureCommentEntity.findOne({ - where:{ id: commentId }, - relations: { user: true } - }, - ); - if(!comment) throw new NotFoundException('존재하지 않는 댓글입니다'); - + where: { id: commentId }, + relations: { user: true }, + }); + if (!comment) throw new NotFoundException('존재하지 않는 댓글입니다'); let forbiddenUser = true; // 댓글 작성자가 로그인한 사용자 본인 혹은 시그니처 작성자가 맞는지 확인 - if(signature.user){ // 시그니처 작성자가 존재한다면 시그니처 작성자와 로그인한 사용자가 일치하는지 확인 - if( signature.user.id == userId ) forbiddenUser = false; + if (signature.user) { + // 시그니처 작성자가 존재한다면 시그니처 작성자와 로그인한 사용자가 일치하는지 확인 + if (signature.user.id == userId) forbiddenUser = false; } - if(comment.user.id){ // 댓글 작성자가 존재한다면 댓글 작성자와 로그인한 사용자가 일치하는지 확인 - if(comment.user.id == userId ) forbiddenUser = false; + if (comment.user.id) { + // 댓글 작성자가 존재한다면 댓글 작성자와 로그인한 사용자가 일치하는지 확인 + if (comment.user.id == userId) forbiddenUser = false; } - if(forbiddenUser) throw new ForbiddenException('댓글 수정 권한이 없습니다'); - + if (forbiddenUser) + throw new ForbiddenException('댓글 수정 권한이 없습니다'); // 댓글 수정하기 comment.content = patchedComment.content; await comment.save(); return comment.id; - } - async deleteSignatureComment(userId: number, signatureId: number, commentId: number) { + async deleteSignatureComment( + userId: number, + signatureId: number, + commentId: number, + ) { try { // 시그니처 유효한지 확인 const signature = await SignatureEntity.findOne({ where: { id: signatureId }, - relations: { user: true } + relations: { user: true }, }); - if (!signature) throw new NotFoundException('존재하지 않는 시그니처입니다'); + if (!signature) + throw new NotFoundException('존재하지 않는 시그니처입니다'); // 댓글 데이터 유효한지 확인 const comment = await SignatureCommentEntity.findOne({ - where: { id: commentId }, - relations: ['user', 'parentComment', 'signature'] - }, - ); + where: { id: commentId }, + relations: ['user', 'parentComment', 'signature'], + }); if (!comment) throw new NotFoundException('존재하지 않는 댓글입니다'); - let forbiddenUser = true; // 댓글 작성자가 로그인한 사용자 본인 혹은 시그니처 작성자가 맞는지 확인 - if(signature.user){ // 시그니처 작성자가 존재한다면 시그니처 작성자와 로그인한 사용자가 일치하는지 확인 - if( signature.user.id == userId ) forbiddenUser = false; + if (signature.user) { + // 시그니처 작성자가 존재한다면 시그니처 작성자와 로그인한 사용자가 일치하는지 확인 + if (signature.user.id == userId) forbiddenUser = false; } - if(comment.user.id){ // 댓글 작성자가 존재한다면 댓글 작성자와 로그인한 사용자가 일치하는지 확인 - if(comment.user.id == userId ) forbiddenUser = false; + if (comment.user.id) { + // 댓글 작성자가 존재한다면 댓글 작성자와 로그인한 사용자가 일치하는지 확인 + if (comment.user.id == userId) forbiddenUser = false; } - if(forbiddenUser) throw new ForbiddenException('댓글 삭제 권한이 없습니다'); - + if (forbiddenUser) + throw new ForbiddenException('댓글 삭제 권한이 없습니다'); // 해당 댓글이 부모 댓글인 경우 자식 댓글 모두 삭제 if (commentId == comment.parentComment.id) { - // 자식 댓글 모두 찾아오기 - const replyComments: SignatureCommentEntity[] = await SignatureCommentEntity.find({ - where: { parentComment: { id: commentId } } - }); + const replyComments: SignatureCommentEntity[] = + await SignatureCommentEntity.find({ + where: { parentComment: { id: commentId } }, + }); // 자식 댓글 모두 삭제 for (const reply of replyComments) { @@ -303,17 +321,15 @@ export class SignatureCommentService{ // 자식 모두 삭제했으면 부모 댓글 삭제 await comment.softRemove(); - - } - else{ // 자식 댓글 없는 경우 본인만 삭제 + } else { + // 자식 댓글 없는 경우 본인만 삭제 await comment.softRemove(); } return commentId; - } catch (error) { console.log(error); throw error; } } -} \ No newline at end of file +} diff --git a/src/signature/signature.controller.ts b/src/signature/signature.controller.ts index ac41eb9..6516a47 100644 --- a/src/signature/signature.controller.ts +++ b/src/signature/signature.controller.ts @@ -1,6 +1,16 @@ // signature.controller.ts -import { Body, Controller, Delete, Get, Param, Patch, Post, Req, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Req, + UseGuards, +} from '@nestjs/common'; import { SignatureService } from './signature.service'; import { CreateSignatureDto } from './dto/signature/create-signature.dto'; import { ResponseCode } from '../response/response-code.enum'; @@ -20,24 +30,24 @@ export class SignatureController { @Get('/') // 시그니처 탭 메인: 내 시그니처 목록 @UseGuards(UserGuard) - async getMySignature(@Req() req: Request): Promise> { - + async getMySignature( + @Req() req: Request, + ): Promise> { const result = await this.signatureService.homeSignature(req.user.id); - if(!result){ + if (!result) { return new ResponseDto( ResponseCode.GET_MY_SIGNATURE_FAIL, false, - "내 시그니처 가져오기 실패", - null + '내 시그니처 가져오기 실패', + null, ); - } - else{ + } else { return new ResponseDto( ResponseCode.GET_MY_SIGNATURES_SUCCESS, true, - "내 시그니처 가져오기 성공", - result + '내 시그니처 가져오기 성공', + result, ); } } @@ -46,23 +56,27 @@ export class SignatureController { @UseGuards(UserGuard) async createNewSignature( @Body() newSignature: CreateSignatureDto, - @Req() req: Request + @Req() req: Request, ): Promise> { - const result = await this.signatureService.createSignature(newSignature, req.user.id); + const result = await this.signatureService.createSignature( + newSignature, + req.user.id, + ); - if(!result){ + if (!result) { return new ResponseDto( ResponseCode.SIGNATURE_CREATION_FAIL, false, - "시그니처 생성에 실패했습니다", - null); - } - else{ + '시그니처 생성에 실패했습니다', + null, + ); + } else { return new ResponseDto( ResponseCode.SIGNATURE_CREATED, true, - "시그니처 기록하기 성공", - result); + '시그니처 기록하기 성공', + result, + ); } } @@ -70,17 +84,24 @@ export class SignatureController { @UseGuards(UserGuard) async patchSignatureLike( @Param('signatureId') signatureId: number, - @Req() req: Request + @Req() req: Request, ): Promise> { - try{ - + try { // [1] 이미 좋아요 했는지 확인 - const liked: SignatureLikeEntity= await this.signatureService.findIfAlreadyLiked(req.user.id,signatureId); + const liked: SignatureLikeEntity = + await this.signatureService.findIfAlreadyLiked( + req.user.id, + signatureId, + ); let result = new SignatureEntity(); - const likeSignatureDto= new LikeSignatureDto(); + const likeSignatureDto = new LikeSignatureDto(); - if(liked){ // 이미 좋아요했던 시그니처라면 좋아요 삭제 - result = await this.signatureService.deleteLikeOnSignature(liked,signatureId); + if (liked) { + // 이미 좋아요했던 시그니처라면 좋아요 삭제 + result = await this.signatureService.deleteLikeOnSignature( + liked, + signatureId, + ); likeSignatureDto.liked = result.liked; likeSignatureDto.signatureId = result.id; @@ -88,11 +109,14 @@ export class SignatureController { ResponseCode.DELETE_LIKE_ON_SIGNATURE_SUCCESS, true, '시그니처 좋아요 취소하기 성공', - likeSignatureDto + likeSignatureDto, + ); + } else { + // 좋아요 한적 없으면 시그니처 좋아요 추가 + result = await this.signatureService.addLikeOnSignature( + req.user.id, + signatureId, ); - - }else{ // 좋아요 한적 없으면 시그니처 좋아요 추가 - result = await this.signatureService.addLikeOnSignature(req.user.id,signatureId); likeSignatureDto.liked = result.liked; likeSignatureDto.signatureId = result.id; @@ -100,12 +124,11 @@ export class SignatureController { ResponseCode.LIKE_ON_SIGNATURE_CREATED, true, '시그니처 좋아요 성공', - likeSignatureDto + likeSignatureDto, ); } - - }catch(error){ - console.log('addSignatureLike: ', error ); + } catch (error) { + console.log('addSignatureLike: ', error); throw error; } } @@ -114,110 +137,113 @@ export class SignatureController { @UseGuards(UserGuard) async getSignatureDetail( @Req() req: Request, - @Param('signatureId') signatureId: number + @Param('signatureId') signatureId: number, ): Promise> { - - try{ + try { // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.detailSignature(req.user.id, signatureId); + const result = await this.signatureService.detailSignature( + req.user.id, + signatureId, + ); - if(result == null){ + if (result == null) { return new ResponseDto( ResponseCode.SIGNATURE_NOT_FOUND, false, - "존재하지 않는 시그니처 입니다", - result + '존재하지 않는 시그니처 입니다', + result, ); } - if(result.author.is_followed){ // 작성자가 본인이 아닌 경우 - if(result.author._id == null){ // 작성자가 탈퇴한 경우 + if (result.author.is_followed) { + // 작성자가 본인이 아닌 경우 + if (result.author._id == null) { + // 작성자가 탈퇴한 경우 return new ResponseDto( ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, true, - "시그니처 상세보기 성공: 작성자 탈퇴", - result + '시그니처 상세보기 성공: 작성자 탈퇴', + result, ); - } - else{ + } else { return new ResponseDto( ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, true, - "시그니처 상세보기 성공: 메이트의 시그니처", - result + '시그니처 상세보기 성공: 메이트의 시그니처', + result, ); } - } - else{ // 작성자가 본인인 경우 author 없음 + } else { + // 작성자가 본인인 경우 author 없음 return new ResponseDto( ResponseCode.GET_SIGNATURE_DETAIL_SUCCESS, true, - "시그니처 상세보기 성공: 내 시그니처", - result + '시그니처 상세보기 성공: 내 시그니처', + result, ); } - } - catch(error){ - console.log('Error on signatureId: ',error); + } catch (error) { + console.log('Error on signatureId: ', error); throw error; } - } @Patch('/:signatureId') // 시그니처 수정하기 async patchSignature( @Body() patchSignatureDto: CreateSignatureDto, - @Param('signatureId') signatureId: number + @Param('signatureId') signatureId: number, ): Promise> { - try{ + try { // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 - const result = await this.signatureService.patchSignature(signatureId, patchSignatureDto); + const result = await this.signatureService.patchSignature( + signatureId, + patchSignatureDto, + ); - if(result == null) { + if (result == null) { return new ResponseDto( ResponseCode.SIGNATURE_NOT_FOUND, false, - "존재하지 않는 시그니처 입니다", - result + '존재하지 않는 시그니처 입니다', + result, ); } return new ResponseDto( ResponseCode.PATCH_SIGNATURE_SUCCESS, true, - "시그니처 수정하기 성공", - result + '시그니처 수정하기 성공', + result, ); - } - catch(error){ + } catch (error) { console.log(error); return new ResponseDto( ResponseCode.SIGNATURE_PATCH_FAIL, false, - "시그니처 수정하기 실패", - null + '시그니처 수정하기 실패', + null, ); } - } @Delete('/:signatureId') // 시그니처 삭제하기 async deleteSignature( - @Param('signatureId') signatureId: number + @Param('signatureId') signatureId: number, ): Promise> { - try{ + try { // 임시로 토큰이 아닌 유저 아이디 받도록 구현 -> 리펙토링 예정 // [1] 시그니처 가져오기 - const signature:SignatureEntity = await SignatureEntity.findSignatureById(signatureId); - console.log("시그니처 정보: ", signature); + const signature: SignatureEntity = + await SignatureEntity.findSignatureById(signatureId); + console.log('시그니처 정보: ', signature); - if(signature == null) { + if (signature == null) { return new ResponseDto( ResponseCode.SIGNATURE_NOT_FOUND, false, - "존재하지 않는 시그니처 입니다", - null + '존재하지 않는 시그니처 입니다', + null, ); } @@ -227,17 +253,16 @@ export class SignatureController { return new ResponseDto( ResponseCode.DELETE_SIGNATURE_SUCCESS, true, - "시그니처 삭제 성공", - null + '시그니처 삭제 성공', + null, ); - } - catch(error){ + } catch (error) { console.log(error); return new ResponseDto( ResponseCode.SIGNATURE_DELETE_FAIL, false, - "시그니처 삭제 실패", - null + '시그니처 삭제 실패', + null, ); } } @@ -246,27 +271,28 @@ export class SignatureController { @UseGuards(UserGuard) async getSignatureLikeList( @Req() req: Request, - @Param('signatureId') signatureId: number + @Param('signatureId') signatureId: number, ): Promise> { - try{ - const getLikeListDto:GetLikeListDto = await this.signatureService.getSignatureLikeList(req.user.id, signatureId); + try { + const getLikeListDto: GetLikeListDto = + await this.signatureService.getSignatureLikeList( + req.user.id, + signatureId, + ); return new ResponseDto( ResponseCode.GET_LIKE_SIGNATURE_PROFILES_SUCCESS, true, - "시그니처 좋아요 목록 불러오기 성공", - getLikeListDto + '시그니처 좋아요 목록 불러오기 성공', + getLikeListDto, ); - - } - catch(error){ + } catch (error) { return new ResponseDto( ResponseCode.GET_LIKE_SIGNATURE_PROFILES_FAIL, false, - "시그니처 좋아요 목록 불러오기 실패", - null + '시그니처 좋아요 목록 불러오기 실패', + null, ); } } - } diff --git a/src/signature/signature.module.ts b/src/signature/signature.module.ts index 0e20e36..66b80d4 100644 --- a/src/signature/signature.module.ts +++ b/src/signature/signature.module.ts @@ -3,10 +3,6 @@ import { Module } from '@nestjs/common'; import { SignatureService } from './signature.service'; import { SignatureController } from './signature.controller'; -import { SignatureEntity } from './domain/signature.entity'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { DataSource } from 'typeorm'; -import { EntityManager } from 'typeorm'; import { UserService } from '../user/user.service'; import { S3UtilService } from '../utils/S3.service'; import { SignatureCommentController } from './signature.comment.controller'; @@ -14,6 +10,11 @@ import { SignatureCommentService } from './signature.comment.service'; @Module({ controllers: [SignatureController, SignatureCommentController], - providers: [SignatureService, SignatureCommentService, UserService, S3UtilService], + providers: [ + SignatureService, + SignatureCommentService, + UserService, + S3UtilService, + ], }) export class SignatureModule {} diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index 1ee3fc3..a668598 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -22,7 +22,6 @@ import { LikeProfileDto } from './dto/like/like-profile.dto'; import { S3UtilService } from '../utils/S3.service'; import { ResponsePageSignatureDto } from './dto/signature/response-page-signature.dto'; import { NotificationEntity } from '../notification/notification.entity'; -import { NotificationService } from '../notification/notification.service'; @Injectable() export class SignatureService { @@ -94,10 +93,7 @@ export class SignatureService { async homeSignature(userId: number): Promise { try { console.log('userId; ', userId); - return this.findMySignature( - userId, - ); - + return this.findMySignature(userId); } catch (error) { // 예외 처리 console.error('Error on HomeSignature: ', error); @@ -109,7 +105,7 @@ export class SignatureService { const mySignatureList: HomeSignatureDto[] = []; const signatures = await SignatureEntity.find({ where: { user: { id: user_id } }, - order: { created: 'DESC' } // 내가 작성한 시그니처 최신 순으로 보여주도록 + order: { created: 'DESC' }, // 내가 작성한 시그니처 최신 순으로 보여주도록 }); for (const signature of signatures) { @@ -160,7 +156,8 @@ export class SignatureService { // [2] 시그니처 작성자 정보 가져오기 const authorDto: AuthorSignatureDto = new AuthorSignatureDto(); - if (!signature.user.isQuit) { // 유저가 탈퇴하지 않았다면 + if (!signature.user.isQuit) { + // 유저가 탈퇴하지 않았다면 authorDto._id = signature.user.id; authorDto.name = signature.user.nickname; @@ -289,14 +286,14 @@ export class SignatureService { signatureId: number, ) { // [1] 해당 좋아요 기록 삭제 - const deleted_like = await SignatureLikeEntity.softRemove(signatureLike); + await SignatureLikeEntity.softRemove(signatureLike); // [2] 시그니처 좋아요 개수 -1 const signature: SignatureEntity = await SignatureEntity.findSignatureById( signatureId, ); signature.liked--; - const newSignature = await SignatureEntity.save(signature); + await SignatureEntity.save(signature); return signature; } @@ -374,10 +371,7 @@ export class SignatureService { )}`; // Base64 이미지 업로드 - const uploadedImage = await this.s3Service.putObjectFromBase64( - key, - patchedPage.image, - ); + await this.s3Service.putObjectFromBase64(key, patchedPage.image); // 이미지 키 저장 originalPage.image = key; diff --git a/src/user/optional.user.guard.ts b/src/user/optional.user.guard.ts index 2b47c05..6e16b2c 100644 --- a/src/user/optional.user.guard.ts +++ b/src/user/optional.user.guard.ts @@ -37,8 +37,8 @@ export class OptionalUserGuard implements CanActivate { } catch (error) { return false; } - } - else{ // 토큰이 없는 경우 + } else { + // 토큰이 없는 경우 request.user = null; } diff --git a/src/user/user.following.entity.ts b/src/user/user.following.entity.ts index 31758c7..65ad120 100644 --- a/src/user/user.following.entity.ts +++ b/src/user/user.following.entity.ts @@ -1,9 +1,12 @@ import { - BaseEntity, CreateDateColumn, DeleteDateColumn, + BaseEntity, + CreateDateColumn, + DeleteDateColumn, Entity, JoinColumn, ManyToOne, - PrimaryGeneratedColumn, UpdateDateColumn, + PrimaryGeneratedColumn, + UpdateDateColumn, } from 'typeorm'; import { UserEntity } from './user.entity'; diff --git a/src/user/user.profile.image.entity.ts b/src/user/user.profile.image.entity.ts index afc8172..c103f05 100644 --- a/src/user/user.profile.image.entity.ts +++ b/src/user/user.profile.image.entity.ts @@ -10,7 +10,6 @@ import { UpdateDateColumn, } from 'typeorm'; import { UserEntity } from './user.entity'; -import { User } from 'aws-sdk/clients/budgets'; @Entity() export class UserProfileImageEntity extends BaseEntity { @@ -34,7 +33,10 @@ export class UserProfileImageEntity extends BaseEntity { deleted: Date; static async findImageKey(userEntity): Promise { - const imageEntity : UserProfileImageEntity = await UserProfileImageEntity.findOneOrFail({ where: { user : userEntity } }); + const imageEntity: UserProfileImageEntity = + await UserProfileImageEntity.findOneOrFail({ + where: { user: userEntity }, + }); const imageKey = imageEntity.imageKey; return imageKey; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 975d893..c8bd25e 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -13,7 +13,6 @@ import { RuleInvitationEntity } from '../rule/domain/rule.invitation.entity'; import * as md5 from 'md5'; import { DiaryEntity } from '../diary/models/diary.entity'; import { S3UtilService } from '../utils/S3.service'; -import {CommentEntity} from "../comment/domain/comment.entity"; @Injectable() export class UserService { @@ -451,7 +450,7 @@ export class UserService { try { return await UserFollowingEntity.find({ where: { - followUser: { id: userId, isQuit: false }, + followUser: { id: userId, isQuit: false }, user: { isQuit: false }, }, relations: { user: { profileImage: true } }, @@ -516,22 +515,19 @@ export class UserService { await user.save(); const followings = await UserFollowingEntity.find({ - where: [ - {user: {id: userId}}, - {followUser: {id: userId}} - ] + where: [{ user: { id: userId } }, { followUser: { id: userId } }], }); - for(const following of followings) { + for (const following of followings) { console.log('삭제될 팔로잉 테이블 ID : ', following.id); await following.softRemove(); } const ruleInvitations = await RuleInvitationEntity.find({ - where: {member: {id: userId}} + where: { member: { id: userId } }, }); - for(const invitation of ruleInvitations) { + for (const invitation of ruleInvitations) { console.log('삭제될 규칙 초대 테이블 ID : ', invitation.id); await invitation.softRemove(); } @@ -563,7 +559,7 @@ export class UserService { const followingMates = await UserEntity.find({ where: { follower: { user: { id: userId } }, - isQuit: false, // 탈퇴한 메이트는 팔로잉 목록에서 제외 + isQuit: false, // 탈퇴한 메이트는 팔로잉 목록에서 제외 }, }); return followingMates; diff --git a/src/utils/S3.controller.ts b/src/utils/S3.controller.ts index d7a2e91..8c2f049 100644 --- a/src/utils/S3.controller.ts +++ b/src/utils/S3.controller.ts @@ -1,22 +1,23 @@ // S3.controller.ts - -import { Body, Controller, Get, Post, Put } from '@nestjs/common'; +import { Body, Controller, Get } from '@nestjs/common'; import { S3UtilService } from './S3.service'; @Controller('image') -export class S3UtilController{ - constructor(private readonly s3Service:S3UtilService) {} +export class S3UtilController { + constructor(private readonly s3Service: S3UtilService) {} @Get('/signature') - GetPresignedUrlForSignature() { // 시그니처 이미지 업로드 요청시 + GetPresignedUrlForSignature() { + // 시그니처 이미지 업로드 요청시 return this.s3Service.GetPresignedUrlForSignature(); } @Get('/test') - TestImageUrlWithKey( //presigned URL 잘 보내졌나 테스트용 - @Body('key') key: string - ){ - return this.s3Service.TestImageUrlWithKey(key); + TestImageUrlWithKey( + //presigned URL 잘 보내졌나 테스트용 + @Body('key') key: string, + ) { + return this.s3Service.TestImageUrlWithKey(key); } -} \ No newline at end of file +} diff --git a/src/utils/S3.presignedUrl.dto.ts b/src/utils/S3.presignedUrl.dto.ts index bdf93e4..824a24a 100644 --- a/src/utils/S3.presignedUrl.dto.ts +++ b/src/utils/S3.presignedUrl.dto.ts @@ -1,6 +1,6 @@ // S3.presignedUrl.dto.ts -export class S3PresignedUrlDto{ +export class S3PresignedUrlDto { key: string; url: string; -} \ No newline at end of file +} diff --git a/src/utils/S3.service.ts b/src/utils/S3.service.ts index 80887ce..eb8e1e0 100644 --- a/src/utils/S3.service.ts +++ b/src/utils/S3.service.ts @@ -62,11 +62,12 @@ export class S3UtilService { } public async GetPresignedUrlForSignature(): Promise { - - const s3PresignedUrlDto:S3PresignedUrlDto= new S3PresignedUrlDto(); + const s3PresignedUrlDto: S3PresignedUrlDto = new S3PresignedUrlDto(); // 이미지 키 생성: 프론트에서는 업로드 후 백엔드에 키값을 보내줘야함 - s3PresignedUrlDto.key = `signature/${this.generateRandomImageKey('signature.png')}`; + s3PresignedUrlDto.key = `signature/${this.generateRandomImageKey( + 'signature.png', + )}`; // 프론트에서 이미지를 업로드할 presignedUrl s3PresignedUrlDto.url = await this.getPresignedUrl(s3PresignedUrlDto.key); From bac22122ec345388e27df016f07841faa2108e48 Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 18 Feb 2024 19:23:01 +0900 Subject: [PATCH 314/316] =?UTF-8?q?fix:=20=EB=8C=93=EA=B8=80=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=EC=9D=B4=20=EC=A0=95=EC=83=81=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=9C=EC=83=9D=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/signature/signature.comment.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index 9f1b461..e12b764 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -40,6 +40,7 @@ export class SignatureCommentService { const user = await UserEntity.findOneOrFail({ where: { id: userId } }); const signature = await SignatureEntity.findOneOrFail({ where: { id: signatureId }, + relations: { user: true }, }); if (!user || !signature) { @@ -58,6 +59,7 @@ export class SignatureCommentService { const parentComment = await SignatureCommentEntity.findOneOrFail({ where: { id: parentCommentId }, + relations: { user: true }, }); if (!parentComment) throw new NotFoundException('404 Not Found'); From d87b5837f5a6ee8044ae1e5264b35b5cc9c3f1df Mon Sep 17 00:00:00 2001 From: kaaang Date: Sun, 18 Feb 2024 19:44:50 +0900 Subject: [PATCH 315/316] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=EC=97=90=20?= =?UTF-8?q?=EC=9B=90=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=A0=9C=EB=AA=A9?= =?UTF-8?q?=EC=9D=84=20=EC=B6=94=EA=B0=80=ED=95=98=EB=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comment/comment.service.ts | 1 + src/notification/notification.entity.ts | 3 +++ src/notification/notification.service.ts | 1 + src/signature/signature.comment.service.ts | 2 ++ src/signature/signature.service.ts | 1 + 5 files changed, 8 insertions(+) diff --git a/src/comment/comment.service.ts b/src/comment/comment.service.ts index acb25f2..f3a2ba1 100644 --- a/src/comment/comment.service.ts +++ b/src/comment/comment.service.ts @@ -43,6 +43,7 @@ export class CommentService { notification.notificationSender = user; notification.notificationTargetType = 'RULE'; notification.notificationTargetId = rule.id; + notification.notificationTargetDesc = rule.mainTitle; notification.notificationAction = 'COMMENT'; await notification.save(); } diff --git a/src/notification/notification.entity.ts b/src/notification/notification.entity.ts index 90fa3d7..90322cf 100644 --- a/src/notification/notification.entity.ts +++ b/src/notification/notification.entity.ts @@ -30,6 +30,9 @@ export class NotificationEntity extends BaseEntity { @Column() notificationTargetId: number; + @Column({ type: 'text' }) + notificationTargetDesc: string; + @Column({ type: 'enum', enum: ['LIKE', 'COMMENT'] }) notificationAction: 'LIKE' | 'COMMENT'; diff --git a/src/notification/notification.service.ts b/src/notification/notification.service.ts index 8e97115..dff1d47 100644 --- a/src/notification/notification.service.ts +++ b/src/notification/notification.service.ts @@ -48,6 +48,7 @@ export class NotificationService { action: notification.notificationAction, }, itemId: notification.notificationTargetId, + itemDesc: notification.notificationTargetDesc, isRead: notification.notificationRead, created: notification.created, })), diff --git a/src/signature/signature.comment.service.ts b/src/signature/signature.comment.service.ts index e12b764..67515de 100644 --- a/src/signature/signature.comment.service.ts +++ b/src/signature/signature.comment.service.ts @@ -69,6 +69,7 @@ export class SignatureCommentService { } notification.notificationReceiver = parentComment.user; + notification.notificationTargetDesc = parentComment.content; } else { // 댓글: parentId는 본인으로 설정 const savedComment = await comment.save(); @@ -76,6 +77,7 @@ export class SignatureCommentService { await savedComment.save(); notification.notificationReceiver = signature.user; + notification.notificationTargetDesc = signature.title; } notification.notificationSender = user; diff --git a/src/signature/signature.service.ts b/src/signature/signature.service.ts index a668598..63e3d1c 100644 --- a/src/signature/signature.service.ts +++ b/src/signature/signature.service.ts @@ -275,6 +275,7 @@ export class SignatureService { notification.notificationSender = loginUser; notification.notificationTargetType = 'SIGNATURE'; notification.notificationTargetId = signature.id; + notification.notificationTargetDesc = signature.title; notification.notificationAction = 'LIKE'; await notification.save(); From 0908bd0a0bd11c7430739e264526797f7a097ced Mon Sep 17 00:00:00 2001 From: moonyaeyoon Date: Sun, 18 Feb 2024 19:51:11 +0900 Subject: [PATCH 316/316] =?UTF-8?q?fix=20:=20=EC=97=AC=EC=A0=95=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/journey/journey.service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/journey/journey.service.ts b/src/journey/journey.service.ts index 715d6f6..10533fa 100644 --- a/src/journey/journey.service.ts +++ b/src/journey/journey.service.ts @@ -118,10 +118,15 @@ export class JourneyService { if (!diary) { return; // 일지가 없으면 삭제할 필요 없음 } - const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); + // 일지 삭제 + await DiaryEntity.deleteDiary(diary); - await DiaryImageEntity.deleteDiaryImg(diaryImg); + // 연결된 이미지 찾기 + const diaryImg = await DiaryImageEntity.findExistImgUrl(diary); - await DiaryEntity.deleteDiary(diary); + // 이미지 삭제 + if (diaryImg) { + await DiaryImageEntity.deleteDiaryImg(diaryImg); + } } }