diff --git a/domains/admin/admin.controller.js b/domains/admin/admin.controller.js index e8782e1..f56fcbd 100644 --- a/domains/admin/admin.controller.js +++ b/domains/admin/admin.controller.js @@ -14,6 +14,7 @@ import { userInviteService, deleteUserService, userSubmitService, + userRequestService, } from "./admin.service.js"; export const createRoomsController = async (req, res, next) => { @@ -54,7 +55,7 @@ export const createPostController = async (req, res, next) => { export const updatePostController = async (req, res, next) => { try { - const result = await updatePostService(req.body); + const result = await updatePostService(req.body, req.params.postId); res.status(200).json(response(status.SUCCESS, result)); } catch (error) { next(error); @@ -70,7 +71,7 @@ export const deletePostController = async (req, res, next) => { } }; -export const unreadUserListController = async(req, res, next) => { +export const unreadUserListController = async (req, res, next) => { try { const result = await unreadUserListService(req.params.postId); res.status(200).json(response(status.SUCCESS, result)); @@ -79,12 +80,12 @@ export const unreadUserListController = async(req, res, next) => { } }; -export const userListController = async (req,res,next) => { - try{ +export const userListController = async (req, res, next) => { + try { const result = await userListService(req.query.nickname, req.query.roomId); res.status(200).json(response(status.SUCCESS, result)); - }catch (error){ - next(error); + } catch (error) { + next(error); } }; @@ -122,4 +123,13 @@ export const userSubmitController = async (req, res, next) => { } catch (error) { next(error); } -} \ No newline at end of file +}; + +export const userRequestController = async (req, res, next) => { + try { + const result = await userRequestService(req.body); + res.status(200).json(response(status.SUCCESS, result)); + } catch (error) { + next(error); + } +}; diff --git a/domains/admin/admin.dao.js b/domains/admin/admin.dao.js index f847888..e10c638 100644 --- a/domains/admin/admin.dao.js +++ b/domains/admin/admin.dao.js @@ -1,7 +1,7 @@ import { pool } from "../../config/db.config.js"; import { BaseError } from "../../config/error.js"; import { status } from "../../config/response.status.js"; -import { getDate, getToday, isInvalidDate } from "../../utils/date.js"; +import { getDate, getToday, isInvalidDate, getNow } from "../../utils/date.js"; import { createRoomsSQL, userRoomSQL, @@ -21,13 +21,14 @@ import { userInviteSQL, checkUserInRoomSQL, deleteUserSQL, - allRoomsSQL, - penaltySQL, - penaltyStateSQL, - addUserSubmitSQL, + imposePenaltyByPostSQL, + initializeSubmitByPostSQL, + getPostsBeforeEndDate, getPostCountSQL, userSubmitSQL, - getSubmitStateSQL, + getSubmitStateSQL, + userRequestAcceptSQL, + userRequestRejectSQL, } from "./admin.sql.js"; import schedule from "node-schedule"; @@ -117,12 +118,9 @@ export const createPostDao = async (body, userId) => { const startDate = `${body.start_date} 00:00`; const endDate = `${body.end_date} 23:59`; - if (isInvalidDate(body.start_date, body.end_date)) - throw new Error("날짜 형식이 올바르지 않습니다."); - - if (getDate(startDate) < getToday()) throw new Error("start_date는 현재보다 미래여야 합니다."); - if (getDate(endDate) <= getDate(startDate)) - throw new Error("end_date는 start_date보다 미래여야 합니다."); + if (isInvalidDate(body.start_date, body.end_date)) return -1; + if (getDate(startDate) < getToday()) return -2; + if (getDate(endDate) <= getDate(startDate)) return -3; const [postResult] = await conn.query(createPostSQL, [ body.room_id, @@ -147,6 +145,7 @@ export const createPostDao = async (body, userId) => { }); }); await conn.commit(); // 트랜잭션 커밋(DB 반영) + return { newPostId: newPostId, // 생성된 공지글 ID postType: body.type, @@ -165,26 +164,33 @@ export const createPostDao = async (body, userId) => { } }; -export const updatePostDao = async ({ postData, imgURLs, imgToDelete }) => { +export const updatePostDao = async ({ postData, imgURLs, imgToDelete }, postId) => { const conn = await pool.getConnection(); try { await conn.beginTransaction(); + const startDate = new Date(`20${postData.start_date}`); + const endDate = new Date(`20${postData.end_date}`); + + if (isInvalidDate(postData.start_date, postData.end_date)) return -1; + if (startDate < getNow()) return -2; + if (endDate <= startDate) return -3; + await conn.query(updatePostSQL, [ postData.title, postData.content, postData.start_date, postData.end_date, postData.question, - postData.id, + postId, ]); // 추가할 이미지 for (const url of imgURLs) { - await conn.query(createPostImgSQL, [url, postData.id]); + await conn.query(createPostImgSQL, [url, postId]); } // 삭제할 이미지 if (imgToDelete.length > 0) { for (const url of imgToDelete) { - await conn.query(deletePostImgSQL, [postData.id, url]); + await conn.query(deletePostImgSQL, [postId, url]); } } @@ -301,45 +307,111 @@ export const deleteUserDao = async (body) => { } }; -export const penaltyDao = async () => { - // UTC 기준 15시, 한국 기준 00시 정각 - schedule.scheduleJob("0 15 * * *", async function () { - let conn; - try { - conn = await pool.getConnection(); - console.log("test"); - const [roomIds] = await conn.query(allRoomsSQL); - for (const row of roomIds) { - const roomId = row.room_id; - - await conn.query(penaltySQL, [roomId]); - await conn.query(penaltyStateSQL, [roomId]); - await conn.query(addUserSubmitSQL, [roomId]); - } - conn.release(); - } catch (error) { - if (conn) conn.release(); - throw new Error("쿼리 실행에 실패하였습니다."); - } - }); +export const initializeSubmitByPostDAO = async (postId) => { + try { + const conn = await pool.getConnection(); + await conn.query(initializeSubmitByPostSQL, [postId]); + console.log("제출 모두 초기화 완료"); + conn.release(); + return true; + } catch (error) { + console.log("공지글에 모든 제출 초기화 에러"); + throw new BaseError(status.INTERNAL_SERVER_ERROR); + } +}; + +export const reserveImposePenaltyByPostDAO = async (postId, endDate) => { + try { + const conn = await pool.getConnection(); + const date = new Date(endDate); + const jobName = `#${postId}PostPenalty`; + console.log(date.toString()); + schedule.scheduleJob(jobName, date, async function () { + await conn.query(imposePenaltyByPostSQL, [postId]); + console.log(postId, "번 공지글 페널티 부여 실행"); + }); + console.log(postId, "번 공지글 페널티 부여 예약 성공"); + const jobList = schedule.scheduledJobs; + console.log(jobList); + conn.release(); + return true; + } catch (error) { + console.log("페널티 부여 에러"); + throw new BaseError(status.INTERNAL_SERVER_ERROR); + } +}; + +export const reserveImposePenaltyForEveryValidPostDAO = async () => { + try { + const conn = await pool.getConnection(); + const [posts] = await conn.query(getPostsBeforeEndDate); + + posts.forEach((post) => { + const postId = post.id; + const date = new Date(post.end_date); + const jobName = `#${postId}PostPenalty`; + console.log(postId, "번 공지글 마감기한: ", date.toString()); + schedule.scheduleJob(jobName, date, async function () { + await conn.query(imposePenaltyByPostSQL, [postId]); + console.log(postId, "번 공지글 페널티 부여 실행"); + }); + }); + console.log("마감기한이 지나지 않은 모든 공지글에 대한 페널티 부여 예약 성공"); + const jobList = schedule.scheduledJobs; + console.log(jobList); + conn.release(); + return true; + } catch (error) { + console.log("마감기한이 지나지 않은 모든 공지글에 대한 페널티 부여 예약 에러"); + throw new BaseError(status.INTERNAL_SERVER_ERROR); + } +}; + +export const cancelImposePenaltyByPostDAO = async (postId) => { + try { + const jobName = `#${postId}PostPenalty`; + schedule.cancelJob(jobName); + console.log(postId, "번 공지글 페널티 부여 예약 취소 완료"); + const jobList = schedule.scheduledJobs; + console.log(jobList); + return true; + } catch (error) { + console.log("기존 페널티 부여 일정 취소 에러"); + throw new BaseError(status.INTERNAL_SERVER_ERROR); + } }; export const userSubmitDao = async (roomId) => { - try{ + try { const conn = await pool.getConnection(); - + const [rows] = await conn.query(getPostCountSQL); - const countPost = rows[0]?.count || 0; - if(countPost === 0) return {massage : "공지가 없습니다."}; + const countPost = rows[0]?.count || 0; + if (countPost === 0) return { massage: "공지가 없습니다." }; const [userSubmissions] = await conn.query(userSubmitSQL, roomId); const [submitStates] = await conn.query(getSubmitStateSQL, roomId); - - conn.release(); - return { userSubmissions, submitStates } ; - }catch(error){ + conn.release(); + return { userSubmissions, submitStates }; + } catch (error) { console.log("확인 요청 조회 에러"); throw new BaseError(status.INTERNAL_SERVER_ERROR); } -} \ No newline at end of file +}; + +export const userRequestDao = async (body) => { + try { + const conn = await pool.getConnection(); + + if (body.type === "accept") await conn.query(userRequestAcceptSQL, body.roomId); + else if (body.type === "reject") await conn.query(userRequestRejectSQL, body.roomId); + else throw new Error("유효하지 않은 type입니다."); + + conn.release(); + return "요청 수행에 성공하였습니다."; + } catch (error) { + console.log("수락/거절 요청 수행 error"); + throw new BaseError(status.INTERNAL_SERVER_ERROR); + } +}; diff --git a/domains/admin/admin.service.js b/domains/admin/admin.service.js index 3f4d3e4..19f14a2 100644 --- a/domains/admin/admin.service.js +++ b/domains/admin/admin.service.js @@ -10,10 +10,20 @@ import { userProfileDao, userInviteDao, deleteUserDao, + initializeSubmitByPostDAO, + reserveImposePenaltyByPostDAO, + cancelImposePenaltyByPostDAO, userSubmitDao, + userRequestDao, } from "./admin.dao.js"; import { createShortUUID } from "./uuid.js"; -import { createRoomsDTO, updateRoomsDTO, createPostDTO, updatePostDTO, userSubmitDTO } from "./admin.dto.js"; +import { + createRoomsDTO, + updateRoomsDTO, + createPostDTO, + updatePostDTO, + userSubmitDTO, +} from "./admin.dto.js"; export const createRoomsService = async (body, userId) => { try { @@ -49,10 +59,17 @@ export const deleteRoomsService = async (body) => { export const createPostService = async (body, userId) => { try { - if(!body.room_id){ + if (!body.room_id) { throw new Error("공지방 ID가 필요합니다."); } const postData = await createPostDao(body, userId); + + if (postData == -1) throw new Error("날짜 형식이 올바르지 않습니다."); + if (postData == -2) throw new Error("start_date는 현재보다 미래여야 합니다."); + if (postData == -3) throw new Error("end_date는 start_date보다 미래여야 합니다."); + + await initializeSubmitByPostDAO(postData.newPostId); + await reserveImposePenaltyByPostDAO(postData.newPostId, `20${postData.endDate}`); return createPostDTO(postData); } catch (error) { console.error("공지글 생성하기 에러:", error); @@ -60,9 +77,16 @@ export const createPostService = async (body, userId) => { } }; -export const updatePostService = async (body) => { +export const updatePostService = async (body, postId) => { try { - const postData = await updatePostDao(body); + const postData = await updatePostDao(body, postId); + + if (postData == -1) throw new Error("날짜 형식이 올바르지 않습니다."); + if (postData == -2) throw new Error("start_date는 현재보다 미래여야 합니다."); + if (postData == -3) throw new Error("end_date는 start_date보다 미래여야 합니다."); + + await cancelImposePenaltyByPostDAO(postId); + await reserveImposePenaltyByPostDAO(postId, `20${postData.endDate}`); return updatePostDTO(postData); } catch (error) { console.error("공지글 수정하기 에러:", error); @@ -76,6 +100,7 @@ export const deletePostService = async (postId) => { throw new Error("삭제할 공지글의 ID가 필요합니다."); } const deleteRoomsData = await deletePostDao(postId); + await cancelImposePenaltyByPostDAO(postId); return deleteRoomsData; } catch (error) { console.error("공지글 삭제 에러:", error); @@ -102,20 +127,19 @@ export const userListService = async (nickname, roomId) => { throw new Error("조회할 공지방의 ID가 필요합니다."); } const result = await userListDao(nickname, roomId); - + if (!result.length) return []; - return result.map(user => ({ - userId : user.user_id, + return result.map((user) => ({ + userId: user.user_id, nickname: user.nickname, - profileImage: user.profile_image, - })); + profileImage: user.profile_image, + })); } catch (error) { console.error("유저 목록 조회 에러:", error); throw error; } }; - export const userProfileService = async (roomId, userId) => { try { if (!roomId || !userId) { @@ -150,13 +174,24 @@ export const deleteUserService = async (body) => { }; export const userSubmitService = async (roomId) => { - try{ - if(!roomId) throw new Error("요청 내역 조회를 위한 roomId가 필요합니다."); + try { + if (!roomId) throw new Error("요청 내역 조회를 위한 roomId가 필요합니다."); const { userSubmissions, submitStates } = await userSubmitDao(roomId); - const result = userSubmitDTO(userSubmissions, submitStates); - return result; - }catch(error){ + const result = userSubmitDTO(userSubmissions, submitStates); + return result; + } catch (error) { + throw error; + } +}; + +export const userRequestService = async (body) => { + try { + const validTypes = ["accept", "reject"]; + if (!validTypes.includes(body.type)) throw new Error("올바른 type을 입력하세요."); + if (!body.roomId) throw new Error("요청을 수행하기를 위한 roomId가 필요합니다."); + return await userRequestDao(body); + } catch (error) { throw error; } }; diff --git a/domains/admin/admin.sql.js b/domains/admin/admin.sql.js index fc7e250..a4f004e 100644 --- a/domains/admin/admin.sql.js +++ b/domains/admin/admin.sql.js @@ -151,10 +151,33 @@ export const addUserSubmitSQL = ` AND NOW() > DATE_ADD(p.end_date, INTERVAL 1 SECOND); `; -// 확인 요청 내역 조회 +export const imposePenaltyByPostSQL = ` + UPDATE \`user-room\` ur + JOIN submit s ON s.submit_state = 'NOT_COMPLETE' OR s.submit_state = 'PENDING' OR s.submit_state = 'REJECT' + JOIN post p ON p.id = ? AND p.id = s.post_id AND p.room_id = ur.room_id + SET ur.penalty_count = ur.penalty_count + 1, + s.penalty_state = true + WHERE s.user_id = ur.user_id; +`; + +export const initializeSubmitByPostSQL = ` + INSERT INTO submit (post_id, user_id, content, submit_state) + SELECT p.id AS post_id, ur.user_id, null AS content, 'NOT_COMPLETE' AS submit_state + FROM \`user-room\` ur + JOIN post p ON p.id = ? AND p.room_id = ur.room_id + JOIN room r ON p.room_id = r.id AND ur.user_id != r.admin_id +`; + +export const getPostsBeforeEndDate = ` + SELECT p.id, p.end_date + FROM post p + WHERE p.end_date > NOW() AND p.state = 'EXIST'; +`; + +// 확인 요청 내역 조회 export const getPostCountSQL = ` SELECT COUNT(*) AS count FROM post; -`; +`; export const userSubmitSQL = ` SELECT @@ -165,7 +188,7 @@ export const userSubmitSQL = ` JOIN room r ON p.room_id = r.id WHERE p.room_id = ? GROUP BY p.id; -`; +`; export const getSubmitStateSQL = ` SELECT u.profile_image, u.nickname, si.URL, s.submit_state @@ -174,5 +197,25 @@ export const getSubmitStateSQL = ` JOIN user u ON s.user_id = u.id LEFT JOIN \`submit-image\` si ON s.id = si.id WHERE p.room_id = ? AND s.submit_state IN ('PENDING', 'COMPLETE'); -`; +`; + +// 대기 중 요청 수락/거절 +export const userRequestAcceptSQL = ` + UPDATE submit + SET submit_state = 'COMPLETE' + WHERE submit_state = 'PENDING' AND post_id IN ( + SELECT p.id + FROM post p + WHERE p.room_id = ? + ); +`; +export const userRequestRejectSQL = ` + UPDATE submit + SET submit_state = 'REJECT' + WHERE submit_state = 'PENDING' AND post_id IN ( + SELECT p.id + FROM post p + WHERE p.room_id = ? + ); +`; diff --git a/domains/room/room.dto.js b/domains/room/room.dto.js index 52bb2d3..e5378d6 100644 --- a/domains/room/room.dto.js +++ b/domains/room/room.dto.js @@ -27,9 +27,12 @@ const formatSubmitState = (data) => { const formatDate = (date) => { const options = { - dateStyle: "short", - timeStyle: "short", - hour12: false, + year: "2-digit", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + hourCycle: "h23", }; return new Intl.DateTimeFormat("ko", options).format(new Date(date)); }; diff --git a/index.js b/index.js index 53a1fce..03286bb 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,7 @@ import { response } from "./config/response.js"; import { userRouter } from "./routes/user.route.js"; import { roomRouter } from "./routes/room.route.js"; import { adminRouter } from "./routes/admin.route.js"; -import { penaltyDao } from "./domains/admin/admin.dao.js"; +import { reserveImposePenaltyForEveryValidPostDAO } from "./domains/admin/admin.dao.js"; dotenv.config(); @@ -32,7 +32,7 @@ app.use((err, req, res, next) => { app.listen(app.get("port"), () => { console.log(`Example app listening on port ${app.get("port")}`); - penaltyDao(); + reserveImposePenaltyForEveryValidPostDAO(); }); app.get("/", (req, res) => { diff --git a/routes/admin.route.js b/routes/admin.route.js index 3cf832b..8575db2 100644 --- a/routes/admin.route.js +++ b/routes/admin.route.js @@ -13,7 +13,8 @@ import { userProfileController, userInviteController, deleteUserController, - userSubmitController + userSubmitController, + userRequestController, } from "../domains/admin/admin.controller.js"; export const adminRouter = express.Router(); @@ -29,4 +30,6 @@ adminRouter.get("/users", tokenAuth, expressAsyncHandler(userListController)); adminRouter.get("/profile", tokenAuth, expressAsyncHandler(userProfileController)); adminRouter.get("/invitation/:roomId", tokenAuth, expressAsyncHandler(userInviteController)); adminRouter.delete("/user-ban", tokenAuth, expressAsyncHandler(deleteUserController)); -adminRouter.get("/submit/:roomId", tokenAuth, expressAsyncHandler(userSubmitController)); \ No newline at end of file + +adminRouter.get("/submit/:roomId", tokenAuth, expressAsyncHandler(userSubmitController)); +adminRouter.patch("/submit/user-request", tokenAuth, expressAsyncHandler(userRequestController)); diff --git a/swagger/admin.swagger.yaml b/swagger/admin.swagger.yaml index 7055c97..61ae773 100644 --- a/swagger/admin.swagger.yaml +++ b/swagger/admin.swagger.yaml @@ -52,9 +52,9 @@ paths: result: type: object properties: - roomId : - type : number - description : 생성된 공지방ID + roomId: + type: number + description: 생성된 공지방ID roomImage: type: string description: 공지방 이미지 url @@ -73,7 +73,7 @@ paths: roomInviteUrl: type: string description: 초대 url - + patch: tags: - admin @@ -90,10 +90,10 @@ paths: application/json: schema: type: object - properties: + properties: roomId: type: integer - description: 삭제할 공지방 아이디 + description: 삭제할 공지방 아이디 responses: "200": description: 공지방 삭제 성공! @@ -113,10 +113,10 @@ paths: example: "success!" result: type: object - properties: - roomId : - type : number - description : 삭제된 공지방의 Id + properties: + roomId: + type: number + description: 삭제된 공지방의 Id /admin/rooms/{roomId}: patch: @@ -159,9 +159,9 @@ paths: max_penalty: type: number description: 최대 페널티 개수 - id: - type : number - description : 수정할 공지방 ID + id: + type: number + description: 수정할 공지방 ID responses: "200": description: 공지방 수정 성공! @@ -217,7 +217,7 @@ paths: type: object properties: roomId: - type: integer + type: integer description: 공지방 id type: type: string @@ -230,7 +230,7 @@ paths: description: 공지글 본문 start_date: type: string - description: 시작 기한 + description: 시작 기한 end_date: type: string description: 마감 기한 @@ -238,13 +238,13 @@ paths: type: string description: 퀴즈/미션 질문 quiz_answer: - type: string - description: 퀴즈 답 - imgURLs : - type : array - items : - type : string - description : 이미지 URL 배열 + type: string + description: 퀴즈 답 + imgURLs: + type: array + items: + type: string + description: 이미지 URL 배열 responses: "200": description: 공지글 생성 성공! @@ -266,12 +266,12 @@ paths: type: object properties: newPostId: - type : integer - description : 생성된 공지글 ID + type: integer + description: 생성된 공지글 ID postType: type: string - enum : [QUIZ,MISSION] - description: 공지글 타입(퀴즈/미션) + enum: [QUIZ, MISSION] + description: 공지글 타입(퀴즈/미션) postTitle: type: string description: 공지글 제목 @@ -287,12 +287,12 @@ paths: endDate: type: string description: 마감 기한 - question: - type : string - description: 퀴즈/미션 질문 - quiz_answer: - type: string - description: 퀴즈 답 + question: + type: string + description: 퀴즈/미션 질문 + quiz_answer: + type: string + description: 퀴즈 답 patch: tags: @@ -313,7 +313,7 @@ paths: properties: id: type: integer - description: 삭제할 공지글 아이디 + description: 삭제할 공지글 아이디 responses: "200": description: 공지글 삭제 성공! @@ -333,10 +333,10 @@ paths: example: "success!" result: type: object - properties: - postId : - type : number - description : 삭제된 공지글의 Id + properties: + deletedPostId: + type: number + description: 삭제된 공지글의 Id /admin/post/{postId}: patch: @@ -364,34 +364,34 @@ paths: schema: type: object properties: - title: - type: string - description: 공지글 제목 - content: - type: string - description: 공지글 본문 - start_date: - type: string - description: 시작 기한 - end_date: - type: string - description: 마감 기한 - question: - type: string - description: 퀴즈/미션 질문 - id: - type: integer - description: 수정할 공지글 아이디 - imgURLs : - type : array - items : - type : string - description : 추가할 이미지 URL 배열 - imgToDelete : - type : array - items : - type : string - description : 삭제할 이미지 URL 배열 + postData: + type: object + properties: + title: + type: string + description: 공지글 제목 + content: + type: string + description: 공지글 본문 + start_date: + type: string + description: 시작 기한 + end_date: + type: string + description: 마감 기한 + question: + type: string + description: 퀴즈/미션 질문 + imgURLs: + type: array + items: + type: string + description: 추가할 이미지 URL 배열 + imgToDelete: + type: array + items: + type: string + description: 삭제할 이미지 URL 배열 responses: "200": description: 공지글 수정 성공! @@ -430,9 +430,9 @@ paths: endDate: type: string description: 마감 기한 - question: - type : string - description: 퀴즈/미션 질문 + question: + type: string + description: 퀴즈/미션 질문 /admin/post/{postId}/unread: get: @@ -470,13 +470,13 @@ paths: example: "success!" result: type: object - properties: - profile_image : - type : string - description : 유저 프로필 사진 - nickname : - type : string - description : 유저 닉네임 + properties: + profile_image: + type: string + description: 유저 프로필 사진 + nickname: + type: string + description: 유저 닉네임 /admin/users: get: @@ -492,18 +492,18 @@ paths: parameters: - in: query name: nickname - required: false + required: false description: 사용자의 닉네임 schema: type: string - example : 제이 + example: 제이 - in: query name: roomId - required: true + required: true description: 사용자가 속한 room의 ID schema: type: integer - example : 1 + example: 1 responses: "200": description: 유저 검색 성공! @@ -523,16 +523,16 @@ paths: example: "success!" result: type: object - properties: - userId : - type : number - description : 유저 아이디 - nickname : - type : string - description : 유저 닉네임 - profile_image : - type : string - description : 유저 프로필 사진 + properties: + userId: + type: number + description: 유저 아이디 + nickname: + type: string + description: 유저 닉네임 + profile_image: + type: string + description: 유저 프로필 사진 /admin/profile: get: @@ -551,15 +551,15 @@ paths: required: true description: 공지방 ID schema: - type: integer - example : 6 + type: integer + example: 6 - in: query name: userId - required: true + required: true description: 유저 ID schema: type: integer - example : 1240 + example: 1240 responses: "200": description: 프로필 조회 성공! @@ -579,18 +579,18 @@ paths: example: "success!" result: type: object - properties: - nickname : - type : string - description : 유저 닉네임 - profile_image : - type : string - description : 유저 프로필 사진 - penalty_count : - type : integer - description : 패널티 개수 + properties: + nickname: + type: string + description: 유저 닉네임 + profile_image: + type: string + description: 유저 프로필 사진 + penalty_count: + type: integer + description: 패널티 개수 - /admin/invitation/{roomId} : + /admin/invitation/{roomId}: get: tags: - admin @@ -602,7 +602,7 @@ paths: security: - bearerAuth: [] parameters: - - name: roomId + - name: roomId in: path schema: type: integer @@ -626,23 +626,23 @@ paths: example: "success!" result: type: object - properties: - room_image : - type : string - description : 공지방 이미지 - room_invite_url : - type : string - description : 공지방 초대 url - room_name : - type : string - description : 공지방 이름 - room_password : - type : string - description : 공지방 비밀번호 - admin_nickname : - type : string - description : 관리자 닉네임 - + properties: + room_image: + type: string + description: 공지방 이미지 + room_invite_url: + type: string + description: 공지방 초대 url + room_name: + type: string + description: 공지방 이름 + room_password: + type: string + description: 공지방 비밀번호 + admin_nickname: + type: string + description: 관리자 닉네임 + /admin/rooms/user-Ban: delete: tags: @@ -664,9 +664,9 @@ paths: nickname: type: string description: 강퇴할 유저 닉네임 - room_id: + room_id: type: integer - description : 강퇴할 유저가 속한 공지방 id + description: 강퇴할 유저가 속한 공지방 id responses: "200": description: 유저 강퇴 성공! @@ -686,109 +686,152 @@ paths: example: "success!" result: type: object - properties: - message : + properties: + message: type: string - example : 유저 강퇴에 성공하였습니다. + example: 유저 강퇴에 성공하였습니다. -/admin/submit/{roomId} : - get: - tags: - - admin - summary: 확인 요청 내역 조회(대기 or 승인 완료) - description: 확인 요청 내역 조회 - operationId: userInvite - consumes: - - application/json - security: - - bearerAuth: [] - parameters: - - name: roomId - in: path - schema: - type: integer - required: true - responses: - "200": - description: 확인 요청 내역 조회 성공! - content: - application/json: - schema: - type: object - properties: - isSuccess: - type: boolean - example: true - code: - type: integer - example: 200 - message: - type: string - example: "success!" - result: - type: object - properties: - userSubmissions: - type: array - items: - type: object - properties: - title: - type: string - example: "test9" - start_date: - type: string - format: date-time - example: "2024-08-11T02:24:00.000Z" - end_date: - type: string - format: date-time - example: "2024-08-10T02:24:03.000Z" - content: - type: string - example: "penaltytestcontent" - room_image: - type: string - nullable: true - example: null - pending_count: - type: integer - example: 2 - pendingStates: - type: array - items: - type: object - properties: - profile_image: - type: string - nullable: true - example: null - nickname: - type: string - example: "제이" - URL: - type: string - example: "https://s3.ap-northeast-2.amazonaws.com/read.me-bucket/43dbd401a34c7be69c9be7b123a13e67_exampleimage.png" - submit_state: - type: string - example: "PENDING" - completeStates: - type: array - items: - type: object - properties: - profile_image: - type: string - nullable: true - example: null - nickname: - type: string - example: "제이" - URL: - type: string - example: "https://s3.ap-northeast-2.amazonaws.com/read.me-bucket/45425843b018b4ac8d235ad195b35a54_exampleimage.png" - submit_state: - type: string - example: "COMPLETE" - +/admin/submit/{roomId}: + get: + tags: + - admin + summary: 확인 요청 내역 조회(대기 or 승인 완료) + description: 확인 요청 내역 조회 + operationId: getSubmitList + consumes: + - application/json + security: + - bearerAuth: [] + parameters: + - name: roomId + in: path + schema: + type: integer + required: true + responses: + "200": + description: 확인 요청 내역 조회 성공! + content: + application/json: + schema: + type: object + properties: + isSuccess: + type: boolean + example: true + code: + type: integer + example: 200 + message: + type: string + example: "success!" + result: + type: object + properties: + userSubmissions: + type: array + items: + type: object + properties: + title: + type: string + example: "test9" + start_date: + type: string + format: date-time + example: "2024-08-11T02:24:00.000Z" + end_date: + type: string + format: date-time + example: "2024-08-10T02:24:03.000Z" + content: + type: string + example: "penaltytestcontent" + room_image: + type: string + nullable: true + example: null + pending_count: + type: integer + example: 2 + pendingStates: + type: array + items: + type: object + properties: + profile_image: + type: string + nullable: true + example: null + nickname: + type: string + example: "제이" + URL: + type: string + example: "https://s3.ap-northeast-2.amazonaws.com/read.me-bucket/43dbd401a34c7be69c9be7b123a13e67_exampleimage.png" + submit_state: + type: string + example: "PENDING" + completeStates: + type: array + items: + type: object + properties: + profile_image: + type: string + nullable: true + example: null + nickname: + type: string + example: "제이" + URL: + type: string + example: "https://s3.ap-northeast-2.amazonaws.com/read.me-bucket/45425843b018b4ac8d235ad195b35a54_exampleimage.png" + submit_state: + type: string + example: "COMPLETE" +/admin/submit/user-request: + patch: + tags: + - admin + summary: 대기중 요청 수락/거절 + description: 대기중 요청 수락/거절 + operationId: submitAcceptOrReject + consumes: + - application/json + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + type: + type: string + description: 요청 수락/거절 + roomId: + type: integer + description: 공지방 아이디 + responses: + "200": + description: 제출 요청 수행 성공! + content: + application/json: + schema: + type: object + properties: + isSuccess: + type: boolean + example: true + code: + type: integer + example: 200 + message: + type: string + example: "success!" + result: + type: string + example: "요청 수행에 성공하였습니다." diff --git a/utils/date.js b/utils/date.js index a7a68e0..eadcc90 100644 --- a/utils/date.js +++ b/utils/date.js @@ -12,3 +12,9 @@ export const getDate = (date) => { export const isInvalidDate = (startDate, endDate) => isNaN(new Date(`20${startDate}`)) || isNaN(new Date(`20${endDate}`)); + +export const getNow = () => { + const locale = new Date().toLocaleString("en-US", { timeZone: "Asia/Seoul" }); + const today = new Date(locale); + return today; +};