const { sequelize } = require('../config/database'); const { Op } = require('sequelize'); const User = require('./User'); const Club = require('./Club'); const Event = require('./Event'); const EventParticipant = require('./EventParticipant'); const EventScore = require('./EventScore'); const Member = require('./Member'); const Notification = require('./Notification'); const Feature = require('./Feature'); const ClubFeature = require('./ClubFeature'); const Menu = require('./Menu'); const SubscriptionPlan = require('./SubscriptionPlan'); const SubscriptionPlanFeature = require('./SubscriptionPlanFeature'); const ClubSubscription = require('./ClubSubscription'); const ActivityLog = require('./activityLog'); // 관계 설정 // User와 Member 관계 User.hasMany(Member, { foreignKey: 'userId' }); Member.belongsTo(User, { foreignKey: 'userId' }); // Club과 Member 관계 Club.hasMany(Member, { foreignKey: 'clubId' }); Member.belongsTo(Club, { foreignKey: 'clubId' }); // Club과 User 사이의 다대다 관계 추가 User.belongsToMany(Club, { through: Member, foreignKey: 'userId' }); Club.belongsToMany(User, { through: Member, foreignKey: 'clubId' }); // Club과 User 사이의 소유자 관계 추가 Club.belongsTo(User, { as: 'owner', foreignKey: 'ownerId' }); User.hasMany(Club, { as: 'ownedClubs', foreignKey: 'ownerId' }); // Club과 Event 관계 Club.hasMany(Event, { foreignKey: 'clubId' }); Event.belongsTo(Club, { foreignKey: 'clubId' }); // Event와 EventParticipant 관계 Event.hasMany(EventParticipant, { foreignKey: 'eventId', as: 'participants' }); EventParticipant.belongsTo(Event, { foreignKey: 'eventId' }); // Member와 EventParticipant 관계 Member.hasMany(EventParticipant, { foreignKey: 'memberId' }); EventParticipant.belongsTo(Member, { foreignKey: 'memberId' }); // Event와 EventScore 관계 Event.hasMany(EventScore, { foreignKey: 'eventId', as: 'scores' }); EventScore.belongsTo(Event, { foreignKey: 'eventId', as: 'event' }); // EventParticipant와 EventScore 관계 EventParticipant.hasMany(EventScore, { foreignKey: 'participantId', as: 'scores' }); EventScore.belongsTo(EventParticipant, { foreignKey: 'participantId', as: 'participant' }); // Feature와 Menu 관계 Feature.hasMany(Menu, { foreignKey: 'featureCode', sourceKey: 'code' }); Menu.belongsTo(Feature, { foreignKey: 'featureCode', targetKey: 'code' }); // Menu 자기 참조 관계 Menu.belongsTo(Menu, { as: 'parent', foreignKey: 'parentId' }); Menu.hasMany(Menu, { as: 'children', foreignKey: 'parentId' }); // Club과 Feature의 관계 Club.belongsToMany(Feature, { through: ClubFeature, foreignKey: 'clubId' }); Feature.belongsToMany(Club, { through: ClubFeature, foreignKey: 'featureId' }); // Feature와 ClubFeature 관계 Feature.hasMany(ClubFeature, { foreignKey: 'featureId', as: 'clubFeatures' }); ClubFeature.belongsTo(Feature, { foreignKey: 'featureId', as: 'feature' }); // Club과 ClubFeature 관계 Club.hasMany(ClubFeature, { foreignKey: 'clubId', as: 'features' }); ClubFeature.belongsTo(Club, { foreignKey: 'clubId', as: 'club' }); // User와 Notification 관계 User.hasMany(Notification, { foreignKey: 'userId' }); Notification.belongsTo(User, { foreignKey: 'userId' }); // Club과 SubscriptionPlan의 관계 (through ClubSubscription) Club.belongsToMany(SubscriptionPlan, { through: ClubSubscription, foreignKey: 'clubId', otherKey: 'planId' }); SubscriptionPlan.belongsToMany(Club, { through: ClubSubscription, foreignKey: 'planId', otherKey: 'clubId' }); // SubscriptionPlan과 Feature의 관계 (through SubscriptionPlanFeature) SubscriptionPlan.belongsToMany(Feature, { through: SubscriptionPlanFeature, foreignKey: 'planId', otherKey: 'featureId' }); Feature.belongsToMany(SubscriptionPlan, { through: SubscriptionPlanFeature, foreignKey: 'featureId', otherKey: 'planId' }); // ClubSubscription과 SubscriptionPlan의 관계 ClubSubscription.belongsTo(SubscriptionPlan, { foreignKey: 'planId' }); SubscriptionPlan.hasMany(ClubSubscription, { foreignKey: 'planId' }); // ClubSubscription과 Club의 관계 ClubSubscription.belongsTo(Club, { foreignKey: 'clubId' }); Club.hasMany(ClubSubscription, { foreignKey: 'clubId' }); // 모델 동기화 함수 const syncModels = async () => { try { // 외래 키 제약 조건을 비활성화하고 테이블 재생성 // 개발 // await sequelize.query('SET FOREIGN_KEY_CHECKS = 0'); // await sequelize.sync({ force: true }); // await sequelize.query('SET FOREIGN_KEY_CHECKS = 1'); // await createAdminUser(); // await createSampleData(); // 운영 alter: true - 구조변경만 alter처리 await sequelize.sync({ alter: true }); } catch (error) { console.error('모델 동기화 중 오류 발생:', error); } }; // 초기 관리자 계정 생성 함수 const createAdminUser = async () => { try { const adminExists = await User.findOne({ where: { email: process.env.ADMIN_EMAIL } }); if (!adminExists) { await User.create({ username: 'master', name: '관리자', email: process.env.ADMIN_EMAIL, password: process.env.ADMIN_PASSWORD, role: 'admin', status: 'active', createdAt: new Date(), updatedAt: new Date() }); console.log('관리자 계정이 생성되었습니다.'); } } catch (error) { console.error('관리자 계정 생성 중 오류 발생:', error); } }; // 초기 샘플 데이터 생성 함수 const createSampleData = async () => { try { // 1. 기존 데이터 삭제 (순서 중요) await ClubFeature.destroy({ where: {} }); await ClubSubscription.destroy({ where: {} }); await Member.destroy({ where: {} }); await Club.destroy({ where: {} }); await Feature.destroy({ where: {} }); await SubscriptionPlan.destroy({ where: {} }); await User.destroy({ where: { email: { [Op.ne]: process.env.ADMIN_EMAIL } } }); console.log('기존 데이터가 모두 삭제되었습니다.'); // 2. 샘플 사용자 데이터 생성 const sampleUsers = await User.bulkCreate([ { username: 'hong', name: '홍길동', email: 'hong@test.com', password: '1234', role: 'user', status: 'active', createdAt: new Date(), updatedAt: new Date() }, { username: 'kim', name: '김철수', email: 'kim@test.com', password: '1234', role: 'user', status: 'active', createdAt: new Date(), updatedAt: new Date() }, { username: 'lee', name: '이영희', email: 'lee@test.com', password: '1234', role: 'user', status: 'active', createdAt: new Date(), updatedAt: new Date() } ]); console.log('샘플 사용자 데이터가 생성되었습니다:', sampleUsers.map(user => user.id)); // 3. 샘플 클럽 데이터 생성 const clubs = await Club.bulkCreate([ { name: '서울 볼링 클럽', description: '서울 지역 볼링 동호회입니다.', location: '서울시 강남구', ownerId: sampleUsers[0].id, status: 'active', memberCount: 3, createdAt: new Date(), updatedAt: new Date() }, { name: '부산 볼링 클럽', description: '부산 지역 볼링 동호회입니다.', location: '부산시 해운대구', ownerId: sampleUsers[1].id, status: 'active', memberCount: 3, createdAt: new Date(), updatedAt: new Date() }, { name: '대전 볼링 클럽', description: '대전 지역 볼링 동호회입니다.', location: '대전시 유성구', ownerId: sampleUsers[2].id, status: 'active', memberCount: 3, createdAt: new Date(), updatedAt: new Date() } ]); console.log('샘플 클럽 데이터가 생성되었습니다:', clubs.map(club => club.id)); // 샘플 구독 플랜 데이터 생성 const subscriptionPlans = [ { name: 'Basic', description: '기본적인 클럽 관리 기능을 제공합니다.', price: 30000, duration: 1, maxMembers: 30, features: ['member_management', 'event_basic'], status: 'active' }, { name: 'Premium', description: '고급 분석 기능과 함께 더 많은 회원을 관리할 수 있습니다.', price: 50000, duration: 1, maxMembers: 100, features: ['member_management', 'event_basic', 'analytics_basic', 'lane_booking'], status: 'active' }, { name: 'Enterprise', description: '무제한 회원 관리와 모든 프리미엄 기능을 제공합니다.', price: 100000, duration: 1, maxMembers: 999999, features: ['member_management', 'event_basic', 'analytics_basic', 'lane_booking', 'tournament', 'analytics_advanced'], status: 'active' } ]; await SubscriptionPlan.bulkCreate(subscriptionPlans); console.log('샘플 구독 플랜 데이터가 생성되었습니다.'); // 샘플 클럽 구독 데이터 생성 const clubSubscriptions = [ { clubId: 1, planId: 3, // Enterprise 플랜 startDate: new Date(), endDate: new Date(new Date().setMonth(new Date().getMonth() + 12)), amount: 1200000, // 연간 구독 status: 'active', paymentStatus: 'paid' }, { clubId: 2, planId: 2, // Premium 플랜 startDate: new Date(), endDate: new Date(new Date().setMonth(new Date().getMonth() + 6)), amount: 300000, // 6개월 구독 status: 'active', paymentStatus: 'paid' }, { clubId: 3, planId: 1, // Basic 플랜 startDate: new Date(), endDate: new Date(new Date().setMonth(new Date().getMonth() + 3)), amount: 90000, // 3개월 구독 status: 'active', paymentStatus: 'paid' } ]; await ClubSubscription.bulkCreate(clubSubscriptions); console.log('샘플 클럽 구독 데이터가 생성되었습니다.'); // 샘플 기능 데이터 생성 const features = [ { code: 'member_management', name: '회원 관리', description: '클럽 회원 관리 기능', purchaseType: 'subscription_only', monthlySubscriptionPrice: 0, createdAt: new Date(), updatedAt: new Date() }, { code: 'event_management', name: '이벤트 관리', description: '클럽 이벤트 관리 기능', purchaseType: 'subscription_only', monthlySubscriptionPrice: 5000, createdAt: new Date(), updatedAt: new Date() }, { code: 'event_calendar', name: '이벤트 캘린더', description: '클럽 이벤트 캘린더 기능', purchaseType: 'subscription_only', monthlySubscriptionPrice: 5000, createdAt: new Date(), updatedAt: new Date() }, { code: 'statistics', name: '통계', description: '클럽 통계 기능', purchaseType: 'subscription_only', monthlySubscriptionPrice: 10000, createdAt: new Date(), updatedAt: new Date() } ]; // 기능 데이터 생성 await Feature.bulkCreate(features); console.log('샘플 기능 데이터가 생성되었습니다.'); // 샘플 클럽 회원 데이터 생성 const members = [ { clubId: 1, userId: 2, name: '홍길동', gender: '남성', memberType: '모임장', status: 'active', joinDate: new Date(), createdAt: new Date(), updatedAt: new Date() }, { clubId: 1, userId: 3, name: '김철수', gender: '남성', memberType: '정회원', status: 'active', joinDate: new Date(), createdAt: new Date(), updatedAt: new Date() }, { clubId: 2, userId: 3, name: '김철수', gender: '남성', memberType: '모임장', status: 'active', joinDate: new Date(), createdAt: new Date(), updatedAt: new Date() }, { clubId: 2, userId: 4, name: '이영희', gender: '여성', memberType: '정회원', status: 'active', joinDate: new Date(), createdAt: new Date(), updatedAt: new Date() }, { clubId: 3, userId: 4, name: '이영희', gender: '여성', memberType: '모임장', status: 'active', joinDate: new Date(), createdAt: new Date(), updatedAt: new Date() }, { clubId: 3, userId: 2, name: '홍길동', gender: '남성', memberType: '정회원', status: 'active', joinDate: new Date(), createdAt: new Date(), updatedAt: new Date() }, { clubId: 1, userId: null, name: '정회원A', gender: '여성', memberType: '정회원', status: 'active', joinDate: new Date('2023-01-15'), createdAt: new Date('2023-01-16'), updatedAt: new Date('2023-01-17') }, { clubId: 2, userId: null, name: '준회원B', gender: '남성', memberType: '준회원', status: 'inactive', joinDate: new Date('2023-02-20'), createdAt: new Date('2023-02-21'), updatedAt: new Date('2023-02-22') }, { clubId: 3, userId: null, name: '정회원C', gender: '여성', memberType: '정회원', status: 'suspended', joinDate: new Date('2023-03-10'), createdAt: new Date('2023-03-11'), updatedAt: new Date('2023-03-12') }, { clubId: 1, userId: null, name: '준회원D', gender: '남성', memberType: '준회원', status: 'active', joinDate: new Date('2023-04-05'), createdAt: new Date('2023-04-06'), updatedAt: new Date('2023-04-07') }, { clubId: 2, userId: null, name: '정회원E', gender: '여성', memberType: '정회원', status: 'suspended', joinDate: new Date('2023-05-15'), createdAt: new Date('2023-05-16'), updatedAt: new Date('2023-05-17') }, { clubId: 3, userId: null, name: '준회원F', gender: '남성', memberType: '준회원', status: 'inactive', joinDate: new Date('2023-06-25'), createdAt: new Date('2023-06-26'), updatedAt: new Date('2023-06-27') }, { clubId: 1, userId: null, name: '정회원G', gender: '여성', memberType: '정회원', status: 'active', joinDate: new Date('2023-07-30'), createdAt: new Date('2023-07-31'), updatedAt: new Date('2023-08-01') }, { clubId: 2, userId: null, name: '준회원H', gender: '남성', memberType: '준회원', status: 'suspended', joinDate: new Date('2023-08-15'), createdAt: new Date('2023-08-16'), updatedAt: new Date('2023-08-17') } ]; await Member.bulkCreate(members); console.log('샘플 클럽 회원 데이터가 생성되었습니다.'); // 샘플 클럽 기능 데이터 생성 // 각 클럽의 기능을 개별적으로 생성 // 클럽 1 (Enterprise) await ClubFeature.create({ clubId: 1, featureId: 1, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 12)), createdAt: new Date(), updatedAt: new Date() }); await ClubFeature.create({ clubId: 1, featureId: 2, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 12)), createdAt: new Date(), updatedAt: new Date() }); // 클럽 2 (Premium) await ClubFeature.create({ clubId: 2, featureId: 1, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 6)), createdAt: new Date(), updatedAt: new Date() }); await ClubFeature.create({ clubId: 2, featureId: 2, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 6)), createdAt: new Date(), updatedAt: new Date() }); // 클럽 3 (Basic) await ClubFeature.create({ clubId: 3, featureId: 1, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 3)), createdAt: new Date(), updatedAt: new Date() }); console.log('샘플 클럽 기능 데이터가 생성되었습니다.'); // 샘플 메뉴 데이터 생성 const menus = [ // 클럽 관리 메뉴 { title: '클럽 관리', icon: 'pi pi-building', to: '/club', role: 'user', order: 1, visible: true, children: [ { title: '대시보드', icon: 'pi pi-home', to: '/club', role: 'user', order: 1, visible: true }, { title: '회원 관리', icon: 'pi pi-users', to: '/club/members', role: 'user', order: 2, visible: true, featureCode: 'member_management', requiredSubscriptionTier: 'basic' }, { title: '이벤트 관리', icon: 'pi pi-calendar', to: '/club/events', role: 'user', order: 3, visible: true, featureCode: 'event_management', requiredSubscriptionTier: 'standard' }, { title: '이벤트 캘린더', icon: 'pi pi-calendar-plus', to: '/club/events/calendar', role: 'user', order: 4, visible: true, featureCode: 'event_calendar', requiredSubscriptionTier: 'standard' }, { title: '통계', icon: 'pi pi-chart-line', to: '/club/statistics', role: 'user', order: 5, visible: true, featureCode: 'statistics', requiredSubscriptionTier: 'premium' } ] } ]; // 부모 메뉴 먼저 생성 for (const menuData of menus) { const { children, ...parentData } = menuData; const parentMenu = await Menu.create(parentData); // 자식 메뉴 생성 및 부모와 연결 if (children) { for (const childData of children) { await Menu.create({ ...childData, parentId: parentMenu.id }); } } } console.log('샘플 메뉴 데이터가 생성되었습니다.'); // 샘플 클럽 생성 const clubsCount = await Club.count(); if (clubsCount === 0) { // 3. 샘플 클럽 데이터 생성 const clubs = await Club.bulkCreate([ { name: '서울 볼링 클럽', description: '서울 지역 볼링 동호회입니다.', ownerId: sampleUsers[0].id, location: '서울시 강남구', status: 'active', memberCount: 3, createdAt: new Date(), updatedAt: new Date() }, { name: '부산 볼링 클럽', description: '부산 지역 볼링 동호회입니다.', ownerId: sampleUsers[1].id, location: '부산시 해운대구', status: 'active', memberCount: 3, createdAt: new Date(), updatedAt: new Date() }, { name: '대전 볼링 클럽', description: '대전 지역 볼링 동호회입니다.', ownerId: sampleUsers[2].id, location: '대전시 유성구', status: 'active', memberCount: 3, createdAt: new Date(), updatedAt: new Date() } ]); console.log('샘플 클럽 데이터가 생성되었습니다:', clubs.map(club => club.id)); } // 샘플 구독 플랜 생성 const subscriptionPlansCount = await SubscriptionPlan.count(); if (subscriptionPlansCount === 0) { const subscriptionPlans = [ { name: 'Basic', description: '기본적인 클럽 관리 기능을 제공합니다.', price: 30000, duration: 1, maxMembers: 30, features: ['member_management', 'event_basic'], status: 'active' }, { name: 'Premium', description: '고급 분석 기능과 함께 더 많은 회원을 관리할 수 있습니다.', price: 50000, duration: 1, maxMembers: 100, features: ['member_management', 'event_basic', 'analytics_basic', 'lane_booking'], status: 'active' }, { name: 'Enterprise', description: '무제한 회원 관리와 모든 프리미엄 기능을 제공합니다.', price: 100000, duration: 1, maxMembers: 999999, // 무제한을 표현하기 위해 큰 숫자 사용 features: ['member_management', 'event_basic', 'analytics_basic', 'lane_booking', 'tournament', 'analytics_advanced'], status: 'active' } ]; await SubscriptionPlan.bulkCreate(subscriptionPlans); console.log('샘플 구독 플랜 데이터가 생성되었습니다.'); } // 샘플 클럽 구독 데이터 생성 const clubSubscriptionsCount = await ClubSubscription.count(); if (clubSubscriptionsCount === 0) { const clubSubscriptions = [ { clubId: 1, planId: 3, // Enterprise 플랜 startDate: new Date(), endDate: new Date(new Date().setMonth(new Date().getMonth() + 12)), amount: 1200000, // 연간 구독 status: 'active', paymentStatus: 'paid' }, { clubId: 2, planId: 2, // Premium 플랜 startDate: new Date(), endDate: new Date(new Date().setMonth(new Date().getMonth() + 6)), amount: 300000, // 6개월 구독 status: 'active', paymentStatus: 'paid' }, { clubId: 3, planId: 1, // Basic 플랜 startDate: new Date(), endDate: new Date(new Date().setMonth(new Date().getMonth() + 3)), amount: 90000, // 3개월 구독 status: 'active', paymentStatus: 'paid' } ]; await ClubSubscription.bulkCreate(clubSubscriptions); console.log('샘플 클럽 구독 데이터가 생성되었습니다.'); } // 5. 샘플 기능 데이터 생성 const createdFeatures = await Feature.bulkCreate([ { name: '토너먼트 관리', description: '클럽 내 토너먼트 대회를 개최하고 관리할 수 있습니다.', code: 'tournament', purchaseType: 'both', price: 50000, status: 'active', createdAt: new Date(), updatedAt: new Date() }, { name: '레인 예약', description: '볼링장 레인을 미리 예약하고 관리할 수 있습니다.', code: 'lane_booking', purchaseType: 'subscription_only', price: 30000, status: 'active', createdAt: new Date(), updatedAt: new Date() }, { name: '점수 분석', description: '회원들의 경기 점수를 분석하고 통계를 제공합니다.', code: 'score_analysis', purchaseType: 'separate_only', price: 40000, status: 'active', createdAt: new Date(), updatedAt: new Date() } ]); console.log('샘플 기능 데이터가 생성되었습니다:', createdFeatures.map(f => f.id)); // 6. 샘플 클럽 기능 데이터 생성 const sampleClubs = await Club.findAll(); if (createdFeatures.length > 0 && sampleClubs.length > 0) { const clubFeatures = await ClubFeature.bulkCreate([ { clubId: sampleClubs[0].id, featureId: createdFeatures[0].id, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 6)), amount: 50000, duration: 6, status: 'active', createdAt: new Date(), updatedAt: new Date() }, { clubId: sampleClubs[1].id, featureId: createdFeatures[1].id, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 3)), amount: 30000, duration: 3, status: 'active', createdAt: new Date(), updatedAt: new Date() }, { clubId: sampleClubs[2].id, featureId: createdFeatures[2].id, enabled: true, expiryDate: new Date(new Date().setMonth(new Date().getMonth() + 12)), amount: 40000, duration: 12, status: 'active', createdAt: new Date(), updatedAt: new Date() } ]); console.log('샘플 클럽 기능 데이터가 생성되었습니다:', clubFeatures.map(cf => `${cf.clubId}-${cf.featureId}`)); } } catch (error) { console.error('샘플 데이터 생성 중 오류 발생:', error); } }; module.exports = { sequelize, User, Club, Event, EventParticipant, EventScore, Member, Notification, Feature, ClubFeature, Menu, SubscriptionPlan, SubscriptionPlanFeature, ClubSubscription, ActivityLog, syncModels, createAdminUser, createSampleData };