public 기능 완료
This commit is contained in:
@@ -141,6 +141,22 @@ exports.createEvent = async (req, res) => {
|
||||
return res.status(404).json({ message: '클럽을 찾을 수 없습니다.' });
|
||||
}
|
||||
const endDateVal = endDate ? new Date(endDate) : null;
|
||||
// 랜덤 해시 생성 함수
|
||||
const generateRandomHash = async () => {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let hash;
|
||||
let exists = true;
|
||||
while (exists) {
|
||||
hash = Array.from({ length: 16 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||||
// 중복 체크
|
||||
exists = await Event.findOne({ where: { publicHash: hash } });
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
const publicHash = await generateRandomHash();
|
||||
const accessPassword = req.body.accessPassword || null;
|
||||
|
||||
const event = await Event.create({
|
||||
clubId,
|
||||
title,
|
||||
@@ -153,7 +169,9 @@ exports.createEvent = async (req, res) => {
|
||||
entryFee: entryFee || 0,
|
||||
gameCount: gameCount || 3,
|
||||
laneCount: laneCount || 1,
|
||||
status: status || '활성'
|
||||
status: status || '활성',
|
||||
publicHash,
|
||||
accessPassword
|
||||
});
|
||||
|
||||
const createdEvent = await Event.findOne({
|
||||
@@ -180,6 +198,248 @@ exports.createEvent = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 이벤트 조회 및 비밀번호 인증
|
||||
exports.getEventByPublicHash = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { password } = req.body || {};
|
||||
|
||||
try {
|
||||
// 1. 이벤트 조회
|
||||
const event = await Event.findOne({
|
||||
where: { publicHash },
|
||||
include: [{
|
||||
model: Club,
|
||||
attributes: ['id', 'name']
|
||||
}]
|
||||
});
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' });
|
||||
|
||||
// 2. 비밀번호 필요 여부 및 인증
|
||||
if (event.accessPassword) {
|
||||
if (!password) return res.status(401).json({ needPassword: true, message: '비밀번호 필요' });
|
||||
if (event.accessPassword !== password) return res.status(401).json({ needPassword: true, message: '비밀번호 불일치' });
|
||||
}
|
||||
|
||||
// 3. 전체 회원 명단 추출 (clubId 사용, clubId는 응답에 포함 X)
|
||||
const allMembers = await Member.findAll({
|
||||
where: { clubId: event.clubId },
|
||||
attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'average']
|
||||
});
|
||||
|
||||
// 4. 참가자 명단 추출 (참가자 테이블 + Member 조인)
|
||||
const participants = await EventParticipant.findAll({
|
||||
where: { eventId: event.id, status: { [Op.ne]: '취소' } },
|
||||
include: [
|
||||
{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'average'] }
|
||||
]
|
||||
});
|
||||
// 5. 참가 신청 가능 명단(아직 참가하지 않은 회원)
|
||||
// Sequelize 인스턴스에서 dataValues 추출
|
||||
const allMemberObjs = allMembers.map(m => m.dataValues);
|
||||
// '취소'가 아닌 status가 하나라도 있으면 참가자로 간주 (중복 제거)
|
||||
const participantMemberIds = [
|
||||
...new Set(
|
||||
participants
|
||||
.filter(p => p.dataValues.status !== '취소')
|
||||
.map(p => parseInt(p.dataValues.memberId))
|
||||
)
|
||||
];
|
||||
const availableMembers = allMemberObjs.filter(m => !participantMemberIds.includes(parseInt(m.memberId)));
|
||||
// 6. 팀 정보 등 추가 조회 (기존 코드 유지)
|
||||
const teams = await EventTeam.findAll({
|
||||
where: { eventId: event.id },
|
||||
order: [['teamNumber', 'ASC']],
|
||||
include: [
|
||||
{
|
||||
model: EventTeamMember,
|
||||
include: [
|
||||
{
|
||||
model: EventParticipant,
|
||||
include: [{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap', 'average'] }]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 점수
|
||||
const scores = await EventScore.findAll({ where: { eventId: event.id } });
|
||||
|
||||
// 팀별 그룹핑
|
||||
let teamList = [];
|
||||
if (teams.length > 0) {
|
||||
teamList = teams.map(team => ({
|
||||
teamId: team.id,
|
||||
teamNumber: team.teamNumber,
|
||||
handicap: team.handicap,
|
||||
members: (team.EventTeamMembers || []).map(tm => {
|
||||
const participant = tm.EventParticipant;
|
||||
const member = participant?.Member;
|
||||
const scoreObj = scores.find(s => s.participantId === participant?.id);
|
||||
return {
|
||||
participantId: participant?.id,
|
||||
memberId: member?.memberId,
|
||||
name: member?.name || `참가자 ${participant?.id}`,
|
||||
gender: member?.gender,
|
||||
memberType: member?.memberType,
|
||||
average: member?.average || 0,
|
||||
handicap: member?.handicap || 0,
|
||||
status: participant?.status,
|
||||
paymentStatus: participant?.paymentStatus,
|
||||
comment: participant?.comment,
|
||||
isGuest: member?.memberType === '게스트',
|
||||
score: scoreObj ? scoreObj.score : null
|
||||
};
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
// 참가자 리스트(팀 없는 경우)
|
||||
let participantList = [];
|
||||
participantList = participants.map(p => {
|
||||
const member = p.Member;
|
||||
const scoreObj = scores.find(s => s.participantId === p.id);
|
||||
return {
|
||||
participantId: p.id,
|
||||
memberId: p.memberId,
|
||||
name: member?.name || `참가자 ${p.id}`,
|
||||
gender: member?.gender,
|
||||
memberType: member?.memberType,
|
||||
handicap: member?.handicap || 0,
|
||||
average: member?.average || 0,
|
||||
status: p.status,
|
||||
paymentStatus: p.paymentStatus,
|
||||
comment: p.comment,
|
||||
isGuest: member?.memberType === '게스트',
|
||||
score: scoreObj ? scoreObj.score : null
|
||||
};
|
||||
});
|
||||
// 반환 데이터
|
||||
res.json({
|
||||
clubName: event.Club.name,
|
||||
event: {
|
||||
id: event.id,
|
||||
title: event.title,
|
||||
description: event.description,
|
||||
eventType: event.eventType,
|
||||
startDate: event.startDate,
|
||||
endDate: event.endDate,
|
||||
location: event.location,
|
||||
gameCount: event.gameCount,
|
||||
registrationDeadline: event.registrationDeadline,
|
||||
maxParticipants: event.maxParticipants,
|
||||
participantFee: event.participantFee,
|
||||
status: event.status
|
||||
},
|
||||
teams: teamList,
|
||||
participants: participantList,
|
||||
availableMembers,
|
||||
needPassword: !!event.accessPassword
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('공개 이벤트 조회 오류:', e);
|
||||
res.status(500).json({ message: '이벤트 정보를 가져오는 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 URL(publicHash) 재생성
|
||||
exports.regeneratePublicHash = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
try {
|
||||
const event = await Event.findOne({ where: { id } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
// 랜덤 해시 생성 (중복 없는 값)
|
||||
const generateRandomHash = async () => {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let hash;
|
||||
let exists = true;
|
||||
while (exists) {
|
||||
hash = Array.from({ length: 16 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||||
exists = await Event.findOne({ where: { publicHash: hash } });
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
const newHash = await generateRandomHash();
|
||||
await event.update({ publicHash: newHash });
|
||||
res.json({ publicHash: newHash });
|
||||
} catch (e) {
|
||||
console.error('공개 URL 재생성 오류:', e);
|
||||
res.status(500).json({ message: '공개 URL 재생성 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 참가자 등록/수정
|
||||
exports.registerPublicParticipant = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { participantId, memberId, status, paymentStatus, comment } = req.body || {};
|
||||
try {
|
||||
const event = await Event.findOne({ where: { publicHash } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' });
|
||||
// 기존 참가자 있는지 검사(이름+연락처 기준)
|
||||
let member = await Member.findOne({ where: { id:memberId } });
|
||||
// if (!member) {
|
||||
// member = await Member.create({ memberId, memberType: '일반' });
|
||||
// }
|
||||
let participant = await EventParticipant.findOne({ where: { id: participantId, memberId: member.id } });
|
||||
if (!participant) {
|
||||
participant = await EventParticipant.create({ eventId: event.id, memberId: member.id, comment, status, paymentStatus });
|
||||
} else {
|
||||
await participant.update({ comment, status, paymentStatus });
|
||||
}
|
||||
res.json({ message: '참가 신청/수정이 완료되었습니다.' });
|
||||
} catch (e) {
|
||||
console.error('공개 참가자 등록 오류:', e);
|
||||
res.status(500).json({ message: '참가 신청/수정 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 게스트 추가
|
||||
exports.addPublicGuest = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { name, password } = req.body || {};
|
||||
try {
|
||||
const event = await Event.findOne({ where: { publicHash } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' });
|
||||
if (event.accessPassword) {
|
||||
if (!password || event.accessPassword !== password) return res.status(401).json({ message: '비밀번호가 올바르지 않습니다.' });
|
||||
}
|
||||
// 게스트 멤버 생성
|
||||
const member = await Member.create({ name, memberType: '게스트' });
|
||||
await EventParticipant.create({ eventId: event.id, memberId: member.id });
|
||||
res.json({ message: '게스트가 추가되었습니다.' });
|
||||
} catch (e) {
|
||||
console.error('공개 게스트 추가 오류:', e);
|
||||
res.status(500).json({ message: '게스트 추가 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 점수 수정
|
||||
exports.updatePublicScore = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { participantId, score, password } = req.body || {};
|
||||
try {
|
||||
const event = await Event.findOne({ where: { publicHash } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status !== '준비' && event.status !== '활성') return res.status(403).json({ message: '점수 수정이 불가능한 상태입니다.' });
|
||||
if (event.accessPassword) {
|
||||
if (!password || event.accessPassword !== password) return res.status(401).json({ message: '비밀번호가 올바르지 않습니다.' });
|
||||
}
|
||||
let eventScore = await EventScore.findOne({ where: { eventId: event.id, participantId } });
|
||||
if (!eventScore) {
|
||||
eventScore = await EventScore.create({ eventId: event.id, participantId, score });
|
||||
} else {
|
||||
await eventScore.update({ score });
|
||||
}
|
||||
res.json({ message: '점수가 저장되었습니다.' });
|
||||
} catch (e) {
|
||||
console.error('공개 점수 수정 오류:', e);
|
||||
res.status(500).json({ message: '점수 저장 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 정보 업데이트
|
||||
exports.updateEvent = async (req, res) => {
|
||||
try {
|
||||
@@ -194,7 +454,9 @@ exports.updateEvent = async (req, res) => {
|
||||
entryFee,
|
||||
gameCount,
|
||||
laneCount,
|
||||
status
|
||||
status,
|
||||
publicHash,
|
||||
accessPassword
|
||||
} = req.body;
|
||||
const eventId = req.body.id;
|
||||
let endDate = req.body.endDate;
|
||||
@@ -228,9 +490,10 @@ exports.updateEvent = async (req, res) => {
|
||||
entryFee: entryFee !== undefined ? entryFee : event.entryFee,
|
||||
gameCount: gameCount || event.gameCount,
|
||||
laneCount: laneCount || event.laneCount,
|
||||
status: status || event.status
|
||||
status: status || event.status,
|
||||
publicHash: publicHash || event.publicHash,
|
||||
accessPassword: accessPassword || null
|
||||
};
|
||||
|
||||
await event.update(updateData);
|
||||
|
||||
const updatedEvent = await Event.findOne({
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
// publicController.js
|
||||
// 공개 이벤트 관련 API만 담당 (비밀번호 인증, 점수입력 등)
|
||||
const { Event, EventParticipant, Member, Club, EventScore, ClubMember, EventTeam, EventTeamMember, sequelize } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const { signPublicEventToken, verifyPublicEventToken } = require('../utils/publicJwt');
|
||||
|
||||
// 공개 이벤트 최초 진입 (토큰 또는 비밀번호 인증)
|
||||
exports.publicEventEntry = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { password } = req.body || {};
|
||||
const auth = req.headers.authorization;
|
||||
let event;
|
||||
try {
|
||||
event = await Event.findOne({
|
||||
where: { publicHash },
|
||||
include: [{ model: Club, attributes: ['id', 'name'] }]
|
||||
});
|
||||
if (!event) {
|
||||
return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
}
|
||||
if (event.status === '취소') {
|
||||
return res.status(403).json({ message: '취소된 이벤트입니다.' });
|
||||
}
|
||||
|
||||
let token = null;
|
||||
let isAuthenticated = false;
|
||||
|
||||
// 1. 토큰 인증 우선
|
||||
if (auth && auth.startsWith('Bearer ')) {
|
||||
try {
|
||||
const payload = verifyPublicEventToken(auth.slice(7));
|
||||
if (payload.publicHash === event.publicHash) {
|
||||
isAuthenticated = true;
|
||||
}
|
||||
} catch (e) {
|
||||
// 토큰 인증 실패: isAuthenticated는 false
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 토큰 인증 실패 시
|
||||
if (!isAuthenticated) {
|
||||
// 비밀번호가 필요한 이벤트
|
||||
if (event.accessPassword) {
|
||||
if (!password) {
|
||||
return res.status(401).json({ message: '비밀번호가 필요합니다.' });
|
||||
}
|
||||
if (password !== event.accessPassword) {
|
||||
return res.status(401).json({ message: '비밀번호가 일치하지 않습니다.' });
|
||||
}
|
||||
}
|
||||
// 비밀번호가 없거나(공개 이벤트) or 비밀번호 인증 성공
|
||||
isAuthenticated = true;
|
||||
token = signPublicEventToken({ publicHash: event.publicHash });
|
||||
}
|
||||
|
||||
// 3. 인증 성공 시 데이터 반환
|
||||
if (isAuthenticated) {
|
||||
const data = await exports.getEventByPublicHash(event, token);
|
||||
return res.json(data);
|
||||
}
|
||||
// 4. 나머지는 모두 인증 실패
|
||||
return res.status(401).json({ message: '인증이 필요합니다.' });
|
||||
} catch (e) {
|
||||
console.error('[publicEventEntry] 공개 이벤트 최초 진입 오류', { publicHash, error: e });
|
||||
res.status(500).json({ message: '이벤트 정보를 가져오는 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 이벤트 조회 (내부 데이터 반환 전용)
|
||||
exports.getEventByPublicHash = async (event, token = null) => {
|
||||
try {
|
||||
const allMembers = await Member.findAll({
|
||||
where: { clubId: event.clubId },
|
||||
attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'average']
|
||||
});
|
||||
const participants = await EventParticipant.findAll({
|
||||
where: { eventId: event.id, status: { [Op.ne]: '취소' } },
|
||||
include: [ { model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'average'] } ]
|
||||
});
|
||||
const allMemberObjs = allMembers.map(m => m.dataValues);
|
||||
const participantMemberIds = [
|
||||
...new Set(participants.filter(p => p.dataValues.status !== '취소').map(p => parseInt(p.dataValues.memberId)))
|
||||
];
|
||||
// 상태별로 미참가자/게스트 추가 제한
|
||||
let availableMembers = [];
|
||||
if (event.status === '준비') {
|
||||
availableMembers = allMemberObjs.filter(m => !participantMemberIds.includes(parseInt(m.memberId)));
|
||||
} // 준비 아닐 땐 빈 배열(신청/게스트 추가 불가)
|
||||
// 팀 정보
|
||||
const teams = await EventTeam.findAll({
|
||||
where: { eventId: event.id },
|
||||
order: [['teamNumber', 'ASC']],
|
||||
include: [{
|
||||
model: EventTeamMember,
|
||||
include: [{
|
||||
model: EventParticipant,
|
||||
where: { status: { [Op.ne]: '취소' } },
|
||||
include: [{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap', 'average'] }]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
const scores = await EventScore.findAll({ where: { eventId: event.id } });
|
||||
// 참가자 id → 참가자 객체 Map
|
||||
const participantMap = new Map();
|
||||
participants.forEach(p => participantMap.set(p.id, p));
|
||||
|
||||
// 팀에 소속된 참가자 id Set
|
||||
const assignedParticipantIds = new Set();
|
||||
const teamList = teams.map(team => ({
|
||||
teamId: team.id,
|
||||
teamNumber: team.teamNumber,
|
||||
handicap: team.handicap,
|
||||
members: (team.EventTeamMembers || []).map(tm => {
|
||||
const participant = tm.EventParticipant;
|
||||
if (participant?.id) assignedParticipantIds.add(participant.id);
|
||||
const member = participant?.Member;
|
||||
// 각 게임별 점수 배열 생성
|
||||
const scoresArr = Array(event.gameCount).fill(null);
|
||||
scores.filter(s => s.participantId === participant?.id).forEach(s => {
|
||||
if (s.gameNumber && s.gameNumber >= 1 && s.gameNumber <= event.gameCount) {
|
||||
scoresArr[s.gameNumber - 1] = s.score;
|
||||
}
|
||||
});
|
||||
return {
|
||||
participantId: participant?.id,
|
||||
memberId: member?.memberId,
|
||||
name: member?.name || `참가자 ${participant?.id}`,
|
||||
gender: member?.gender,
|
||||
memberType: member?.memberType,
|
||||
average: member?.average || 0,
|
||||
handicap: member?.handicap || 0,
|
||||
status: participant?.status,
|
||||
paymentStatus: participant?.paymentStatus,
|
||||
comment: participant?.comment,
|
||||
isGuest: member?.memberType === '게스트',
|
||||
scores: scoresArr
|
||||
};
|
||||
})
|
||||
}));
|
||||
|
||||
// 팀에 소속되지 않은 참가자(미배정)
|
||||
const unassignedMembers = participants
|
||||
.filter(p => !assignedParticipantIds.has(p.id))
|
||||
.map(p => {
|
||||
const member = p.Member;
|
||||
// 각 게임별 점수 배열 생성
|
||||
const scoresArr = Array(event.gameCount).fill(null);
|
||||
scores.filter(s => s.participantId === p.id).forEach(s => {
|
||||
if (s.gameNumber && s.gameNumber >= 1 && s.gameNumber <= event.gameCount) {
|
||||
scoresArr[s.gameNumber - 1] = s.score;
|
||||
}
|
||||
});
|
||||
return {
|
||||
participantId: p.id,
|
||||
memberId: p.memberId,
|
||||
name: member?.name || `참가자 ${p.id}`,
|
||||
gender: member?.gender,
|
||||
memberType: member?.memberType,
|
||||
handicap: member?.handicap || 0,
|
||||
average: member?.average || 0,
|
||||
status: p.status,
|
||||
paymentStatus: p.paymentStatus,
|
||||
comment: p.comment,
|
||||
isGuest: member?.memberType === '게스트',
|
||||
scores: scoresArr
|
||||
};
|
||||
});
|
||||
let participantList = participants.map(p => {
|
||||
const member = p.Member;
|
||||
// 각 게임별 점수 배열 생성
|
||||
const scoresArr = Array(event.gameCount).fill(null);
|
||||
scores.filter(s => s.participantId === p.id).forEach(s => {
|
||||
if (s.gameNumber && s.gameNumber >= 1 && s.gameNumber <= event.gameCount) {
|
||||
scoresArr[s.gameNumber - 1] = s.score;
|
||||
}
|
||||
});
|
||||
return {
|
||||
participantId: p.id,
|
||||
memberId: p.memberId,
|
||||
name: member?.name || `참가자 ${p.id}`,
|
||||
gender: member?.gender,
|
||||
memberType: member?.memberType,
|
||||
handicap: member?.handicap || 0,
|
||||
average: member?.average || 0,
|
||||
status: p.status,
|
||||
paymentStatus: p.paymentStatus,
|
||||
comment: p.comment,
|
||||
isGuest: member?.memberType === '게스트',
|
||||
scores: scoresArr
|
||||
};
|
||||
});
|
||||
return {
|
||||
clubName: event.Club.name,
|
||||
event: {
|
||||
id: event.id,
|
||||
title: event.title,
|
||||
description: event.description,
|
||||
eventType: event.eventType,
|
||||
startDate: event.startDate,
|
||||
endDate: event.endDate,
|
||||
location: event.location,
|
||||
gameCount: event.gameCount,
|
||||
registrationDeadline: event.registrationDeadline,
|
||||
maxParticipants: event.maxParticipants,
|
||||
participantFee: event.participantFee,
|
||||
status: event.status
|
||||
},
|
||||
teams: teamList,
|
||||
participants: participantList,
|
||||
unassignedMembers,
|
||||
availableMembers,
|
||||
needPassword: !!event.accessPassword,
|
||||
...(token ? { token } : {})
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('공개 이벤트 조회 오류:', e);
|
||||
throw new Error('이벤트 정보를 가져오는 중 오류가 발생했습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 참가자 등록/수정
|
||||
exports.registerPublicParticipant = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { participantId, memberId, status, paymentStatus, comment } = req.body || {};
|
||||
try {
|
||||
const event = await Event.findOne({ where: { publicHash } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' });
|
||||
let member = await Member.findOne({ where: { id: memberId } });
|
||||
let participant = await EventParticipant.findOne({ where: { id: participantId, memberId: member.id } });
|
||||
if (!participant) {
|
||||
participant = await EventParticipant.create({ eventId: event.id, memberId: member.id, comment, status, paymentStatus });
|
||||
} else {
|
||||
await participant.update({ comment, status, paymentStatus });
|
||||
}
|
||||
res.json({ message: '참가 신청/수정이 완료되었습니다.' });
|
||||
} catch (e) {
|
||||
console.error('공개 참가자 등록 오류:', e);
|
||||
res.status(500).json({ message: '참가 신청/수정 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 게스트 추가
|
||||
exports.addPublicGuest = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
const { name, phone, gender, average } = req.body || {};
|
||||
try {
|
||||
const event = await Event.findOne({ where: { publicHash } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' });
|
||||
// publicAuth 미들웨어로 인증됨
|
||||
const member = await Member.create({
|
||||
name,
|
||||
phone,
|
||||
gender,
|
||||
clubId: event.clubId,
|
||||
memberType: '게스트',
|
||||
average: average || 0
|
||||
});
|
||||
const participant = await EventParticipant.create({ eventId: event.id, memberId: member.id });
|
||||
res.json({
|
||||
message: '게스트가 추가되었습니다.',
|
||||
data: {
|
||||
memberId: member.id,
|
||||
participantId: participant.id,
|
||||
name: member.name,
|
||||
phone: member.phone,
|
||||
gender: member.gender,
|
||||
memberType: member.memberType,
|
||||
handicap: member.handicap,
|
||||
average: member.average,
|
||||
group: '미신청'
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('공개 게스트 추가 오류:', e);
|
||||
res.status(500).json({ message: '게스트 추가 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 공개 점수 수정
|
||||
exports.updatePublicScore = async (req, res) => {
|
||||
const { publicHash } = req.params;
|
||||
let { participantId, gameNumber, score, handicap, teamNumber } = req.body || {};
|
||||
score = Number(score);
|
||||
handicap = Number(handicap) || 0; // undefined/null이면 0
|
||||
teamNumber = teamNumber ?? null;
|
||||
try {
|
||||
const event = await Event.findOne({ where: { publicHash } });
|
||||
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
if (event.status !== '준비' && event.status !== '활성') return res.status(403).json({ message: '점수 수정이 불가능한 상태입니다.' });
|
||||
// publicAuth 미들웨어로 인증됨
|
||||
let eventScore = await EventScore.findOne({ where: { eventId: event.id, participantId, gameNumber } });
|
||||
if (!eventScore) {
|
||||
eventScore = await EventScore.create({ eventId: event.id, participantId, gameNumber, teamNumber, score, handicap, totalScore: score + handicap });
|
||||
} else {
|
||||
await eventScore.update({ teamNumber, score, handicap, totalScore: score + handicap });
|
||||
}
|
||||
res.json({ message: '점수가 저장되었습니다.' });
|
||||
} catch (e) {
|
||||
console.error('공개 점수 수정 오류:', e);
|
||||
res.status(500).json({ message: '점수 저장 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user