refactor(backend): 일정 API 응답 형식을 검색 API와 통일

변경 사항:
- getMonthlySchedules: 날짜별 그룹화 → 플랫 배열
- getUpcomingSchedules: 날짜별 그룹화 → 플랫 배열

새 형식:
{
  "schedules": [
    {
      "id": 123,
      "title": "...",
      "datetime": "2025-01-21T19:00:00",
      "category": { "id": 1, "name": "...", "color": "#..." },
      "source": { "name": "...", "url": "..." },
      "members": ["name1", "name2"]
    }
  ]
}

주요 변경:
- date/time 분리 → datetime 통합
- members: [{ name: "..." }] → ["name1", "name2"]
- categories 카운트 제거
- _rankingScore 없음 (검색 API에만 존재)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-01-21 20:47:40 +09:00
parent 0255b35616
commit 22ce21f908

View file

@ -121,10 +121,11 @@ export async function getScheduleDetail(db, id, getXProfile = null) {
/** /**
* 월별 일정 조회 (생일 포함) * 월별 일정 조회 (생일 포함)
* 검색 API와 동일한 형식으로 반환
* @param {object} db - 데이터베이스 연결 * @param {object} db - 데이터베이스 연결
* @param {number} year - 연도 * @param {number} year - 연도
* @param {number} month - * @param {number} month -
* @returns {object} 날짜별로 그룹화된 일정 * @returns {object} { schedules: [] } 형식의 일정 배열
*/ */
export async function getMonthlySchedules(db, year, month) { export async function getMonthlySchedules(db, year, month) {
const startDate = `${year}-${String(month).padStart(2, '0')}-01`; const startDate = `${year}-${String(month).padStart(2, '0')}-01`;
@ -169,7 +170,7 @@ export async function getMonthlySchedules(db, year, month) {
if (!memberMap[sm.schedule_id]) { if (!memberMap[sm.schedule_id]) {
memberMap[sm.schedule_id] = []; memberMap[sm.schedule_id] = [];
} }
memberMap[sm.schedule_id].push({ name: sm.name }); memberMap[sm.schedule_id].push(sm.name);
} }
} }
@ -182,37 +183,31 @@ export async function getMonthlySchedules(db, year, month) {
WHERE m.is_former = 0 AND MONTH(m.birth_date) = ? WHERE m.is_former = 0 AND MONTH(m.birth_date) = ?
`, [month]); `, [month]);
// 날짜별로 그룹화 // 결과 배열
const grouped = {}; const result = [];
// 일정 추가 // 일정 추가
for (const s of schedules) { for (const s of schedules) {
const dateKey = s.date instanceof Date const dateStr = s.date instanceof Date
? s.date.toISOString().split('T')[0] ? s.date.toISOString().split('T')[0]
: s.date; : s.date;
if (!grouped[dateKey]) { // datetime 생성
grouped[dateKey] = { const datetime = s.time ? `${dateStr}T${s.time}` : dateStr;
categories: [],
schedules: [],
};
}
// 멤버 정보 (5명 이상이면 프로미스나인) // 멤버 이름 배열 (문자열 배열)
const scheduleMembers = memberMap[s.id] || []; const members = memberMap[s.id] || [];
const members = scheduleMembers.length >= 5
? [{ name: '프로미스나인' }]
: scheduleMembers;
const schedule = { const schedule = {
id: s.id, id: s.id,
title: s.title, title: s.title,
time: s.time, datetime,
category: { category: {
id: s.category_id, id: s.category_id,
name: s.category_name, name: s.category_name,
color: s.category_color, color: s.category_color,
}, },
source: null,
members, members,
}; };
@ -232,20 +227,7 @@ export async function getMonthlySchedules(db, year, month) {
}; };
} }
grouped[dateKey].schedules.push(schedule); result.push(schedule);
// 카테고리 카운트
const existingCategory = grouped[dateKey].categories.find(c => c.id === s.category_id);
if (existingCategory) {
existingCategory.count++;
} else {
grouped[dateKey].categories.push({
id: s.category_id,
name: s.category_name,
color: s.category_color,
count: 1,
});
}
} }
// 생일 일정 추가 // 생일 일정 추가
@ -259,54 +241,37 @@ export async function getMonthlySchedules(db, year, month) {
const birthdayThisYear = new Date(year, birthDate.getMonth(), birthDate.getDate()); const birthdayThisYear = new Date(year, birthDate.getMonth(), birthDate.getDate());
const dateKey = birthdayThisYear.toISOString().split('T')[0]; const dateKey = birthdayThisYear.toISOString().split('T')[0];
if (!grouped[dateKey]) {
grouped[dateKey] = {
categories: [],
schedules: [],
};
}
// 생일 카테고리
const BIRTHDAY_CATEGORY = {
id: CATEGORY_IDS.BIRTHDAY,
name: '생일',
color: '#f472b6',
};
const birthdaySchedule = { const birthdaySchedule = {
id: `birthday-${member.id}`, id: `birthday-${member.id}`,
title: `HAPPY ${member.name_en} DAY`, title: `HAPPY ${member.name_en} DAY`,
time: null, datetime: dateKey,
category: BIRTHDAY_CATEGORY, category: {
id: CATEGORY_IDS.BIRTHDAY,
name: '생일',
color: '#f472b6',
},
source: null,
members: [member.name],
is_birthday: true, is_birthday: true,
member_name: member.name,
member_image: member.image_url, member_image: member.image_url,
}; };
grouped[dateKey].schedules.push(birthdaySchedule); result.push(birthdaySchedule);
// 생일 카테고리 카운트
const existingBirthdayCategory = grouped[dateKey].categories.find(c => c.id === CATEGORY_IDS.BIRTHDAY);
if (existingBirthdayCategory) {
existingBirthdayCategory.count++;
} else {
grouped[dateKey].categories.push({
...BIRTHDAY_CATEGORY,
count: 1,
});
}
} }
return grouped; // 날짜 + 시간 순으로 정렬
result.sort((a, b) => a.datetime.localeCompare(b.datetime));
return { schedules: result };
} }
/** /**
* 다가오는 일정 조회 (startDate부터 limit개) * 다가오는 일정 조회 (startDate부터 limit개)
* getMonthlySchedules동일한 형식으로 반환 * 검색 API와 동일한 형식으로 반환
* @param {object} db - 데이터베이스 연결 * @param {object} db - 데이터베이스 연결
* @param {string} startDate - 시작 날짜 * @param {string} startDate - 시작 날짜
* @param {number} limit - 조회 개수 * @param {number} limit - 조회 개수
* @returns {object} 날짜별로 그룹화된 일정 * @returns {object} { schedules: [] } 형식의 일정 배열
*/ */
export async function getUpcomingSchedules(db, startDate, limit) { export async function getUpcomingSchedules(db, startDate, limit) {
// 일정 조회 (YouTube, X 소스 정보 포함) // 일정 조회 (YouTube, X 소스 정보 포함)
@ -349,40 +314,34 @@ export async function getUpcomingSchedules(db, startDate, limit) {
if (!memberMap[sm.schedule_id]) { if (!memberMap[sm.schedule_id]) {
memberMap[sm.schedule_id] = []; memberMap[sm.schedule_id] = [];
} }
memberMap[sm.schedule_id].push({ name: sm.name }); memberMap[sm.schedule_id].push(sm.name);
} }
} }
// 날짜별로 그룹화 (getMonthlySchedules와 동일한 형식) // 결과 배열
const grouped = {}; const result = [];
for (const s of schedules) { for (const s of schedules) {
const dateKey = s.date instanceof Date const dateStr = s.date instanceof Date
? s.date.toISOString().split('T')[0] ? s.date.toISOString().split('T')[0]
: s.date; : s.date;
if (!grouped[dateKey]) { // datetime 생성
grouped[dateKey] = { const datetime = s.time ? `${dateStr}T${s.time}` : dateStr;
categories: [],
schedules: [],
};
}
// 멤버 정보 (5명 이상이면 프로미스나인) // 멤버 이름 배열 (문자열 배열)
const scheduleMembers = memberMap[s.id] || []; const members = memberMap[s.id] || [];
const members = scheduleMembers.length >= 5
? [{ name: '프로미스나인' }]
: scheduleMembers;
const schedule = { const schedule = {
id: s.id, id: s.id,
title: s.title, title: s.title,
time: s.time, datetime,
category: { category: {
id: s.category_id, id: s.category_id,
name: s.category_name, name: s.category_name,
color: s.category_color, color: s.category_color,
}, },
source: null,
members, members,
}; };
@ -402,21 +361,8 @@ export async function getUpcomingSchedules(db, startDate, limit) {
}; };
} }
grouped[dateKey].schedules.push(schedule); result.push(schedule);
// 카테고리 카운트
const existingCategory = grouped[dateKey].categories.find(c => c.id === s.category_id);
if (existingCategory) {
existingCategory.count++;
} else {
grouped[dateKey].categories.push({
id: s.category_id,
name: s.category_name,
color: s.category_color,
count: 1,
});
}
} }
return grouped; return { schedules: result };
} }