feat: 봇 일정 추가 시 Meilisearch 실시간 동기화
- syncScheduleById 함수 추가: 개별 일정 동기화 - YouTube 봇: 영상 추가 시 Meilisearch 동기화 - X 봇: 트윗/유튜브 링크 추가 시 Meilisearch 동기화 - description 컬럼 제거 (schedules 테이블에 없음) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ea9922de00
commit
2fec6c552d
3 changed files with 71 additions and 7 deletions
|
|
@ -178,7 +178,7 @@ function formatScheduleResponse(hit) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 일정 추가/업데이트
|
||||
* 일정 추가/업데이트 (데이터 직접 전달)
|
||||
*/
|
||||
export async function addOrUpdateSchedule(meilisearch, schedule) {
|
||||
try {
|
||||
|
|
@ -187,7 +187,6 @@ export async function addOrUpdateSchedule(meilisearch, schedule) {
|
|||
const document = {
|
||||
id: schedule.id,
|
||||
title: schedule.title,
|
||||
description: schedule.description || '',
|
||||
date: schedule.date,
|
||||
time: schedule.time || '',
|
||||
category_id: schedule.category_id,
|
||||
|
|
@ -204,6 +203,59 @@ export async function addOrUpdateSchedule(meilisearch, schedule) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 일정 ID로 DB에서 조회 후 Meilisearch에 동기화
|
||||
*/
|
||||
export async function syncScheduleById(meilisearch, db, scheduleId) {
|
||||
try {
|
||||
const [rows] = await db.query(`
|
||||
SELECT
|
||||
s.id,
|
||||
s.title,
|
||||
s.date,
|
||||
s.time,
|
||||
s.category_id,
|
||||
c.name as category_name,
|
||||
c.color as category_color,
|
||||
sy.channel_name as source_name,
|
||||
GROUP_CONCAT(DISTINCT m.name ORDER BY m.id SEPARATOR ',') as member_names
|
||||
FROM schedules s
|
||||
LEFT JOIN schedule_categories c ON s.category_id = c.id
|
||||
LEFT JOIN schedule_youtube sy ON s.id = sy.schedule_id
|
||||
LEFT JOIN schedule_members sm ON s.id = sm.schedule_id
|
||||
LEFT JOIN members m ON sm.member_id = m.id AND m.is_former = 0
|
||||
WHERE s.id = ?
|
||||
GROUP BY s.id
|
||||
`, [scheduleId]);
|
||||
|
||||
if (rows.length === 0) {
|
||||
logger.warn(`일정을 찾을 수 없음: ${scheduleId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const s = rows[0];
|
||||
const document = {
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
date: s.date instanceof Date ? s.date.toISOString().split('T')[0] : s.date,
|
||||
time: s.time || '',
|
||||
category_id: s.category_id,
|
||||
category_name: s.category_name || '',
|
||||
category_color: s.category_color || '',
|
||||
source_name: s.source_name || '',
|
||||
member_names: s.member_names || '',
|
||||
};
|
||||
|
||||
const index = meilisearch.index(INDEX_NAME);
|
||||
await index.addDocuments([document]);
|
||||
logger.info(`일정 동기화: ${scheduleId}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
logger.error(`일정 동기화 오류 (${scheduleId}): ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 일정 삭제
|
||||
*/
|
||||
|
|
@ -233,7 +285,6 @@ export async function syncAllSchedules(meilisearch, db) {
|
|||
SELECT
|
||||
s.id,
|
||||
s.title,
|
||||
s.description,
|
||||
s.date,
|
||||
s.time,
|
||||
s.category_id,
|
||||
|
|
@ -255,7 +306,6 @@ export async function syncAllSchedules(meilisearch, db) {
|
|||
const documents = schedules.map(s => ({
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
description: s.description || '',
|
||||
date: s.date instanceof Date ? s.date.toISOString().split('T')[0] : s.date,
|
||||
time: s.time || '',
|
||||
category_id: s.category_id,
|
||||
|
|
@ -310,7 +360,7 @@ async function recreateIndex(meilisearch) {
|
|||
|
||||
// 설정 복원
|
||||
await index.updateSearchableAttributes([
|
||||
'title', 'member_names', 'description', 'source_name', 'category_name',
|
||||
'title', 'member_names', 'source_name', 'category_name',
|
||||
]);
|
||||
await index.updateFilterableAttributes(['category_id', 'date']);
|
||||
await index.updateSortableAttributes(['date', 'time']);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { fetchVideoInfo } from '../youtube/api.js';
|
|||
import { formatDate, formatTime, nowKST } from '../../utils/date.js';
|
||||
import bots from '../../config/bots.js';
|
||||
import { withTransaction } from '../../utils/transaction.js';
|
||||
import { syncScheduleById } from '../meilisearch/index.js';
|
||||
|
||||
const X_CATEGORY_ID = 3;
|
||||
const YOUTUBE_CATEGORY_ID = 2;
|
||||
|
|
@ -141,8 +142,12 @@ async function xBotPlugin(fastify, opts) {
|
|||
// 관리 중인 채널이면 스킵
|
||||
if (managedChannels.includes(video.channelId)) continue;
|
||||
|
||||
const saved = await saveYoutubeFromTweet(video);
|
||||
if (saved) addedCount++;
|
||||
const scheduleId = await saveYoutubeFromTweet(video);
|
||||
if (scheduleId) {
|
||||
// Meilisearch 동기화
|
||||
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||
addedCount++;
|
||||
}
|
||||
} catch (err) {
|
||||
fastify.log.error(`YouTube 영상 처리 오류 (${videoId}): ${err.message}`);
|
||||
}
|
||||
|
|
@ -166,6 +171,8 @@ async function xBotPlugin(fastify, opts) {
|
|||
for (const tweet of tweets) {
|
||||
const scheduleId = await saveTweet(tweet);
|
||||
if (scheduleId) {
|
||||
// Meilisearch 동기화
|
||||
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||
addedCount++;
|
||||
// YouTube 링크 처리
|
||||
ytAddedCount += await processYoutubeLinks(tweet);
|
||||
|
|
@ -187,6 +194,8 @@ async function xBotPlugin(fastify, opts) {
|
|||
for (const tweet of tweets) {
|
||||
const scheduleId = await saveTweet(tweet);
|
||||
if (scheduleId) {
|
||||
// Meilisearch 동기화
|
||||
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||
addedCount++;
|
||||
ytAddedCount += await processYoutubeLinks(tweet);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { fetchRecentVideos, fetchAllVideos, getUploadsPlaylistId } from './api.j
|
|||
import bots from '../../config/bots.js';
|
||||
import { CATEGORY_IDS } from '../../config/index.js';
|
||||
import { withTransaction } from '../../utils/transaction.js';
|
||||
import { syncScheduleById } from '../meilisearch/index.js';
|
||||
|
||||
const YOUTUBE_CATEGORY_ID = CATEGORY_IDS.YOUTUBE;
|
||||
const PLAYLIST_CACHE_PREFIX = 'yt_uploads:';
|
||||
|
|
@ -126,6 +127,8 @@ async function youtubeBotPlugin(fastify, opts) {
|
|||
for (const video of videos) {
|
||||
const scheduleId = await saveVideo(video, bot);
|
||||
if (scheduleId) {
|
||||
// Meilisearch 동기화
|
||||
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||
addedCount++;
|
||||
}
|
||||
}
|
||||
|
|
@ -144,6 +147,8 @@ async function youtubeBotPlugin(fastify, opts) {
|
|||
for (const video of videos) {
|
||||
const scheduleId = await saveVideo(video, bot);
|
||||
if (scheduleId) {
|
||||
// Meilisearch 동기화
|
||||
await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
|
||||
addedCount++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue