삭제처리 수정
This commit is contained in:
@@ -687,7 +687,7 @@ exports.updateEvent = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 삭제 (소프트 삭제)
|
||||
// 이벤트 삭제 (소프트 삭제: 숨김 처리)
|
||||
exports.deleteEvent = async (req, res) => {
|
||||
try {
|
||||
const eventId = req.params.id;
|
||||
@@ -716,9 +716,9 @@ exports.deleteEvent = async (req, res) => {
|
||||
return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
|
||||
}
|
||||
|
||||
// 참가자 정보도 함께 삭제 처리
|
||||
// 참가자 소프트 삭제(취소 상태로 표시)
|
||||
await EventParticipant.update(
|
||||
{ status: '취소' },
|
||||
{ status: 'canceled' },
|
||||
{
|
||||
where: {
|
||||
eventId,
|
||||
@@ -727,10 +727,11 @@ exports.deleteEvent = async (req, res) => {
|
||||
}
|
||||
);
|
||||
|
||||
await event.update({ status: '삭제' });
|
||||
// 이벤트 숨김 처리(표준 상태값 사용)
|
||||
await event.update({ status: 'deleted' });
|
||||
|
||||
res.json({
|
||||
message: '이벤트가 삭제되었습니다.',
|
||||
message: '이벤트가 숨김(소프트 삭제)되었습니다.',
|
||||
event: {
|
||||
id: event.id,
|
||||
title: event.title,
|
||||
@@ -744,6 +745,50 @@ exports.deleteEvent = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 완전 삭제 (하드 삭제): 팀배정, 점수, 참가자, 팀, 이벤트 순서로 물리 삭제
|
||||
exports.deleteEventPermanently = async (req, res) => {
|
||||
try {
|
||||
const eventId = Number(req.params.id);
|
||||
const { clubId } = req.body || {};
|
||||
const cid = Number(clubId);
|
||||
|
||||
if (!eventId || !cid) {
|
||||
return res.status(400).json({ message: '이벤트 ID와 클럽 ID가 필요합니다.' });
|
||||
}
|
||||
|
||||
await sequelize.transaction(async (t) => {
|
||||
const event = await Event.findOne({ where: { id: eventId, clubId: cid }, transaction: t });
|
||||
if (!event) {
|
||||
throw new Error('이벤트를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
// 팀멤버 삭제
|
||||
const teams = await EventTeam.findAll({ where: { eventId }, transaction: t });
|
||||
const teamIds = teams.map((tm) => tm.id);
|
||||
if (teamIds.length > 0) {
|
||||
await EventTeamMember.destroy({ where: { eventTeamId: teamIds }, transaction: t });
|
||||
}
|
||||
|
||||
// 점수 삭제
|
||||
await EventScore.destroy({ where: { eventId }, transaction: t });
|
||||
|
||||
// 참가자 삭제
|
||||
await EventParticipant.destroy({ where: { eventId }, transaction: t });
|
||||
|
||||
// 팀 삭제
|
||||
await EventTeam.destroy({ where: { eventId }, transaction: t });
|
||||
|
||||
// 이벤트 삭제
|
||||
await Event.destroy({ where: { id: eventId, clubId: cid }, transaction: t });
|
||||
});
|
||||
|
||||
res.json({ message: '이벤트 및 관련 데이터가 완전히 삭제되었습니다.' });
|
||||
} catch (error) {
|
||||
console.error('이벤트 완전 삭제 오류:', error);
|
||||
res.status(500).json({ message: '이벤트 완전 삭제 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 점수 조회
|
||||
exports.getEventScores = async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -50,6 +50,79 @@ const fileFilter = (req, file, cb) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 업로드 저장 롤백: 이벤트 및 관련 데이터 삭제 (옵션: 게스트 정리)
|
||||
exports.rollbackEventImport = async (req, res) => {
|
||||
try {
|
||||
const { eventId, clubId, includeGuests } = req.body || {};
|
||||
const eid = Number(eventId);
|
||||
const cid = Number(clubId);
|
||||
if (!eid || !cid) {
|
||||
return res.status(400).json({ message: '유효한 eventId, clubId가 필요합니다.' });
|
||||
}
|
||||
|
||||
await sequelize.transaction(async (t) => {
|
||||
// 이벤트 검증 (동일 클럽)
|
||||
const event = await Event.findOne({ where: { id: eid, clubId: cid }, transaction: t });
|
||||
if (!event) {
|
||||
throw new Error('대상 이벤트를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
// 관련 팀멤버 삭제
|
||||
const teams = await EventTeam.findAll({ where: { eventId: eid }, transaction: t });
|
||||
const teamIds = teams.map((tm) => tm.id);
|
||||
if (teamIds.length > 0) {
|
||||
await EventTeamMember.destroy({ where: { eventTeamId: teamIds }, transaction: t });
|
||||
}
|
||||
|
||||
// 점수 삭제
|
||||
await EventScore.destroy({ where: { eventId: eid }, transaction: t });
|
||||
|
||||
// 참가자 삭제 전, 게스트 후보 수집
|
||||
let guestMemberIds = [];
|
||||
if (includeGuests) {
|
||||
const participants = await EventParticipant.findAll({ where: { eventId: eid }, transaction: t });
|
||||
const memberIds = participants.map((p) => p.memberId).filter(Boolean);
|
||||
if (memberIds.length > 0) {
|
||||
const members = await Member.findAll({ where: { id: memberIds, clubId: cid, memberType: 'guest' }, transaction: t });
|
||||
guestMemberIds = members.map((m) => m.id);
|
||||
}
|
||||
}
|
||||
|
||||
// 참가자 삭제
|
||||
await EventParticipant.destroy({ where: { eventId: eid }, transaction: t });
|
||||
|
||||
// 팀 삭제
|
||||
await EventTeam.destroy({ where: { eventId: eid }, transaction: t });
|
||||
|
||||
// 이벤트 삭제
|
||||
await Event.destroy({ where: { id: eid, clubId: cid }, transaction: t });
|
||||
|
||||
// 게스트 정리: 다른 참가 기록이 없고, 동일 클럽의 조인만 있는 경우 제거
|
||||
if (includeGuests && guestMemberIds.length > 0) {
|
||||
// 다른 참가 기록이 없는 멤버만 필터
|
||||
const others = await EventParticipant.findAll({ where: { memberId: guestMemberIds }, transaction: t });
|
||||
const stillUsed = new Set(others.map((p) => p.memberId));
|
||||
const deletable = guestMemberIds.filter((id) => !stillUsed.has(id));
|
||||
if (deletable.length > 0) {
|
||||
// 클럽 조인 제거
|
||||
const club = await Club.findOne({ where: { id: cid }, transaction: t });
|
||||
if (club) {
|
||||
// removeMember 지원 여부에 따라 destroy 직접 수행
|
||||
await sequelize.models.ClubMembers?.destroy?.({ where: { clubId: cid, memberId: deletable }, transaction: t }).catch(() => {});
|
||||
}
|
||||
// 멤버 삭제
|
||||
await Member.destroy({ where: { id: deletable, clubId: cid, memberType: 'guest' }, transaction: t });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
res.json({ message: '롤백이 완료되었습니다.' });
|
||||
} catch (error) {
|
||||
console.error('롤백 오류:', error);
|
||||
res.status(500).json({ message: `롤백 중 오류가 발생했습니다: ${error.message}` });
|
||||
}
|
||||
};
|
||||
|
||||
const upload = multer({
|
||||
storage: storage,
|
||||
fileFilter: fileFilter,
|
||||
@@ -531,6 +604,12 @@ exports.saveEventFileData = async (req, res) => {
|
||||
try {
|
||||
// 트랜잭션 시작
|
||||
const result = await sequelize.transaction(async (t) => {
|
||||
// 프런트에서 전달할 수 있는 핸디 포함 여부 플래그 (기본: true)
|
||||
const scoresIncludeHandicap = (
|
||||
(typeof req.body.scoresIncludeHandicap === 'boolean') ? req.body.scoresIncludeHandicap
|
||||
: (eventData && typeof eventData.scoresIncludeHandicap === 'boolean') ? eventData.scoresIncludeHandicap
|
||||
: true
|
||||
);
|
||||
// 이벤트 타입/상태 정규화 (모델 enum 호환)
|
||||
const normalizeEventType = (v) => {
|
||||
if (!v) return 'regular';
|
||||
@@ -569,13 +648,14 @@ exports.saveEventFileData = async (req, res) => {
|
||||
await event.update({
|
||||
title: eventData.title,
|
||||
description: eventData.description || '',
|
||||
eventType: normalizeEventType(eventData.eventType),
|
||||
// 비즈니스 규칙: 이벤트 유형은 정기전, 상태는 완료로 고정
|
||||
eventType: 'regular',
|
||||
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: normalizeStatus(eventData.status)
|
||||
status: 'completed'
|
||||
}, { transaction: t });
|
||||
} else {
|
||||
// 새 이벤트 생성
|
||||
@@ -583,13 +663,15 @@ exports.saveEventFileData = async (req, res) => {
|
||||
clubId: cid,
|
||||
title: eventData.title,
|
||||
description: eventData.description || '',
|
||||
eventType: normalizeEventType(eventData.eventType),
|
||||
// 비즈니스 규칙: 이벤트 유형은 정기전으로 저장
|
||||
eventType: 'regular',
|
||||
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: normalizeStatus(eventData.status),
|
||||
// 비즈니스 규칙: 상태는 완료로 저장
|
||||
status: 'completed',
|
||||
publicHash: generateRandomHash()
|
||||
}, { transaction: t });
|
||||
}
|
||||
@@ -702,6 +784,7 @@ exports.saveEventFileData = async (req, res) => {
|
||||
eventId: event.id,
|
||||
memberId: newMember.id,
|
||||
status: 'confirmed',
|
||||
paymentStatus: 'paid',
|
||||
teamId: teamId,
|
||||
comment: ''
|
||||
}, { transaction: t });
|
||||
@@ -750,9 +833,15 @@ exports.saveEventFileData = async (req, res) => {
|
||||
|
||||
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;
|
||||
// scoresIncludeHandicap 플래그에 따라 저장 방식 결정
|
||||
const provided = s.totalScore !== undefined ? Number(s.totalScore) : Number(s.score ?? 0) || 0;
|
||||
const totalScore = scoresIncludeHandicap
|
||||
? provided
|
||||
: Math.max(0, provided + (Number.isFinite(handicap) ? handicap : 0));
|
||||
const baseScore = scoresIncludeHandicap
|
||||
? Math.max(0, provided - (Number.isFinite(handicap) ? handicap : 0))
|
||||
: provided;
|
||||
if (gameNumber <= 0) continue;
|
||||
|
||||
await EventScore.create({
|
||||
@@ -778,6 +867,10 @@ exports.saveEventFileData = async (req, res) => {
|
||||
title: result.title,
|
||||
eventType: result.eventType,
|
||||
startDate: result.startDate
|
||||
},
|
||||
rollbackToken: {
|
||||
eventId: result.id,
|
||||
clubId: Number(result.clubId)
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -11,6 +11,8 @@ router.get('/:id', authenticateJWT, requireFeature('event_management'), eventCon
|
||||
router.post('/create', authenticateJWT, requireFeature('event_management'), eventController.createEvent);
|
||||
router.put('/:id', authenticateJWT, requireFeature('event_management'), eventController.updateEvent);
|
||||
router.delete('/:id', authenticateJWT, requireFeature('event_management'), eventController.deleteEvent);
|
||||
// 이벤트 완전 삭제 (하드 삭제)
|
||||
router.delete('/:id/permanent', authenticateJWT, requireFeature('event_management'), eventController.deleteEventPermanently);
|
||||
|
||||
// 이벤트 참가자 관리
|
||||
router.post('/:id/participants/list', authenticateJWT, requireFeature('event_management'), eventController.getEventParticipants);
|
||||
@@ -42,5 +44,7 @@ router.post('/upload', authenticateJWT, requireFeature('event_management'), file
|
||||
router.post('/parse-preview', authenticateJWT, requireFeature('event_management'), fileUploadController.parseEventFile);
|
||||
router.post('/save-file-data', authenticateJWT, requireFeature('event_management'), fileUploadController.saveEventFileData);
|
||||
router.post('/delete-file', authenticateJWT, requireFeature('event_management'), fileUploadController.deleteUploadedFile);
|
||||
// 업로드 저장 롤백
|
||||
router.post('/rollback-import', authenticateJWT, requireFeature('event_management'), fileUploadController.rollbackEventImport);
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user