diff --git a/backend/controllers/eventController.js b/backend/controllers/eventController.js index d0dfe9f..6c11b1f 100644 --- a/backend/controllers/eventController.js +++ b/backend/controllers/eventController.js @@ -1260,14 +1260,30 @@ exports.getEventParticipants = async (req, res) => { } }; -// 이벤트 참가자 등록 +// 참가자 정보 핵심 update 함수 (중복 제거) +async function updateEventParticipantCore({ id, eventId, memberId, clubId, status, paymentStatus, comment }) { + const [updatedCount] = await EventParticipant.update( + { status, paymentStatus, comment }, + { + where: { + id, + eventId, + memberId + } + } + ); + return updatedCount; +} + +// 이벤트 참가자 등록 (이미 있으면 update, 없으면 create) exports.addEventParticipant = async (req, res) => { try { const { clubId, memberId, status, - paymentStatus + paymentStatus, + comment } = req.body; const eventId = req.params.id; if (!eventId || !clubId || !memberId) { @@ -1311,51 +1327,58 @@ exports.addEventParticipant = async (req, res) => { } }); + let participant; + let isUpdate = false; if (existingParticipant) { - return res.status(400).json({ message: '이미 참가 신청한 사용자입니다.' }); - } - - // 참가자 수 제한 확인 - if (event.maxParticipants > 0) { - const currentParticipants = await EventParticipant.count({ - where: { - eventId, - status: { [Op.ne]: '취소' } - } + // 이미 있으면 update (핵심 update 함수 활용) + await updateEventParticipantCore({ + id: existingParticipant.id, + eventId, + memberId, + clubId, + status: status || existingParticipant.status, + paymentStatus: paymentStatus || existingParticipant.paymentStatus, + comment: comment || existingParticipant.comment + }); + participant = await EventParticipant.findOne({ + where: { id: existingParticipant.id }, + include: [ + { + model: Member, + attributes: ['id', 'name'] + } + ] + }); + isUpdate = true; + } else { + // 없으면 create + participant = await EventParticipant.create({ + eventId, + memberId, + status: status || '참가예정', + paymentStatus: paymentStatus || '미납', + comment: comment || null, + createdAt: new Date(), + updatedAt: new Date() + }); + participant = await EventParticipant.findOne({ + where: { id: participant.id }, + include: [ + { + model: Member, + attributes: ['id', 'name'] + } + ] }); - - if (currentParticipants >= event.maxParticipants) { - return res.status(400).json({ message: '참가자 수가 초과되었습니다.' }); - } } - const participant = await EventParticipant.create({ - eventId, - memberId, - status: status || '참가예정', - paymentStatus: paymentStatus || '미납', - createdAt: new Date(), - updatedAt: new Date() - }); - - // 참가자 정보 조회 - const participantWithDetails = await EventParticipant.findOne({ - where: { id: participant.id }, - include: [ - { - model: Member, - attributes: ['id', 'name'] - } - ] - }); - res.status(201).json({ - message: '참가자가 등록되었습니다.', - participant: participantWithDetails + message: isUpdate ? '기존 참가자 정보를 수정했습니다.' : '참가자가 등록되었습니다.', + participant }); } catch (error) { - console.error('참가자 등록 오류:', error); - res.status(500).json({ message: '참가자 등록 중 오류가 발생했습니다.' }); + console.error('참가자 등록/수정 오류:', error); + res.status(500).json({ message: '참가자 등록/수정 중 오류가 발생했습니다.' }); } }; @@ -1363,7 +1386,7 @@ exports.addEventParticipant = async (req, res) => { exports.updateEventParticipant = async (req, res) => { try { const eventId = req.params.id; - const { id, clubId, memberId, status, paymentStatus } = req.body; + const { id, clubId, memberId, status, paymentStatus, comment } = req.body; if (!eventId || !clubId || !memberId || !id) { return res.status(400).json({ message: '이벤트 ID, 클럽 ID, 사용자 ID가 필요합니다.' }); @@ -1384,7 +1407,7 @@ exports.updateEventParticipant = async (req, res) => { // 참가자 정보 수정 const [updatedCount] = await EventParticipant.update( - { status, paymentStatus }, + { status, paymentStatus, comment }, { where: { id, diff --git a/backend/models/EventParticipant.js b/backend/models/EventParticipant.js index ca83795..3bb0eec 100644 --- a/backend/models/EventParticipant.js +++ b/backend/models/EventParticipant.js @@ -55,6 +55,13 @@ const EventParticipant = sequelize.define('EventParticipant', { }, { tableName: 'EventParticipants', comment: '이벤트별 참가자 정보를 저장하는 테이블', + indexes: [ + { + unique: true, + fields: ['eventId', 'memberId'], + name: 'unique_event_member' + } + ], timestamps: true }); diff --git a/backend/models/index.js b/backend/models/index.js index 462f7d7..717116f 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -27,8 +27,8 @@ const syncModels = async () => { // await sequelize.query('SET FOREIGN_KEY_CHECKS = 1'); // 운영 alter: true - 구조변경만 alter처리 - await sequelize.sync({ alter: true }); - // await sequelize.sync(); + // await sequelize.sync({ alter: true }); + await sequelize.sync(); } catch (error) { console.error('모델 동기화 중 오류 발생:', error); } diff --git a/backend/seeders/2025042302-sample-club-subscriptions.js b/backend/seeders/2025042302-sample-club-subscriptions.js index f168c82..8c85535 100644 --- a/backend/seeders/2025042302-sample-club-subscriptions.js +++ b/backend/seeders/2025042302-sample-club-subscriptions.js @@ -4,7 +4,7 @@ module.exports = { const clubSubscriptions = []; for (let clubId = 1; clubId <= 5; clubId++) { const planId = Math.floor(Math.random() * 2) + 1; // 랜덤으로 플랜 1, 2 중 하나 선택 - const startDate = new Date(2025, 3, 1 + clubId); + const startDate = new Date(2025, 4, 1 + clubId); const endDate = new Date(startDate.getFullYear(), startDate.getMonth()+1, startDate.getDate()); clubSubscriptions.push({ clubId, diff --git a/backend/seeders/20250424-init-menus.js b/backend/seeders/20250424-init-menus.js index 80ed2a2..760fa89 100644 --- a/backend/seeders/20250424-init-menus.js +++ b/backend/seeders/20250424-init-menus.js @@ -43,7 +43,6 @@ module.exports = { visible: true, parentId: clubMenuId, featureCode: 'member_management', - requiredSubscriptionTier: 'basic', createdAt: new Date(), updatedAt: new Date() }, diff --git a/frontend/src/components/event/EventDetailsDialog.vue b/frontend/src/components/event/EventDetailsDialog.vue index 30780e6..98973df 100644 --- a/frontend/src/components/event/EventDetailsDialog.vue +++ b/frontend/src/components/event/EventDetailsDialog.vue @@ -7,70 +7,81 @@ :modal="true" class="event-details-dialog" > - + + + 기본정보 + 참가자 관리 + 점수 관리 + 팀 편성 + + - -
-
- - -
- -
-
-

이벤트 정보

+ +
+
+ + +
+
-
-

시작일: {{ formattedStartDate }}

-

종료일: {{ formattedEndDate }}

-

장소: {{ eventModel.location }}

-

- 공개 URL: - -

+
+ 공개 비밀번호: + {{ eventModel.publicPassword }}
- -
-

설명

-

{{ eventModel.description || '설명이 없습니다.' }}

+
+ 설명 +
{{ eventModel.description }}
-
-
+ - - - - + + + + - - - - - - - - - + + + + + + + + + + - +
@@ -89,11 +89,7 @@