팀 생성 임시 완료
This commit is contained in:
@@ -48,6 +48,15 @@ io.on('connection', (socket) => {
|
||||
// 전역 Socket.IO 인스턴스 설정
|
||||
app.set('io', io);
|
||||
|
||||
// app.js 또는 서버 초기화 파일
|
||||
const session = require('express-session');
|
||||
app.use(session({
|
||||
secret: process.env.JWT_SECRET,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: { secure: process.env.NODE_ENV === 'production' } // https 환경이면 true, 개발환경은 false
|
||||
}));
|
||||
|
||||
// 데이터베이스 연결 및 모델 동기화 실행
|
||||
sequelize.authenticate()
|
||||
.then(() => {
|
||||
|
||||
@@ -16,6 +16,48 @@ const isAdmin = (user) => {
|
||||
return user && (user.role === 'admin' || user.role === 'superadmin');
|
||||
};
|
||||
|
||||
// 클럽 선택
|
||||
const allowedMemberTypes = ['owner', 'manager'];
|
||||
|
||||
exports.selectClub = async (req, res) => {
|
||||
try {
|
||||
const { clubId } = req.body;
|
||||
const userId = req.user && req.user.id;
|
||||
|
||||
// 클럽 존재 여부 확인
|
||||
const club = await Club.findByPk(clubId);
|
||||
if (!club) {
|
||||
return res.status(404).json({ message: '클럽을 찾을 수 없습니다.' });
|
||||
}
|
||||
|
||||
// 관리자 여부 체크
|
||||
const isAdmin = req.user && (req.user.role === 'admin' || req.user.role === 'superadmin');
|
||||
let isClubManager = false;
|
||||
|
||||
if (!isAdmin) {
|
||||
// Members 테이블에서 모임장/운영진 여부 확인
|
||||
const member = await Member.findOne({
|
||||
where: {
|
||||
clubId,
|
||||
userId,
|
||||
memberType: ['모임장', '운영진']
|
||||
}
|
||||
});
|
||||
isClubManager = !!member;
|
||||
}
|
||||
|
||||
if (!isAdmin && !isClubManager) {
|
||||
return res.status(403).json({ message: '해당 클럽에 대한 권한이 없습니다.' });
|
||||
}
|
||||
|
||||
req.session.clubId = clubId;
|
||||
res.status(200).json({ message: '클럽을 선택했습니다.' });
|
||||
} catch (error) {
|
||||
console.error('클럽 선택 오류:', error);
|
||||
res.status(500).json({ message: '클럽을 선택하는 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 모든 클럽 조회
|
||||
exports.getAllClubs = async (req, res) => {
|
||||
try {
|
||||
@@ -57,7 +99,7 @@ exports.getAllClubs = async (req, res) => {
|
||||
exports.getClubById = async (req, res) => {
|
||||
try {
|
||||
const { clubId } = req.body;
|
||||
console.log(`clubId: ${clubId}`);
|
||||
// console.log(`clubId: ${clubId}`);
|
||||
if (!clubId) {
|
||||
return res.status(400).json({ message: "유효하지 않은 클럽 ID입니다." });
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 이벤트 컨트롤러
|
||||
* 이벤트 관련 기능을 처리하는 컨트롤러
|
||||
*/
|
||||
const { Event, EventParticipant, Member, Club, EventScore, ClubMember } = require('../models');
|
||||
const { Event, EventParticipant, Member, Club, EventScore, ClubMember, EventTeam, EventTeamMember } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const { sequelize } = require('../models');
|
||||
|
||||
@@ -78,7 +78,7 @@ exports.getEventById = async (req, res) => {
|
||||
},
|
||||
{
|
||||
model: EventParticipant,
|
||||
attributes: ['id', 'status', 'paymentStatus', 'teamNumber'],
|
||||
attributes: ['id', 'status', 'paymentStatus'],
|
||||
where: { status: { [Op.ne]: '취소' } },
|
||||
required: false,
|
||||
include: [
|
||||
@@ -538,7 +538,6 @@ exports.updateEventScore = async (req, res) => {
|
||||
const newScore = await EventScore.create({
|
||||
eventId,
|
||||
participantId: existingScore.participantId,
|
||||
teamNumber: existingScore.teamNumber,
|
||||
gameNumber,
|
||||
score,
|
||||
handicap,
|
||||
@@ -1004,9 +1003,8 @@ exports.addEventParticipant = async (req, res) => {
|
||||
const {
|
||||
clubId,
|
||||
memberId,
|
||||
teamNumber,
|
||||
paymentStatus,
|
||||
paymentAmount
|
||||
status,
|
||||
paymentStatus
|
||||
} = req.body;
|
||||
const eventId = req.params.id;
|
||||
if (!eventId || !clubId || !memberId) {
|
||||
@@ -1071,12 +1069,10 @@ exports.addEventParticipant = async (req, res) => {
|
||||
const participant = await EventParticipant.create({
|
||||
eventId,
|
||||
memberId,
|
||||
teamNumber: teamNumber || 1,
|
||||
status: req.body.status || '참가예정',
|
||||
attendance: false,
|
||||
paymentStatus: req.body.paymentStatus || '미납',
|
||||
paymentAmount: paymentAmount || event.entryFee || 0,
|
||||
registeredAt: new Date()
|
||||
status: status || '참가예정',
|
||||
paymentStatus: paymentStatus || '미납',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
});
|
||||
|
||||
// 참가자 정보 조회
|
||||
@@ -1166,6 +1162,96 @@ exports.updateEventParticipant = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// [팀 생성 기능] 이벤트별 팀 목록 조회
|
||||
exports.getEventTeams = async (req, res) => {
|
||||
try {
|
||||
const eventId = req.params.id;
|
||||
if (!eventId) {
|
||||
return res.status(400).json({ message: '이벤트 ID가 필요합니다.' });
|
||||
}
|
||||
const teams = await EventTeam.findAll({
|
||||
where: { eventId },
|
||||
include: [
|
||||
{
|
||||
model: EventTeamMember,
|
||||
include: [
|
||||
{
|
||||
model: EventParticipant,
|
||||
include: [{ model: Member }]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
order: [['teamNumber', 'ASC']]
|
||||
});
|
||||
const result = teams.map(team => ({
|
||||
id: team.id,
|
||||
teamNumber: team.teamNumber,
|
||||
handicap: team.handicap,
|
||||
members: (team.EventTeamMembers || [])
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map(tm => ({
|
||||
id: tm.eventParticipantId,
|
||||
name: tm.EventParticipant?.Member?.name || `참가자 ${tm.eventParticipantId}`,
|
||||
order: tm.order
|
||||
}))
|
||||
}));
|
||||
res.json({ teams: result });
|
||||
} catch (error) {
|
||||
console.error('팀 목록 조회 오류:', error);
|
||||
res.status(500).json({ message: '팀 목록을 가져오는 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// [팀 생성 기능] 이벤트별 팀 배정 저장
|
||||
exports.createOrUpdateEventTeams = async (req, res) => {
|
||||
const eventId = req.params.id;
|
||||
const { teams } = req.body; // [{teamNumber, handicap, members:[id]}]
|
||||
|
||||
if (!eventId || !Array.isArray(teams)) {
|
||||
return res.status(400).json({ message: '이벤트 ID와 팀 정보가 필요합니다.' });
|
||||
}
|
||||
|
||||
try {
|
||||
await sequelize.transaction(async (t) => {
|
||||
// 기존 팀 및 팀멤버 데이터 삭제
|
||||
await EventTeam.destroy({ where: { eventId }, transaction: t });
|
||||
await EventTeamMember.destroy({
|
||||
where: {}, // eventId로만 삭제하려면, eventTeamId로 팀 id 목록을 먼저 조회해서 삭제
|
||||
transaction: t
|
||||
});
|
||||
|
||||
// 새 팀 및 팀멤버 데이터 저장
|
||||
for (const team of teams) {
|
||||
// 팀 생성
|
||||
const createdTeam = await EventTeam.create({
|
||||
eventId,
|
||||
teamNumber: team.teamNumber,
|
||||
handicap: team.handicap || 0
|
||||
}, { transaction: t });
|
||||
|
||||
// 팀 멤버(순서 포함) 저장
|
||||
if (Array.isArray(team.members) && team.members.length > 0) {
|
||||
const teamMemberRows = team.members
|
||||
.filter(m => m.id != null)
|
||||
.map(m => ({
|
||||
eventTeamId: createdTeam.id,
|
||||
eventParticipantId: m.id,
|
||||
order: m.order ?? 1
|
||||
}));
|
||||
if (teamMemberRows.length > 0) {
|
||||
await EventTeamMember.bulkCreate(teamMemberRows, { transaction: t });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
res.json({ message: '팀 배정이 저장되었습니다.' });
|
||||
} catch (error) {
|
||||
console.error('팀 배정 저장 오류:', error);
|
||||
res.status(500).json({ message: '팀 배정 저장 중 오류가 발생했습니다.' });
|
||||
}
|
||||
};
|
||||
|
||||
// 참가자 삭제 (소프트 삭제)
|
||||
exports.deleteEventParticipant = async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -4,7 +4,8 @@ const { clubHasFeature } = require('../utils/featureAccess');
|
||||
function requireFeature(featureCode) {
|
||||
return async function(req, res, next) {
|
||||
try {
|
||||
const clubId = req.clubId || req.body.clubId || req.params.clubId;
|
||||
const clubId = req.session.clubId || req.clubId || req.body.clubId || req.params.clubId;
|
||||
console.log('backendClubId:', clubId);
|
||||
if (!clubId) {
|
||||
return res.status(400).json({ error: '클럽 정보가 필요합니다.' });
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const EventTeam = sequelize.define('EventTeam', {
|
||||
EventTeam.associate = (models) => {
|
||||
EventTeam.belongsTo(models.Event, { foreignKey: 'eventId' });
|
||||
EventTeam.hasMany(models.EventParticipant, { foreignKey: 'teamId' });
|
||||
EventTeam.hasMany(models.EventTeamMember, { foreignKey: 'eventTeamId' });
|
||||
};
|
||||
|
||||
module.exports = EventTeam;
|
||||
@@ -0,0 +1,30 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
const EventTeamMember = sequelize.define('EventTeamMember', {
|
||||
eventTeamId: {
|
||||
type: DataTypes.INTEGER.UNSIGNED,
|
||||
allowNull: false,
|
||||
primaryKey: true
|
||||
},
|
||||
eventParticipantId: {
|
||||
type: DataTypes.INTEGER.UNSIGNED,
|
||||
allowNull: false,
|
||||
primaryKey: true
|
||||
},
|
||||
order: {
|
||||
type: DataTypes.INTEGER.UNSIGNED,
|
||||
allowNull: false,
|
||||
defaultValue: 1
|
||||
}
|
||||
}, {
|
||||
tableName: 'EventTeamMembers',
|
||||
timestamps: false
|
||||
});
|
||||
|
||||
EventTeamMember.associate = (models) => {
|
||||
EventTeamMember.belongsTo(models.EventTeam, { foreignKey: 'eventTeamId' });
|
||||
EventTeamMember.belongsTo(models.EventParticipant, { foreignKey: 'eventParticipantId' });
|
||||
};
|
||||
|
||||
module.exports = EventTeamMember;
|
||||
@@ -15,6 +15,7 @@ const SubscriptionPlan = require('./SubscriptionPlan');
|
||||
const SubscriptionPlanFeature = require('./SubscriptionPlanFeature');
|
||||
const ClubSubscription = require('./ClubSubscription');
|
||||
const ActivityLog = require('./activityLog');
|
||||
const EventTeamMember = require('./EventTeamMember');
|
||||
|
||||
// 모델 동기화 함수
|
||||
const syncModels = async () => {
|
||||
@@ -50,6 +51,7 @@ const db = {
|
||||
SubscriptionPlanFeature,
|
||||
ClubSubscription,
|
||||
ActivityLog,
|
||||
EventTeamMember,
|
||||
};
|
||||
Object.values(db).forEach(model => {
|
||||
if (model.associate) model.associate(db);
|
||||
@@ -72,5 +74,6 @@ module.exports = {
|
||||
SubscriptionPlanFeature,
|
||||
ClubSubscription,
|
||||
ActivityLog,
|
||||
EventTeamMember,
|
||||
syncModels
|
||||
};
|
||||
|
||||
Generated
+56
@@ -15,6 +15,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"express-session": "^1.18.1",
|
||||
"express-validator": "^7.2.1",
|
||||
"helmet": "^8.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
@@ -1204,6 +1205,40 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz",
|
||||
"integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "0.7.2",
|
||||
"cookie-signature": "1.0.7",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-headers": "~1.0.2",
|
||||
"parseurl": "~1.3.3",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "~2.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session/node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session/node_modules/cookie-signature": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
||||
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/express-validator": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.1.tgz",
|
||||
@@ -2721,6 +2756,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -3474,6 +3518,18 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"random-bytes": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/umzug": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"express-session": "^1.18.1",
|
||||
"express-validator": "^7.2.1",
|
||||
"helmet": "^8.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
@@ -5,6 +5,8 @@ const clubController = require('../controllers/clubController');
|
||||
const { authenticateJWT } = require('../middleware/authMiddleware');
|
||||
const requireFeature = require('../middleware/requireFeature');
|
||||
|
||||
// 클럽 선택
|
||||
router.post('/select-club', authenticateJWT, clubController.selectClub);
|
||||
|
||||
// 모든 클럽 조회
|
||||
router.get('/', clubController.getAllClubs);
|
||||
|
||||
@@ -17,6 +17,10 @@ router.post('/:id/participants', authenticateJWT, requireFeature('event_manageme
|
||||
router.put('/:id/participants', authenticateJWT, requireFeature('event_management'), eventController.updateEventParticipant);
|
||||
router.delete('/:id/participants', authenticateJWT, requireFeature('event_management'), eventController.deleteEventParticipant);
|
||||
|
||||
// [팀 생성 기능] 이벤트별 팀 배정 저장
|
||||
router.post('/:id/teams', authenticateJWT, requireFeature('team_create'), eventController.createOrUpdateEventTeams);
|
||||
router.get('/:id/teams', authenticateJWT, requireFeature('team_create'), eventController.getEventTeams);
|
||||
|
||||
// 이벤트 점수 관리
|
||||
router.get('/:id/scores', authenticateJWT, requireFeature('event_management'), eventController.getEventScores);
|
||||
router.post('/:id/scores', authenticateJWT, requireFeature('event_management'), eventController.addEventScore);
|
||||
|
||||
Reference in New Issue
Block a user