Quellcode durchsuchen

add:绥化学院适配(#563)

Haooz vor 2 Wochen
Ursprung
Commit
b64d7a715f
3 geänderte Dateien mit 247 neuen und 1 gelöschten Zeilen
  1. 5 1
      index/root_index.yaml
  2. 8 0
      resources/SHXY/adapters.yaml
  3. 234 0
      resources/SHXY/shxy.js

+ 5 - 1
index/root_index.yaml

@@ -867,4 +867,8 @@ schools:
     name: "临沂大学"
     initial: "L"
     resource_folder: "LYU"
-    
+
+  - id: "SHXY"
+    name: "绥化学院"
+    initial: "S"
+    resource_folder: "SHXY"    

+ 8 - 0
resources/SHXY/adapters.yaml

@@ -0,0 +1,8 @@
+adapters:
+  - adapter_id: "SHXY"
+    adapter_name: "绥化学院乘方教务"
+    category: "BACHELOR_AND_ASSOCIATE"
+    asset_js_path: "shxy.js"
+    import_url: "http://jwgl.shxy.edu.cn/"
+    maintainer: "Haooz"
+    description: "绥化学院乘方教务系统,进入教务系统后点击导入"

+ 234 - 0
resources/SHXY/shxy.js

@@ -0,0 +1,234 @@
+// 绥化学院(shxy.edu.cn) 拾光课程表适配脚本
+// 基于乘方教务系统接口适配
+// 出现问题请提issues或者提交pr更改
+
+/**
+ * 解析周次字符串,例如 "1,2,3,4,5" -> [1, 2, 3, 4, 5]
+ */
+function parseWeeks(weekStr) {
+    if (!weekStr) return [];
+    const weeks = weekStr.split(',')
+        .map(w => Number(w.trim()))
+        .filter(w => !isNaN(w) && w > 0);
+    return [...new Set(weeks)].sort((a, b) => a - b);
+}
+
+/**
+ * 将教务系统返回的 JSON 转换为拾光标准的 CourseJsonModel 数组
+ */
+function parseJsonData(jsonData) {
+    console.log("JS: 开始解析课程 JSON...");
+    if (!jsonData || jsonData.code !== 0 || !Array.isArray(jsonData.data)) {
+        return [];
+    }
+
+    let allProcessedCourses = [];
+
+    jsonData.data.forEach(raw => {
+        // 基础字段校验
+        if (!(raw.kcmc && raw.xq && raw.ps && raw.pe)) return;
+
+        // 识别并解析 jxcdmc2
+        const locationMap = new Map();
+        if (raw.jxcdmc2) {
+            const parts = raw.jxcdmc2.split(',');
+            parts.forEach(part => {
+                const lastDashIndex = part.lastIndexOf('-');
+                if (lastDashIndex !== -1) {
+                    const room = part.substring(0, lastDashIndex).trim();
+                    const week = Number(part.substring(lastDashIndex + 1));
+                    if (!isNaN(week)) {
+                        if (!locationMap.has(room)) locationMap.set(room, []);
+                        locationMap.get(room).push(week);
+                    }
+                }
+            });
+        }
+
+        if (locationMap.size > 0) {
+            // 如果解析成功,则根据地点拆分为多个课程对象
+            locationMap.forEach((weeks, room) => {
+                allProcessedCourses.push({
+                    name: raw.kcmc.trim(),
+                    teacher: (raw.teaxms || "未知教师").trim(),
+                    position: room || "未知地点",
+                    day: Number(raw.xq),
+                    startSection: Number(raw.ps),
+                    endSection: Number(raw.pe),
+                    weeks: weeks.sort((a, b) => a - b)
+                });
+            });
+        } else if (raw.zc) {
+            // 兜底方案:如果 jxcdmc2 无效,使用原有的 jxcdmc 和 zc 逻辑
+            allProcessedCourses.push({
+                name: raw.kcmc.trim(),
+                teacher: (raw.teaxms || "未知教师").trim(),
+                position: (raw.jxcdmc || "未知地点").trim(),
+                day: Number(raw.xq),
+                startSection: Number(raw.ps),
+                endSection: Number(raw.pe),
+                weeks: parseWeeks(raw.zc)
+            });
+        }
+    });
+
+    return allProcessedCourses.filter(course => {
+        return course.weeks.length > 0 && course.startSection <= course.endSection;
+    });
+}
+
+/**
+ * 校验函数:验证用户输入的学年格式
+ */
+function validateYearInput(input) {
+    return /^[0-9]{4}$/.test(input) ? false : "请输入四位数字的起始学年(如 2025)";
+}
+
+/**
+ * 步骤 A: 显示引导公告
+ */
+async function promptUserToStart() {
+    return await window.shiguangBridgePromise.showAlert(
+        "教务导入说明",
+        "1. 请确保已在浏览器中成功登录教务系统\n2. 导入过程中请勿关闭页面",
+        "好的,开始导入"
+    );
+}
+
+/**
+ * 步骤 B: 获取用户输入的学年
+ */
+async function getAcademicYear() {
+    const currentYear = new Date().getFullYear().toString();
+    return await window.shiguangBridgePromise.showPrompt(
+        "选择学年",
+        "请输入要导入的起始学年(例如 2025-2026 应输入2025):",
+        currentYear,
+        "validateYearInput"
+    );
+}
+
+/**
+ * 步骤 C: 选择学期
+ */
+async function selectSemester() {
+    const semesters = ["第一学期 (秋季)", "第二学期 (春季)"];
+    return await window.shiguangBridgePromise.showSingleSelection(
+        "选择学期",
+        JSON.stringify(semesters),
+        0
+    );
+}
+
+/**
+ * 发起网络请求并获取数据
+ */
+async function fetchCourses(academicYear, semesterIndex) {
+    window.shiguangBridge.showToast("正在请求教务数据...");
+    
+    const semesterCode = semesterIndex === 0 ? "01" : "02"; 
+    const body = `xnxqdm=${academicYear}${semesterCode}`;
+    const url = "http://jwgl.shxy.edu.cn/new/student/xsgrkb/getCalendarWeekDatas";
+
+    try {
+        const response = await fetch(url, {
+            method: "POST",
+            headers: { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
+            body: body,
+            credentials: "include"
+        });
+
+        if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
+        
+        const jsonData = await response.json();
+        return parseJsonData(jsonData);
+    } catch (e) {
+        console.error("Fetch Error:", e);
+        window.shiguangBridge.showToast("网络请求失败,请检查登录状态");
+        return null;
+    }
+}
+
+const CampusTimeSlots = {
+    "老校区(厚德楼)": [
+        { number: 1, startTime: "08:10", endTime: "08:55" },
+        { number: 2, startTime: "09:00", endTime: "09:45" },
+        { number: 3, startTime: "10:15", endTime: "11:00" },
+        { number: 4, startTime: "11:05", endTime: "11:50" },
+        { number: 5, startTime: "13:45", endTime: "14:30" },
+        { number: 6, startTime: "14:35", endTime: "15:20" },
+        { number: 7, startTime: "15:40", endTime: "16:25" },
+        { number: 8, startTime: "16:30", endTime: "17:15" },
+        { number: 9, startTime: "18:30", endTime: "19:15" },
+        { number: 10, startTime: "19:20", endTime: "20:05" }
+    ],
+    "新校区(崇德楼)": [
+        { number: 1, startTime: "08:10", endTime: "08:55" },
+        { number: 2, startTime: "09:00", endTime: "09:45" },
+        { number: 3, startTime: "10:30", endTime: "11:15" },
+        { number: 4, startTime: "11:20", endTime: "12:05" },
+        { number: 5, startTime: "13:45", endTime: "14:30" },
+        { number: 6, startTime: "14:35", endTime: "15:20" },
+        { number: 7, startTime: "15:55", endTime: "16:40" },
+        { number: 8, startTime: "16:45", endTime: "17:30" },
+        { number: 9, startTime: "18:30", endTime: "19:15" },
+        { number: 10, startTime: "19:20", endTime: "20:05" }
+    ]
+};
+
+async function selectCampus() {
+    const campuses = Object.keys(CampusTimeSlots);
+    const idx = await window.shiguangBridgePromise.showSingleSelection(
+        "选择校区", JSON.stringify(campuses), 0
+    );
+    if (idx === null || idx === -1) return null;
+    return campuses[idx];
+}
+
+// 主流程编排
+async function runImportFlow() {
+    // 1. 前置检查
+    const isReady = await promptUserToStart();
+    if (!isReady) return;
+
+    // 2. 选择校区
+    const campus = await selectCampus();
+    if (!campus) {
+        window.shiguangBridge.showToast("未选择校区,导入终止。");
+        return;
+    }
+
+    // 3. 获取参数(学年、学期)
+    const year = await getAcademicYear();
+    if (!year) {
+        window.shiguangBridge.showToast("导入已取消");
+        return;
+    }
+
+    const semesterIdx = await selectSemester();
+    if (semesterIdx === null) {
+        window.shiguangBridge.showToast("导入已取消");
+        return;
+    }
+
+    // 4. 执行获取与解析
+    const courses = await fetchCourses(year, semesterIdx);
+    if (!courses || courses.length === 0) {
+        if (courses && courses.length === 0) window.shiguangBridge.showToast("该学期暂无课程数据");
+        return;
+    }
+
+    // 5. 数据保存
+    try {
+        await window.shiguangBridgePromise.saveImportedCourses(JSON.stringify(courses));
+        await window.shiguangBridgePromise.savePresetTimeSlots(JSON.stringify(CampusTimeSlots[campus]));
+        window.shiguangBridge.showToast(`成功导入 ${courses.length} 门课程!(${campus})`);
+        window.shiguangBridge.notifyTaskCompletion();
+        console.log("JS: 流程成功完成");
+    } catch (e) {
+        window.shiguangBridge.showToast("保存失败: " + e.message);
+    }
+}
+
+// 启动入口
+runImportFlow();