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({
|
||||
|
||||
Reference in New Issue
Block a user