更新各班级出勤登记与课评汇总,新增教学日程查询技能与CSP03枚举算法教案

This commit is contained in:
chengzi
2026-05-05 18:53:11 +08:00
parent 1f276f874b
commit b7cd74392c
447 changed files with 57291 additions and 148 deletions

View File

@@ -0,0 +1,222 @@
#!/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
};