更新各班级出勤登记与课评汇总,新增教学日程查询技能与CSP03枚举算法教案
This commit is contained in:
222
.claude/skills/教学日程查询/index.js
Normal file
222
.claude/skills/教学日程查询/index.js
Normal 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
|
||||
};
|
||||
Reference in New Issue
Block a user