| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /**
- * 华中科技大学 HUB 系统课表适配器。
- *
- * 接口与解析规则参考 StayUP-Calendar 的 HUST importer:
- * https://github.com/Lucas04-nhr/StayUP-Calendar/blob/main/lib/src/pages/crawler/hust.dart
- */
- const HUST_COURSE_API = new URL("/LsController/findNameCourse", location.origin).toString();
- const HUST_DEFAULT_SEMESTER_WEEKS = 20;
- function getDefaultTerm(now = new Date()) {
- return {
- academicYear: now.getMonth() === 0 ? now.getFullYear() - 1 : now.getFullYear(),
- semester: now.getMonth() >= 1 && now.getMonth() <= 6 ? 2 : 1
- };
- }
- /**
- * 生成与原 importer 相同范围的学期:当前年份前 4 年至后 1 年,共 12 个。
- * semester=1 为秋季,semester=2 为春季;倒序排列便于优先选择近期学期。
- */
- function buildTermOptions(now = new Date()) {
- const currentYear = now.getFullYear();
- const terms = [];
- for (let year = currentYear + 1; year >= currentYear - 4; year--) {
- terms.push({ academicYear: year, semester: 1, label: `${year} 年秋季学期` });
- terms.push({ academicYear: year, semester: 2, label: `${year} 年春季学期` });
- }
- return terms;
- }
- async function selectTerm() {
- const terms = buildTermOptions();
- const defaultTerm = getDefaultTerm();
- const defaultIndex = Math.max(0, terms.findIndex((term) =>
- term.academicYear === defaultTerm.academicYear &&
- term.semester === defaultTerm.semester
- ));
- const selectedIndex = await window.AndroidBridgePromise.showSingleSelection(
- "选择学期",
- JSON.stringify(terms.map((term) => term.label)),
- defaultIndex
- );
- if (selectedIndex === null || selectedIndex === -1) {
- return null;
- }
- if (!Number.isInteger(selectedIndex) || selectedIndex < 0 || selectedIndex >= terms.length) {
- throw new Error("学期选择结果无效");
- }
- return terms[selectedIndex];
- }
- function parsePositiveInteger(value) {
- const parsed = Number.parseInt(String(value), 10);
- return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
- }
- function expandWeeks(startWeek, endWeek) {
- const weeks = [];
- for (let week = startWeek; week <= endWeek; week++) {
- weeks.push(week);
- }
- return weeks;
- }
- /**
- * HUST 的一门课程可包含多个 tr 时段。本项目没有 extraSlots 字段,
- * 因此每个有效时段转换成一个标准课程块。
- */
- function parseCourses(payload) {
- if (!payload || String(payload.code) !== "200" || !Array.isArray(payload.data)) {
- throw new Error(`课表接口返回异常(code: ${payload?.code ?? "未知"})`);
- }
- const courses = [];
- for (const item of payload.data) {
- const name = String(item?.KCMC ?? "").trim();
- if (!name || !Array.isArray(item?.tr)) {
- continue;
- }
- for (const slot of item.tr) {
- if (slot?.XQS === "<待定>" || slot?.QSJC === "<待定>") {
- continue;
- }
- const day = parsePositiveInteger(slot?.XQS);
- const startSection = parsePositiveInteger(slot?.QSJC);
- const endSection = parsePositiveInteger(slot?.JSJC);
- const startWeek = parsePositiveInteger(slot?.QSZC);
- const endWeek = parsePositiveInteger(slot?.JSZC);
- if (
- day === null || day > 7 ||
- startSection === null || endSection === null || endSection < startSection ||
- startWeek === null || endWeek === null || endWeek < startWeek
- ) {
- console.warn("跳过无法解析的 HUST 课程时段:", name, slot);
- continue;
- }
- courses.push({
- name,
- teacher: String(slot?.XM ?? "").trim(),
- position: String(slot?.JSMC ?? "").trim(),
- day,
- startSection,
- endSection,
- weeks: expandWeeks(startWeek, endWeek)
- });
- }
- }
- return courses;
- }
- async function fetchCourses(term) {
- const termCode = `${term.academicYear}${term.semester}`;
- const response = await fetch(`${HUST_COURSE_API}?kcbxqh=${encodeURIComponent(termCode)}`, {
- method: "GET",
- credentials: "include",
- headers: {
- Accept: "application/json, text/plain, */*"
- }
- });
- if (!response.ok) {
- throw new Error(`课表请求失败(HTTP ${response.status})`);
- }
- const raw = await response.text();
- if (!raw.trim() || raw.trim().startsWith("<")) {
- throw new Error("未读取到课表数据,请确认已登录 HUB 系统并选择了正确的学期");
- }
- try {
- return parseCourses(JSON.parse(raw));
- } catch (error) {
- if (error instanceof SyntaxError) {
- throw new Error("课表接口未返回有效 JSON,请重新登录后再试");
- }
- throw error;
- }
- }
- async function saveCourses(courses) {
- await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
- const semesterTotalWeeks = courses.reduce((maximum, course) => {
- const courseMaximum = course.weeks.length > 0 ? Math.max(...course.weeks) : 0;
- return Math.max(maximum, courseMaximum);
- }, HUST_DEFAULT_SEMESTER_WEEKS);
- await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify({ semesterTotalWeeks }));
- }
- async function runImportFlow() {
- try {
- const term = await selectTerm();
- if (term === null) {
- AndroidBridge.showToast("已取消导入");
- return;
- }
- AndroidBridge.showToast(`正在获取 ${term.label} 课表...`);
- const courses = await fetchCourses(term);
- if (courses.length === 0) {
- AndroidBridge.showToast("该学期未查询到有效课程,请检查学期或登录状态");
- return;
- }
- await saveCourses(courses);
- AndroidBridge.showToast(`成功导入 ${courses.length} 个课程时段,请按校历设置开学日期`);
- AndroidBridge.notifyTaskCompletion();
- } catch (error) {
- console.error("课表导入失败:", error);
- AndroidBridge.showToast(`导入失败:${error?.message ?? error}`);
- }
- }
- runImportFlow();
|