diff --git a/backend/controllers/fileUploadController.js b/backend/controllers/fileUploadController.js index 59fc43a..8b46de3 100644 --- a/backend/controllers/fileUploadController.js +++ b/backend/controllers/fileUploadController.js @@ -183,7 +183,7 @@ exports.parseEventFile = async (req, res) => { console.error(`회원 ${member.id}의 에버리지 계산 오류:`, error); } - memberMap.set(member.name, { + memberMap.set(member.name.trim(), { id: member.id, name: member.name, gender: member.gender, @@ -340,7 +340,7 @@ function processRawData(rawData, memberMap) { const row = rawData[i]; if (row.length < 2) continue; // 이름: 한글 2자 이상 - const name = colMap.name !== undefined ? row[colMap.name] : row.find(cell => /^[가-힣a-z]{2,}$/i.test(cell)); + const name = (colMap.name !== undefined ? row[colMap.name] : row.find(cell => /^[가-힣a-z]{2,}$/i.test(cell)))?.toString().trim(); if (!name) continue; const teamNumber = colMap.team !== undefined ? row[colMap.team] : ''; const scores = (colMap.scores || []).map(idx => parseInt(row[idx], 10)).filter(n => !isNaN(n)); @@ -348,7 +348,7 @@ function processRawData(rawData, memberMap) { const total = colMap.total !== undefined ? parseInt(row[colMap.total], 10) : null; const avg = colMap.avg !== undefined ? parseFloat(row[colMap.avg]) : null; // 회원 매핑 (이름 기준) - const memberMatch = memberMap && memberMap.get && name ? memberMap.get(name) : null; + const memberMatch = memberMap && memberMap.get && name ? memberMap.get(name.trim()) : null; participants.push({ name, teamNumber, @@ -526,6 +526,28 @@ exports.saveEventFileData = async (req, res) => { try { // 트랜잭션 시작 const result = await sequelize.transaction(async (t) => { + // 이벤트 타입/상태 정규화 (모델 enum 호환) + const normalizeEventType = (v) => { + if (!v) return 'regular'; + const s = String(v).toLowerCase(); + if (['regular','일반'].includes(s)) return 'regular'; + if (['lightning','번개'].includes(s)) return 'lightning'; + if (['practice','연습'].includes(s)) return 'practice'; + if (['exchange','교류전'].includes(s)) return 'exchange'; + if (['event','행사'].includes(s)) return 'event'; + return 'other'; + }; + const normalizeStatus = (v) => { + if (!v) return 'completed'; + const s = String(v).toLowerCase(); + if (['active','활성'].includes(s)) return 'active'; + if (['ready','대기','준비','draft'].includes(s)) return 'ready'; + if (['completed','완료','done','finished'].includes(s)) return 'completed'; + if (['canceled','cancelled','취소'].includes(s)) return 'canceled'; + if (['deleted','삭제'].includes(s)) return 'deleted'; + return 'completed'; + }; + // 1. 이벤트 생성 또는 업데이트 let event; if (eventData.id) { @@ -542,13 +564,13 @@ exports.saveEventFileData = async (req, res) => { await event.update({ title: eventData.title, description: eventData.description || '', - eventType: eventData.eventType, + eventType: normalizeEventType(eventData.eventType), startDate: new Date(eventData.startDate), endDate: eventData.endDate ? new Date(eventData.endDate) : null, location: eventData.location || '', gameCount: eventData.gameCount || 3, maxParticipants: eventData.maxParticipants || 0, - status: eventData.status || '완료' + status: normalizeStatus(eventData.status) }, { transaction: t }); } else { // 새 이벤트 생성 @@ -556,13 +578,13 @@ exports.saveEventFileData = async (req, res) => { clubId, title: eventData.title, description: eventData.description || '', - eventType: eventData.eventType, + eventType: normalizeEventType(eventData.eventType), startDate: new Date(eventData.startDate), endDate: eventData.endDate ? new Date(eventData.endDate) : null, location: eventData.location || '', gameCount: eventData.gameCount || 3, maxParticipants: eventData.maxParticipants || 0, - status: eventData.status || '완료', + status: normalizeStatus(eventData.status), publicHash: generateRandomHash() }, { transaction: t }); } @@ -635,8 +657,8 @@ exports.saveEventFileData = async (req, res) => { eventParticipant = await EventParticipant.create({ eventId: event.id, memberId: participant.memberId, - status: '참가확정', - paymentStatus: '납부완료', + status: 'confirmed', + paymentStatus: 'paid', teamId: teamId, comment: '' }, { transaction: t }); @@ -645,9 +667,10 @@ exports.saveEventFileData = async (req, res) => { // 새 회원 생성 및 참가자 등록 const newMember = await Member.create({ name: participant.name, - gender: participant.gender || '남성', - memberType: '정회원', - status: '활성' + // normalize enum values to match Member model + gender: (participant.gender === 'female' || participant.gender === '여성') ? 'female' : 'male', + memberType: 'guest', + status: 'active' }, { transaction: t }); // 클럽 회원으로 등록 @@ -667,7 +690,7 @@ exports.saveEventFileData = async (req, res) => { eventParticipant = await EventParticipant.create({ eventId: event.id, memberId: newMember.id, - status: '참가확정', + status: 'confirmed', teamId: teamId, comment: '' }, { transaction: t }); @@ -705,20 +728,29 @@ exports.saveEventFileData = async (req, res) => { transaction: t }); - // 새 점수 등록 - for (const scoreData of participant.scores) { - // 핵디캡 값이 없는 경우 참가자의 핵디캡 값 사용 - const handicap = scoreData.handicap !== undefined ? scoreData.handicap : (participant.handicap || 0); - // 총점수가 없는 경우 원점수 사용 - const totalScore = scoreData.totalScore !== undefined ? scoreData.totalScore : scoreData.score; - + // 새 점수 등록 (숫자 배열/객체 배열 모두 지원) + const scoreItems = Array.isArray(participant.scores) + ? participant.scores.map((s, idx) => ( + typeof s === 'number' + ? { gameNumber: idx + 1, score: s } + : s + )) + : []; + + for (const s of scoreItems) { + const gameNumber = Number(s.gameNumber ?? 0) || 0; + const baseScore = Number(s.score ?? 0) || 0; + const handicap = s.handicap !== undefined ? Number(s.handicap) : Number(participant.handicap || 0); + const totalScore = s.totalScore !== undefined ? Number(s.totalScore) : baseScore; + if (gameNumber <= 0) continue; + await EventScore.create({ eventId: event.id, participantId: eventParticipant.id, - gameNumber: scoreData.gameNumber, - score: scoreData.score, - handicap: handicap, - totalScore: totalScore, + gameNumber, + score: baseScore, + handicap, + totalScore, teamNumber: participant.teamNumber || null }, { transaction: t }); }