#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const axios = require('axios'); require('dotenv').config({ path: path.join(__dirname, '../../../.env') }); // API配置 const API_BASE_URL = process.env.API_BASE_URL || 'https://api.qonnwolf.com/api/v1'; const AUTHORIZATION = process.env.AUTHORIZATION || ''; const TEACHER_NAME = process.env.EMPLOYEE_NAME || '橙子(程城)'; // 日期处理工具函数 function formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } function parseDate(dateStr) { // 支持多种日期格式:2026-04-24、2026/04/24、20260424、04-24、4月24日等 dateStr = dateStr.replace(/年|月/g, '-').replace(/日/g, ''); // 如果是短日期,默认使用当前年份 if (/^\d{1,2}-\d{1,2}$/.test(dateStr)) { const currentYear = new Date().getFullYear(); dateStr = `${currentYear}-${dateStr}`; } // 处理纯数字格式 if (/^\d{8}$/.test(dateStr)) { dateStr = `${dateStr.slice(0, 4)}-${dateStr.slice(4, 6)}-${dateStr.slice(6, 8)}`; } // 替换斜杠为横杠 dateStr = dateStr.replace(/\//g, '-'); const date = new Date(dateStr); if (isNaN(date.getTime())) { throw new Error(`无效的日期格式:${dateStr},请使用YYYY-MM-DD格式`); } return date; } function getDateRange(startDateStr, endDateStr) { const startDate = parseDate(startDateStr); const endDate = endDateStr ? parseDate(endDateStr) : startDate; if (startDate > endDate) { throw new Error('开始日期不能晚于结束日期'); } const dates = []; const currentDate = new Date(startDate); while (currentDate <= endDate) { dates.push(formatDate(currentDate)); currentDate.setDate(currentDate.getDate() + 1); } return dates; } // 调用API获取单天数据 async function fetchSchedule(date) { try { console.log(`正在获取 ${date} 的教学日程数据...`); const response = await axios.get(`${API_BASE_URL}/reports/teaching-schedule`, { headers: { 'Authorization': AUTHORIZATION }, params: { teaching_date: date, teacher_name: TEACHER_NAME } }); if (response.data.code !== 0) { throw new Error(`API返回错误:${response.data.message}`); } const data = response.data.data; return { teaching_date: date, items: data.items.map(item => ({ class_id: item.class_id, class_name: item.class_name, start_time: item.teaching_time_period.split('-')[0].trim(), end_time: item.teaching_time_period.split('-')[1].trim(), teacher_name: item.teacher_name, classroom: item.school_name, students: item.student_names.map(name => { // 先默认都是出勤,后续可以根据实际出勤数据调整 return { student_name: name, attendance_status: '✅ 出勤', remark: '' }; }) })) }; } catch (error) { console.error(`获取 ${date} 数据失败:`, error.message); throw error; } } // 批量获取日期范围内的数据 async function fetchScheduleRange(dates) { const scheduleData = []; for (const date of dates) { const data = await fetchSchedule(date); scheduleData.push(data); } return scheduleData; } // 生成HTML文件 function generateHTML(scheduleData, startDate, endDate) { // 读取模板文件 const templatePath = path.join(__dirname, 'template.html'); let template = fs.readFileSync(templatePath, 'utf-8'); // 生成日期范围显示文本 const dateRangeText = startDate === endDate ? startDate : `${startDate} 至 ${endDate}`; const updateTime = formatDate(new Date()) + ' ' + new Date().toTimeString().slice(0, 5); // 替换模板中的占位符 template = template.replace(/{{dateRange}}/g, dateRangeText); template = template.replace(/{{updateTime}}/g, updateTime); template = template.replace(/{{totalDays}}/g, scheduleData.length); template = template.replace(/const scheduleData = {{scheduleData}};/, `const scheduleData = ${JSON.stringify(scheduleData, null, 2)};`); // 确保输出目录存在 const outputDir = path.join(__dirname, `../../../output/teaching-schedule/${dateRangeText.replace(/ 至 /g, '_')}`); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // 写入HTML文件 const htmlPath = path.join(outputDir, 'schedule.html'); fs.writeFileSync(htmlPath, template, 'utf-8'); // 写入JSON数据文件 const jsonPath = path.join(outputDir, 'schedule.json'); fs.writeFileSync(jsonPath, JSON.stringify(scheduleData, null, 2), 'utf-8'); return { htmlPath, jsonPath, outputDir, dateRangeText }; } // 主函数 async function main() { try { // 解析命令行参数 const args = process.argv.slice(2); let startDateStr, endDateStr; if (args.length === 0) { // 默认查询今天 const today = new Date(); startDateStr = formatDate(today); endDateStr = startDateStr; } else if (args.length === 1) { // 查询单天 startDateStr = args[0]; endDateStr = startDateStr; } else { // 查询时间段 startDateStr = args[0]; endDateStr = args[1]; } // 获取日期范围 const dates = getDateRange(startDateStr, endDateStr); const startDate = dates[0]; const endDate = dates[dates.length - 1]; console.log(`查询日期范围:${startDate} 至 ${endDate}`); console.log(`共 ${dates.length} 天`); // 获取数据 const scheduleData = await fetchScheduleRange(dates); // 统计信息 const totalClasses = scheduleData.reduce((sum, day) => sum + day.items.length, 0); const totalStudents = scheduleData.reduce((sum, day) => { return sum + day.items.reduce((daySum, cls) => daySum + cls.students.length, 0); }, 0); console.log(`查询完成:共 ${totalClasses} 节课,${totalStudents} 人次`); // 生成HTML const result = generateHTML(scheduleData, startDate, endDate); console.log(''); console.log('✅ 教学日程生成成功!'); console.log('📁 输出目录:', result.outputDir); console.log('📄 网页文件:', result.htmlPath); console.log('📊 数据文件:', result.jsonPath); console.log(''); console.log('直接打开schedule.html文件即可查看教学日程!'); } catch (error) { console.error('❌ 生成失败:', error.message); process.exit(1); } } // 运行主函数 if (require.main === module) { main(); } module.exports = { fetchSchedule, fetchScheduleRange, generateHTML };