Parcourir la source

improve:更新csuft开学日期适配,去除旧版v1代码 (#783)

IKUN il y a 1 mois
Parent
commit
4678e75c4d
1 fichiers modifiés avec 86 ajouts et 5 suppressions
  1. 86 5
      resources/CSUFT/csuft.js

+ 86 - 5
resources/CSUFT/csuft.js

@@ -12,9 +12,12 @@
 //   5. 2026-09 按官方《适配脚本开发指南 v2》迁移桥接 API:
 //      window.shiguangBridgePromise(异步)+ window.shiguangBridge(同步)
 //      notifyTaskCompletion 仅在整条流程成功后调用
+//   6. 2026-09-30 通过 /jsxsd/jxzl/jxzl_query(教学周历)自动获取开学日期,
+//      传入 saveCourseConfig 的 semesterStartDate;失败时静默降级
 
 var JWXT_BASE = "https://http-jwxt-csuft-edu-cn-80.webvpn.csuft.edu.cn/jsxsd";
 var KB_URL = JWXT_BASE + "/xskb/xskb_list.do";
+var JXZL_URL = JWXT_BASE + "/jxzl/jxzl_query"; // 教学周历(校历),用于推算开学日期
 
 var WEEK_DAY_MAP = { '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '日': 7, '天': 7 };
 
@@ -194,6 +197,81 @@ function parseSchedule(doc) {
     return mergeCourses(raw);
 }
 
+// 从教学周历页(jxzl_query)解析开学日期与总周数,返回 {startDate, totalWeeks}。
+// 新版强智(qz-)校历每行形如 "第1周 | 07 | 08 | 09 | 10 | 11 | 09月12日 | 09月13日":
+// 周一~周五只显示日号,周末才有"月日",所以取最早的带月份单元格作锚点,
+// 用 (锚点日期 - 距第1周周一的天数) 反推第一周周一;年份由学期号推断。
+function parseTermInfo(html, semId) {
+    var doc = new DOMParser().parseFromString(html, "text/html");
+    var pad = function (n) { return (n < 10 ? '0' : '') + n; };
+
+    var fullRe = /(\d{4})\s*[-/.年]\s*(\d{1,2})\s*[-/.月]\s*(\d{1,2})/;
+    var mdRe = /(\d{1,2})\s*月\s*(\d{1,2})/;
+    var anchors = [], maxWeek = 0;
+    var table = null, tables = doc.getElementsByTagName('table');
+    for (var i = 0; i < tables.length; i++) {
+        if (/第\s*1\s*周/.test(tables[i].textContent)) { table = tables[i]; break; }
+    }
+    if (!table) return null;
+
+    var rows = table.getElementsByTagName('tr');
+    for (var r = 0; r < rows.length; r++) {
+        var cells = rows[r].getElementsByTagName('td');
+        if (cells.length < 2) continue;
+        var wm = trimText(cells[0].textContent).match(/第\s*(\d+)\s*周/);
+        if (!wm) continue;
+        var w = parseInt(wm[1], 10);
+        if (w > maxWeek) maxWeek = w;
+        for (var d = 0; d < 7 && d + 1 < cells.length; d++) {
+            var t = trimText(cells[d + 1].textContent);
+            var m = t.match(fullRe) || t.match(mdRe);
+            if (!m) continue;
+            anchors.push({
+                idx: (w - 1) * 7 + d,
+                year: m.length === 4 ? parseInt(m[1], 10) : null,
+                month: parseInt(m[m.length - 2], 10),
+                day: parseInt(m[m.length - 1], 10)
+            });
+        }
+    }
+    if (!anchors.length) return null;
+
+    anchors.sort(function (a, b) { return a.idx - b.idx; });
+    var a0 = anchors[0];
+    var y0 = parseInt((String(semId).match(/^(\d{4})/) || [])[1], 10);
+    var year = a0.year ||
+        (a0.month >= 8 ? (y0 || new Date().getFullYear())
+                        : (y0 ? y0 + 1 : new Date().getFullYear()));
+    var start = new Date(year, a0.month - 1, a0.day - a0.idx); // Date 自动处理跨月/负数日
+    if (isNaN(start.getTime())) return null;
+    return {
+        startDate: start.getFullYear() + '-' + pad(start.getMonth() + 1) + '-' + pad(start.getDate()),
+        totalWeeks: maxWeek || null
+    };
+}
+
+// 按 YSSDUFE 标准姿势获取学期配置:POST 带 xnxq01id 请求所选学期的校历,
+// 失败则退回 GET 带学期参数(ZHKU 姿势)→ GET 不带参数(仅当前学期)。
+async function fetchTermInfo(semesterId) {
+    var postUrl = JXZL_URL;
+    var getUrl = JXZL_URL + (semesterId ? "?xnxq01id=" + encodeURIComponent(semesterId) : "");
+    var attempts = [
+        { url: postUrl, options: { method: "POST",
+            headers: { "Content-Type": "application/x-www-form-urlencoded" },
+            body: "xnxq01id=" + encodeURIComponent(semesterId || "") } },
+        { url: getUrl, options: { method: "GET" } }
+    ];
+    for (var i = 0; i < attempts.length; i++) {
+        try {
+            var resp = await fetch(attempts[i].url, Object.assign({ credentials: "include" }, attempts[i].options));
+            if (!resp.ok) continue;
+            var info = parseTermInfo(await resp.text(), semesterId);
+            if (info && info.startDate) return info;
+        } catch (e) { /* 尝试下一种方式 */ }
+    }
+    return null;
+}
+
 function mergeCourses(courses) {
     if (courses.length <= 1) return courses;
     courses.sort(function (a, b) {
@@ -333,10 +411,11 @@ async function fetchAndParseCourses(term) {
     return courses;
 }
 
-async function saveAll(courses) {
+async function saveAll(courses, termInfo) {
     try {
-        await window.shiguangBridgePromise.saveCourseConfig(
-            JSON.stringify({ semesterTotalWeeks: 20, firstDayOfWeek: 1 }));
+        var config = { semesterTotalWeeks: (termInfo && termInfo.totalWeeks) || 20, firstDayOfWeek: 1 };
+        if (termInfo && termInfo.startDate) config.semesterStartDate = termInfo.startDate;
+        await window.shiguangBridgePromise.saveCourseConfig(JSON.stringify(config));
         await window.shiguangBridgePromise.savePresetTimeSlots(JSON.stringify(TIME_SLOTS));
         await window.shiguangBridgePromise.saveImportedCourses(JSON.stringify(courses));
         return true;
@@ -362,10 +441,12 @@ async function runImportFlow() {
     const courses = await fetchAndParseCourses(term);
     if (courses === null) return; // 失败原因已在 fetchAndParseCourses 中提示
 
-    if (!(await saveAll(courses))) return;
+    const termInfo = await fetchTermInfo(term.semId);
+    if (!(await saveAll(courses, termInfo))) return;
 
     // 整条流程成功后才发送结束信号
-    window.shiguangBridge.showToast("成功导入 " + courses.length + " 门课程");
+    window.shiguangBridge.showToast("成功导入 " + courses.length + " 门课程" +
+        (termInfo && termInfo.startDate ? ",开学日期 " + termInfo.startDate : ""));
     window.shiguangBridge.notifyTaskCompletion();
 }