Skip to content
This repository has been archived by the owner on May 14, 2024. It is now read-only.

[Feat] 시그니처: 댓글 | 답글 생성 조회 API 개발 #180 #189

Merged
merged 5 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/response/response-code.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -51,6 +50,7 @@ export enum ResponseCode {
LIKE_ON_SIGNATURE_CREATED = 'CREATED',
COMMENT_CREATED = 'CREATED',
INVITATION_CREATED = 'CREATED',
CREATE_SIGNATURE_COMMENT_SUCCESS = 'CREATED',



Expand Down
50 changes: 50 additions & 0 deletions src/signature/domain/signature.comment.entity.ts
Original file line number Diff line number Diff line change
@@ -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;
}
9 changes: 7 additions & 2 deletions src/signature/domain/signature.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,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';
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<SignatureLikeEntity>{
Expand All @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/signature/domain/signature.page.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions src/signature/dto/comment/create-comment.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// create-comment.dto.ts

export class CreateCommentDto{
content: string; // 댓글 내용

}
8 changes: 8 additions & 0 deletions src/signature/dto/comment/get-comment-writer.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// get-comment-writer.dto.ts

export class GetCommentWriterDto{
_id: number;
name: string;
image: string; // 프로필 이미지
is_writer: boolean; // 로그인 유저의 수정 삭제 가능 여부
}
12 changes: 12 additions & 0 deletions src/signature/dto/comment/get-signature-comment.dto.ts
Original file line number Diff line number Diff line change
@@ -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; // 댓글 수정 여부
}
107 changes: 107 additions & 0 deletions src/signature/signature.comment.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// signature.comment.controller.ts

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 { 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{
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)

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( // 시그니처 답글 생성하기
@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 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
);
}
}





}
155 changes: 155 additions & 0 deletions src/signature/signature.comment.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// 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/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{

constructor(
private readonly signatureService: SignatureService,
private readonly userService: UserService,
private readonly s3Service: S3UtilService,
) {}

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 }});


if( !user || !signature ) {
throw new NotFoundException('404 Not Found');
}
else {

comment.user = user;
comment.signature = signature;
comment.content = createCommentDto.content;

// 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 );


}
}
Loading