166 lines
4.3 KiB
JavaScript
166 lines
4.3 KiB
JavaScript
const { Menu, Feature, ClubFeature, ClubSubscription, SubscriptionPlan, Member } = require('../models');
|
|
const { Op } = require('sequelize');
|
|
|
|
/**
|
|
* 사용자의 접근 가능한 메뉴 목록을 반환합니다.
|
|
*/
|
|
exports.getUserMenus = async (req, res) => {
|
|
try {
|
|
const { clubId, role = 'user' } = req.body;
|
|
const userId = req.user.id; // JWT 토큰에서 사용자 ID 가져오기
|
|
|
|
if (!clubId) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '클럽 ID가 필요합니다.'
|
|
});
|
|
}
|
|
|
|
// 멤버 정보 조회
|
|
const member = await Member.findOne({
|
|
where: {
|
|
clubId,
|
|
userId
|
|
}
|
|
});
|
|
|
|
const menus = await getAccessibleMenus(clubId, role, member);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: menus
|
|
});
|
|
} catch (error) {
|
|
console.error('메뉴 조회 중 오류 발생:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '메뉴 조회 중 오류가 발생했습니다.'
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 사용자 권한과 클럽의 구독/기능 상태에 따라 접근 가능한 메뉴 목록을 조회합니다.
|
|
*/
|
|
async function getAccessibleMenus(clubId, userRole, member) {
|
|
try {
|
|
// 1. 클럽의 현재 구독 정보 조회
|
|
const clubSubscription = await ClubSubscription.findOne({
|
|
where: {
|
|
clubId,
|
|
status: 'active',
|
|
endDate: {
|
|
[Op.gt]: new Date()
|
|
}
|
|
},
|
|
include: [{
|
|
model: SubscriptionPlan,
|
|
attributes: ['name']
|
|
}]
|
|
});
|
|
|
|
// 2. 클럽의 활성화된 기능 목록 조회
|
|
const activeFeatures = await ClubFeature.findAll({
|
|
where: {
|
|
clubId,
|
|
enabled: true,
|
|
[Op.or]: [
|
|
{ expiryDate: null },
|
|
{ expiryDate: { [Op.gt]: new Date() } }
|
|
]
|
|
},
|
|
include: [{
|
|
model: Feature,
|
|
as: 'feature',
|
|
attributes: ['code']
|
|
}]
|
|
});
|
|
|
|
const activeFeatureCodes = activeFeatures.map(f => f.feature.code);
|
|
const subscriptionTier = clubSubscription ? clubSubscription.SubscriptionPlan.name.toLowerCase() : 'basic';
|
|
|
|
// 3. 메뉴 조회 및 필터링
|
|
const allMenus = await Menu.findAll({
|
|
where: {
|
|
visible: true,
|
|
[Op.and]: [
|
|
// 역할 기반 필터링
|
|
{
|
|
[Op.or]: [
|
|
{ role: userRole },
|
|
{ role: 'user' }
|
|
]
|
|
},
|
|
// 구독 등급 기반 필터링
|
|
{
|
|
[Op.or]: [
|
|
{ requiredSubscriptionTier: null },
|
|
{ requiredSubscriptionTier: subscriptionTier }
|
|
]
|
|
}
|
|
]
|
|
},
|
|
include: [{
|
|
model: Menu,
|
|
as: 'children',
|
|
required: false
|
|
}],
|
|
order: [
|
|
['order', 'ASC'],
|
|
[{ model: Menu, as: 'children' }, 'order', 'ASC']
|
|
]
|
|
});
|
|
|
|
// 4. 기능 코드 기반 필터링 및 메뉴 구조화
|
|
const processMenu = (menu) => {
|
|
// 기능 코드가 있는 경우 접근 권한 확인
|
|
if (menu.featureCode && !activeFeatureCodes.includes(menu.featureCode)) {
|
|
return null;
|
|
}
|
|
|
|
const processedMenu = {
|
|
id: menu.id,
|
|
title: menu.title,
|
|
icon: menu.icon,
|
|
to: menu.to,
|
|
order: menu.order
|
|
};
|
|
|
|
// 하위 메뉴 처리
|
|
if (menu.children && menu.children.length > 0) {
|
|
const children = menu.children
|
|
.map(processMenu)
|
|
.filter(child => child !== null);
|
|
|
|
if (children.length > 0) {
|
|
processedMenu.children = children;
|
|
}
|
|
}
|
|
|
|
return processedMenu;
|
|
};
|
|
|
|
// 최상위 메뉴만 필터링 (parentId가 null인 항목)
|
|
const rootMenus = allMenus
|
|
.filter(menu => !menu.parentId)
|
|
.map(processMenu)
|
|
.filter(menu => menu !== null);
|
|
|
|
// 구독 관리 메뉴 추가 (모임장, 운영진만 접근 가능)
|
|
if (member && (member.memberType === '모임장' || member.memberType === '운영진')) {
|
|
rootMenus.push({
|
|
id: 'subscription',
|
|
title: '구독/결제 관리',
|
|
icon: 'pi pi-credit-card',
|
|
to: '/club/subscription',
|
|
order: 90,
|
|
children: []
|
|
});
|
|
}
|
|
|
|
return rootMenus;
|
|
} catch (error) {
|
|
console.error('메뉴 조회 중 오류 발생:', error);
|
|
throw error;
|
|
}
|
|
} |