diff --git a/src/index.js b/src/index.js index c5da0ef..66f7911 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,14 @@ const morgan = require('morgan'); const express = require('express'); + const { corsMiddleware } = require('./middlewares/_.loader'); -const { globalRouter, musicRouter, commentRouter } = require('./layers/_.loader'); +const { + globalRouter, + musicRouter, + userRouter, + boardRouter, + commentRouter, +} = require('./layers/_.loader'); // TS 님의 Contritubte...., 아래 명령어 실행하면 migrate 자동실행 // const { sequelize } = require('./sequelize/models'); @@ -21,6 +28,8 @@ app.all('*', corsMiddleware); app.use('/api', globalRouter); app.use('/api/musics', musicRouter); +app.use('/api/users', userRouter); app.use('/api/comments', commentRouter); +app.use('/api/posts', boardRouter); app.listen(3000, () => console.log(`Server is running on 3000`)); diff --git a/src/layers/_.loader.js b/src/layers/_.loader.js index ae977fd..94840b5 100644 --- a/src/layers/_.loader.js +++ b/src/layers/_.loader.js @@ -1,9 +1,13 @@ const globalRouter = require('./routers/global.router'); +const userRouter = require('./routers/user.router'); const musicRouter = require('./routers/music.router'); +const boardRouter = require('./routers/board.router'); const commentRouter = require('./routers/comment.router'); module.exports = { globalRouter, musicRouter, + userRouter, + boardRouter, commentRouter, }; diff --git a/src/layers/controllers/board.controller.js b/src/layers/controllers/board.controller.js new file mode 100644 index 0000000..0c1c3ba --- /dev/null +++ b/src/layers/controllers/board.controller.js @@ -0,0 +1,95 @@ +const e = require('express'); +const joi = require('joi'); + +const BoardService = require('../services/board.service'); +const { BoardPostDto, BoardGetDto } = require('../../models/_.loader'); +const { + BadRequestException, + UnkownException, + NotFoundException, +} = require('../../models/_.loader'); +const { FormDtoProvider, JoiValidator, exceptionHandler } = require('../../modules/_.loader'); + +class BoardController { + boardService; + formProvider; + joiValidator; + + constructor() { + this.boardService = new BoardService(); + this.formProvider = new FormDtoProvider(); + this.joiValidator = new JoiValidator(); + } + + // 게시글 생성 + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ + postBoard = async (req, res, next) => { + console.log(req.body); + + try { + const boardPostDto = new BoardPostDto(req.body); + await this.joiValidator.validate(boardPostDto); + + const post = await this.boardService.postBoard(boardPostDto); + console.log(post.dataValues); + + return res.status(200).json( + this.formProvider.getSuccessFormDto('게시물 작성이 완료됐습니다.', { + post: post, + }), + ); + } catch (err) { + const exception = exceptionHandler(err); + + return res.status(exception.statusCode).json( + this.formProvider.getFailureFormDto(exception.message, { + post: { + title: boardPostDto.title, + content: boardPostDto.password, + }, + }), + ); + } + }; + + // 게시글 전체 조회 + getBoard = async (req, res, next) => { + try { + const page = req?.query?.page ?? 1; + + const boardGetDto = new BoardGetDto({ page }); + await this.joiValidator.validate(boardGetDto); + + const post = await this.boardService.getBoard(boardGetDto); + + return res.status(200).json( + this.formProvider.getSuccessFormDto('게시글 조회가 완료되었습니다.', { + postList: post, + }), + ); + } catch (err) { + const exception = exceptionHandler(err); + + return res + .status(exception.statusCode) + .json(this.formProvider.getFailureFormDto(exception.message)); + } + }; + + // 게시글 상세 조회 + getOneBoard = async (req, res, next) => { + return 'hello'; + }; + + // 게시글 수정 + putBoard = async (req, res, next) => { + return 'hello'; + }; + + // 게시글 삭제 + deleteBoard = async (req, res, next) => { + return 'hello'; + }; +} + +module.exports = BoardController; diff --git a/src/layers/controllers/comment.controller.js b/src/layers/controllers/comment.controller.js index c8520e8..f09ecc6 100644 --- a/src/layers/controllers/comment.controller.js +++ b/src/layers/controllers/comment.controller.js @@ -1,45 +1,32 @@ const e = require('express'); -const joi = require('joi'); const CommentService = require('../services/comment.service'); -const { - PostCommentDto, - GetCommentsDto, - UpdateCommentsDto, - DeleteCommentsDto, -} = require('../../models/_.loader'); const { FormDtoProvider, JoiValidator, exceptionHandler } = require('../../modules/_.loader'); +const { PostCommentDto, DeleteCommentDto } = require('../../models/_.loader'); class CommentController { formProvider; - joiValidator; + joiValidatorMusic; commentService; - constructor() { - this.formProvider = new FormDtoProvider(); this.joiValidator = new JoiValidator(); + this.formProvider = new FormDtoProvider(); this.commentService = new CommentService(); } /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ - postComments = async (req, res, next) => { - const userId = 1; - const { commentId } = req.params; - const { content } = req.body; - + postComment = async (req, res, next) => { try { - const postCommentDto = new PostCommentDto({ - commentId, - userId, - content, - }); - this.joiValidator.validate(postCommentDto); + const { userId, musicId, content } = req?.body; - const comment = await this.commentService.postComments(postCommentDto); + const postCommentDto = new PostCommentDto({ userId, musicId, content }); + await this.joiValidator.validate(postCommentDto); + + const comment = await this.commentService.postComment(postCommentDto); return res .status(200) .json( - this.formProvider.getSuccessFormDto('댓글 생성에 성공하셨습니다.', { comment }), + this.formProvider.getSuccessFormDto('댓글 작성에 성공했습니다.', { comment }), ); } catch (err) { const exception = exceptionHandler(err); @@ -51,15 +38,19 @@ class CommentController { }; /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ - getComments = async (req, res, next) => { + deleteComment = async (req, res, next) => { try { - const comment = await this.commentService.getComments(); + const userId = req?.body?.userId; + const commentId = req?.params?.commentId; + + const deleteCommentDto = new DeleteCommentDto({ userId, commentId }); + await this.joiValidator.validate(deleteCommentDto); + + const result = await this.commentService.deleteComment(deleteCommentDto); - return res.status(200).json( - this.formProvider.getSuccessFormDto('댓글 전체 조회에 성공했습니다.', { - comment, - }), - ); + return res + .status(200) + .json(this.formProvider.getSuccessFormDto('댓글 삭제에 성공했습니다.', {})); } catch (err) { const exception = exceptionHandler(err); @@ -68,66 +59,6 @@ class CommentController { .json(this.formProvider.getFailureFormDto(exception.message)); } }; - - // 댓글 상세 조회 - // getOneComment = async (req, res, next) => { - // const commentId = 1; - // try { - // const commentOne = await this.commentService.getOneComment(); - - // return res.status(200).json( - // this.formProvider.getSuccessFormDto('댓글 상세 조회에 성공했습니다.', { - // commentOne, - // }), - // ); - // } catch (err) { - // const exception = exceptionHandler(err); - - // return res - // .status(exception.statusCode) - // .json(this.formProvider.getFailureFormDto(exception.message)); - // } - // } - updateComments = async (req, res, next) => { - const { commentId } = req.params; - const { content, userId } = req.body; - - try { - await joi - .object({ - userId: joi.number().required(), - commentId: joi.number().required(), - content: joi.string().required(), - }) - .validateAsync({ userId, commentId, content }); - - const result = await this.commentService.updateComment(userId, commentId, content); - - return res.json(result); - } catch (err) { - return res.json(err.message); - } - }; - - deleteComments = async (req, res, next) => { - const { commentId } = req.params; - const { userId } = req.body; - - try { - await joi - .object({ - userId: joi.number().required(), - commentId: joi.number().required(), - }) - .validateAsync({ userId, commentId }); - - const result = await this.commentService.deleteCommentById(userId, commentId); - - return res.json(result); - } catch (err) { - return res.json(err.message); - } - }; } module.exports = CommentController; diff --git a/src/layers/controllers/music.controller.js b/src/layers/controllers/music.controller.js index 5d76b02..19a2105 100644 --- a/src/layers/controllers/music.controller.js +++ b/src/layers/controllers/music.controller.js @@ -3,10 +3,12 @@ const joi = require('joi'); const MusicService = require('../services/music.service'); const { - GetMusicDto, + GetMusicsDto, PostMusicDto, BadRequestException, UnkownException, + MusicDto, + LikeMusicDto, } = require('../../models/_.loader'); const { FormDtoProvider, JoiValidator, exceptionHandler } = require('../../modules/_.loader'); // const etMusicsDto = require('../../models/dto/music/get.musics.dto'); @@ -25,6 +27,7 @@ class MusicController { } // 음악 생성 + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ postMusics = async (req, res, next) => { const userId = 1; const { title, artist, album } = req.query; @@ -35,13 +38,15 @@ class MusicController { throw new UnkownException('알 수 없는 이유로 업로드에 실패하였습니다.'); const postMusicDto = new PostMusicDto({ userId, title, artist, album, musicUrl }); - this.joiValidator.validate(postMusicDto); + await this.joiValidator.validate(postMusicDto); const music = await this.musicService.postMusics(postMusicDto); - return res - .status(200) - .json(this.formProvider.getSuccessFormDto('노래 생성에 성공했습니다.', { music })); + return res.status(200).json( + this.formProvider.getSuccessFormDto('노래 생성에 성공했습니다.', { + musicDesc: music, + }), + ); } catch (err) { const exception = exceptionHandler(err); @@ -52,13 +57,19 @@ class MusicController { }; // 음악 전체 조회 + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ getMusics = async (req, res, next) => { try { - const music = await this.musicService.getMusics(); + const page = req?.query?.page ?? 1; + + const getMusicDto = new GetMusicsDto({ page }); + await this.joiValidator.validate(getMusicDto); + + const music = await this.musicService.getMusics(getMusicDto); return res.status(200).json( this.formProvider.getSuccessFormDto('노래 전체 조회에 성공했습니다.', { - music, + musicList: music, }), ); } catch (err) { @@ -71,14 +82,15 @@ class MusicController { }; // 음악 상세 조회 + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ getOneMusic = async (req, res, next) => { try { - const musicId = this.joiValidator.validateNumber(req?.params?.musicId); + const musicId = await this.joiValidator.validateNumber(req?.params?.musicId); const musicOne = await this.musicService.getOneMusic(musicId); return res.status(200).json( this.formProvider.getSuccessFormDto('노래 상세 조회에 성공했습니다.', { - musicOne, + musicDesc: musicOne, }), ); } catch (err) { @@ -89,6 +101,37 @@ class MusicController { .json(this.formProvider.getFailureFormDto(exception.message)); } }; + + // 음악 좋아요 반영 및 취소 + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ + toggleLike = async (req, res, next) => { + try { + const { userId } = req.body; + const { musicId } = req.params; + + const likeMusicDto = new LikeMusicDto({ userId, musicId }); + await this.joiValidator.validate(likeMusicDto); + + const result = await this.musicService.toggleLike(likeMusicDto); + + return res + .status(200) + .json( + this.formProvider.getSuccessFormDto( + `해당 노래의 좋아요가 ${ + result.isLikeUp ? '적용(+1)' : '취소(-1)' + } 되었습니다.`, + { result }, + ), + ); + } catch (err) { + const exception = exceptionHandler(err); + + return res + .status(exception.statusCode) + .json(this.formProvider.getFailureFormDto(exception.message)); + } + }; } module.exports = MusicController; diff --git a/src/layers/controllers/user.controller.js b/src/layers/controllers/user.controller.js index 47c2910..2769bc5 100644 --- a/src/layers/controllers/user.controller.js +++ b/src/layers/controllers/user.controller.js @@ -1,6 +1,6 @@ const e = require('express'); const UserService = require('../services/user.service'); -const { UserJoinDto, UserLoginDto } = require('../../models/_.loader'); +const { UserJoinDto, UserLoginDto, PaginationDto } = require('../../models/_.loader'); const { FormDtoProvider, JoiValidator, exceptionHandler } = require('../../modules/_.loader'); class UserController { @@ -31,11 +31,14 @@ class UserController { } catch (err) { const exception = exceptionHandler(err); - return res - .status(exception.statusCode) - .json( - this.formProvider.getFailureFormDto(exception.message, { user: userJoinDto }), - ); + return res.status(exception.statusCode).json( + this.formProvider.getFailureFormDto(exception.message, { + user: { + email: userJoinDto.email, + nickanme: userJoinDto.password, + }, + }), + ); } }; @@ -57,11 +60,77 @@ class UserController { } catch (err) { const exception = exceptionHandler(err); + return res.status(exception.statusCode).json( + this.formProvider.getFailureFormDto(exception.message, { + user: { + email: userLoginDto.email, + }, + }), + ); + } + }; + + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ + getMyUploadedMusics = async (req, res, next) => { + try { + const page = req?.query?.page ?? 1; + const userId = req?.body?.userId; + const pageDto = new PaginationDto({ userId, page }); + + await this.joiValidator.validate(pageDto); + + const musicList = await this.userService.getMyUploadedMusics(pageDto); + return res.json( + this.formProvider.getSuccessFormDto('get My Upload Music', { musicList }), + ); + } catch (err) { + const exception = exceptionHandler(err); + + return res + .status(exception.statusCode) + .json(this.formProvider.getFailureFormDto(exception.message)); + } + }; + + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ + getMyLikedMusics = async (req, res, next) => { + try { + const page = req?.query?.page ?? 1; + const userId = req?.body?.userId; + const pageDto = new PaginationDto({ userId, page }); + + await this.joiValidator.validate(pageDto); + + const musicList = await this.userService.getMyLikedMusics(pageDto); + return res.json( + this.formProvider.getSuccessFormDto('get My Like Music', { musicList }), + ); + } catch (err) { + const exception = exceptionHandler(err); + + return res + .status(exception.statusCode) + .json(this.formProvider.getFailureFormDto(exception.message)); + } + }; + + /** @param { e.Request } req @param { e.Response } res @param { e.NextFunction } next */ + getMyLikedPosts = async (req, res, next) => { + try { + const page = req?.query?.page ?? 1; + const userId = req?.body?.userId; + const pageDto = new PaginationDto({ userId, page }); + + await this.joiValidator.validate(pageDto); + + const result = await this.userService.getMyLikedPosts(pageDto); + return res.json(this.formProvider.getSuccessFormDto('get My Like Posts', { result })); + } catch (err) { + const exception = exceptionHandler(err); + return res .status(exception.statusCode) - .json( - this.formProvider.getFailureFormDto(exception.message, { user: userLoginDto }), - ); + .json(this.formProvider.getFailureFormDto(exception.message)); } }; } diff --git a/src/layers/repositories/board.repository.js b/src/layers/repositories/board.repository.js new file mode 100644 index 0000000..f3ea993 --- /dev/null +++ b/src/layers/repositories/board.repository.js @@ -0,0 +1,83 @@ +const { Board } = require('../../sequelize/models'); +// const dto +const { BoardPostDto, BoardGetDto } = require('../../models/_.loader'); +const { + CustomException, + ConflictException, + UnkownException, + UnhandleMysqlSequelizeError, + NotFoundException, +} = require('../../models/_.loader'); +const BaseRepository = require('./base.repository'); + +class BoardRepository extends BaseRepository { + constructor() { + super(); + } + + /** @param { BoardPostDto } boardPostDto @returns */ + postBoard = async (boardPostDto) => { + try { + const post = await Board.create({ + userId: boardPostDto.userId, + title: boardPostDto.title, + content: boardPostDto.content, + }); + + return post; + } catch (err) { + console.log(err); + throw err; + } + }; + + /** @param { BoardGetDto } getMusicsDto @returns */ + getBoard = async (boardGetDto) => { + try { + console.log(Board); + + const { page, pageCount } = boardGetDto; + const posts = await Post.findAll({ + offset: pageCount * (page - 1), + limit: pageCount, + }); + console.log(Object.keys(posts)); + + // const musicList = []; + // for (const music of musics) { + // musicList.push(music.dataValues); + // } + return 'hello'; + } catch (err) { + console.log(err); + throw err; + } + }; + + /** + * @param { number } musicId + */ + getOneBoard = async (musicId) => { + // console.log(musicId); + + // const findResult = await Music.findOne({ + // where: { musicId }, + // attributes: ['musicId', 'title', 'artist', 'album', 'musicUrl'], + // }); + // console.log(findResult); + + // if (findResult === null) throw new NotFoundException('존재하지 않는 음악입니다.'); + + return; + }; + + putBoard = async () => { + return; + }; + + deleteBoard = async () => { + return; + }; +} + +module.exports = BoardRepository; diff --git a/src/layers/repositories/comment.repository.js b/src/layers/repositories/comment.repository.js index c81da49..97d933b 100644 --- a/src/layers/repositories/comment.repository.js +++ b/src/layers/repositories/comment.repository.js @@ -1,66 +1,68 @@ +const BaseRepository = require('./base.repository'); const { Comment } = require('../../sequelize/models'); + const { - GetMusicCommentsDto, PostCommentDto, - GetCommentsDto, - UpdateCommentsDto, - DeleteCommentsDto, - CustomException, - ConflictException, - UnkownException, - UnhandleMysqlSequelizeError, + NotFoundException, + DeleteCommentDto, + CommentDto, } = require('../../models/_.loader'); -const BaseRepository = require('./base.repository'); class CommentRepository extends BaseRepository { constructor() { super(); } - postComments = async (postCommentDto) => { + /** + * + * @param { number } commentId + * @throws { UnkownException | UnhandleMysqlSequelizeError } + * @returns { Promise } + */ + isExistsCommentByCommentId = async (commentId) => { + try { + const findResult = await Comment.findOne({ + where: { commentId }, + attributes: ['commentId'], + }); + + if (findResult === null) return false; + else return true; + } catch (err) { + throw this.exeptionHandler(err); + } + }; + + /** @param { PostCommentDto } postCommentDto */ + createComment = async (postCommentDto) => { try { - // (추후 추가)s3 변환 정보(musicUrl) 받아오고 Post DB에 저장 - console.log('테스트', postCommentDto); const comment = await Comment.create({ userId: postCommentDto.userId, - commentId: postCommentDto.commentId, + musicId: postCommentDto.musicId, content: postCommentDto.content, }); - const postDto = new PostCommentDto(comment?.dataValues); - - return postDto; + return new CommentDto(comment.dataValues); } catch (err) { - console.log(err); - throw err; + throw this.exeptionHandler(err); } }; - getComments = async (getCommentsDto) => { + /** @param { DeleteCommentDto } deleteCommentDto */ + deleteComment = async (deleteCommentDto) => { try { - console.log(Comment); - console.log('테스트', getCommentsDto); - - const comments = await Comment.findAll(); - - for (const comment of comments) { - // const getAllMusic = music.dataValues; - console.log(comment.dataValues); - } - // console.log(Object.keys(musics)); - // const getDto = new GetMusicsDto(musics?.dataValues); - return; + const comment = await Comment.destroy({ + where: { + userId: deleteCommentDto.commentId, + commentId: deleteCommentDto.userId, + }, + }); + if (comment === 0) throw new NotFoundException('이미 존재하지 않는 댓글입니다.'); + else return true; } catch (err) { - console.log(err); - throw err; + throw this.exeptionHandler(err); } - return musics; }; - - // getOneComment = () => { - // console.log('왜 안 되니?'); - // return 'smile'; - // }; } module.exports = CommentRepository; diff --git a/src/layers/repositories/music.like.repository.js b/src/layers/repositories/music.like.repository.js new file mode 100644 index 0000000..0be2e3a --- /dev/null +++ b/src/layers/repositories/music.like.repository.js @@ -0,0 +1,80 @@ +const { LikeMusicDto, ConflictException, NotFoundException } = require('../../models/_.loader'); +const { Music, MusicLike } = require('../../sequelize/models'); +const BaseRepository = require('./base.repository'); + +class MusicLikeRepository extends BaseRepository { + constructor() { + super(); + } + + /** @param { LikeMusicDto } likeMusicDto @returns { Promies} */ + isLikedMusicByLikeMusicDto = async (likeMusicDto) => { + try { + const findResult = await MusicLike.findOne({ + where: { + userId: likeMusicDto.userId, + musicId: likeMusicDto.musicId, + }, + }); + + if (findResult === null) return false; + else return true; + } catch (err) { + throw this.exeptionHandler(err); + } + }; + + /** + * Model.create 메서드는 생성할 대상의 고유값(UNIQUE) 가 이미 있으면 에러가 납니다. + * Model.create 메서드는 생성할 대상이 없을 경우에는 생성 후 Sequelize 모델 전용 객체를 반환합니다. + * + * 에러가 나지 않았을 경우에는 반드시 try {} 안에서 성공했음을 의미하기에 { isLiked: up } 을 사용합니다. + * 에러가 났을 경우에는 에러 값의 err.original.code 가 'ER_DUP_ENTTRY' 인 경우를 확인하고 그렇다면 좋아요가 반영 되었음을 알립니다. + * + * @param { LikeMusicDto } likeMusicDto @returns { Promise<{ isLikeUp: false }>} */ + increaseMusicLike = async (likeMusicDto) => { + try { + const result = await MusicLike.create({ + userId: likeMusicDto.userId, + musicId: likeMusicDto.musicId, + }); + // result.dataValues + return { + isLikeUp: true, + }; + } catch (err) { + // 또는 err?.origin?.errno 1062 일 떄, ... + if (err?.original?.code === 'ER_DUP_ENTRY') + throw new ConflictException(`이미 좋아요가 반영되어 있습니다.`); + else throw this.exeptionHandler(err); + } + }; + + /** + * Model.destory 메서드는 삭제할 대상이 없어도 에러가 나지 않습니다. + * Model.destory 메서드는 삭제할 대상이 없을 경우 `0` 있을 경우 `삭제한 숫자` 를 반환합니다. + * + * result 가 0 인 경우에는 에러를 삭제할 좋아요를 찾지 못했다는 404 에러와 함께 반환 해줍니다. + * result 가 0 이 아닌 경우에는 해당하는 모든 친구를 삭제하고 false 를 줍니다. + * + * @param { LikeMusicDto } likeMusicDto @returns { Promise<{ isLikeUp: false }> } */ + decreaseMusicLike = async (likeMusicDto) => { + try { + const result = await MusicLike.destroy({ + where: { + musicId: likeMusicDto.musicId, + userId: likeMusicDto.userId, + }, + }); + if (result === 0) throw new NotFoundException('이미 좋아요가 취소되어 있습니다.'); + else + return { + isLikeUp: false, + }; + } catch (err) { + throw this.exeptionHandler(err); + } + }; +} + +module.exports = MusicLikeRepository; diff --git a/src/layers/repositories/music.repository.js b/src/layers/repositories/music.repository.js index f6f77c8..05eec08 100644 --- a/src/layers/repositories/music.repository.js +++ b/src/layers/repositories/music.repository.js @@ -1,11 +1,15 @@ -const { Music } = require('../../sequelize/models'); +const { Music, MusicLike } = require('../../sequelize/models'); const { + MusicDto, GetMusicsDto, PostMusicDto, + OneMusicsDto, CustomException, ConflictException, UnkownException, UnhandleMysqlSequelizeError, + NotFoundException, + PaginationDto, } = require('../../models/_.loader'); const BaseRepository = require('./base.repository'); @@ -14,62 +18,145 @@ class MusicRepository extends BaseRepository { super(); } - /** @param { PostMusicDto } postMusicDto @returns */ - postMusics = async (postMusicDto) => { + /** + * + * @param { number } musicId + * @throws { UnkownException | UnhandleMysqlSequelizeError } + * @returns { Promise } + */ + isExistsMusicByMusicId = async (musicId) => { try { - // (추후 추가)s3 변환 정보(musicUrl) 받아오고 Post DB에 저장 - - const music = await Music.create({ - userId: postMusicDto.userId, - title: postMusicDto.title, - artist: postMusicDto.artist, - album: postMusicDto.album, - musicUrl: postMusicDto.musicUrl, + const findResult = await Music.findOne({ + where: { musicId }, + attributes: ['musicId'], }); - return music; + if (findResult === null) return false; + else return true; } catch (err) { - console.log(err); - throw err; + throw this.exeptionHandler(err); } }; + /** @param { GetMusicsDto } getMusicsDto @returns */ getMusics = async (getMusicsDto) => { try { - console.log(Music); - console.log('테스트', getMusicsDto); + // https://kyounghwan01.github.io/blog/etc/sequelize/sequelize-pagenation/#%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%92%E1%85%A1%E1%86%B8 - const musics = await Music.findAll(); + const { page, pageCount } = getMusicsDto; + const musics = await Music.findAll({ + offset: pageCount * (page - 1), + limit: pageCount, + attributes: ['musicId', 'title', 'artist', 'album'], + }); - // const getAllMusic = musics.dataValues; + let musicList = []; + for (const music of musics) { + musicList.push(new MusicDto(music?.dataValues)); + } + return musicList; + } catch (err) { + console.log(err); + throw err; + } + }; + + /** @param { PaginationDto } pageDto */ + getMyUploadedMusicsByUserId = async (pageDto) => { + try { + const { userId, page, pageCount } = pageDto; + const musics = await Music.findAll({ + where: { + userId, + }, + offset: pageCount * (page - 1), + limit: pageCount, + }); + + let musicList = []; for (const music of musics) { - // const getAllMusic = music.dataValues; - console.log(music.dataValues); + musicList.push(new MusicDto(music?.dataValues)); } - // console.log(Object.keys(musics)); - // const getDto = new GetMusicsDto(musics?.dataValues); - return; + + return musicList; } catch (err) { console.log(err); throw err; } - return musics; + }; + + /** @param { PaginationDto } pageDto */ + getMyLikedMusicsByUserId = async (pageDto) => { + try { + const { userId, page, pageCount } = pageDto; + const likeList = await MusicLike.findAll({ + include: [ + { + model: Music, + attributes: ['musicId', 'title', 'artist', 'album'], + }, + ], + where: { + userId: userId, + }, + offset: pageCount * (page - 1), + limit: pageCount, + }); + + let musicList = []; + for (const like of likeList) { + /** + * like.dataValues.Music.dataValues; + * + * MusicLike.findAll() 을 하면 MusicLike 가 배열로 나옵니다. + * 반복문 안에서 호출하면 하나의 MusicLike 가 나오고 원하는 값은 MusicLike.dataValue 안에 있습니다. + * dataValues 안에는 musicId, userId, Music 이 있습니다. + * Music 안에 있는 원하는 값은 Music.dataValues 안에 있습니다. + */ + const music = like.dataValues; + musicList.push(music?.dataValues); + } + + return musicList; + } catch (err) { + console.log(err); + throw this.exeptionHandler(err); + } }; /** * @param { number } musicId */ getOneMusic = async (musicId) => { - console.log(musicId); - const findResult = await Music.findOne({ where: { musicId }, attributes: ['musicId', 'title', 'artist', 'album', 'musicUrl'], }); - console.log(findResult); - return 'smile'; + if (findResult === null) throw new NotFoundException('존재하지 않는 음악 입니다.'); + + return findResult?.dataValues; + }; + + /** @param { PostMusicDto } postMusicDto @returns */ + postMusics = async (postMusicDto) => { + try { + // (추후 추가)s3 변환 정보(musicUrl) 받아오고 Post DB에 저장 + + const music = await Music.create({ + userId: postMusicDto.userId, + title: postMusicDto.title, + artist: postMusicDto.artist, + album: postMusicDto.album, + musicUrl: postMusicDto.musicUrl, + }); + + return music; + } catch (err) { + console.log(err); + throw err; + } }; } diff --git a/src/layers/repositories/user.repository.js b/src/layers/repositories/user.repository.js index 2efb797..cd84872 100644 --- a/src/layers/repositories/user.repository.js +++ b/src/layers/repositories/user.repository.js @@ -121,6 +121,7 @@ class UserRepository extends BaseRepository { return userDto; } catch (err) { + // 또는 err?.origin?.errno 1062 일 떄, ... if (err?.original?.code === 'ER_DUP_ENTRY') throw new ConflictException(`${userJoinDto.email} 은 사용 중인 이메일입니다.`); else throw this.exeptionHandler(err); diff --git a/src/layers/routers/board.router.js b/src/layers/routers/board.router.js new file mode 100644 index 0000000..421f42f --- /dev/null +++ b/src/layers/routers/board.router.js @@ -0,0 +1,14 @@ +const { Router } = require('express'); +const { preventUnLoginGuard, s3Middleware, tokenGuard } = require('../../middlewares/_.loader'); + +const boardRouter = Router(); +const BoardController = require('../controllers/board.controller'); + +const boardController = new BoardController(); + +boardRouter.get('', boardController.getBoard); +boardRouter.post('', tokenGuard, boardController.postBoard); +boardRouter.put('/:postId', boardController.putBoard); +boardRouter.delete('/:postId', boardController.deleteBoard); + +module.exports = boardRouter; diff --git a/src/layers/routers/comment.router.js b/src/layers/routers/comment.router.js index 810ee89..83b4e7f 100644 --- a/src/layers/routers/comment.router.js +++ b/src/layers/routers/comment.router.js @@ -1,13 +1,12 @@ const { Router } = require('express'); +const { tokenGuard } = require('../../middlewares/_.loader'); const CommentController = require('../controllers/comment.controller'); +const commentController = new CommentController(); const commentRouter = Router(); -const commentController = new CommentController(); -commentRouter.get('', commentController.getComments); -commentRouter.post('', commentController.postComments); -commentRouter.put('/:commentId', commentController.updateComments); -commentRouter.delete('/:comentId', commentController.deleteComments); +commentRouter.post('', tokenGuard, commentController.postComment); +commentRouter.delete('/:commentId', tokenGuard, commentController.deleteComment); module.exports = commentRouter; diff --git a/src/layers/routers/music.router.js b/src/layers/routers/music.router.js index 9bb3892..69e012f 100644 --- a/src/layers/routers/music.router.js +++ b/src/layers/routers/music.router.js @@ -1,5 +1,5 @@ const { Router } = require('express'); -const { preventUnLoginGuard, s3Middleware } = require('../../middlewares/_.loader'); +const { preventUnLoginGuard, s3Middleware, tokenGuard } = require('../../middlewares/_.loader'); const musicRouter = Router(); const MusicController = require('../controllers/music.controller'); @@ -14,5 +14,6 @@ musicRouter.post( musicController.postMusics, ); musicRouter.get('/:musicId', musicController.getOneMusic); +musicRouter.patch('/:musicId/like', tokenGuard, musicController.toggleLike); module.exports = musicRouter; diff --git a/src/layers/routers/user.router.js b/src/layers/routers/user.router.js new file mode 100644 index 0000000..032df57 --- /dev/null +++ b/src/layers/routers/user.router.js @@ -0,0 +1,12 @@ +const { Router } = require('express'); + +const UserController = require('../controllers/user.controller'); +const { tokenGuard } = require('../../middlewares/_.loader'); +const userRouter = Router(); +const userController = new UserController(); + +userRouter.get('/my-upload-musics', tokenGuard, userController.getMyUploadedMusics); +userRouter.get('/my-like-musics', tokenGuard, userController.getMyLikedMusics); +userRouter.get('/my-like-posts', tokenGuard, userController.getMyLikedPosts); + +module.exports = userRouter; diff --git a/src/layers/services/board.service.js b/src/layers/services/board.service.js new file mode 100644 index 0000000..781850e --- /dev/null +++ b/src/layers/services/board.service.js @@ -0,0 +1,47 @@ +const { BoardPostDto, BoardGetDto, BoardDto, ConflictException } = require('../../models/_.loader'); +const BoardRepository = require('../repositories/board.repository'); + +class BoardService { + boardRepository; + + constructor() { + this.boardRepository = new BoardRepository(); + } + + /** @param { BoardPostDto } BoardPostDto @return { string }*/ + postBoard = async (boardPostDto) => { + const result = await this.boardRepository.postBoard(boardPostDto); + + return result; + }; + + /** @param { BoardGetDto } BoardPostDto @return { string }*/ + getBoard = async (BoardGetDto) => { + const result = await this.boardRepository.getBoard(boardGetDto); + + return result; + }; + + /** @param { number } postId @return { string }*/ + getOneBoard = async (postId) => { + const result = await this.boardRepository.getOneBoard(); + + return result; + }; + + /** @param { number } postId @return { string }*/ + putBoard = async (postId) => { + const result = await this.boardRepository.putBoard(); + + return result; + }; + + /** @param { number } postId @return { string }*/ + deleteBoard = async (postId) => { + const result = await this.boardRepository.deleteBoard(); + + return result; + }; +} + +module.exports = BoardService; diff --git a/src/layers/services/comment.service.js b/src/layers/services/comment.service.js index ca06343..cc152b9 100644 --- a/src/layers/services/comment.service.js +++ b/src/layers/services/comment.service.js @@ -1,54 +1,53 @@ -const { - PostCommentDto, - GetCommentDto, - UpdateCommentDto, - DeleteCommentDto, - UserJoinDto, - ConflictException, -} = require('../../models/_.loader'); +const { PostCommentDto, NotFoundException, DeleteCommentDto } = require('../../models/_.loader'); + +const UserRepository = require('../repositories/user.repository'); +const MusicRepository = require('../repositories/music.repository'); const CommentRepository = require('../repositories/comment.repository'); class CommentService { + userRepository; + musicRepository; commentRepository; constructor() { + this.userRepository = new UserRepository(); + this.musicRepository = new MusicRepository(); this.commentRepository = new CommentRepository(); } - /** @param { PostCommentDto } postCommentDto @return { string }*/ + /** @param { PostCommentDto } postCommentDto */ postComment = async (postCommentDto) => { - const result = await this.commentRepository.postComments(postCommentDto); - - return result; - }; - - /** @param { GetCommentDto } getCommentDto @return { string }*/ - getComments = async (getCommentsDto) => { - const result = await this.commentRepository.getComments(getCommentDto); - - return result; + try { + const isExistsUser = this.userRepository.isExistsUserById(postCommentDto.userId); + if (!isExistsUser) throw new NotFoundException('존재 하지 않는 사용자 입니다.'); + + const isExistsMusic = this.musicRepository.isExistsMusicByMusicId( + postCommentDto.musicId, + ); + if (!isExistsMusic) throw new NotFoundException('존재 하지 않는 음악 입니다.'); + + return await this.commentRepository.createComment(postCommentDto); + } catch (err) { + throw err; + } }; - /** @param { UpdateCommentDto } getCommentDto @return { string }*/ - updateComments = async (updateComments) => { - const result = await this.commentRepository.updateComments(updateCommentDto); - - return result; + /** @param { DeleteCommentDto } deleteCommentDto */ + deleteComment = async (deleteCommentDto) => { + try { + const isExistsUser = this.userRepository.isExistsUserById(deleteCommentDto.userId); + if (!isExistsUser) throw new NotFoundException('존재 하지 않는 사용자 입니다.'); + + const isExistsMusic = this.commentRepository.isExistsCommentByCommentId( + deleteCommentDto.commentId, + ); + if (!isExistsMusic) throw new NotFoundException('존재 하지 않는 댓글 입니다.'); + + return await this.commentRepository.deleteComment(deleteCommentDto); + } catch (err) { + throw err; + } }; - - /** @param { DeleteCommentDto } getCommentDto @return { string }*/ - deleteComments = async (deleteComments) => { - const result = await this.commentRepository.deleteComments(deleteCommentDto); - - return result; - }; - - // /** @param { GetCommentDto } getCommentDto @return { string }*/ - // getOneComment = async (getOneComment) => { - // const result = await this.commentRepository.getOneComment(); - - // return result; - // }; } module.exports = CommentService; diff --git a/src/layers/services/music.service.js b/src/layers/services/music.service.js index 9719b5c..6060108 100644 --- a/src/layers/services/music.service.js +++ b/src/layers/services/music.service.js @@ -1,12 +1,21 @@ -const { PostMusicDto, GetMusicDto, ConflictException } = require('../../models/_.loader'); +const { + PostMusicDto, + GetMusicsDto, + ConflictException, + LikeMusicDto, + NotFoundException, +} = require('../../models/_.loader'); const MusicRepository = require('../repositories/music.repository'); +const MusicLikeRepository = require('../repositories/music.like.repository'); class MusicService { musicRepository; + musicLikeRepository; // postMusicDto; constructor() { this.musicRepository = new MusicRepository(); + this.musicLikeRepository = new MusicLikeRepository(); // this.postMusicDto = new PostMusicDto(); } @@ -17,18 +26,34 @@ class MusicService { return result; }; - /** @param { GetMusicDto } getMusicDto @return { string }*/ - getMusics = async (getMusics) => { - const result = await this.musicRepository.getMusics(); + /** @param { GetMusicsDto } getMusicDto @return { string }*/ + getMusics = async (getMusicDto) => { + const result = await this.musicRepository.getMusics(getMusicDto); return result; }; /** @param { number } musicId @return { string }*/ getOneMusic = async (musicId) => { - const result = await this.musicRepository.getOneMusic(musicId); + return await this.musicRepository.getOneMusic(musicId); + }; - return musicId; + /** @param { LikeMusicDto } likeMusicDto @returns { Promise<{ isLikeUp: boolean }> } */ + toggleLike = async (likeMusicDto) => { + try { + const isExists = await this.musicRepository.isExistsMusicByMusicId( + likeMusicDto.musicId, + ); + if (!isExists) throw new NotFoundException('존재 하지 않는 음악 입니다.'); + + const isLiked = await this.musicLikeRepository.isLikedMusicByLikeMusicDto(likeMusicDto); + + // 이미 좋아요가 있으면(isLiked === true) 좋아요를 취소 시킵니다. + if (isLiked) return await this.musicLikeRepository.decreaseMusicLike(likeMusicDto); + else return await this.musicLikeRepository.increaseMusicLike(likeMusicDto); + } catch (err) { + throw err; + } }; } diff --git a/src/layers/services/user.service.js b/src/layers/services/user.service.js index 3b240a1..5532438 100644 --- a/src/layers/services/user.service.js +++ b/src/layers/services/user.service.js @@ -1,5 +1,7 @@ -const { Sequelize } = require('sequelize'); -const { User } = require('../../sequelize/models'); +const UserRepository = require('../repositories/user.repository'); +const MusicRepository = require('../repositories/music.repository'); +const { JwtProvider, BcryptProvider } = require('../../modules/_.loader'); + const { UserDto, UserJoinDto, @@ -7,17 +9,18 @@ const { ConflictException, NotFoundException, ForbiddenException, + PaginationDto, } = require('../../models/_.loader'); -const { JwtProvider, BcryptProvider } = require('../../modules/_.loader'); -const UserRepository = require('../repositories/user.repository'); class UserService { userRepository; + musicRepository; bcryptProvider; jwtProvider; constructor() { this.userRepository = new UserRepository(); + this.musicRepository = new MusicRepository(); this.bcryptProvider = new BcryptProvider(); this.jwtProvider = new JwtProvider(); } @@ -78,6 +81,42 @@ class UserService { throw err; } }; + + /** @param { PaginationDto } pageDto */ + getMyUploadedMusics = async (pageDto) => { + try { + const isExists = await this.userRepository.isExistsUserById(pageDto.userId); + if (!isExists) throw new NotFoundException('존재 하지 않는 사용자입니다.'); + + return await this.musicRepository.getMyUploadedMusicsByUserId(pageDto); + } catch (err) { + throw err; + } + }; + + /** @param { PaginationDto } pageDto */ + getMyLikedMusics = async (pageDto) => { + try { + const isExists = await this.userRepository.isExistsUserById(pageDto.userId); + if (!isExists) throw new NotFoundException('존재 하지 않는 사용자입니다.'); + + return await this.musicRepository.getMyLikedMusicsByUserId(pageDto); + } catch (err) { + throw err; + } + }; + + /** @param { PaginationDto } pageDto */ + getMyLikedPosts = async (pageDto) => { + try { + const isExists = await this.userRepository.isExistsUserById(pageDto.userId); + if (!isExists) throw new NotFoundException('존재 하지 않는 사용자입니다.'); + + return { pageDto }; + } catch (err) { + throw err; + } + }; } module.exports = UserService; diff --git a/src/middlewares/guards/prevent.unlogin.guard.js b/src/middlewares/guards/prevent.unlogin.guard.js index 7f46e5f..e4fb869 100644 --- a/src/middlewares/guards/prevent.unlogin.guard.js +++ b/src/middlewares/guards/prevent.unlogin.guard.js @@ -26,7 +26,8 @@ const preventUnLoginGuard = (req, res, next) => { try { const payload = jwtProvider.verifyToken(jwtProvider.extract(bearer)); - req.body.username = payload.userId; + + req.body.userId = payload.userId; req.body.nickname = payload.nickname; const isExists = new UserPository().isExistsUserById(payload.userId); @@ -34,6 +35,7 @@ const preventUnLoginGuard = (req, res, next) => { return next(); } catch (err) { + console.log(err); return res.json('알 수 없는 에러'); } } diff --git a/src/middlewares/guards/token.guard.js b/src/middlewares/guards/token.guard.js index 935976c..5e0ba31 100644 --- a/src/middlewares/guards/token.guard.js +++ b/src/middlewares/guards/token.guard.js @@ -25,7 +25,7 @@ const tokenGuard = (req, res, next) => { const payload = jwtProvider.verifyToken(jwtProvider.extract(bearer)); - req.body.username = payload.userId; + req.body.userId = payload.userId; req.body.nickname = payload.nickname; return next(); diff --git a/src/models/_.loader.js b/src/models/_.loader.js index 21d5158..c2e0087 100644 --- a/src/models/_.loader.js +++ b/src/models/_.loader.js @@ -8,13 +8,21 @@ const UserDto = require('./dto/user/user.dto'); const UserJoinDto = require('./dto/user/user.join.dto'); const UserLoginDto = require('./dto/user/user.login.dto'); +const MusicDto = require('./dto/music/musics.dto'); +const LikeMusicDto = require('./dto/music/like.music.dto'); const GetMusicsDto = require('./dto/music/get.musics.dto'); const PostMusicDto = require('./dto/music/post.musics.dto'); +const OneMusicsDto = require('./dto/music/one.musics.dto'); -const GetCommentsDto = require('./dto/comment/get.comments.dto'); -const PostCommentsDto = require('./dto/comment/post.comments.dto'); -const DeleteCommentsDto = require('./dto/comment/delete.comments.dto'); -const UpdateCommentsDto = require('./dto/comment/update.comments.dto'); +const CommentDto = require('./dto/comment/comment.dto'); +const PostCommentDto = require('./dto/comment/post.comment.dto'); +const DeleteCommentDto = require('./dto/comment/delete.comment.dto'); + +const BoardDto = require('./dto/board/board.dto'); +const BoardPostDto = require('./dto/board/board.post.dto'); +const BoardGetDto = require('./dto/board/board.get.dto'); + +const PaginationDto = require('./dto/pagination.dto'); const { CustomException, @@ -38,13 +46,20 @@ module.exports = { UserJoinDto, UserLoginDto, - PostCommentsDto, - GetCommentsDto, - UpdateCommentsDto, - DeleteCommentsDto, + PaginationDto, + MusicDto, GetMusicsDto, PostMusicDto, + OneMusicsDto, + LikeMusicDto, + + CommentDto, + PostCommentDto, + DeleteCommentDto, + BoardDto, + BoardPostDto, + BoardGetDto, CustomException, BadRequestException, diff --git a/src/models/dto/board/board.dto.js b/src/models/dto/board/board.dto.js new file mode 100644 index 0000000..e7c5b44 --- /dev/null +++ b/src/models/dto/board/board.dto.js @@ -0,0 +1,30 @@ +const joi = require('joi'); +// const { SequelizeScopeError } = require('sequelize/types'); +const BaseDto = require('../base/base.dto'); + +class BoardDto extends BaseDto { + postId; + userId; + title; + content; + + constructor({ postId, userId, title, content }) { + super(); + + this.postId = postId; + this.userId = userId; + this.title = title; + this.content = content; + } + + getJoiInstance() { + return { + postId: joi.number().required(), + musicId: joi.number().required(), + title: joi.string().required(), + content: joi.string().required(), + }; + } +} + +module.exports = BoardDto; diff --git a/src/models/dto/board/board.get.dto.js b/src/models/dto/board/board.get.dto.js new file mode 100644 index 0000000..32117e7 --- /dev/null +++ b/src/models/dto/board/board.get.dto.js @@ -0,0 +1,29 @@ +const joi = require('joi'); +// const { SequelizeScopeError } = require('sequelize/types'); +const BaseDto = require('../base/base.dto'); + +class BoardGetDto extends BaseDto { + /** @type { number } */ + page; + + /** @type { number } */ + pageCount; + + /** @param { { page: number, pageCount: number | undefined } } IUserDto */ + constructor({ page = 1, pageCount = 3 }) { + // 프론트랑 상의 + super(); + + this.page = page; + this.pageCount = pageCount; + } + + getJoiInstance() { + return { + page: joi.number().required(), + pageCount: joi.number().required(), + }; + } +} + +module.exports = BoardGetDto; diff --git a/src/models/dto/comment/post.comments.dto.js b/src/models/dto/board/board.post.dto.js similarity index 56% rename from src/models/dto/comment/post.comments.dto.js rename to src/models/dto/board/board.post.dto.js index eb7e8ba..845e3c0 100644 --- a/src/models/dto/comment/post.comments.dto.js +++ b/src/models/dto/board/board.post.dto.js @@ -1,25 +1,27 @@ const joi = require('joi'); +// const { SequelizeScopeError } = require('sequelize/types'); const BaseDto = require('../base/base.dto'); -class PostCommentsDto extends BaseDto { +class BoardPostDto extends BaseDto { userId; - commentId; + title; content; - constructor({ userId, commentId, content }) { + constructor({ userId, title, content }) { super(); + this.userId = userId; - this.commentId = commentId; + this.title = title; this.content = content; } getJoiInstance() { return { userId: joi.number().required(), - commentId: joi.number().required(), + title: joi.string().required(), content: joi.string().required(), }; } } -module.exports = PostCommentsDto; +module.exports = BoardPostDto; diff --git a/src/models/dto/comment/comment.dto.js b/src/models/dto/comment/comment.dto.js new file mode 100644 index 0000000..b1de6c7 --- /dev/null +++ b/src/models/dto/comment/comment.dto.js @@ -0,0 +1,31 @@ +const joi = require('joi'); +const BaseDto = require('../base/base.dto'); + +class CommentDto extends BaseDto { + /** @type { number } */ + userId; + + /** @type { number } */ + musicId; + + /** @type { string } */ + content; + + /** @param { { userId: number, musicId: number, content: string } } IUserDto */ + constructor({ userId, musicId, content }) { + super(); + + this.userId = userId; + this.musicId = musicId; + this.content = content; + } + + getJoiInstance() { + return { + musicId: joi.number().required(), + content: joi.string().required(), + }; + } +} + +module.exports = CommentDto; diff --git a/src/models/dto/comment/delete.comments.dto.js b/src/models/dto/comment/delete.comment.dto.js similarity index 63% rename from src/models/dto/comment/delete.comments.dto.js rename to src/models/dto/comment/delete.comment.dto.js index 69991b6..235c9ec 100644 --- a/src/models/dto/comment/delete.comments.dto.js +++ b/src/models/dto/comment/delete.comment.dto.js @@ -1,12 +1,17 @@ const joi = require('joi'); const BaseDto = require('../base/base.dto'); -class DeleteCommentsDto extends BaseDto { +class DeleteCommentDto extends BaseDto { + /** @type { number } */ + userId; + + /** @type { number } */ commentId; - content; + /** @param { { userId: number, commentId: number } } IUserDto */ constructor({ userId, commentId }) { super(); + this.userId = userId; this.commentId = commentId; } @@ -19,4 +24,4 @@ class DeleteCommentsDto extends BaseDto { } } -module.exports = DeleteCommentsDto; +module.exports = DeleteCommentDto; diff --git a/src/models/dto/comment/get.comments.dto.js b/src/models/dto/comment/get.comments.dto.js deleted file mode 100644 index 5a450c0..0000000 --- a/src/models/dto/comment/get.comments.dto.js +++ /dev/null @@ -1,17 +0,0 @@ -const joi = require('joi'); -const BaseDto = require('../base/base.dto'); - -class GetCommentsDto extends BaseDto { - page; - pageCount; - - constructor({}) { - super(); - this.page = page; - this.pageCount = pageCount; - } - - // Dto는 핊요한 요소들을 꺼내오는 자원 -} - -module.exports = GetCommentsDto; diff --git a/src/models/dto/comment/post.comment.dto.js b/src/models/dto/comment/post.comment.dto.js new file mode 100644 index 0000000..2474c66 --- /dev/null +++ b/src/models/dto/comment/post.comment.dto.js @@ -0,0 +1,32 @@ +const joi = require('joi'); +const BaseDto = require('../base/base.dto'); + +class PostCommentDto extends BaseDto { + /** @type { number } */ + userId; + + /** @type { number } */ + musicId; + + /** @type { string } */ + content; + + /** @param { { userId: number, musicId: number, content: string } } IUserDto */ + constructor({ userId, musicId, content }) { + super(); + + this.userId = userId; + this.musicId = musicId; + this.content = content; + } + + getJoiInstance() { + return { + userId: joi.number().required(), + musicId: joi.number().required(), + content: joi.string().required(), + }; + } +} + +module.exports = PostCommentDto; diff --git a/src/models/dto/comment/update.comments.dto.js b/src/models/dto/comment/update.comments.dto.js deleted file mode 100644 index f638457..0000000 --- a/src/models/dto/comment/update.comments.dto.js +++ /dev/null @@ -1,25 +0,0 @@ -const joi = require('joi'); -const BaseDto = require('../base/base.dto'); - -class UpdateCommentsDto extends BaseDto { - userId; - commentId; - content; - - constructor({ userId, commentId, content }) { - super(); - this.userId = userId; - this.commentId = commentId; - this.content = content; - } - - getJoiInstance() { - return { - userId: joi.number().required(), - commentId: joi.number().required(), - content: joi.string().required(), - }; - } -} - -module.exports = UpdateCommentsDto; diff --git a/src/models/dto/music/get.musics.dto.js b/src/models/dto/music/get.musics.dto.js index af040d6..b42dbed 100644 --- a/src/models/dto/music/get.musics.dto.js +++ b/src/models/dto/music/get.musics.dto.js @@ -2,16 +2,26 @@ const joi = require('joi'); const BaseDto = require('../base/base.dto'); class GetMusicsDto extends BaseDto { + /** @type { number } */ page; + + /** @type { number } */ pageCount; - constructor({}) { + /** @param { { page: number, pageCount: number | undefined } } IUserDto */ + constructor({ page = 1, pageCount = 3 }) { super(); + this.page = page; this.pageCount = pageCount; } - // Dto는 핊요한 요소들을 꺼내오는 자원 + getJoiInstance() { + return { + page: joi.number().required(), + pageCount: joi.number().required(), + }; + } } module.exports = GetMusicsDto; diff --git a/src/models/dto/music/like.music.dto.js b/src/models/dto/music/like.music.dto.js new file mode 100644 index 0000000..d34770e --- /dev/null +++ b/src/models/dto/music/like.music.dto.js @@ -0,0 +1,23 @@ +const joi = require('joi'); +const BaseDto = require('../base/base.dto'); + +class LikeMusicDto extends BaseDto { + userId; + musicId; + + constructor({ userId, musicId }) { + super(); + + this.userId = userId; + this.musicId = musicId; + } + + getJoiInstance() { + return { + userId: joi.number().required(), + musicId: joi.number().required(), + }; + } +} + +module.exports = LikeMusicDto; diff --git a/src/models/dto/music/musics.dto.js b/src/models/dto/music/musics.dto.js new file mode 100644 index 0000000..680f2f0 --- /dev/null +++ b/src/models/dto/music/musics.dto.js @@ -0,0 +1,35 @@ +const joi = require('joi'); +const BaseDto = require('../base/base.dto'); + +class MusicDto extends BaseDto { + userId; + musicId; + title; + artist; + album; + musicUrl; + + constructor({ userId, musicId, title, artist, album, musicUrl }) { + super(); + + this.userId = userId; + this.musicId = musicId; + this.title = title; + this.artist = artist; + this.album = album; + this.musicUrl = musicUrl; + } + + getJoiInstance() { + return { + userId: joi.number().required(), + musicId: joi.number().required(), + title: joi.string().required(), + artist: joi.string().required(), + album: joi.string().required(), + musicUrl: joi.string().required(), + }; + } +} + +module.exports = MusicDto; diff --git a/src/models/dto/music/one.musics.dto.js b/src/models/dto/music/one.musics.dto.js index 32c1c3b..9a6321a 100644 --- a/src/models/dto/music/one.musics.dto.js +++ b/src/models/dto/music/one.musics.dto.js @@ -22,6 +22,8 @@ class OneMusicsDto extends BaseDto { // Dto는 핊요한 요소들을 꺼내오는 자원 getJoiInstance() { return { + musicId: joi.number().required(), + userId: joi.number().required(), title: joi.string().required(), artist: joi.string().required(), album: joi.string().required(), @@ -30,4 +32,4 @@ class OneMusicsDto extends BaseDto { } } -module.exports = GetMusicsDto; +module.exports = OneMusicsDto; diff --git a/src/models/dto/music/post.musics.dto.js b/src/models/dto/music/post.musics.dto.js index b3e0c68..adad880 100644 --- a/src/models/dto/music/post.musics.dto.js +++ b/src/models/dto/music/post.musics.dto.js @@ -1,7 +1,7 @@ const joi = require('joi'); const BaseDto = require('../base/base.dto'); -class PostMusicsDto extends BaseDto { +class MusicsDto extends BaseDto { userId; title; artist; @@ -28,4 +28,4 @@ class PostMusicsDto extends BaseDto { } } -module.exports = PostMusicsDto; +module.exports = MusicsDto; diff --git a/src/models/dto/pagination.dto.js b/src/models/dto/pagination.dto.js new file mode 100644 index 0000000..d3cef55 --- /dev/null +++ b/src/models/dto/pagination.dto.js @@ -0,0 +1,32 @@ +const joi = require('joi'); +const BaseDto = require('./base/base.dto'); + +class PaginationDto extends BaseDto { + /** @type { number } */ + userId; + + /** @type { number } */ + page; + + /** @type { number } */ + pageCount; + + /** @param { { userId: number, page: number, pageCount: number | undefined } } IUserDto */ + constructor({ userId, page = 1, pageCount = 3 }) { + super(); + + this.userId = userId; + this.page = page; + this.pageCount = pageCount; + } + + getJoiInstance() { + return { + userId: joi.number().required(), + page: joi.number().required(), + pageCount: joi.number().required(), + }; + } +} + +module.exports = PaginationDto; diff --git a/src/models/dto/user/get.profile.pagination.dto.js b/src/models/dto/user/get.profile.pagination.dto.js new file mode 100644 index 0000000..91e5928 --- /dev/null +++ b/src/models/dto/user/get.profile.pagination.dto.js @@ -0,0 +1,32 @@ +const joi = require('joi'); +const BaseDto = require('../base/base.dto'); + +class UserDto extends BaseDto { + /** @type { number } */ + userId; + + /** @type { number } */ + page; + + /** @type { number } */ + pageCount; + + /** @param { { userId: number, page: number, pageCount: number | undefined } } IUserDto */ + constructor({ userId, page = 1, pageCount = 3 }) { + super(); + + this.userId = userId; + this.page = page; + this.pageCount = pageCount; + } + + getJoiInstance() { + return { + userId: joi.number().required(), + page: joi.number().required(), + pageCount: joi.number().required(), + }; + } +} + +module.exports = UserDto; diff --git a/src/sequelize/migrations/20220813143102-create-user.js b/src/sequelize/migrations/20220813143102-create-user.js index 1cb0d9e..4bb9422 100644 --- a/src/sequelize/migrations/20220813143102-create-user.js +++ b/src/sequelize/migrations/20220813143102-create-user.js @@ -21,16 +21,6 @@ module.exports = { allowNull: false, type: Sequelize.STRING, }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.NOW, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.NOW, - }, }); }, async down(queryInterface, Sequelize) { diff --git a/src/sequelize/migrations/20220813143159-create-comment.js b/src/sequelize/migrations/20220813143159-create-comment.js index 2afab09..578c70e 100644 --- a/src/sequelize/migrations/20220813143159-create-comment.js +++ b/src/sequelize/migrations/20220813143159-create-comment.js @@ -16,14 +16,6 @@ module.exports = { allowNull: false, type: Sequelize.STRING, }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - }, }); }, async down(queryInterface, Sequelize) { diff --git a/src/sequelize/migrations/20220813143451-create-music.js b/src/sequelize/migrations/20220813143451-create-music.js index 89a268b..13d345c 100644 --- a/src/sequelize/migrations/20220813143451-create-music.js +++ b/src/sequelize/migrations/20220813143451-create-music.js @@ -28,16 +28,16 @@ module.exports = { allowNull: false, type: Sequelize.STRING, }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.NOW, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.NOW, - }, + // createdAt: { + // allowNull: false, + // type: Sequelize.DATE, + // defaultValue: Sequelize.NOW, + // }, + // updatedAt: { + // allowNull: false, + // type: Sequelize.DATE, + // defaultValue: Sequelize.NOW, + // }, }); }, async down(queryInterface, Sequelize) { diff --git a/src/sequelize/migrations/20220817032207-create-board.js b/src/sequelize/migrations/20220817032207-create-board.js new file mode 100644 index 0000000..54b461a --- /dev/null +++ b/src/sequelize/migrations/20220817032207-create-board.js @@ -0,0 +1,31 @@ +'use strict'; +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Boards', { + postId: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + + userId: { + allowNull: false, + primaryKey: true, + type: Sequelize.INTEGER, + }, + + title: { + allowNull: false, + type: Sequelize.STRING, + }, + content: { + allowNull: false, + type: Sequelize.STRING, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Boards'); + }, +}; diff --git a/src/sequelize/migrations/20220817043508-create-music-like.js b/src/sequelize/migrations/20220817043508-create-music-like.js new file mode 100644 index 0000000..b6aa15b --- /dev/null +++ b/src/sequelize/migrations/20220817043508-create-music-like.js @@ -0,0 +1,19 @@ +'use strict'; +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('MusicLikes', { + musicId: { + allowNull: false, + primaryKey: true, + type: Sequelize.INTEGER, + }, + userId: { + allowNull: false, + type: Sequelize.INTEGER, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('MusicLikes'); + }, +}; diff --git a/src/sequelize/models/board.js b/src/sequelize/models/board.js new file mode 100644 index 0000000..796620f --- /dev/null +++ b/src/sequelize/models/board.js @@ -0,0 +1,54 @@ +'use strict'; +const { Model } = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class Board extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + Board.init( + { + postId: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + + userId: { + allowNull: false, + primaryKey: true, + type: DataTypes.INTEGER, + }, + + title: { + allowNull: false, + type: DataTypes.STRING, + }, + content: { + allowNull: false, + type: DataTypes.STRING, + }, + }, + { + sequelize, + timestamps: false, + modelName: 'Board', + }, + ); + Board.associate = function (models) { + Board.belongsTo(models.User, { + foreignKey: 'userId', + targetKey: 'userId', + onUpdate: 'cascade', + onDelete: 'cascade', + }); + }; + + return Board; +}; diff --git a/src/sequelize/models/comment.js b/src/sequelize/models/comment.js index 50b6f1d..436b6a6 100644 --- a/src/sequelize/models/comment.js +++ b/src/sequelize/models/comment.js @@ -14,16 +14,23 @@ module.exports = (sequelize, DataTypes) => { Comment.init( { commentId: { - type: DataTypes.INTEGER, + allowNull: false, + autoIncrement: true, primaryKey: true, + type: DataTypes.INTEGER, + }, + userId: { + allowNull: false, + type: DataTypes.INTEGER, + }, + content: { + allowNull: false, + type: DataTypes.STRING, }, - userId: DataTypes.INTEGER, - content: DataTypes.STRING, - createdAt: DataTypes.DATE, - updatedAt: DataTypes.DATE, }, { sequelize, + timestamps: false, modelName: 'Comment', }, ); diff --git a/src/sequelize/models/music.js b/src/sequelize/models/music.js index 15056fc..1911a0f 100644 --- a/src/sequelize/models/music.js +++ b/src/sequelize/models/music.js @@ -39,17 +39,10 @@ module.exports = (sequelize, DataTypes) => { allowNull: false, type: DataTypes.STRING, }, - createdAt: { - allowNull: false, - type: DataTypes.DATE, - }, - updatedAt: { - allowNull: false, - type: DataTypes.DATE, - }, }, { sequelize, + timestamps: false, modelName: 'Music', }, ); diff --git a/src/sequelize/models/musiccomment.js b/src/sequelize/models/musiccomment.js index 045b5b2..1bde2b2 100644 --- a/src/sequelize/models/musiccomment.js +++ b/src/sequelize/models/musiccomment.js @@ -23,6 +23,7 @@ module.exports = (sequelize, DataTypes) => { }, { sequelize, + timestamps: false, modelName: 'MusicComment', }, ); diff --git a/src/sequelize/models/musiclike.js b/src/sequelize/models/musiclike.js new file mode 100644 index 0000000..fc04f03 --- /dev/null +++ b/src/sequelize/models/musiclike.js @@ -0,0 +1,47 @@ +'use strict'; +const { Model } = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class MusicLike extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + MusicLike.init( + { + musicId: { + allowNull: false, + primaryKey: true, + type: DataTypes.INTEGER, + }, + userId: { + allowNull: false, + type: DataTypes.INTEGER, + }, + }, + { + sequelize, + timestamps: false, + modelName: 'MusicLike', + }, + ); + MusicLike.associate = function (models) { + MusicLike.belongsTo(models.Music, { + foreignKey: 'musicId', // MusicLike.musicId + targetKey: 'musicId', // User.musicId + onUpdate: 'cascade', + onDelete: 'cascade', + }); + MusicLike.belongsTo(models.User, { + foreignKey: 'userId', // MusicLike.userId + targetKey: 'userId', // User.userId + onUpdate: 'cascade', + onDelete: 'cascade', + }); + }; + return MusicLike; +}; diff --git a/src/sequelize/models/user.js b/src/sequelize/models/user.js index 11bd824..fa4a93b 100644 --- a/src/sequelize/models/user.js +++ b/src/sequelize/models/user.js @@ -33,11 +33,10 @@ module.exports = (sequelize, DataTypes) => { }, nickname: DataTypes.STRING, password: DataTypes.STRING, - createdAt: DataTypes.DATE, - updatedAt: DataTypes.DATE, }, { sequelize, + timestamps: false, modelName: 'User', }, );