feat: 初始提交(仅核心代码,已排除大文件)

This commit is contained in:
yangxiangyuan
2026-07-08 10:29:50 +08:00
commit be7c5842f1
623 changed files with 332897 additions and 0 deletions
@@ -0,0 +1,195 @@
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);