102 lines
2.9 KiB
JavaScript
102 lines
2.9 KiB
JavaScript
|
|
/**
|
||
|
|
* 앨범 서비스
|
||
|
|
* 앨범 관련 비즈니스 로직
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 앨범 상세 정보 조회 (트랙, 티저, 컨셉포토 포함)
|
||
|
|
* @param {object} db - 데이터베이스 연결
|
||
|
|
* @param {object} album - 앨범 기본 정보
|
||
|
|
* @returns {object} 상세 정보가 포함된 앨범
|
||
|
|
*/
|
||
|
|
export async function getAlbumDetails(db, album) {
|
||
|
|
const [tracks] = await db.query(
|
||
|
|
'SELECT * FROM album_tracks WHERE album_id = ? ORDER BY track_number',
|
||
|
|
[album.id]
|
||
|
|
);
|
||
|
|
album.tracks = tracks;
|
||
|
|
|
||
|
|
const [teasers] = await db.query(
|
||
|
|
`SELECT original_url, medium_url, thumb_url, video_url, media_type
|
||
|
|
FROM album_teasers WHERE album_id = ? ORDER BY sort_order`,
|
||
|
|
[album.id]
|
||
|
|
);
|
||
|
|
album.teasers = teasers;
|
||
|
|
|
||
|
|
const [photos] = await db.query(
|
||
|
|
`SELECT
|
||
|
|
p.id, p.original_url, p.medium_url, p.thumb_url, p.photo_type, p.concept_name, p.sort_order,
|
||
|
|
p.width, p.height,
|
||
|
|
GROUP_CONCAT(m.name ORDER BY m.id SEPARATOR ', ') as members
|
||
|
|
FROM album_photos p
|
||
|
|
LEFT JOIN album_photo_members pm ON p.id = pm.photo_id
|
||
|
|
LEFT JOIN members m ON pm.member_id = m.id
|
||
|
|
WHERE p.album_id = ?
|
||
|
|
GROUP BY p.id
|
||
|
|
ORDER BY p.sort_order`,
|
||
|
|
[album.id]
|
||
|
|
);
|
||
|
|
|
||
|
|
const conceptPhotos = {};
|
||
|
|
for (const photo of photos) {
|
||
|
|
const concept = photo.concept_name || 'Default';
|
||
|
|
if (!conceptPhotos[concept]) {
|
||
|
|
conceptPhotos[concept] = [];
|
||
|
|
}
|
||
|
|
conceptPhotos[concept].push({
|
||
|
|
id: photo.id,
|
||
|
|
original_url: photo.original_url,
|
||
|
|
medium_url: photo.medium_url,
|
||
|
|
thumb_url: photo.thumb_url,
|
||
|
|
width: photo.width,
|
||
|
|
height: photo.height,
|
||
|
|
type: photo.photo_type,
|
||
|
|
members: photo.members,
|
||
|
|
sortOrder: photo.sort_order,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
album.conceptPhotos = conceptPhotos;
|
||
|
|
|
||
|
|
return album;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 앨범 목록과 트랙 조회 (N+1 최적화)
|
||
|
|
* @param {object} db - 데이터베이스 연결
|
||
|
|
* @returns {array} 트랙 포함된 앨범 목록
|
||
|
|
*/
|
||
|
|
export async function getAlbumsWithTracks(db) {
|
||
|
|
const [albums] = await db.query(`
|
||
|
|
SELECT id, title, folder_name, album_type, album_type_short, release_date,
|
||
|
|
cover_original_url, cover_medium_url, cover_thumb_url, description
|
||
|
|
FROM albums
|
||
|
|
ORDER BY release_date DESC
|
||
|
|
`);
|
||
|
|
|
||
|
|
if (albums.length === 0) return albums;
|
||
|
|
|
||
|
|
// 모든 트랙을 한 번에 조회
|
||
|
|
const albumIds = albums.map(a => a.id);
|
||
|
|
const [allTracks] = await db.query(
|
||
|
|
`SELECT id, album_id, track_number, title, is_title_track, duration, lyricist, composer, arranger
|
||
|
|
FROM album_tracks WHERE album_id IN (?) ORDER BY album_id, track_number`,
|
||
|
|
[albumIds]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 앨범 ID별로 트랙 그룹화
|
||
|
|
const tracksByAlbum = {};
|
||
|
|
for (const track of allTracks) {
|
||
|
|
if (!tracksByAlbum[track.album_id]) {
|
||
|
|
tracksByAlbum[track.album_id] = [];
|
||
|
|
}
|
||
|
|
tracksByAlbum[track.album_id].push(track);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 각 앨범에 트랙 할당
|
||
|
|
for (const album of albums) {
|
||
|
|
album.tracks = tracksByAlbum[album.id] || [];
|
||
|
|
}
|
||
|
|
|
||
|
|
return albums;
|
||
|
|
}
|