From e57c45585cc9cff87f77838de13e4c8b00274a32 Mon Sep 17 00:00:00 2001
From: Wo-ogie <siwall0105@gmail.com>
Date: Tue, 5 Dec 2023 11:33:18 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B0=80=EC=9E=A5=20=EB=A7=8E=EC=9D=B4?=
 =?UTF-8?q?=20=EC=84=A0=ED=83=9D=ED=95=9C=20=EC=8B=9C=EA=B0=84=20=EC=A1=B0?=
 =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 controllers/meeting.js  | 15 +++++++++++++++
 errors/meetingErrors.js |  8 ++++++++
 repository/meeting.js   | 31 +++++++++++++++++++++++++++++++
 routes/meeting.js       |  3 +++
 4 files changed, 57 insertions(+)
 create mode 100644 repository/meeting.js

diff --git a/controllers/meeting.js b/controllers/meeting.js
index 27532ee..0ab9479 100644
--- a/controllers/meeting.js
+++ b/controllers/meeting.js
@@ -1,10 +1,12 @@
 const bcrypt = require('bcrypt');
 const { Meeting, Participant, Schedule } = require('../models');
+const meetingRepositotry = require('../repository/meeting');
 const {
   createMeetingNotFoundError,
   createMeetingIsAlreadyClosedError,
   createPasswordNotMatchedError,
   createPasswordIsNullError,
+  createMostConfirmedTimeNotFoundError,
 } = require('../errors/meetingErrors');
 const MeetingResponse = require('../dto/response/meetingResponse');
 const MeetingWithParticipantsResponse = require('../dto/response/meetingWithParticipantsResponse');
@@ -175,6 +177,19 @@ exports.getMeetingDetailById = async (req, res, next) => {
   }
 };
 
+exports.getMostConfirmedTime = async (req, res, next) => {
+  const { purpose } = req.query;
+  if (!purpose) {
+    return res.status(400).json({ message: 'Purpose is required' });
+  }
+  try {
+    const result = await meetingRepositotry.getMostConfirmedTime(purpose);
+    return res.json(result);
+  } catch (error) {
+    return next(error);
+  }
+};
+
 function validateMeetingIsNotClosed(meeting) {
   if (meeting.isClosed === true) {
     throw createMeetingIsAlreadyClosedError();
diff --git a/errors/meetingErrors.js b/errors/meetingErrors.js
index 5b1e71f..b52602f 100644
--- a/errors/meetingErrors.js
+++ b/errors/meetingErrors.js
@@ -21,3 +21,11 @@ exports.createPasswordIsNullError = () => {
   error.status = 400;
   return error;
 };
+
+exports.createMostConfirmedTimeNotFoundError = () => {
+  const error = new Error(
+    '데이터가 충분하지 않아 통계 정보를 찾을 수 없습니다.',
+  );
+  error.status = 404;
+  return error;
+};
diff --git a/repository/meeting.js b/repository/meeting.js
new file mode 100644
index 0000000..d634c3f
--- /dev/null
+++ b/repository/meeting.js
@@ -0,0 +1,31 @@
+const Sequelize = require('sequelize');
+const { sequelize } = require('../models/index');
+const {
+  createMostConfirmedTimeNotFoundError,
+} = require('../errors/meetingErrors');
+
+exports.getMostConfirmedTime = async (purpose) => {
+  const query = `
+      SELECT EXTRACT(HOUR FROM m.confirmed_time) AS confirmed_time_hour,
+             COUNT(1)                            AS confirmed_time_count
+      FROM meetings m
+      WHERE m.is_closed
+        AND m.purpose = :purpose
+        AND m.confirmed_time IS NOT NULL
+      GROUP BY confirmed_time_hour
+      ORDER BY confirmed_time_count DESC;
+  `;
+  const params = {
+    replacements: { purpose },
+    type: Sequelize.QueryTypes.SELECT,
+    plain: true,
+  };
+  const result = await sequelize.query(query, params);
+  if (!result) {
+    throw createMostConfirmedTimeNotFoundError();
+  }
+  return {
+    hour: result.confirmed_time_hour,
+    count: result.confirmed_time_count,
+  };
+};
diff --git a/routes/meeting.js b/routes/meeting.js
index d32bd4e..4ad462d 100644
--- a/routes/meeting.js
+++ b/routes/meeting.js
@@ -5,6 +5,7 @@ const {
   entry,
   getMeetingById,
   getMeetingDetailById,
+  getMostConfirmedTime,
   closeMeeting,
   confirmTime,
 } = require('../controllers/meeting');
@@ -13,6 +14,8 @@ const router = express.Router();
 
 router.post('/', createMeeting);
 
+router.get('/most-confirmed-time', getMostConfirmedTime);
+
 router.post('/:meetingId/entry', entry);
 
 router.get('/:meetingId', isAuthenticated, getMeetingById);
-- 
GitLab