오류 수정, 통계 보강

This commit is contained in:
2025-06-08 20:21:37 +09:00
parent 1a3706d7a5
commit 141a0de6d0
42 changed files with 2645 additions and 799 deletions
+208 -51
View File
@@ -5,6 +5,7 @@
const { Event, EventParticipant, Member, Club, EventScore, ClubMember, EventTeam, EventTeamMember } = require('../models');
const { Op } = require('sequelize');
const { sequelize } = require('../models');
const averageService = require('../services/averageService');
// 이벤트 목록 조회
exports.getAllEvents = async (req, res) => {
@@ -18,7 +19,7 @@ exports.getAllEvents = async (req, res) => {
const events = await Event.findAll({
where: {
clubId,
status: { [Op.ne]: '삭제' }
status: { [Op.ne]: 'deleted' }
},
include: [
{
@@ -29,7 +30,7 @@ exports.getAllEvents = async (req, res) => {
model: EventParticipant,
attributes: ['id', 'status', 'paymentStatus'],
where: {
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
},
required: false,
include: [{
@@ -69,7 +70,7 @@ exports.getEventById = async (req, res) => {
const event = await Event.findOne({
where: {
id: eventId,
status: { [Op.ne]: '삭제' }
status: { [Op.ne]: 'deleted' }
},
include: [
{
@@ -79,12 +80,12 @@ exports.getEventById = async (req, res) => {
{
model: EventParticipant,
attributes: ['id', 'status', 'paymentStatus'],
where: { status: { [Op.ne]: '취소' } },
where: { status: { [Op.ne]: 'canceled' } },
required: false,
include: [
{
model: Member,
attributes: ['id', 'name', 'average']
attributes: ['id', 'name']
}
]
}
@@ -95,10 +96,48 @@ exports.getEventById = async (req, res) => {
return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
}
const eventData = event.toJSON();
// 참가자들의 동적 에버리지 계산
if (eventData.participants && eventData.participants.length > 0) {
const participantsWithDynamicAverage = await Promise.all(
eventData.participants.map(async (participant) => {
if (participant.Member && participant.Member.id) {
try {
const averageData = await averageService.calculateMemberAverageByClubSetting(
participant.Member.id,
eventData.clubId
);
return {
...participant,
Member: {
...participant.Member,
average: averageData.average
}
};
} catch (error) {
console.error(`회원 ${participant.Member.id}의 에버리지 계산 오류:`, error);
return {
...participant,
Member: {
...participant.Member,
average: 0
}
};
}
}
return participant;
})
);
eventData.participants = participantsWithDynamicAverage;
}
const formattedEvent = {
...event.toJSON(),
participantCount: event.participants ? event.participants.length : 0,
clubName: event.Club ? event.Club.name : ''
...eventData,
participantCount: eventData.participants ? eventData.participants.length : 0,
clubName: eventData.Club ? eventData.Club.name : ''
};
res.json(formattedEvent);
@@ -224,28 +263,80 @@ exports.getEventByPublicHash = async (req, res) => {
// 3. 전체 회원 명단 추출 (clubId 사용, clubId는 응답에 포함 X)
const allMembers = await Member.findAll({
where: { clubId: event.clubId },
attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'average']
attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender']
});
// 전체 회원의 동적 에버리지 계산
const allMembersWithAverage = await Promise.all(
allMembers.map(async (member) => {
try {
const averageData = await averageService.calculateMemberAverageByClubSetting(
member.dataValues.memberId,
event.clubId
);
return {
...member.dataValues,
average: averageData.average
};
} catch (error) {
console.error(`회원 ${member.dataValues.memberId}의 에버리지 계산 오류:`, error);
return {
...member.dataValues,
average: 0
};
}
})
);
// 4. 참가자 명단 추출 (참가자 테이블 + Member 조인)
const participants = await EventParticipant.findAll({
where: { eventId: event.id, status: { [Op.ne]: '취소' } },
where: { eventId: event.id, status: { [Op.ne]: 'canceled' } },
include: [
{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'average'] }
{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender'] }
]
});
// 참가자들의 동적 에버리지 계산
const participantsWithAverage = await Promise.all(
participants.map(async (participant) => {
if (participant.Member && participant.Member.dataValues.memberId) {
try {
const averageData = await averageService.calculateMemberAverageByClubSetting(
participant.Member.dataValues.memberId,
event.clubId
);
// 기존 객체 복사
const participantObj = {...participant.dataValues};
participantObj.Member = {
...participant.Member.dataValues,
average: averageData.average
};
return participantObj;
} catch (error) {
console.error(`참가자 회원 ${participant.Member.dataValues.memberId}의 에버리지 계산 오류:`, error);
const participantObj = {...participant.dataValues};
participantObj.Member = {
...participant.Member.dataValues,
average: 0
};
return participantObj;
}
}
return participant.dataValues;
})
);
// 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))
participantsWithAverage
.filter(p => p.status !== '취소')
.map(p => parseInt(p.memberId))
)
];
const availableMembers = allMemberObjs.filter(m => !participantMemberIds.includes(parseInt(m.memberId)));
const availableMembers = allMembersWithAverage.filter(m => !participantMemberIds.includes(parseInt(m.memberId)));
// 6. 팀 정보 등 추가 조회 (기존 코드 유지)
const teams = await EventTeam.findAll({
where: { eventId: event.id },
@@ -256,7 +347,7 @@ exports.getEventByPublicHash = async (req, res) => {
include: [
{
model: EventParticipant,
include: [{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap', 'average'] }]
include: [{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap'] }]
}
]
}
@@ -269,35 +360,60 @@ exports.getEventByPublicHash = async (req, res) => {
// 팀별 그룹핑
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 teamsWithMembers = [];
for (const team of teams) {
const membersWithAverage = [];
for (const tm of (team.EventTeamMembers || [])) {
const participant = tm.EventParticipant;
const member = participant?.Member;
const scoreObj = scores.find(s => s.participantId === participant?.id);
return {
// 회원의 동적 에버리지 계산
let dynamicAverage = 0;
if (member && member.memberId) {
try {
const averageData = await averageService.calculateMemberAverageByClubSetting(
member.memberId,
event.clubId
);
dynamicAverage = averageData.average;
} catch (error) {
console.error(`팀 멤버 ${member.memberId}의 에버리지 계산 오류:`, error);
}
}
membersWithAverage.push({
participantId: participant?.id,
memberId: member?.memberId,
name: member?.name || `참가자 ${participant?.id}`,
gender: member?.gender,
memberType: member?.memberType,
average: member?.average || 0,
average: dynamicAverage,
handicap: member?.handicap || 0,
status: participant?.status,
paymentStatus: participant?.paymentStatus,
comment: participant?.comment,
isGuest: member?.memberType === '게스트',
score: scoreObj ? scoreObj.score : null
};
})
}));
});
}
teamsWithMembers.push({
teamId: team.id,
teamNumber: team.teamNumber,
handicap: team.handicap,
members: membersWithAverage
});
}
teamList = teamsWithMembers;
}
// 참가자 리스트(팀 없는 경우)
let participantList = [];
participantList = participants.map(p => {
// 참가자들의 동적 에버리지 계산
participantList = participantsWithAverage.map(p => {
const member = p.Member;
const scoreObj = scores.find(s => s.participantId === p.id);
return {
@@ -307,7 +423,7 @@ exports.getEventByPublicHash = async (req, res) => {
gender: member?.gender,
memberType: member?.memberType,
handicap: member?.handicap || 0,
average: member?.average || 0,
average: member?.average || 0, // 이미 participantsWithAverage에서 동적 계산된 값
status: p.status,
paymentStatus: p.paymentStatus,
comment: p.comment,
@@ -507,7 +623,7 @@ exports.updateEvent = async (req, res) => {
model: EventParticipant,
attributes: ['id', 'status'],
where: {
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
},
required: false
}
@@ -567,7 +683,7 @@ exports.deleteEvent = async (req, res) => {
{
where: {
eventId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
}
}
);
@@ -669,7 +785,7 @@ exports.addEventScore = async (req, res) => {
where: {
id: participantId,
eventId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
}
});
@@ -733,6 +849,9 @@ exports.addEventScore = async (req, res) => {
average: averageScore
});
// 점수 등록 후 에버리지 캐시 무효화
await averageService.invalidateMemberAverageCache(participant.memberId);
res.status(201).json({
message: '점수가 등록되었습니다.',
score: {
@@ -762,11 +881,7 @@ exports.updateEventScore = async (req, res) => {
score,
handicap = 0
} = req.body;
console.log('Updating score:', {
params: { eventId, scoreId },
body: { gameNumber, score, handicap }
});
if (!eventId || !scoreId || !gameNumber || score === undefined || score === null) {
return res.status(400).json({
@@ -821,6 +936,9 @@ exports.updateEventScore = async (req, res) => {
totalScore: score + handicap
});
// 캐시 무효화 추가
await averageService.invalidateMemberAverageCache(scoreRecord.participantId);
// 업데이트된 레코드 조회
const updatedScore = await EventScore.findOne({
where: {
@@ -835,10 +953,10 @@ exports.updateEventScore = async (req, res) => {
});
} catch (error) {
console.error('점수 수정 오류:', error);
console.error('점수 수정 오류:', error, JSON.stringify(error, Object.getOwnPropertyNames(error)));
res.status(500).json({
message: '점수 수정 중 오류가 발생했습니다.',
error: error.message
error: error.message || error.toString() || error
});
}
};
@@ -891,6 +1009,16 @@ exports.deleteEventScore = async (req, res) => {
await score.destroy();
// 점수 삭제 시 해당 회원의 에버리지 캡시 무효화
if (score.participant && score.participant.Member) {
try {
await averageService.invalidateMemberAverageCache(score.participant.Member.id);
console.log(`회원 ${score.participant.Member.id}의 에버리지 캡시 무효화 완료`);
} catch (error) {
console.error(`회원 ${score.participant.Member.id}의 에버리지 캡시 무효화 오류:`, error);
}
}
res.json({
message: '점수가 삭제되었습니다.',
score: {
@@ -1057,7 +1185,7 @@ exports.registerForEvent = async (req, res) => {
where: {
eventId,
userId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
}
});
@@ -1094,7 +1222,7 @@ exports.removeParticipant = async (req, res) => {
where: {
eventId,
userId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
},
include: [{
model: Event,
@@ -1132,7 +1260,7 @@ exports.updateParticipantStatus = async (req, res) => {
where: {
eventId,
userId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
},
include: [{
model: Event,
@@ -1233,12 +1361,12 @@ exports.getEventParticipants = async (req, res) => {
const participants = await EventParticipant.findAll({
where: {
eventId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
},
include: [
{
model: Member,
attributes: ['id', 'name', 'email', 'phone', 'memberType', 'average']
attributes: ['id', 'name', 'email', 'phone', 'memberType']
}
],
order: [
@@ -1246,13 +1374,42 @@ exports.getEventParticipants = async (req, res) => {
]
});
const formattedParticipants = participants.map(participant => ({
...participant.toJSON(),
}));
// 참가자들의 동적 에버리지 계산
const participantsWithAverage = await Promise.all(
participants.map(async (participant) => {
const participantData = participant.toJSON();
if (participantData.Member && participantData.Member.id) {
try {
const averageData = await averageService.calculateMemberAverageByClubSetting(
participantData.Member.id,
clubId
);
return {
...participantData,
Member: {
...participantData.Member,
average: averageData.average
}
};
} catch (error) {
console.error(`회원 ${participantData.Member.id}의 에버리지 계산 오류:`, error);
return {
...participantData,
Member: {
...participantData.Member,
average: 0
}
};
}
}
return participantData;
})
);
res.json({
message: '참가자 목록 조회가 완료되었습니다.',
participants: formattedParticipants
participants: participantsWithAverage
});
} catch (error) {
console.error('참가자 목록 조회 오류:', error);
@@ -1323,7 +1480,7 @@ exports.addEventParticipant = async (req, res) => {
where: {
eventId,
memberId,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
}
});
@@ -1567,7 +1724,7 @@ exports.deleteEventParticipant = async (req, res) => {
where: {
eventId,
id,
status: { [Op.ne]: '취소' }
status: { [Op.ne]: 'canceled' }
},
include: [
{