const axios = require('axios'); const API_BASE_URL = 'http://47.101.56.101:8976/api/app_calendar'; // 复制 recurrence 相关函数 function parseRecurrenceStr(str) { const r = (str || '').trim(); if (!r) return { type: 'NONE', interval: 0, by: [] }; const type = r.startsWith('DAILY') ? 'DAILY' : (r.startsWith('WEEKLY') ? 'WEEKLY' : 'NONE'); const mInt = /interval=(\d+)/.exec(r); const interval = mInt ? Math.max(1, parseInt(mInt[1], 10)) : 1; const mBy = /by=([0-9,]+)/.exec(r); const by = mBy ? mBy[1].split(',').map(s => parseInt(s, 10)).filter(n => n >= 1 && n <= 7) : []; return { type, interval, by }; } function extractDateStr(dateStr) { if (dateStr.includes('T')) { return dateStr.split('T')[0]; } if (dateStr.includes(' ')) { return dateStr.split(' ')[0]; } return dateStr; } function safeParseDate(dateStr) { try { let date = new Date(dateStr); if (!isNaN(date.getTime())) { return date; } } catch (e) {} if (dateStr.includes(' ')) { try { let date = new Date(dateStr.replace(' ', 'T')); if (!isNaN(date.getTime())) { return date; } } catch (e) {} } const match = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/); if (match) { const year = parseInt(match[1], 10); const month = parseInt(match[2], 10) - 1; const day = parseInt(match[3], 10); return new Date(year, month, day); } return new Date(); } function shouldShowEventOnDate(event, targetDate) { const eventDate = extractDateStr(event.start); const targetDateStr = targetDate.toISOString().split('T')[0]; if (eventDate === targetDateStr) return true; const evStart = safeParseDate(event.start); const targetDayStart = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()); const eventStartDay = new Date(evStart.getFullYear(), evStart.getMonth(), evStart.getDate()); if (event.end) { const evEnd = safeParseDate(event.end); const eventEndDay = new Date(evEnd.getFullYear(), evEnd.getMonth(), evEnd.getDate()); if (targetDayStart >= eventStartDay && targetDayStart <= eventEndDay) { return true; } } const rec = parseRecurrenceStr(event.recurrence); if (rec.type === 'NONE') return false; if (targetDayStart < eventStartDay) return false; if (event.end) { const evEnd = safeParseDate(event.end); const eventEndDay = new Date(evEnd.getFullYear(), evEnd.getMonth(), evEnd.getDate()); if (targetDayStart > eventEndDay) return false; } if (rec.type === 'DAILY') { const diffDays = Math.floor((targetDayStart.getTime() - eventStartDay.getTime()) / (24 * 3600 * 1000)); return diffDays % rec.interval === 0; } if (rec.type === 'WEEKLY') { const weeksSinceStart = Math.floor((targetDayStart.getTime() - eventStartDay.getTime()) / (7 * 24 * 3600 * 1000)); const weekday = targetDayStart.getDay() === 0 ? 7 : targetDayStart.getDay(); const by = rec.by.length ? rec.by : [(safeParseDate(event.start).getDay() || 7)]; return by.includes(weekday) && (weeksSinceStart % rec.interval === 0); } return false; } async function getEvents(year, month) { const response = await axios.get(`${API_BASE_URL}/events`, { params: { year, month } }); return response.data.data || []; } async function loadTodayTabEvents(targetDate) { console.log('加载【今日】Tab 数据...'); const allEvents = await getEvents(targetDate.getFullYear(), targetDate.getMonth() + 1); const todayEvents = allEvents.filter(event => { if (event.deleted) return false; return shouldShowEventOnDate(event, targetDate); }).sort((a, b) => safeParseDate(a.start).getTime() - safeParseDate(b.start).getTime()); return todayEvents; } async function loadCalendarTabEvents(targetDate) { console.log('加载【日历】Tab 数据...'); const allEvents = []; const seenIds = new Set(); for (let i = -6; i <= 1; i++) { let tempDate = new Date(targetDate); tempDate.setMonth(tempDate.getMonth() + i); const year = tempDate.getFullYear(); const month = tempDate.getMonth() + 1; const monthEvents = await getEvents(year, month); for (const event of monthEvents) { if (!seenIds.has(event.id)) { seenIds.add(event.id); allEvents.push(event); } } } const validEvents = allEvents.filter(e => !e.deleted); const targetEvents = validEvents.filter(event => { return shouldShowEventOnDate(event, targetDate); }).sort((a, b) => safeParseDate(a.start).getTime() - safeParseDate(b.start).getTime()); return targetEvents; } async function compare() { console.log('========================================'); console.log('对比 2026-06-29 两个 Tab 的事件'); console.log('========================================\n'); const targetDate = new Date(2026, 5, 29); const [todayEvents, calendarEvents] = await Promise.all([ loadTodayTabEvents(targetDate), loadCalendarTabEvents(targetDate) ]); console.log('\n===== 【今日】Tab 2026-06-29 事件 ====='); console.log('总数:', todayEvents.length); todayEvents.forEach((event, index) => { console.log(`${index + 1}. ${event.title} (id: ${event.id})`); }); console.log('\n===== 【日历】Tab 2026-06-29 事件 ====='); console.log('总数:', calendarEvents.length); calendarEvents.forEach((event, index) => { console.log(`${index + 1}. ${event.title} (id: ${event.id})`); }); console.log('\n===== 差异对比 ====='); const todayIds = new Set(todayEvents.map(e => e.id)); const calendarIds = new Set(calendarEvents.map(e => e.id)); const onlyInToday = todayEvents.filter(e => !calendarIds.has(e.id)); const onlyInCalendar = calendarEvents.filter(e => !todayIds.has(e.id)); if (onlyInToday.length === 0 && onlyInCalendar.length === 0) { console.log('✅ 两个 Tab 的事件完全一致!'); } else { if (onlyInToday.length > 0) { console.log(`❌ 只在【今日】Tab 中的事件 (${onlyInToday.length} 个):`); onlyInToday.forEach((event, index) => { console.log(` ${index + 1}. ${event.title} (id: ${event.id})`); }); } if (onlyInCalendar.length > 0) { console.log(`❌ 只在【日历】Tab 中的事件 (${onlyInCalendar.length} 个):`); onlyInCalendar.forEach((event, index) => { console.log(` ${index + 1}. ${event.title} (id: ${event.id})`); }); } } console.log('\n========================================'); } compare().catch(console.error);