Просмотр исходного кода

fix: 修复广东工业大学课程导入重复和缺失问题 (#579)

forer4433 2 недель назад
Родитель
Сommit
f5d5524a64
1 измененных файлов с 84 добавлено и 63 удалено
  1. 84 63
      resources/GDUT/gdut.js

+ 84 - 63
resources/GDUT/gdut.js

@@ -1,10 +1,10 @@
 // 文件: gdut.js
 // 文件: gdut.js
 
 
 if (typeof Strings === 'undefined') {
 if (typeof Strings === 'undefined') {
-    var Strings = {
+    const Strings = {
         BASE_URL: "https://jxfw.gdut.edu.cn",
         BASE_URL: "https://jxfw.gdut.edu.cn",
-        GET_WEEK_COURSES_URL: "https://jxfw.gdut.edu.cn/xsgrkbcx!getKbRq.action",
-        GET_ALL_COURSES_URL: "https://jxfw.gdut.edu.cn/xsgrkbcx!getDataList.action",
+        GET_WEEK_COURSES_API_URL: "https://jxfw.gdut.edu.cn/xsgrkbcx!getKbRq.action",
+        GET_ALL_COURSES_API_URL: "https://jxfw.gdut.edu.cn/xsgrkbcx!getDataList.action",
         GET_ALL_COURSES_HTML_URL: "https://jxfw.gdut.edu.cn/xsgrkbcx!xsAllKbList.action",
         GET_ALL_COURSES_HTML_URL: "https://jxfw.gdut.edu.cn/xsgrkbcx!xsAllKbList.action",
         GET_ALL_COURSES_HTML_URL_REFERRER: "https://jxfw.gdut.edu.cn/xsgrkbcx!getXsgrbkList.action"
         GET_ALL_COURSES_HTML_URL_REFERRER: "https://jxfw.gdut.edu.cn/xsgrkbcx!getXsgrbkList.action"
     };
     };
@@ -14,7 +14,7 @@ async function stepDescriptionAlert() {
     try {
     try {
         const confirmed = await window.shiguangBridgePromise.showAlert(
         const confirmed = await window.shiguangBridgePromise.showAlert(
             "提示",
             "提示",
-            "即将执行导入课程操作。请确保你已处于登录状态,无需打开课程表页面。",
+            "即将执行导入课程操作,确保当前处于已登录状态(无需打开课程表页面)",
             "确认"
             "确认"
         );
         );
         
         
@@ -29,7 +29,7 @@ async function selectSemesterSelection(){
     const now = new Date();
     const now = new Date();
     const currentYear = now.getFullYear();
     const currentYear = now.getFullYear();
     const currentMonth = now.getMonth() + 1;
     const currentMonth = now.getMonth() + 1;
-    const currentSemester = currentMonth >= 8 || currentMonth <= 2 ? 1 : 2;
+    const currentSemester = currentMonth >= 7 || currentMonth <= 1 ? 1 : 2;
 
 
     const nextSemester = currentSemester === 1 ? 2 : 1;
     const nextSemester = currentSemester === 1 ? 2 : 1;
     const nextSemesterYear = currentSemester === 1 ? currentYear : currentYear + 1;
     const nextSemesterYear = currentSemester === 1 ? currentYear : currentYear + 1;
@@ -40,7 +40,7 @@ async function selectSemesterSelection(){
     for (let year = nextSemesterYear; year >= nextSemesterYear - 3; year--){
     for (let year = nextSemesterYear; year >= nextSemesterYear - 3; year--){
         for (let semester = nextSemester; semester >= 1; semester--){
         for (let semester = nextSemester; semester >= 1; semester--){
             presetSemetersId.push(`${year}0${semester}`);
             presetSemetersId.push(`${year}0${semester}`);
-            const semesterName = `${year}-${year + 1}学年 ${semester === 1 ? "秋季" : "春季"}(第${semester}学期)`;
+            const semesterName = `${year} 年${semester === 1 ? "秋季" : "春季"} (${year}-${year + 1} 学年第${semester}学期)`;
             presetSemestersName.push(semesterName);
             presetSemestersName.push(semesterName);
         }
         }
     }
     }
@@ -49,7 +49,7 @@ async function selectSemesterSelection(){
         const selectedIndex = await window.shiguangBridgePromise.showSingleSelection(
         const selectedIndex = await window.shiguangBridgePromise.showSingleSelection(
             "选择要导入的学期",
             "选择要导入的学期",
             JSON.stringify(presetSemestersName),
             JSON.stringify(presetSemestersName),
-            2
+            1
         );
         );
         if (selectedIndex !== null && selectedIndex >= 0 && selectedIndex < presetSemetersId.length) {
         if (selectedIndex !== null && selectedIndex >= 0 && selectedIndex < presetSemetersId.length) {
             console.log("用户选择了: " + presetSemestersName[selectedIndex] + " (索引: " + selectedIndex + ")");
             console.log("用户选择了: " + presetSemestersName[selectedIndex] + " (索引: " + selectedIndex + ")");
@@ -70,10 +70,6 @@ function extractFirstDay(dateInfoJsonData) {
         const jsonArray = JSON.parse(dateInfoJsonData);
         const jsonArray = JSON.parse(dateInfoJsonData);
 
 
         const dateInfoArray = jsonArray[1];
         const dateInfoArray = jsonArray[1];
-        if (!Array.isArray(dateInfoArray)) {
-            console.error('JSON 数据格式异常:索引 [1] 不是数组');
-            return null;
-        }
 
 
         // 遍历查找 xqmc === "1"(周一)的项
         // 遍历查找 xqmc === "1"(周一)的项
         for (const dateInfo of dateInfoArray) {
         for (const dateInfo of dateInfoArray) {
@@ -82,7 +78,7 @@ function extractFirstDay(dateInfoJsonData) {
             }
             }
         }
         }
 
 
-        console.warn('未找到 xqmc=1 的日期项');
+        console.error('未找到 xqmc=1 的日期项');
         return null;
         return null;
     } catch (error) {
     } catch (error) {
         console.error('解析 JSON 失败:', error);
         console.error('解析 JSON 失败:', error);
@@ -91,11 +87,10 @@ function extractFirstDay(dateInfoJsonData) {
 }
 }
 
 
 async function fetchStartDate(semesterId) {
 async function fetchStartDate(semesterId) {
-    const url = `${"https://jxfw.gdut.edu.cn"}/xsgrkbcx!getKbRq.action?xnxqdm=${semesterId}&zc=1`;
+    const url = `${Strings.GET_WEEK_COURSES_API_URL}?xnxqdm=${semesterId}&zc=1`;
     
     
     try {
     try {
-
-        console.log(`正在获取学期开始日期。学期代码:${semesterId}}`);
+        console.log(`正在获取学期开始日期。学期代码:${semesterId}`);
         const response = await fetch(url, {
         const response = await fetch(url, {
             method: 'GET',
             method: 'GET',
             headers: {
             headers: {
@@ -136,55 +131,62 @@ async function fetchCourses(semesterId){
 
 
         const rawCourses = [];
         const rawCourses = [];
 
 
-        const pageSize = 100;
-        let pageIndex = 1;
-
-        while (true) {
-            const formData = new URLSearchParams();
-            formData.append('xnxqdm', semesterId);
-            formData.append('zc', '');
-            formData.append('page', String(pageIndex));
-            formData.append('rows', String(pageSize));
-            formData.append('sort', 'kxh');
-            formData.append('order', 'asc');
-
-            const response = await fetch(Strings.GET_ALL_COURSES_URL, {
-                method: 'POST',
-                headers: {
-                    'Content-Type': 'application/x-www-form-urlencoded',
-                    'Referer': Strings.BASE_URL
-                },
-                body: formData.toString(),
-                credentials: 'include'
-            });
-
-            if (!response.ok) {
-                throw new Error(`请求失败: ${response.status}`);
-            }
+        // 理论上此处应分页遍历处理,但分页处理将导致教务系统返回错误的数据(课程重复和缺失)。
+        // 此处假设总课程数量总是小于 1000,并设置一页的最大课程数量为 1000。
+        const pageSize = 1000;
 
 
-            const rawData = await response.json();
+        const formData = new URLSearchParams();
+        formData.append('zc', '');
+        formData.append('xnxqdm', semesterId);
+        formData.append('page', '1');
+        formData.append('rows', String(pageSize));
+        formData.append('sort', 'kxh');
+        formData.append('order', 'asc');
 
 
-            if (rawData.total === 0 || rawData.rows.length === 0) {
-                console.log(`学期 ${semesterId} 没有找到课程数据。`);
-                if (await checkSemesterIsOpened(semesterId)) {
-                    throw new Error('该学期没有找到课程,请确认选择了正确的学期');
-                }
-                throw new Error('学期未开放课表查询!');
-            }
+        const response = await fetch(Strings.GET_ALL_COURSES_API_URL, {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/x-www-form-urlencoded',
+                'Referer': Strings.BASE_URL
+            },
+            body: formData.toString(),
+            credentials: 'include'
+        });
 
 
-            for (const row of rawData.rows) {
-                rawCourses.push(row);
-            }
+        if (!response.ok) {
+            throw new Error(`请求失败: ${response.status}`);
+        }
 
 
-            if (rawCourses.length >= rawData.total) {
-                break;
-            }
+        const rawData = await response.json();
 
 
-            pageIndex++;
+        if (rawData.total === 0 || rawData.rows.length === 0) {
+            console.log(`学期 ${semesterId} 没有找到课程数据。`);
+            if (await checkSemesterIsOpened(semesterId)) {
+                throw new Error('该学期没有找到课程!请确认选择了正确的学期。');
+            }
+            throw new Error('学期未开放课表查询!');
+        }
 
 
-            await new Promise(resolve => setTimeout(resolve, 100));
+        for (const row of rawData.rows) {
+            for (const existingCourse of rawCourses) {
+                if (row.xq === existingCourse.xq 
+                    && row.zc === existingCourse.zc 
+                    && row.kcmc === existingCourse.kcmc 
+                    && row.jcdm === existingCourse.jcdm 
+                    && row.kxh === existingCourse.kxh) {
+                    console.error(`检查到有与现有课程重复的课程: `);
+                    console.error(row);
+                    console.error(`已有课程:`);
+                    console.error(existingCourse);
+                    window.shiguangBridge.showToast(`导入课程失败: ${row.kcmc} 意外地出现了重复的课程。请联系开发者解决此问题。`);
+                    return null;
+                }
+            }
+            rawCourses.push(row);
         }
         }
 
 
+        console.log(`成功获取学期 ${semesterId} 的课程数据,共 ${rawCourses.length} 条记录。`);
+
         const courses = parseCourses(rawCourses);
         const courses = parseCourses(rawCourses);
 
 
         return courses;
         return courses;
@@ -216,22 +218,29 @@ async function checkSemesterIsOpened(semesterId) {
 }
 }
 
 
 function parseCourses(rawCourses){
 function parseCourses(rawCourses){
-    console.log(`正在解析课程数据,共 ${rawCourses.length} 条原始记录...`);
+    console.log(`正在转换原始课程数据...`);
 
 
     const courses = [];
     const courses = [];
     for (const raw of rawCourses) {
     for (const raw of rawCourses) {
+
         const sectionMatch = raw.jcdm.match(/\d{2}/g);
         const sectionMatch = raw.jcdm.match(/\d{2}/g);
-        if (!sectionMatch) continue;
-        
+        if (!sectionMatch) {
+            console.error(`课程节次解析失败,原始数据:${raw.jcdm},跳过该课程。`);
+            continue;
+        }
+
         const sections = sectionMatch.map(Number);
         const sections = sectionMatch.map(Number);
         const startSection = sections[0];
         const startSection = sections[0];
         const endSection = sections[sections.length - 1];
         const endSection = sections[sections.length - 1];
         
         
         // 周次
         // 周次
         const week = Number(raw.zc);
         const week = Number(raw.zc);
-        if (isNaN(week)) continue;
+        if (isNaN(week)) {
+            console.error(`课程周次解析失败,原始数据:${raw.zc},跳过该课程。`);
+            continue;
+        }
 
 
-        courses.push({
+        const course = {
             name: raw.kcmc.trim(),
             name: raw.kcmc.trim(),
             teacher: (raw.teaxms || "").trim(),
             teacher: (raw.teaxms || "").trim(),
             position: (raw.jxcdmc || "").trim(),
             position: (raw.jxcdmc || "").trim(),
@@ -240,7 +249,9 @@ function parseCourses(rawCourses){
             endSection: endSection,
             endSection: endSection,
             weeks: [week],
             weeks: [week],
             isCustomTime: false
             isCustomTime: false
-        });
+        };
+
+        courses.push(course);
     }
     }
 
 
     return courses;
     return courses;
@@ -318,7 +329,17 @@ async function saveConfig(config) {
  * 编排这些异步操作,并在用户取消时停止后续执行。
  * 编排这些异步操作,并在用户取消时停止后续执行。
  */
  */
 async function runImportFlow() {
 async function runImportFlow() {
-    var result = await stepDescriptionAlert();
+    if (window.location.hostname === "authserver.gdut.edu.cn"){
+        window.shiguangBridge.showToast("执行导入课表操作前,必须先登录到教务系统!");
+        return;
+    } 
+
+    if (window.location.hostname !== "jxfw.gdut.edu.cn") {
+        window.shiguangBridge.showToast("当前页面不是教务系统页面!");
+        return;
+    }
+
+    const result = await stepDescriptionAlert();
 
 
     if (!result) {
     if (!result) {
         console.log("用户取消了操作,停止后续执行。");
         console.log("用户取消了操作,停止后续执行。");