Files
bowlingManager/backend/models/index.js
T
2025-04-22 20:19:17 +09:00

857 lines
26 KiB
JavaScript

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
};