diff --git a/src/DTO/adminDTO.ts b/src/DTO/adminDTO.ts index 3e78a34..94e5428 100644 --- a/src/DTO/adminDTO.ts +++ b/src/DTO/adminDTO.ts @@ -1,31 +1,18 @@ namespace adminDTO { - export interface IAdmin { - title: String; - registerStartDT: Date; - registerEndDT: Date; - challengeStartDT: Date; - challengeEndDT: Date; - generation: Number; - limitNum: Number; - img: string; - createdDT: Date; - applyNum: Number; - } - - interface Admin { + export interface adminResDetailDTO { registerStartDT: Date; registerEndDT: Date; challengeStartDT: Date; challengeEndDT: Date; generation: number; - createdDT: Date; + createdAT?: Date; applyNum: number; - participants: number; - postNum: number; - img: string; + participants?: number; + postNum?: number; + img?: string; } export interface adminResDTO { - offsetAdmin: Admin[]; + offsetAdmin: adminResDetailDTO[]; totalAdminNum?: number; } diff --git a/src/DTO/authDTO.ts b/src/DTO/authDTO.ts index 39a677a..d9fdfb5 100644 --- a/src/DTO/authDTO.ts +++ b/src/DTO/authDTO.ts @@ -28,6 +28,24 @@ namespace authDTO { email: string; password: string; } + + export interface emailReqDTO { + email: string; + } + + export interface codeReqDTO { + email: string; + emailCode: string; + } + + export interface codeResDTO { + isOkay: boolean; + } + + export interface passwordReqDTO { + email: string; + password: string; + } } export default authDTO; diff --git a/src/DTO/concertDTO.ts b/src/DTO/concertDTO.ts index cabfbd8..2107b4a 100644 --- a/src/DTO/concertDTO.ts +++ b/src/DTO/concertDTO.ts @@ -7,7 +7,7 @@ namespace concertDTO { updatedAt: Date; userID: number; nickname: string; - authorNickname: string; + authorNickname?: string; title: string; videoLink: string; img: string; @@ -17,8 +17,8 @@ namespace concertDTO { hashtag: string[]; isDeleted: Boolean; isNotice: Boolean; - likeNum: number; - scrapNum: number; + likeNum?: number; + scrapNum?: number; commentNum: number; comment: commentDTO.IComment[]; isLike?: boolean; @@ -31,7 +31,7 @@ namespace concertDTO { updatedAt: Date; userID: number; nickname: string; - authorNickname: string; + authorNickname?: string; title: string; videoLink: string; imgThumbnail: string; @@ -40,8 +40,8 @@ namespace concertDTO { hashtag: string[]; isDeleted: Boolean; isNotice: Boolean; - likeNum: number; - scrapNum: number; + likeNum?: number; + scrapNum?: number; commentNum: number; comment: commentDTO.IComment[]; isLike?: boolean; @@ -52,5 +52,10 @@ namespace concertDTO { concerts: getConcertResDTO[]; totalConcertNum: number; } + + export interface noticeAllResDTO { + notices: getConcertResDTO[]; + totalNoticeNum: number; + } } export default concertDTO; diff --git a/src/controller/admin.ts b/src/controller/admin.ts index ba375b6..4fbb54f 100644 --- a/src/controller/admin.ts +++ b/src/controller/admin.ts @@ -1,176 +1,198 @@ -import { Router, Request, Response } from "express"; +import { Request, Response } from "express"; // libraries -import { returnCode } from "../library/returnCode"; -import { response, dataResponse } from "../library/response"; -//middlewares -import auth from "../middleware/auth"; -const upload = require("../modules/upload"); -// interfaces -// import { IAdmin } from "../interfaces/IAdmin"; -// import { IConcert } from "../interfaces/IConcert"; -//services -import { - postAdminList, - postAdminChallenge, - postAdminConcert, - postAdminNotice, - getAdminRegist, -} from "../service/adminService"; - +import { response, returnCode } from "../library"; +// services +import { adminService } from "../service"; // DTO -import { - adminResDTO, - adminRegistReqDTO, - adminWriteReqDTO, - adminRegistResDTO, -} from "../DTO/adminDTO"; - -const router = Router(); +import { adminDTO, commentDTO } from "../DTO"; /** * @관리자_페이지_조회 - * @route Get admin + * @route Get /admin?offset=&limit= * @access private */ -router.get("/", auth, async (req: Request, res: Response) => { +const getAdminListController = async (req: Request, res: Response) => { try { - const data: adminResDTO | -1 | -2 = await postAdminList( - req.body.userID.id, - req.query.offset, - req.query.limit - ); + const resData: adminDTO.adminResDTO | -1 | -2 = + await adminService.getAdminList( + Number(req.body.userID.id), + Number(req.query.offset), + Number(req.query.limit) + ); // limit 없을 때 - if (data === -1) { - response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); + if (resData === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); } // 유저 id가 관리자가 아님 - if (data === -2) { - response(res, returnCode.NOT_FOUND, "관리자 아이디가 아닙니다"); + else if (resData === -2) { + response.basicResponse( + res, + returnCode.NOT_FOUND, + "관리자 아이디가 아닙니다" + ); } // 관리자 챌린지 조회 성공 else { - dataResponse(res, returnCode.OK, "관리자 페이지 조회 성공", data); + response.dataResponse( + res, + returnCode.OK, + "관리자 페이지 조회 성공", + resData + ); } } catch (err) { console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -}); +}; /** * @관리자_챌린지_등록 * @route Post admin/challenge * @access private */ -router.post( - "/challenge", - upload.fields([{ name: "img", maxCount: 1 }]), - auth, - - async (req: Request, res: Response) => { - try { - const url = { - img: (req as any).files.img - ? (req as any).files.img[0].location - : "https://o2-server.s3.ap-northeast-2.amazonaws.com/origin/default_img_100%403x.jpg", - }; - const reqData: adminRegistReqDTO = req.body; - const data = await postAdminChallenge(req.body.userID.id, reqData, url); - - // 요청 바디가 부족할 경우 - if (data === -1) { - response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); - } - // 유저 id가 관리자가 아님 - else if (data === -2) { - response(res, returnCode.NOT_FOUND, "관리자 아이디가 아닙니다"); - } - // 챌린지 기간이 신청 기간보다 빠른 경우 or 기간 입력이 잘못된 경우 - else if (data === -3) { - response(res, returnCode.BAD_REQUEST, "잘못된 기간을 입력하셨습니다"); - } - - // 관리자 챌린지 등록 성공 - else { - response(res, returnCode.OK, "관리자 챌린지 등록 성공"); - } - } catch (err) { - console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + +const postAdminChallengeController = async (req: Request, res: Response) => { + try { + const url = { + img: (req as any).files.img + ? (req as any).files.img[0].location + : "https://o2-server.s3.ap-northeast-2.amazonaws.com/origin/default_img_100%403x.jpg", + }; + const reqData: adminDTO.adminRegistReqDTO = req.body; + const data = await adminService.postAdminChallenge( + req.body.userID.id, + reqData, + url + ); + + // 요청 바디가 부족할 경우 + if (data === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + // 유저 id가 관리자가 아님 + else if (data === -2) { + response.basicResponse( + res, + returnCode.NOT_FOUND, + "관리자 아이디가 아닙니다" + ); + } + // 챌린지 기간이 신청 기간보다 빠른 경우 or 기간 입력이 잘못된 경우 + else if (data === -3) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "잘못된 기간을 입력하셨습니다" + ); } + + // 관리자 챌린지 등록 성공 + else { + response.basicResponse(res, returnCode.OK, "관리자 챌린지 등록 성공"); + } + } catch (err) { + console.error(err.message); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -); +}; /** * @관리자_챌린지_신청페이지 * @route Get admin/regist - * @access private + * @access public */ -router.get("/regist", async (req: Request, res: Response) => { +const getAdminRegistController = async (req: Request, res: Response) => { try { - const data: adminRegistResDTO | -1 | -2 = await getAdminRegist(); + const resData: adminDTO.adminRegistResDTO | -1 | -2 = + await adminService.getAdminRegist(); // 현재 진행중인 기수가 없음 - if (data === -1) { - response(res, returnCode.BAD_REQUEST, "현재 신청 기간인 기수가 없습니다"); + if (resData === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "현재 신청 기간인 기수가 없습니다" + ); } // 챌린지 신청 페이지 조회 성공 else { - dataResponse(res, returnCode.OK, "신청 페이지 조회 성공", data); + response.dataResponse( + res, + returnCode.OK, + "신청 페이지 조회 성공", + resData + ); } } catch (err) { console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -}); +}; /** - * @관리자_오픈콘서트_등록 + * @관리자_콘서트_등록 * @route Post admin/concert * @access private */ -router.post( - "/concert", - upload.fields([ - { name: "videoLink", maxCount: 1 }, - { name: "imgThumbnail", maxCount: 1 }, - ]), - auth, - async (req: Request, res: Response) => { - try { - const url = { - videoLink: (req as any).files.videoLink - ? (req as any).files.videoLink[0].location - : "", - imgThumbnail: (req as any).files.imgThumbnail - ? (req as any).files.imgThumbnail[0].location - : "https://o2-server.s3.ap-northeast-2.amazonaws.com/origin/default_img_100%403x.jpg", - }; - const reqData: adminWriteReqDTO = req.body; - const data = await postAdminConcert(req.body.userID.id, reqData, url); - - // 요청 바디가 부족할 경우 - if (data === -1) { - response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); - } - // 유저 id가 관리자가 아님 - else if (data === -2) { - response(res, returnCode.BAD_REQUEST, "관리자 아이디가 아닙니다"); - } - - // 관리자 챌린지 등록 성공 - else { - response(res, returnCode.OK, "관리자 오투콘서트 등록 성공"); - } - } catch (err) { - console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); +const postAdminConcertController = async (req: Request, res: Response) => { + try { + const url = { + videoLink: (req as any).files.videoLink + ? (req as any).files.videoLink[0].location + : "", + imgThumbnail: (req as any).files.imgThumbnail + ? (req as any).files.imgThumbnail[0].location + : "https://o2-server.s3.ap-northeast-2.amazonaws.com/origin/default_img_100%403x.jpg", + }; + const reqData: adminDTO.adminWriteReqDTO = req.body; + const resData = await adminService.postAdminConcert( + Number(req.body.userID.id), + reqData, + url + ); + + // 요청 바디가 부족할 경우 + if (resData === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + // 유저 id가 관리자가 아님 + else if (resData === -2) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "관리자 아이디가 아닙니다" + ); + } + + // 관리자 챌린지 등록 성공 + else { + response.basicResponse( + res, + returnCode.CREATED, + "관리자 오투콘서트 등록 성공" + ); } + } catch (err) { + console.error(err.message); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -); +}; /** * @관리자_공지사항_등록 @@ -178,50 +200,60 @@ router.post( * @access private */ -router.post( - "/notice", - upload.fields([ - { name: "videoLink", maxCount: 1 }, - { name: "imgThumbnail", maxCount: 1 }, - ]), - auth, - async (req: Request, res: Response) => { - try { - const url = { - videoLink: (req as any).files.videoLink - ? (req as any).files.videoLink[0].location - : "", - imgThumbnail: (req as any).files.imgThumbnail - ? (req as any).files.imgThumbnail[0].location - : "https://o2-server.s3.ap-northeast-2.amazonaws.com/origin/default_img_100%403x.jpg", - }; - - const reqData: adminWriteReqDTO = req.body; - const data = await postAdminNotice( - req.body.userID.id, - reqData, - // JSON.parse(req.body.json), - url +const postAdminNoticeController = async (req: Request, res: Response) => { + try { + const url = { + videoLink: (req as any).files.videoLink + ? (req as any).files.videoLink[0].location + : "", + imgThumbnail: (req as any).files.imgThumbnail + ? (req as any).files.imgThumbnail[0].location + : "https://o2-server.s3.ap-northeast-2.amazonaws.com/origin/default_img_100%403x.jpg", + }; + + const reqData: adminDTO.adminWriteReqDTO = req.body; + const data = await adminService.postAdminNotice( + Number(req.body.userID.id), + reqData, + url + ); + + // 요청 바디가 부족할 경우 + if (data === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + // 유저 id가 관리자가 아님 + else if (data === -2) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "관리자 아이디가 아닙니다" ); + } - // 요청 바디가 부족할 경우 - if (data === -1) { - response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); - } - // 유저 id가 관리자가 아님 - else if (data === -2) { - response(res, returnCode.BAD_REQUEST, "관리자 아이디가 아닙니다"); - } - - // 관리자 공지사항 등록 성공 - else { - response(res, returnCode.OK, "관리자 공지사항 등록 성공"); - } - } catch (err) { - console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + // 관리자 공지사항 등록 성공 + else { + response.basicResponse( + res, + returnCode.CREATED, + "관리자 공지사항 등록 성공" + ); } + } catch (err) { + console.error(err.message); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -); +}; -module.exports = router; +const adminController = { + getAdminListController, + getAdminRegistController, + postAdminConcertController, + postAdminNoticeController, + postAdminChallengeController, +}; +export default adminController; diff --git a/src/controller/auth.ts b/src/controller/auth.ts index 8c18463..16ea9a4 100644 --- a/src/controller/auth.ts +++ b/src/controller/auth.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { check, validationResult } from "express-validator"; +import { validationResult } from "express-validator"; // libraries import { response, returnCode } from "../library"; // services @@ -14,9 +14,14 @@ import { authDTO } from "../DTO"; */ const signupController = async (req: Request, res: Response) => { + // 이메일 형식이 잘못된 경우 const errors = validationResult(req); if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); + return response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); } try { @@ -57,9 +62,14 @@ const signupController = async (req: Request, res: Response) => { */ const signinController = async (req: Request, res: Response) => { + // 이메일 형식이 잘못된 경우 const errors = validationResult(req); if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); + return response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); } try { @@ -107,113 +117,167 @@ const signinController = async (req: Request, res: Response) => { } }; -// /** -// * @이메일_인증번호_전송 -// * @route Post auth/email -// * @access Public -// */ - -// router.post( -// "/email", -// [check("email", "Please include a valid email").isEmail()], -// async (req: Request, res: Response) => { -// const errors = validationResult(req); -// if (!errors.isEmpty()) { -// return res.status(400).json({ errors: errors.array() }); -// } - -// try { -// const data = await postEmail(req.body); - -// // 요청 바디가 부족할 경우 -// if (data == -1) { -// response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); -// } -// // email이 DB에 없을 경우 -// if (data == -2) { -// response(res, returnCode.BAD_REQUEST, "아이디가 존재하지 않습니다"); -// } -// // 성공 -// response(res, returnCode.OK, "인증번호 전송 성공"); -// } catch (err) { -// console.error(err.message); -// response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); -// } -// } -// ); - -// /** -// * @인증번호_확인 -// * @route Post auth/code -// * @access Public -// */ - -// router.post("/code", async (req: Request, res: Response) => { -// try { -// const data = await postCode(req.body); - -// // 요청 바디가 부족할 경우 -// if (data === -1) { -// response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); -// } -// // email이 DB에 없을 경우 -// if (data === -2) { -// response(res, returnCode.BAD_REQUEST, "아이디가 존재하지 않습니다"); -// } -// // 인증번호가 올바르지 않은 경우 -// if (data === -3) { -// dataResponse(res, returnCode.OK, "인증번호 인증 실패", { isOkay: false }); -// } -// // 인증번호 인증 성공 -// dataResponse(res, returnCode.OK, "인증번호 인증 성공", { isOkay: true }); -// } catch (err) { -// console.error(err.message); -// response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); -// } -// }); - -// /** -// * @비밀번호_변경 -// * @route Patch auth/pw -// * @desc Authenticate user & get token -// * @access Public -// */ - -// router.patch( -// "/pw", -// [ -// check("email", "Please include a valid email").isEmail(), -// check( -// "password", -// "Please enter a password with 6 or more characters" -// ).isLength({ min: 6 }), -// ], -// async (req: Request, res: Response) => { -// const errors = validationResult(req); -// if (!errors.isEmpty()) { -// return res.status(400).json({ errors: errors.array() }); -// } - -// try { -// const reqData: pwReqDTO = req.body; -// const data = await patchPassword(reqData); - -// // 요청 바디가 부족할 경우 -// if (data === -1) { -// response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); -// } -// // email이 DB에 없을 경우 -// if (data === -2) { -// response(res, returnCode.BAD_REQUEST, "아이디가 존재하지 않습니다"); -// } -// // 성공 -// response(res, returnCode.OK, "비밀번호 변경 성공"); -// } catch (err) { -// console.error(err.message); -// response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); -// } -// } -// ); +/** + * @이메일_인증번호_전송 + * @route Post auth/email + * @desc post email code for certification + * @access Public + */ + +const postEmailController = async (req: Request, res: Response) => { + // 이메일 형식이 잘못된 경우 + const errors = validationResult(req); + if (!errors.isEmpty()) { + return response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + + try { + const reqData: authDTO.emailReqDTO = req.body; + const resData: undefined | -1 | -2 | -3 = await authService.postEmail( + reqData + ); + + // 요청 바디가 부족할 경우 + if (resData === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + // email이 DB에 없을 경우 + else if (resData === -2) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "아이디가 존재하지 않습니다" + ); + } + // 이메일 전송이 실패한 경우 + else if (resData === -3) { + response.basicResponse( + res, + returnCode.SERVICE_UNAVAILABLE, + "이메일 전송이 실패하였습니다" + ); + } + // 성공 + else { + response.basicResponse(res, returnCode.NO_CONTENT, "인증번호 전송 성공"); + } + } catch (err) { + console.error(err.message); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + } +}; + +/** + * @인증번호_확인 + * @route Post auth/code + * @desc check the certification code + * @access Public + */ + +const postCodeController = async (req: Request, res: Response) => { + // 이메일 형식이 잘못된 경우 + const errors = validationResult(req); + if (!errors.isEmpty()) { + return response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + + try { + const reqData: authDTO.codeReqDTO = req.body; + const resData: undefined | -1 | -2 | -3 = await authService.postCode( + reqData + ); + + // 요청 바디가 부족할 경우 + if (resData === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + // email이 DB에 없을 경우 + if (resData === -2) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "아이디가 존재하지 않습니다" + ); + } + // 인증번호가 올바르지 않은 경우 + if (resData === -3) { + response.dataResponse(res, returnCode.OK, "인증번호 인증 실패", { + isOkay: false, + }); + } + // 인증번호 인증 성공 + response.dataResponse(res, returnCode.OK, "인증번호 인증 성공", { + isOkay: true, + }); + } catch (err) { + console.error(err.message); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + } +}; + +/** + * @비밀번호_변경 + * @route Patch auth/pw + * @desc change password + * @access Public + */ + +const patchPasswordController = async (req: Request, res: Response) => { + // 이메일 형식 또는 비밀번호 형식이 잘못된 경우 + const errors = validationResult(req); + if (!errors.isEmpty()) { + return response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + + try { + const reqData: authDTO.passwordReqDTO = req.body; + const data: undefined | -1 | -2 = await authService.patchPassword(reqData); + + // 요청 바디가 부족할 경우 + if (data === -1) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); + } + // email이 DB에 없을 경우 + else if (data === -2) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "아이디가 존재하지 않습니다" + ); + } + // 성공 + else { + response.basicResponse(res, returnCode.NO_CONTENT, "비밀번호 변경 성공"); + } + } catch (err) { + console.error(err.message); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + } +}; /** * @햄버거바 @@ -238,6 +302,9 @@ const authController = { signinController, signupController, hamburgerController, + postEmailController, + postCodeController, + patchPasswordController, }; export default authController; diff --git a/src/controller/index.ts b/src/controller/index.ts index 15fb849..de8211b 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -2,3 +2,5 @@ export { default as authController } from "./auth"; export { default as userController } from "./user"; export { default as challengeController } from "./challenge"; export { default as concertController } from "./concert"; +export { default as noticeController } from "./notice"; +export { default as adminController } from "./admin"; \ No newline at end of file diff --git a/src/controller/notice.ts b/src/controller/notice.ts index 65db05b..2882cb4 100644 --- a/src/controller/notice.ts +++ b/src/controller/notice.ts @@ -1,138 +1,169 @@ -import { Router, Request, Response } from "express"; +import { Request, Response } from "express"; // libraries -import { returnCode } from "../library/returnCode"; -import { response, dataResponse } from "../library/response"; +import { response, returnCode } from "../library"; // services -import { - getNoticeAll, - getNoticeOne, - getNoticeSearch, - postNoticeComment, -} from "../service/noticeService"; - -// middlewares -import auth from "../middleware/auth"; -import publicAuth from "../middleware/publicAuth"; - +import { noticeService } from "../service"; // DTO -import { - noticesResDTO, - noticeResDTO, - INotice, - noticeSearchResDTO, -} from "../DTO/noticeDTO"; -import { commentReqDTO } from "../DTO/commentDTO"; - -const router = Router(); +import { concertDTO, commentDTO } from "../DTO"; /** - * @공지사항_전체_가져오기 - * @route Get /notice - * @access private + * @오투공지사항_전체_가져오기 + * @route Get /notice?offset=@&limit= + * @access public */ -router.get("/", publicAuth, async (req: Request, res: Response) => { +const getNoticeAllController = async (req: Request, res: Response) => { try { - const data: noticesResDTO | -1 = await getNoticeAll( - req.query.offset, - req.query.limit - ); - - // 공지사항 불러오기 성공 - const notice = data; - - // limit 없을 때 - if (data === -1) { - response(res, returnCode.NOT_FOUND, "요청 경로가 올바르지 않습니다"); + const resData: concertDTO.noticeAllResDTO | -1 = + await noticeService.getNoticeAll( + req.body.userID?.id, + Number(req.query.offset), + Number(req.query.limit) + ); + + // 요청 데이터가 부족할 때 + if (resData === -1) { + response.basicResponse( + res, + returnCode.NOT_FOUND, + "요청 데이터가 부족합니다." + ); + } else { + // 회고 전체 불러오기 성공 + response.dataResponse( + res, + returnCode.OK, + "공지사항 전체 불러오기 성공", + resData + ); } - - dataResponse(res, returnCode.OK, "공지사항 불러오기 성공", notice); } catch (err) { console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -}); +}; /** - * @공지사항_검색_또는_필터 - * @route Get /notice/search?keyword=검색할단어 - * @access private + * @오투공지사항_검색_또는_필터 + * @route Get /notice/search?offset=&limit=&tag=&keyword= + * @access public */ -router.get("/search", publicAuth, async (req: Request, res: Response) => { +const getNoticeSearchController = async (req: Request, res: Response) => { try { - const data = await getNoticeSearch( - req.query.keyword, - req.query.offset, - req.query.limit - ); - - // 검색 불러오기 성공 - const noticeSearch: noticeSearchResDTO | -1 = data; + const resData: concertDTO.noticeAllResDTO | -1 = + await noticeService.getNoticeSearch( + Number(req.query.offset), + Number(req.query.limit), + req.body.userID?.id, + req.query.tag ? String(req.query.tag) : undefined, + req.query.keyword ? String(req.query.keyword) : undefined + ); // limit 없을 때 - if (data === -1) { - response(res, returnCode.NOT_FOUND, "요청 경로가 올바르지 않습니다"); + if (resData === -1) { + response.basicResponse( + res, + returnCode.NOT_FOUND, + "요청 경로가 올바르지 않습니다" + ); + } else { + // 검색 불러오기 성공 + response.dataResponse(res, returnCode.OK, "검색 성공", resData); } - - dataResponse(res, returnCode.OK, "검색 성공", noticeSearch); } catch (err) { console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -}); +}; /** - * @공지사항_디테일 + * @오투공지사항_Detail * @route Get /notice/:noticeID - * @access private + * @access public + * @error + * 1. 올바르지 않은 게시글 */ - -router.get("/:id", publicAuth, async (req: Request, res: Response) => { +const getNoticeDetailController = async (req: Request, res: Response) => { try { - // const data: INotice = await getNoticeOne(req.params.id); - const data = await getNoticeOne(req.params.id); - - dataResponse(res, returnCode.OK, "해당 공지사항 불러오기 성공", data); + const resData: concertDTO.concertDetailDTO | -1 = + await noticeService.getNoticeOne( + req.body.userID?.id, + Number(req.params.noticeID) + ); + + if (resData === -1) { + response.basicResponse( + res, + returnCode.NOT_FOUND, + "존재하지 않는 게시글입니다" + ); + } else { + response.dataResponse( + res, + returnCode.OK, + "해당 공지사항 게시글 불러오기 성공", + resData + ); + } } catch (err) { console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -}); +}; /** * @공지사항_댓글_등록 * @route Post /notice/comment/:noticeID * @access private */ -router.post("/comment/:id", auth, async (req: Request, res: Response) => { + +const postNoticeCommentController = async (req: Request, res: Response) => { try { - const reqData: commentReqDTO = req.body; - const data = await postNoticeComment( - req.params.id, + const reqData: commentDTO.postCommentReqDTO = req.body; + const resData = await noticeService.postNoticeComment( + Number(req.params.noticeID), req.body.userID.id, reqData ); - // 공지사항 id가 잘못된 경우 - if (data === -1) { - response(res, returnCode.NOT_FOUND, "요청 경로가 올바르지 않습니다"); + // 회고 id가 잘못된 경우 + if (resData === -1) { + response.basicResponse( + res, + returnCode.NOT_FOUND, + "요청 경로가 올바르지 않습니다" + ); } // 요청 바디가 부족한 경우 - if (data === -2) { - response(res, returnCode.BAD_REQUEST, "요청 값이 올바르지 않습니다"); + else if (resData === -2) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "요청 값이 올바르지 않습니다" + ); } // 부모 댓글 id가 잘못된 경우 - if (data === -3) { - response(res, returnCode.BAD_REQUEST, "부모 댓글 id가 올바르지 않습니다"); + else if (resData === -3) { + response.basicResponse( + res, + returnCode.BAD_REQUEST, + "부모 댓글 id가 올바르지 않습니다" + ); + } else { + // 댓글 등록 성공 + response.basicResponse(res, returnCode.CREATED, "댓글 등록 성공"); } - // 댓글 등록 성공 - const noticeID = data; - dataResponse(res, returnCode.OK, "댓글 등록 성공", noticeID); } catch (err) { console.error(err.message); - response(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); + response.basicResponse(res, returnCode.INTERNAL_SERVER_ERROR, "서버 오류"); } -}); - -module.exports = router; +}; + +const noticeController = { + getNoticeAllController, + getNoticeDetailController, + getNoticeSearchController, + postNoticeCommentController, +}; +export default noticeController; \ No newline at end of file diff --git a/src/library/array.ts b/src/library/array.ts index a23d2fc..c833e41 100644 --- a/src/library/array.ts +++ b/src/library/array.ts @@ -22,9 +22,23 @@ function stringToArray(str: String) { return resultArr; } +function stringToHashtag(str: String) { + let arrayStr = str + .replace("[", "#") + .replace("]", "") + .replace(/,/gi, ",#") + .replace(/"/gi, ""); + return arrayStr; +} +function stringToInterest(str: String) { + let arrayStr = str.replace("[", "").replace("]", "").replace(/"/gi, ""); + return arrayStr; +} const array = { stringToArray, + stringToHashtag, + stringToInterest, }; export default array; diff --git a/src/library/emailTemplete.ejs b/src/library/emailTemplete.ejs index ed4ad1c..003994b 100644 --- a/src/library/emailTemplete.ejs +++ b/src/library/emailTemplete.ejs @@ -26,7 +26,7 @@
-

비밀번호 찾기를 위한 인증번호 입니다.

+

💡 비밀번호 찾기를 위한 인증번호 입니다.

비밀번호를 꼭 변경해주세요!

<%= authCode %> diff --git a/src/library/index.ts b/src/library/index.ts index d9eca6b..5d1237f 100644 --- a/src/library/index.ts +++ b/src/library/index.ts @@ -4,3 +4,4 @@ export { default as emailSender } from "./emailSender"; export { default as jwt } from "./jwt"; export { default as response } from "./response"; export { default as returnCode } from "./returnCode"; +export { default as validator } from "./validator"; diff --git a/src/library/jwt.ts b/src/library/jwt.ts index e097534..415a595 100644 --- a/src/library/jwt.ts +++ b/src/library/jwt.ts @@ -1,6 +1,5 @@ import jwt from "jsonwebtoken"; -import { returnCode } from "../library/returnCode"; -import { response } from "../library/response"; +import { returnCode, response } from "../library"; import config from "../config"; function verify(authorization) { @@ -39,7 +38,7 @@ function isLogin(req, res, next) { next(); } catch (error) { // 유효하지 않은 경우 - response(res, returnCode.UNAUTHORIZED, error.message); // + response.basicResponse(res, returnCode.UNAUTHORIZED, error.message); // } } } @@ -53,7 +52,7 @@ function checkLogin(req, res, next) { next(); } catch (error) { // 유효하지 않은 경우 - response(res, returnCode.UNAUTHORIZED, error.message); + response.basicResponse(res, returnCode.UNAUTHORIZED, error.message); } } diff --git a/src/library/validator.ts b/src/library/validator.ts new file mode 100644 index 0000000..3851f23 --- /dev/null +++ b/src/library/validator.ts @@ -0,0 +1,13 @@ +function emailValidator(str) { + var email = /^([0-9a-zA-Z_\.-]+)@([0-9a-zA-Z_-]+)(\.[0-9a-zA-Z_-]+){1,2}$/; + + if (!email.test(str)) { + return false; + } else { + return true; + } +} + +const validator = { emailValidator }; + +export default validator; diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index 75f5bdc..d22a4fd 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -1,9 +1,10 @@ // libararies +import { Request, Response } from "express"; import jwt from "jsonwebtoken"; import config from "../config"; import { response, returnCode } from "../library"; -export default (req, res, next) => { +export default (req: Request, res: Response, next) => { // 토큰 검사 if (req.headers.authorization == null) { response.basicResponse( @@ -17,7 +18,7 @@ export default (req, res, next) => { // Verify token try { const decoded = jwt.verify(token, config.jwtSecret); - + req.body.userID = decoded.user; next(); } catch (err) { diff --git a/src/middleware/index.ts b/src/middleware/index.ts index c5d688b..af11905 100644 --- a/src/middleware/index.ts +++ b/src/middleware/index.ts @@ -1,2 +1,3 @@ export { default as authMiddleware } from "./auth"; export { default as publicAuthMiddleware } from "./publicAuth"; +export { default as typeCheckMiddleware } from "./typeCheck"; diff --git a/src/middleware/publicAuth.ts b/src/middleware/publicAuth.ts index e32058f..6ee4bbd 100644 --- a/src/middleware/publicAuth.ts +++ b/src/middleware/publicAuth.ts @@ -1,9 +1,10 @@ // libararies +import { Request, Response } from "express"; import jwt from "jsonwebtoken"; import config from "../config"; import { response, returnCode } from "../library"; -export default (req, res, next) => { +export default (req: Request, res: Response, next) => { // 토큰 검사 if (req.headers.authorization == null) { req.body.userID = null; diff --git a/src/middleware/typeCheck.ts b/src/middleware/typeCheck.ts new file mode 100644 index 0000000..a8530c9 --- /dev/null +++ b/src/middleware/typeCheck.ts @@ -0,0 +1,9 @@ +import { check } from "express-validator"; + +export default [ + check("email", "Please include a valid email").isEmail(), + check( + "password", + "Please enter a password with 6 or more characters" + ).isLength({ min: 6 }), +]; diff --git a/src/models/Concert.ts b/src/models/Concert.ts index f59cd6e..d86d09e 100644 --- a/src/models/Concert.ts +++ b/src/models/Concert.ts @@ -46,7 +46,6 @@ export default class Concert extends Model { @Column text!: string; - @Unique @Column authorNickname: string; diff --git a/src/router/admin.ts b/src/router/admin.ts index e69de29..a72b453 100644 --- a/src/router/admin.ts +++ b/src/router/admin.ts @@ -0,0 +1,43 @@ +import { Router } from "express"; +// controller +import { adminController } from "../controller"; +// middleware +import { authMiddleware, publicAuthMiddleware } from "../middleware"; +const upload = require("../modules/upload"); + +const router = Router(); + +router.get("/", authMiddleware, adminController.getAdminListController); +router.get( + "/regist", + publicAuthMiddleware, + adminController.getAdminRegistController +); + +router.post( + "/concert", + upload.fields([ + { name: "videoLink", maxCount: 1 }, + { name: "imgThumbnail", maxCount: 1 }, + ]), + authMiddleware, + adminController.postAdminConcertController +); + +router.post( + "/notice", + upload.fields([ + { name: "videoLink", maxCount: 1 }, + { name: "imgThumbnail", maxCount: 1 }, + ]), + authMiddleware, + adminController.postAdminNoticeController +); +router.post( + "/challenge", + upload.fields([{ name: "img", maxCount: 1 }]), + authMiddleware, + adminController.postAdminChallengeController +); + +export default router; diff --git a/src/router/auth.ts b/src/router/auth.ts index 1d59328..0345eed 100644 --- a/src/router/auth.ts +++ b/src/router/auth.ts @@ -1,10 +1,18 @@ import express from "express"; import { authController } from "../controller"; +import { typeCheckMiddleware } from "../middleware"; const router = express.Router(); -router.post("/signup", authController.signupController); -router.post("/signin", authController.signinController); +router.post("/signup", typeCheckMiddleware, authController.signupController); +router.post("/signin", typeCheckMiddleware, authController.signinController); +router.post("/email", typeCheckMiddleware, authController.postEmailController); +router.post("/code", typeCheckMiddleware, authController.postCodeController); router.get("/hamburger", authController.hamburgerController); +router.patch( + "/pw", + typeCheckMiddleware, + authController.patchPasswordController +); export default router; diff --git a/src/router/concert.ts b/src/router/concert.ts index e7c1304..2827ecb 100644 --- a/src/router/concert.ts +++ b/src/router/concert.ts @@ -1,6 +1,6 @@ import { Router } from "express"; // controller -import { challengeController, concertController } from "../controller"; +import { concertController } from "../controller"; // middleware import { authMiddleware, publicAuthMiddleware } from "../middleware"; diff --git a/src/router/index.ts b/src/router/index.ts index 8acec8b..8645808 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -4,6 +4,8 @@ import authRouter from "./auth"; import userRouter from "./user"; import challengeRouter from "./challenge"; import concertRouter from "./concert"; +import noticeRouter from "./notice"; +import adminRouter from "./admin"; const router = express.Router(); @@ -11,5 +13,6 @@ router.use("/auth", authRouter); router.use("/user", userRouter); router.use("/challenge", challengeRouter); router.use("/concert", concertRouter); - +router.use("/notice", noticeRouter); +router.use("/admin", adminRouter); export default router; diff --git a/src/router/notice.ts b/src/router/notice.ts index e69de29..ef85d42 100644 --- a/src/router/notice.ts +++ b/src/router/notice.ts @@ -0,0 +1,27 @@ +import { Router } from "express"; +// controller +import { noticeController } from "../controller"; +// middleware +import { authMiddleware, publicAuthMiddleware } from "../middleware"; + +const router = Router(); + +router.get("", publicAuthMiddleware, noticeController.getNoticeAllController); +router.get( + "/search", + publicAuthMiddleware, + noticeController.getNoticeSearchController +); +router.get( + "/:noticeID", + publicAuthMiddleware, + noticeController.getNoticeDetailController +); + +router.post( + "/:noticeID/comment", + authMiddleware, + noticeController.postNoticeCommentController +); + +export default router; diff --git a/src/service/adminService.ts b/src/service/adminService.ts index 5c75587..eea232a 100644 --- a/src/service/adminService.ts +++ b/src/service/adminService.ts @@ -1,98 +1,111 @@ -// models -import Admin from "../models/Admin"; -import User from "../models/User"; -import Concert from "../models/Concert"; +import sequelize, { Op } from "sequelize"; -// library -import { stringToDate, stringToEndDate } from "../library/date"; -import Challenge from "../models/Challenge"; -import { stringToArray } from "../library/array"; +// models +import { + Badge, + Concert, + Comment, + Like, + Post, + Challenge, + User, + Admin, +} from "../models"; // DTO -import { - adminResDTO, - adminRegistReqDTO, - adminWriteReqDTO, - adminRegistResDTO, -} from "../DTO/adminDTO"; +import { adminDTO } from "../DTO"; + +// library +import { array, date } from "../library"; /** * @관리자_페이지_조회 - * @route Get admin + * @route Get /admin?offset=&limit= * @body * @error * 1. 유저 id가 관리자가 아님 */ -export const postAdminList = async (userID, offset, limit) => { +export const getAdminList = async ( + userID: number, + offset: number, + limit: number +) => { + // isDelete = true 인 애들만 가져오기 + // offset 뒤에서 부터 가져오기 + // 최신순으로 정렬 + // 댓글, 답글 최신순으로 정렬 if (!offset) { offset = 0; } + // 1. 요청 부족 if (!limit) { return -1; } - // 1. 유저 id가 관리자가 아님 - let user = await User.findById(userID); - if (!(user.userType === 1)) { + // 2. 유저 id가 관리자가 아님 + const user = await User.findOne({ + where: { id: userID }, + attributes: ["isAdmin", "nickName"], + }); + if (user.isAdmin === false) { return -2; } - const admins = await Admin.find( - {}, - { - _id: false, - title: false, - limitNum: false, - __v: false, - } - ).sort({ generation: -1 }); + const admins: adminDTO.adminResDetailDTO[] = await Admin.findAll({ + order: [["generation", "DESC"]], + limit, + offset, + }); const adminList = await Promise.all( - admins.map(async function (admin) { - let totalNum = await Challenge.aggregate([ - { - $match: { generation: admin.generation }, - }, - { - $group: { - _id: "$user", - // 참여 인원 - total: { $sum: 1 }, + admins.map(async (admin) => { + // 해당 기수 글 작성자 + const participantList = await Post.findAll({ + where: { generation: admin.generation }, + include: [ + { + model: Challenge, + required: true, + }, + ], + attributes: [[sequelize.fn("count", sequelize.col("userID")), "count"]], + }); + + // 해당 기수 글 개수 + const postNum = await Post.findAll({ + where: { generation: admin.generation }, + include: [ + { + model: Challenge, + required: true, }, - }, - { $project: { _id: 0 } }, - ]); - let participants = 0; - if (totalNum[0]) { - participants = totalNum[0]["total"]; - } - const admintemp = { + ], + }); + + let returnData: adminDTO.adminResDetailDTO = { registerStartDT: admin.registerStartDT, registerEndDT: admin.registerEndDT, challengeStartDT: admin.challengeStartDT, challengeEndDT: admin.challengeEndDT, generation: admin.generation, - createdDT: admin.createdDT, - // 신청 인원 + createdAT: admin.createdAT, applyNum: admin.applyNum, - // 참여 인원 - participants, - postNum: await Challenge.find({ generation: admin.generation }).count(), + participants: participantList.length, + postNum: postNum.length, img: admin.img, }; - return admintemp; + + return returnData; }) ); - var offsetAdmin = []; - for (var i = Number(offset); i < Number(offset) + Number(limit); i++) { - offsetAdmin.push(adminList[i]); - } - const resData: adminResDTO = { - offsetAdmin, - totalAdminNum: adminList.length, + const totalAdmin = await Admin.findAll(); + + const resData: adminDTO.adminResDTO = { + offsetAdmin: adminList, + totalAdminNum: totalAdmin.length, }; return resData; @@ -102,14 +115,15 @@ export const postAdminList = async (userID, offset, limit) => { * @관리자_챌린지_등록 * @route Post admin/challenge * @body registerStartDT, registerEndDT, challengeStartDT, challengeEndDT, limitNum, img + * @access private * @error * 1. 요청 바디 부족 * 2. 유저 id가 관리자가 아님 * 3. 챌린지 기간이 잘못됨 */ export const postAdminChallenge = async ( - userID, - reqData: adminRegistReqDTO, + userID: number, + reqData: adminDTO.adminRegistReqDTO, url ) => { const img = url.img; @@ -135,30 +149,25 @@ export const postAdminChallenge = async ( } // 2. 유저 id가 관리자가 아님 - let user = await User.findById(userID); - if (!(user.userType === 1)) { + const user = await User.findOne({ + where: { id: userID }, + attributes: ["isAdmin", "nickName"], + }); + if (user.isAdmin === false) { return -2; } - /* - var = new Date('2020-10-23'); - var date2 = new Date('2020-10-22'); - - console.log(date1 > date2); // true - */ - //기수 증가 - const changeGen = (await Admin.find().count()) + 1; - const admin = new Admin({ + const changeGen = (await (await Admin.findAll()).length) + 1; + await Admin.create({ title, - registerStartDT: stringToDate(registerStartDT), - registerEndDT: stringToEndDate(registerEndDT), - challengeStartDT: stringToDate(challengeStartDT), - challengeEndDT: stringToEndDate(challengeEndDT), + registerStartDT: date.stringToDate(registerStartDT), + registerEndDT: date.stringToEndDate(registerEndDT), + challengeStartDT: date.stringToDate(challengeStartDT), + challengeEndDT: date.stringToEndDate(challengeEndDT), generation: changeGen, limitNum, img, - createdAt: new Date(), }); // 3. 챌린지 기간이 잘못됨 // 신청 마감날짜가 신청 시작 날짜보다 빠름 @@ -173,23 +182,24 @@ export const postAdminChallenge = async ( else if (challengeStartDT < registerEndDT) { return -3; } - await admin.save(); - return; + return 1; }; /** * @관리자_챌린지_신청페이지 * @route Get admin/regist - * @access private + * @access public */ export const getAdminRegist = async () => { // 신청 기간을 확인 현재 진행중인 기수를 가져옴 let dateNow = new Date(); const admin = await Admin.findOne({ - $and: [ - { registerStartDT: { $lte: dateNow } }, - { registerEndDT: { $gte: dateNow } }, - ], + where: { + [sequelize.Op.and]: { + registerStartDT: { [sequelize.Op.lte]: dateNow }, + registerEndDT: { [sequelize.Op.gte]: dateNow }, + }, + }, }); // 현재 진행중인 기수가 없음 @@ -197,7 +207,7 @@ export const getAdminRegist = async () => { return -1; } - const resData: adminRegistResDTO = { + const resData: adminDTO.adminRegistResDTO = { img: admin.img, title: admin.title, registerStartDT: admin.registerStartDT, @@ -211,9 +221,9 @@ export const getAdminRegist = async () => { }; /** - * @관리자_오투콘서트_등록 + * @관리자_콘서트_등록 * @route Post admin/concert - * @body createdAt, title, videoLink, text, interest, hashtag, authorNickname + * @access private * @error * 1. 요청 바디 부족 * 2. 유저 id가 관리자가 아님 @@ -221,21 +231,13 @@ export const getAdminRegist = async () => { */ export const postAdminConcert = async ( - userID, - reqData: adminWriteReqDTO, - url + userID: number, + reqData: adminDTO.adminWriteReqDTO, + url: any ) => { const { title, text, authorNickname } = reqData; - let interest = stringToArray(reqData.interest); - // .toLowerCase() - // .slice(1, -1) - // .replace(/"/gi, "") - // .split(/,\s?/); - let hashtag = stringToArray(reqData.hashtag); - // .toLowerCase() - // .slice(1, -1) - // .replace(/"/gi, "") - // .split(/,\s?/); + let interest = array.stringToInterest(reqData.interest); + let hashtag = array.stringToHashtag(reqData.hashtag); // 1. 요청 바디 부족 if (!title || !text || !interest || !hashtag || !authorNickname) { @@ -243,33 +245,51 @@ export const postAdminConcert = async ( } // 2. 유저 id가 관리자가 아님 - let user = await User.findById(userID); - if (!(user.userType === 1)) { + const user = await User.findOne({ + where: { id: userID }, + attributes: ["isAdmin", "nickName"], + }); + if (user.isAdmin === false) { return -2; } - const authorUser = await User.findOne({ nickname: authorNickname }); - var authorID; + // 해당 닉네임을 가진 유저를 찾음 + const authorUser = await User.findOne({ + where: { nickname: authorNickname }, + }); + + // 원글 작성자 아이디 + let authorID; + + // 해당 닉네임을 가진 유저가 있음 if (authorUser) { - authorID = authorUser._id; + // 해당 닉네임을 가진 유저의 id를 넣음 + authorID = authorUser.id; } else { + // 해당 닉네임을 가진 유저가 없음 + // 아이디는 관리자 아이디를 사용, 닉네임은 그 자체(authorNickname)를 사용 authorID = userID; } - const concert = new Concert({ - title: title.toLowerCase(), - user: authorID, - createdAt: new Date(), + + // Concert 등록 + const newPost = await Post.create({ + userID, + interest, + }); + + await Concert.create({ + id: newPost.id, + userID: authorID, + title, videoLink: url.videoLink, imgThumbnail: url.imgThumbnail, - text: text.toLowerCase(), - interest, - hashtag, + text, + isNotice: false, authorNickname, + hashtag, }); - await concert.save(); - - return; + return 1; }; /** @@ -281,26 +301,15 @@ export const postAdminConcert = async ( * 2. 유저 id가 관리자가 아님 */ -export const postAdminNotice = async (userID, reqData, url) => { +export const postAdminNotice = async ( + userID: number, + reqData: adminDTO.adminWriteReqDTO, + url: any +) => { const { title, text } = reqData; - let interest; - if (reqData.interest) { - interest = stringToArray(reqData.interest); - // .toLowerCase() - // .slice(1, -1) - // .replace(/"/gi, "") - // .split(/,\s?/); - } - - let hashtag; - if (reqData.hashtag) { - hashtag = stringToArray(reqData.hashtag); - // .toLowerCase() - // .slice(1, -1) - // .replace(/"/gi, "") - // .split(/,\s?/); - } + let interest = array.stringToInterest(reqData.interest); + let hashtag = array.stringToHashtag(reqData.hashtag); // 1. 요청 바디 부족 if (!title || !text) { @@ -308,23 +317,39 @@ export const postAdminNotice = async (userID, reqData, url) => { } // 2. 유저 id가 관리자가 아님 - let user = await User.findById(userID); - if (!(user.userType === 1)) { + const user = await User.findOne({ + where: { id: userID }, + attributes: ["isAdmin", "nickName"], + }); + if (user.isAdmin === false) { return -2; } - const notice = new Concert({ - title: title.toLowerCase(), + // Notice 등록 + const newPost = await Post.create({ + userID, interest, - user: userID, - isNotice: true, + }); + + await Concert.create({ + id: newPost.id, + userID, + title, videoLink: url.videoLink, imgThumbnail: url.imgThumbnail, - text: text.toLowerCase(), + text, + isNotice: true, hashtag, }); - await notice.save(); + return 1; +}; - return; +const adminService = { + getAdminList, + getAdminRegist, + postAdminConcert, + postAdminNotice, + postAdminChallenge, }; +export default adminService; diff --git a/src/service/authService.ts b/src/service/authService.ts index 146772e..f579e76 100644 --- a/src/service/authService.ts +++ b/src/service/authService.ts @@ -1,5 +1,5 @@ // models -import { User, UserInterest, Badge, Admin } from "../models"; +import { User, Badge, Admin } from "../models"; // DTO import { authDTO } from "../DTO"; // library @@ -27,7 +27,6 @@ const postSignup = async (data: authDTO.signupReqDTO) => { if (!email || !password || !nickname || !interest) { return -1; } - // 2. 아이디 중복 const existUser = await User.findOne({ where: { email: email } }); @@ -51,16 +50,7 @@ const postSignup = async (data: authDTO.signupReqDTO) => { password: hashPassword, nickname, isMarketing, - }); - - // UserInterest 생성 - const userID = (await user).id; - const interests = await interest.map((interestOne) => { - UserInterest.create({ - userID: userID, - interest: interestOne, - }); - return interestOne; + interest: interest.join(","), }); // Badge 생성 @@ -77,7 +67,7 @@ const postSignup = async (data: authDTO.signupReqDTO) => { // Return jsonwebtoken const payload = { user: { - id: userID, + id: user.id, }, }; @@ -240,137 +230,138 @@ const getHamburger = async () => { return resData; }; -// /** -// * @이메일_인증번호_전송 -// * @route Post auth/email -// * @body email -// * @error -// * 1. 요청 바디 부족 -// * 2. 아이디가 존재하지 않음 -// */ -// export async function postEmail(body) { -// const { email } = body; - -// // 1. 요청 바디 부족 -// if (!email) { -// return -1; -// } - -// // 2. email이 DB에 존재하지 않음 -// let user = await User.findOne({ email }); -// if (!user) { -// return -2; -// } - -// // 인증번호를 user에 저장 -> 제한 시간 설정하기! -// const authNum = Math.random().toString().substr(2, 6); -// user.emailCode = authNum; -// await user.save(); - -// let emailTemplate; -// ejs.renderFile( -// "src/library/emailTemplete.ejs", -// { authCode: authNum }, -// (err, data) => { -// if (err) { -// console.log(err); -// } -// emailTemplate = data; -// } -// ); - -// const mailOptions = { -// front: "hyunjin5697@gmail.com", -// to: email, -// subject: "메일 제목", -// text: "메일 내용", -// html: emailTemplate, -// }; - -// await emailSender.sendMail(mailOptions, (error, info) => { -// if (error) { -// // res.json({ msg: "err" }); -// console.log(error); -// } else { -// // res.json({ msg: "sucess" }); -// console.log("success"); -// } -// emailSender.close(); -// }); - -// return 0; -// } - -// /** -// * @인증번호_인증 -// * @route Post auth/code -// * @body email -// * @error -// * 1. 요청 바디 부족 -// * 2. 유저가 존재하지 않음 -// */ -// export async function postCode(body) { -// // 저장해놓은 authNum이랑 body로 온 인증번호랑 비교 -// const { email, emailCode } = body; - -// // 1. 요청 바디 부족 -// if (!email || !emailCode) { -// return -1; -// } - -// // 2. 유저가 존재하지 않음 -// // isDeleted = false 인 유저를 찾아야함 -// // 회원 탈퇴했다가 다시 가입한 경우 생각하기 -// let user = await User.findOne({ email: email }); -// if (!user) { -// return -2; -// } - -// if (emailCode !== user.emailCode) { -// // 인증번호가 일치하지 않음 -// return -3; -// } else { -// // 인증번호 일치 -// return 0; -// } - -// return; -// } - -// /** -// * @비밀번호_재설정 -// * @route Patch auth/pw -// * @body email -// * @error -// * 1. 요청 바디 부족 -// * 2. 아이디가 존재하지 않음 -// */ -// export async function patchPassword(reqData: pwReqDTO) { -// const { email, password } = reqData; - -// // 1. 요청 바디 부족 -// if (!email || !password) { -// return -1; -// } - -// // 2. email이 DB에 존재하지 않음 -// let user = await User.findOne({ email }); -// if (!user) { -// return -2; -// } - -// // 비밀번호 변경 로직 -// // Encrpyt password -// const salt = await bcrypt.genSalt(10); -// user.password = await bcrypt.hash(password, salt); -// await user.save(); -// return; -// } +/** + * @이메일_인증번호_전송 + * @route Post auth/email + * @body email + * @error + * 1. 요청 바디 부족 + * 2. 이매일이 DB에 존재하지 않음 + * 3. 이메일 전송 실패 + */ +const postEmail = async (body: authDTO.emailReqDTO) => { + const { email } = body; + + // 1. 요청 바디 부족 + if (!email) { + return -1; + } + + // 2. email이 DB에 존재하지 않음 + const user = await User.findOne({ where: { email } }); + if (!user) { + return -2; + } + + // 인증번호를 user에 저장 -> 제한 시간 설정하기! + const authNum = Math.random().toString().substr(2, 6); + user.emailCode = authNum; + await user.save(); + + let emailTemplate: string; + ejs.renderFile( + "src/library/emailTemplete.ejs", + { authCode: authNum }, + (err, data) => { + if (err) { + console.log(err); + } + emailTemplate = data; + } + ); + + const mailOptions = { + front: process.env.EMAIL_ADDRESS, + to: email, + subject: "O2 이메일 인증번호입니다.", + text: "O2 이메일 인증번호 입니다.", + html: emailTemplate, + }; + + emailSender.sendMail(mailOptions, (error, info) => { + if (error) { + console.log(error); + return -3; + } else { + console.log("success"); + } + emailSender.close(); + }); + + return undefined; +}; + +/** + * @인증번호_인증 + * @route Post auth/code + * @body email, emailCode + * @error + * 1. 요청 바디 부족 + * 2. 유저가 존재하지 않음 + * 3. 인증번호 인증 실패 + */ +export async function postCode(body: authDTO.codeReqDTO) { + const { email, emailCode } = body; + + // 1. 요청 바디 부족 + if (!email || !emailCode) { + return -1; + } + + // 2. 유저가 존재하지 않음 + // isDeleted = false 인 유저를 찾아야함 + // 회원 탈퇴했다가 다시 가입한 경우 생각하기 + const user = await User.findOne({ where: { email } }); + if (!user) { + return -2; + } + + if (emailCode !== user.emailCode) { + // 인증번호가 일치하지 않음 + return -3; + } + + // 인증번호 일치 + return undefined; +} + +/** + * @비밀번호_재설정 + * @route Patch auth/pw + * @body email, password + * @error + * 1. 요청 바디 부족 + * 2. 아이디가 존재하지 않음 + */ +export async function patchPassword(reqData: authDTO.passwordReqDTO) { + const { email, password } = reqData; + + // 1. 요청 바디 부족 + if (!email || !password) { + return -1; + } + + // 2. email이 DB에 존재하지 않음 + const user = await User.findOne({ where: { email } }); + if (!user) { + return -2; + } + + // 비밀번호 변경 로직 + // Encrpyt password + const salt = await bcrypt.genSalt(10); + user.password = await bcrypt.hash(password, salt); + await user.save(); + return; +} const authService = { postSignup, postSignin, getHamburger, + postEmail, + postCode, + patchPassword, }; export default authService; diff --git a/src/service/concertService.ts b/src/service/concertService.ts index 25c55bd..4915828 100644 --- a/src/service/concertService.ts +++ b/src/service/concertService.ts @@ -135,7 +135,13 @@ export const getConcertAll = async ( return returnData; }); - const totalConcertNum = concertList.length; + + const totalConcertNum = await Post.count({ + where: { + isDeleted: false, + }, + include: [{ model: Concert, required: true, where: { isNotice: false } }], + }); const resData: concertDTO.concertAllResDTO = { concerts, totalConcertNum, @@ -403,7 +409,10 @@ export const getConcertSearch = async ( return returnData; }); - const totalConcertNum = concertList.length; + const totalConcertNum = await Post.count({ + where, + include: [{ model: Concert, required: true, where: { isNotice: false } }], + }); const resData: concertDTO.concertAllResDTO = { concerts, totalConcertNum, @@ -576,7 +585,7 @@ export const deleteConcertLike = async (concertID: number, userID: number) => { }, include: [ { model: Concert, required: true, where: { isNotice: false } }, - Like, + { model: Like, as: "likes", required: false }, ], }); @@ -599,7 +608,7 @@ export const deleteConcertLike = async (concertID: number, userID: number) => { /** * @오투콘서트_스크랩하기 - * @route Post /user/concert/:concertID + * @route Post /concert/:concertID/scrap * @access private * @error * 1. 콘서트 id 잘못됨 diff --git a/src/service/index.ts b/src/service/index.ts index d87acdb..43c1082 100644 --- a/src/service/index.ts +++ b/src/service/index.ts @@ -2,3 +2,5 @@ export { default as authService } from "./authService"; export { default as userService } from "./userService"; export { default as challengeService } from "./challengeService"; export { default as concertService } from "./concertService"; +export { default as noticeService } from "./noticeService"; +export { default as adminService } from "./adminService"; diff --git a/src/service/noticeService.ts b/src/service/noticeService.ts index 47c8346..5599c37 100644 --- a/src/service/noticeService.ts +++ b/src/service/noticeService.ts @@ -1,180 +1,348 @@ +import sequelize, { Op } from "sequelize"; + // models -import Concert from "../models/Concert"; -import User from "../models/User"; -import Badge from "../models/Badge"; -import Comment from "../models/Comment"; +import { Badge, Concert, Comment, Like, Post, Scrap, User } from "../models"; // DTO -import { - noticesResDTO, - noticeResDTO, - INotice, - noticeSearchResDTO, -} from "../DTO/noticeDTO"; - -import { commentReqDTO } from "../DTO/commentDTO"; +import { concertDTO, commentDTO } from "../DTO"; + /** * @공지사항_전체_가져오기 - * @route Get /notice + * @route Get /notice?offset=@&limit= + * @access public + * @error + * 1. limit이 없는 경우 */ -export const getNoticeAll = async (offset, limit) => { - // isDelete = true 인 애들만 가져오기 +export const getNoticeAll = async ( + userID: number, + offset: number, + limit: number +) => { + // isDelete = false 인 애들만 가져오기 + // isNotice = true 인 애들만 가져오기 // offset 뒤에서 부터 가져오기 // 최신순으로 정렬 - // 댓글, 답글 populate // 댓글, 답글 최신순으로 정렬 + + // 1. 요청 부족 if (!limit) { return -1; } + if (!offset) { offset = 0; } - let notices; - notices = await Concert.find({ - isDeleted: false, - isNotice: true, - }) - .skip(Number(offset)) - .limit(Number(limit)) - .sort({ _id: -1 }) - .populate("user", ["nickname", "img"]) - .populate({ - path: "comments", - select: { userID: 1, text: 1, isDeleted: 1 }, - options: { sort: { _id: -1 } }, - populate: [ - { - path: "childrenComment", - select: { userID: 1, text: 1, isDeleted: 1 }, - options: { sort: { _id: -1 } }, - populate: { - path: "userID", - select: ["nickname", "img"], - }, - }, + let include: sequelize.Includeable | sequelize.Includeable[] = [ + { model: Concert, required: true, where: { isNotice: true } }, + User, + { + model: Comment, + as: "comments", + required: false, + where: { level: 0 }, + include: [ + User, { - path: "userID", - select: ["nickname", "img"], + model: Comment, + as: "children", + separate: true, + order: [["id", "DESC"]], + include: [User], }, ], + }, + ]; + + const noticeList = await Post.findAll({ + order: [ + ["createdAt", "DESC"], + ["comments", "id", "DESC"], + ], + where: { + isDeleted: false, + }, + include, + limit, + offset, + }); + + const notices: concertDTO.getConcertResDTO[] = noticeList.map((notice) => { + // 댓글 형식 변환 + let comment: commentDTO.IComment[] = []; + notice.comments.forEach((c) => { + const children = c.children.map((child) => ({ + id: child.id, + userID: child.userID, + nickname: child.user.nickname, + img: child.user.img, + text: child.text, + isDeleted: child.isDeleted, + })); + + comment.push({ + id: c.id, + userID: c.userID, + nickname: c.user.nickname, + img: c.user.img, + text: c.text, + children, + isDeleted: c.isDeleted, + }); }); - const totalNoticeNum = await Concert.find({ - isDeleted: false, - isNotice: true, - }).countDocuments(); - const resData: noticesResDTO = { + let returnData: concertDTO.getConcertResDTO = { + id: notice.id, + createdAt: notice.createdAt, + updatedAt: notice.updatedAt, + userID: notice.userID, + nickname: notice.user.nickname, + img: notice.user.img, + title: notice.concert.title, + videoLink: notice.concert.videoLink, + imgThumbnail: notice.concert.imgThumbnail, + text: notice.concert.text, + interest: notice.interest.split(","), + hashtag: notice.concert.hashtag + ? notice.concert.hashtag.slice(1).split("#") + : undefined, + commentNum: notice.comments.length, + comment, + isDeleted: notice.isDeleted, + isNotice: notice.concert.isNotice, + }; + + return returnData; + }); + const totalNoticeNum = noticeList.length; + const resData: concertDTO.noticeAllResDTO = { notices, totalNoticeNum, }; + return resData; }; /** * @공지사항_Detail * @route Get /notice/:noticeID + * @access public + * @error + * 1. noticeID가 없을 경우 */ -export const getNoticeOne = async (noticeID) => { - // 댓글, 답글 populate - // isDelete = true 인 애들만 가져오기 - // isNotice: true - const notice = await Concert.find({ _id: noticeID }, { isDeleted: false }) - .populate("user", ["nickname", "img"]) - .populate({ - path: "comments", - select: { userID: 1, text: 1, isDeleted: 1 }, - options: { sort: { _id: -1 } }, - populate: [ - { - path: "childrenComment", - select: { userID: 1, text: 1, isDeleted: 1 }, - options: { sort: { _id: -1 } }, - populate: { - path: "userID", - select: ["nickname", "img"], - }, - }, +export const getNoticeOne = async (userID: number, noticeID: number) => { + // 1. noticeID가 없을 경우 + if (!noticeID) { + return -1; + } + + let include: sequelize.Includeable | sequelize.Includeable[] = [ + { model: Concert, required: true, where: { isNotice: true } }, + User, + { + model: Comment, + as: "comments", + required: false, + where: { level: 0 }, + include: [ + User, { - path: "userID", - select: ["nickname", "img"], + model: Comment, + as: "children", + separate: true, + order: [["id", "DESC"]], + include: [User], }, ], + }, + ]; + + // 댓글, 답글 join + // isDeleted = false + const notice = await Post.findOne({ + where: { isDeleted: false, "$concert.id$": noticeID }, + order: [["comments", "id", "DESC"]], + include, + }); + // 댓글 형식 변환 + let comment: commentDTO.IComment[] = []; + notice.comments.forEach((c) => { + const children = c.children.map((child) => ({ + id: child.id, + userID: child.userID, + nickname: child.user.nickname, + img: child.user.img, + text: child.text, + isDeleted: child.isDeleted, + })); + + comment.push({ + id: c.id, + userID: c.userID, + nickname: c.user.nickname, + img: c.user.img, + text: c.text, + children, + isDeleted: c.isDeleted, }); + }); - // const resData: INotice = notice - // return resData; + let resData: concertDTO.getConcertResDTO = { + id: notice.id, + createdAt: notice.createdAt, + updatedAt: notice.updatedAt, + userID: notice.userID, + nickname: notice.user.nickname, + img: notice.user.img, + title: notice.concert.title, + videoLink: notice.concert.videoLink, + imgThumbnail: notice.concert.imgThumbnail, + text: notice.concert.text, + interest: notice.interest.split(","), + hashtag: notice.concert.hashtag + ? notice.concert.hashtag.slice(1).split("#") + : undefined, + commentNum: notice.comments.length, + comment, + isDeleted: notice.isDeleted, + isNotice: notice.concert.isNotice, + }; - return notice; + return resData; }; /** * @공지사항_검색_또는_필터 - * @route Get /notice/search?keyword=검색할단어 + * @route Get /notice/search?offset=&limit=&tag=&keyword= + * @error + * 1. limit이 없는 경우 */ -export const getNoticeSearch = async (keyword, offset, limit) => { - // isDelete = true 인 애들만 가져오기 - // isNotice: true +export const getNoticeSearch = async ( + offset: number, + limit: number, + userID?: number, + tag?: string, + keyword?: string +) => { + // isDelete = false 인 애들만 가져오기 // offset 뒤에서 부터 가져오기 // 최신순으로 정렬 // 댓글, 답글 populate + // tag 검사 + // keyword 검사 + if (!limit) { return -1; } if (!offset) { offset = 0; } - let notices; - notices = await Concert.find({ + + // where + let where: any = { isDeleted: false, - isNotice: true, - }) - .sort({ _id: -1 }) - .populate("user", ["nickname", "img"]) - .populate({ - path: "comments", - select: { userID: 1, text: 1, isDeleted: 1 }, - options: { sort: { _id: -1 } }, - populate: [ - { - path: "childrenComment", - select: { userID: 1, text: 1, isDeleted: 1 }, - options: { sort: { _id: -1 } }, - populate: { - path: "userID", - select: ["nickname", "img"], - }, - }, + }; + + if (tag && tag !== "전체") { + where = { + ...where, + interest: { [Op.like]: `%${tag}%` }, + }; + } + + if (keyword) { + where = { + ...where, + [Op.or]: [ + { "$concert.text$": { [Op.like]: `%${keyword}%` } }, + { "$concert.title$": { [Op.like]: `%${keyword}%` } }, + { "$concert.hashtag$": { [Op.like]: `%${keyword}%` } }, + ], + }; + } + + // include + let include: sequelize.Includeable | sequelize.Includeable[] = [ + { model: Concert, required: true, where: { isNotice: true } }, + User, + { + model: Comment, + as: "comments", + required: false, + where: { level: 0 }, + include: [ + User, { - path: "userID", - select: ["nickname", "img"], + model: Comment, + as: "children", + separate: true, + order: [["id", "DESC"]], + include: [User], }, ], - }); + }, + ]; + + const noticeList = await Post.findAll({ + order: [["createdAt", "DESC"]], + where, + include, + limit, + offset, + }); - let filteredData = notices; - - // 검색 단어 필터링 - if (keyword !== "" && keyword) { - filteredData = filteredData.filter((fd) => { - if ( - fd.text.includes(keyword.toLowerCase().trim()) || - fd.title.includes(keyword.toLowerCase().trim()) || - fd.hashtag.includes(keyword.toLowerCase().trim()) || - fd.interest.includes(keyword.toLowerCase().trim()) - ) - return fd; + const notices: concertDTO.getConcertResDTO[] = noticeList.map((notice) => { + // 댓글 형식 변환 + let comment: commentDTO.IComment[] = []; + notice.comments.forEach((c) => { + const children = c.children.map((child) => ({ + id: child.id, + userID: child.userID, + nickname: child.user.nickname, + img: child.user.img, + text: child.text, + isDeleted: child.isDeleted, + })); + + comment.push({ + id: c.id, + userID: c.userID, + nickname: c.user.nickname, + img: c.user.img, + text: c.text, + children, + isDeleted: c.isDeleted, + }); }); - } - var searchData = []; - for (var i = Number(offset); i < Number(offset) + Number(limit); i++) { - searchData.push(filteredData[i]); - } + let returnData: concertDTO.getConcertResDTO = { + id: notice.id, + createdAt: notice.createdAt, + updatedAt: notice.updatedAt, + userID: notice.userID, + nickname: notice.user.nickname, + img: notice.user.img, + title: notice.concert.title, + videoLink: notice.concert.videoLink, + imgThumbnail: notice.concert.imgThumbnail, + text: notice.concert.text, + interest: notice.interest.split(","), + hashtag: notice.concert.hashtag + ? notice.concert.hashtag.slice(1).split("#") + : undefined, + commentNum: notice.comments.length, + comment, + isDeleted: notice.isDeleted, + isNotice: notice.concert.isNotice, + }; - const resData: noticeSearchResDTO = { - searchData: searchData, - totalNoticeSearchNum: filteredData.length, + return returnData; + }); + + const totalNoticeNum = noticeList.length; + const resData: concertDTO.noticeAllResDTO = { + notices, + totalNoticeNum, }; return resData; @@ -182,106 +350,99 @@ export const getNoticeSearch = async (keyword, offset, limit) => { /** * @공지사항_댓글_등록 - * @route POST /notice/comment/:noticeID + * @route Post /notice/comment/:noticeID + * @access private * @error * 1. 공지사항 id 잘못됨 * 2. 요청 바디 부족 * 3. 부모 댓글 id 값이 유효하지 않을 경우 */ export const postNoticeComment = async ( - noticeID, - userID, - reqData: commentReqDTO + noticeID: number, + userID: number, + reqData: commentDTO.postCommentReqDTO ) => { const { parentID, text } = reqData; - - // 1. 공지사항 id 잘못됨 - const notice = await Concert.findById(noticeID); + // 1. 회고록 id 잘못됨 + const notice = await Post.findOne({ + where: { + "$concert.id$": noticeID, + isDeleted: false, + }, + include: [{ model: Concert, required: true, where: { isNotice: true } }], + }); if (!notice || notice.isDeleted) { return -1; } + // 2. 요청 바디 부족 if (!text) { return -2; } - let comment; // 답글인 경우 if (parentID) { - const parentComment = await Comment.findById(parentID); + const parentComment = await Comment.findOne({ + where: { id: parentID, isDeleted: false }, + }); // 3. 부모 댓글 id 값이 유효하지 않을 경우 if (!parentComment) { return -3; } - comment = new Comment({ - postModel: "Notice", - post: noticeID, - userID: userID, - parentComment: parentID, + await Comment.create({ + userID, + postID: noticeID, + parentID, text, + level: 1, }); - await comment.save(); - - await parentComment.childrenComment.push(comment._id); - await parentComment.save(); // 첫 답글 작성 시 뱃지 추가 - const badge = await Badge.findOne({ user: userID }); - if (!badge.firstReplyBadge) { - badge.firstReplyBadge = true; - await badge.save(); - } + await Badge.update( + { firstReplyBadge: true }, + { + where: { id: userID, firstReplyBadge: false }, + } + ); } else { // 댓글인 경우 - comment = new Comment({ - postModel: "Notice", - post: noticeID, - userID: userID, + await Comment.create({ + userID, + postID: noticeID, text, }); - await comment.save(); - notice.comments.push(comment._id); - await notice.save(); - // 댓글 1개 작성 시 뱃지 추가 - const badge = await Badge.findOne({ user: userID }); + const badge = await Badge.findOne({ where: { id: userID } }); if (!badge.oneCommentBadge) { badge.oneCommentBadge = true; await badge.save(); } + // 댓글 5개 작성 시 뱃지 추가 - const user = await User.findById(userID); - if (!badge.fiveCommentBadge && user.commentCNT === 4) { + const user = await User.findOne({ + where: { id: userID }, + include: [Comment], + }); + + // 댓글 5개 작성 시 뱃지 추가 + if (!badge.fiveCommentBadge && user.comments.length > 4) { badge.fiveCommentBadge = true; await badge.save(); } - // 유저 댓글 수 1 증가 - await user.update({ - commentCNT: user.commentCNT + 1, - }); + return 1; } +}; - // 게시글 댓글 수 1 증가 - await Concert.findOneAndUpdate( - { _id: noticeID }, - { - $inc: { commentNum: 1 }, - } - ); - - const user = await User.findById(userID); - - return { - _id: comment._id, - nickname: user.nickname, - text: text, - createdAt: comment.createdAt, - }; - - return; +const noticeService = { + getNoticeAll, + getNoticeOne, + getNoticeSearch, + postNoticeComment, }; + +export default noticeService;