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

feat: CAPU教务系统新适配,新增结构化字段支持

YuXuann 1 день назад
Родитель
Сommit
3782b038af
1 измененных файлов с 260 добавлено и 284 удалено
  1. 260 284
      resources/CAPU/capadap.js

+ 260 - 284
resources/CAPU/capadap.js

@@ -1,6 +1,6 @@
 // 文件: capadap.js
 //后期可加入接口-获取校区  https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMyScheduledCampus.do
-
+// 新版适配 - 接口新增了每个字段,可以直接使用无需再做正则提取
 
 /**
  * 显示导入提示
@@ -23,156 +23,162 @@ async function promptUserToStart() {
  * 请求工具
  */
 async function api(url, options = {}) {
-    //设置默认值
     const method = options.method || (options.data ? "POST" : "GET");
-
     const headers = {
-        "fetch-api": "true",
+        "accept": "application/json, text/javascript, */*; q=0.01",
         "x-requested-with": "XMLHttpRequest",
-        "Referer": "https://jwxt.cap.edu.cn/jwapp/sys/homeapp/home/index.html",
+        "Referer": "https://jwxt.cap.edu.cn/jwapp/sys/kbapp/*default/index.do",
         ...(options.data && { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" }),
-        ...options.headers // 允许传入自定义 header 覆盖上面这些
+        ...options.headers
     };
-    //发起请求
     const res = await fetch(url, {
         method: method,
         headers: headers,
         body: options.data || null,
         credentials: "include"
     });
-
     return res.json();
 }
 
-//共享变量
+// ========== 共享变量 ==========
 const AppConfig = {
     currentSemester: null,
     postData: null,
 };
 
-/**
- * 提取上课时间   开学时间   课程周数
- */
-async function extractCourseTime() { 
-
-    try {  //上课时间
-        const userRes = await api("https://jwxt.cap.edu.cn/jwapp/sys/homeapp/api/home/currentUser.do");
-        AppConfig.currentSemester = userRes.datas.welcomeInfo.xnxqdm; //获取学期
+// ========== 1. 提取上课时间 & 学期信息 ==========
+async function extractCourseTime() {
+    try {
+        // 1. 获取当前学期
+        const userRes = await api(
+            "https://jwxt.cap.edu.cn/jwapp/sys/homeapp/api/home/currentUser.do"
+        );
+        AppConfig.currentSemester = userRes.datas.welcomeInfo.xnxqdm;
         console.log("检测到当前学期:", AppConfig.currentSemester);
 
         AppConfig.postData = `XNXQDM=${AppConfig.currentSemester}&XQDM=01`;
-        //XQDM这里暂不知道有什么用,2返回的也是一个时间 不知道是不是代表不同校区 暂时用(‘龙泉’校区)替代
-        const res = await api("https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMySectionList.do", {
-            data: AppConfig.postData,
-        })
-        const rawSections = res.datas.getMySectionList;
 
+        // 2. 获取节次时间表(小节),这里原来被 return 挡在后面了
+        const sectionRes = await api(
+            "https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMySectionList.do",
+            { data: AppConfig.postData }
+        );
+        const rawSections = sectionRes.datas.getMySectionList;
         const cleanSections = rawSections
-        .filter(item => item.name.includes("第")) 
-        .map(item => ({
-            "number": parseInt(item.name.replace(/[^0-9]/g, "")),
-            "startTime": item.startTime, // 必须叫 startTime
-            "endTime": item.endTime      // 必须叫 endTime
-        }))
-        .sort((a, b) => a.number - b.number);
-        console.log(cleanSections)
-        //开学时间 课程周数
-        
-        const weekRes = await api("https://jwxt.cap.edu.cn/jwapp/sys/homeapp/api/home/getTermWeeks.do",
-            {
-            data: `termCode=${AppConfig.currentSemester}`
-            });
+            .filter(item => item.name.includes("第"))
+            .map(item => ({
+                number: parseInt(item.name.replace(/[^0-9]/g, "")),
+                startTime: item.startTime,
+                endTime: item.endTime
+            }))
+            .sort((a, b) => a.number - b.number);
+        console.log("节次时间表:", cleanSections);
+
+        // 3. 获取学期周次
+        const weekRes = await api(
+            "https://jwxt.cap.edu.cn/jwapp/sys/homeapp/api/home/getTermWeeks.do",
+            { data: `termCode=${AppConfig.currentSemester}` }
+        );
         const finalWeeks = weekRes.datas.map(item => ({
-        "week": item.serialNumber,          // 周序 (1, 2, 3...)
-        "startTime": item.startDate.split(' ')[0], // 格式化为 YYYY-MM-DD
-        "endTime": item.endDate.split(' ')[0],   // 格式化为 YYYY-MM-DD
-        "isCurrent": item.curWeek           // 是否为当前周     
+            week: item.serialNumber,
+            startTime: item.startDate.split(' ')[0],
+            endTime: item.endDate.split(' ')[0],
+            isCurrent: item.curWeek
         }));
         const totalWeeks = finalWeeks.length;
         const startDate = finalWeeks[0].startTime;
-        console.log(AppConfig.currentSemester, totalWeeks,startDate,cleanSections)
-        
+        console.log("学期信息:", {
+            semester: AppConfig.currentSemester,
+            totalWeeks,
+            startDate
+        });
+
+        // 把 cleanSections 带出去
         return {
             currentSemester: AppConfig.currentSemester,
             totalWeeks,
             startDate,
-            cleanSections  //每日课程时间  timeSlots!!!
+            cleanSections
         };
-    
-    }
-    catch (error) {
-        console.error('解析开学时间时出错:', error);
-        AndroidBridge.showToast(`解析开学时间失败: ${error.message}`);
+    } catch (error) {
+        console.error('解析基础信息时出错:', error);
+        AndroidBridge.showToast(`解析失败: ${error.message}`);
         return null;
     }
+}
 
-}//返回 学期时间 课程周数 开始时间 时间表
-//      2025-2026-2 19 2026-03-09 Array
+// ========== 2. 获取课表原始数据 ==========
+async function getCourseData(totalWeeks) {
+    const allRaw = [];
+    const seen = new Set();
+
+    const weekRequests = [];
+    for (let zc = 1; zc <= totalWeeks; zc++) {
+        weekRequests.push(
+            api("https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMyScheduleDetail.do", {
+                data: `${AppConfig.postData}&ZC=${zc}`,
+            }).then(res => {
+                const list = res?.datas?.getMyScheduleDetail?.arrangedList || [];
+                list.forEach(item => {
+                    // 更精确的去重键:课程名+教学班ID+星期+节次+周次字符串
+                    // 加上周次字符串可以避免同一门课在不同周被误判为重复
+                    const key = `${item.courseCode || item.courseName}|${item.teachClassId}|${item.dayOfWeek}|${item.beginSection}`;
+                    
+                    if (!seen.has(key)) {
+                        seen.add(key);
+                        allRaw.push(item);
+                    }
+                });
+            }).catch(e => {
+                console.warn(`第${zc}周请求失败:`, e.message);
+            })
+        );
+    }
 
-/**
- * 获取课表数据 返回的是原始数据
- */
-async function getCourseData() {
-    const courseRes = await api("https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMyScheduleDetail.do", {
-        data: AppConfig.postData,
-    })
-    const rawCourses = courseRes?.datas?.getMyScheduleDetail?.arrangedList || [];
-    // console.log("获取到课程数据:", rawCourses);
-    return rawCourses;
+    await Promise.all(weekRequests);
+    console.log(`获取到 ${allRaw.length} 条课程数据(含短期实验/实习)`);
+    return allRaw;
 }
 
-function parseWeeks(weekStr) {
+// ========== 3. 辅助周次解析函数 ==========
+function parseWeekString(weekStr) {
+    // "101101011111111111" -> [1,3,4,6,8,9,...]
     if (!weekStr) return [];
     const weeks = [];
-    // 1. 处理逗号分隔的多个区间
-    const segments = weekStr.replace(/周/g, "").split(",");
-
-    segments.forEach(seg => {
-        // 处理单双周逻辑
-        const isOnlyOdd = seg.includes("(单)");
-        const isOnlyEven = seg.includes("(双)");
-        const cleanSeg = seg.replace(/\(单\)|\(双\)/g, "");
-
-        if (cleanSeg.includes("-")) {
-            // 处理范围型:1-4
-            const [start, end] = cleanSeg.split("-").map(Number);
-            for (let i = start; i <= end; i++) {
-                if (isOnlyOdd && i % 2 === 0) continue;
-                if (isOnlyEven && i % 2 !== 0) continue;
-                weeks.push(i);
-            }
-        } else {
-            // 处理单个数字
-            const num = Number(cleanSeg);
-            if (!isNaN(num)) weeks.push(num);
+    for (let i = 0; i < weekStr.length; i++) {
+        if (weekStr[i] === '1') {
+            weeks.push(i + 1);
         }
-    });
-
-    return [...new Set(weeks)].sort((a, b) => a - b);
+    }
+    return weeks;
 }
 
 /**
- * 1. 展开周次函数:支持 1-3周(单), 7-17周(单) 等
+ * 解析学期间的周次描述,例如 "14-15周"、"3周"、"1-3周(单),7周,11-17周(单)"
+ * 返回周次数组
  */
-function expandWeeks(rawStr) {
+function parseWeeksDescription(desc) {
+    if (!desc) return [];
     const weeks = [];
-    if (!rawStr) return weeks;
-
-    const cleanStr = rawStr.replace(/\s+/g, '').replace(/,/g, ',').replace(/周/g, '');
-    const isOdd = cleanStr.includes('(单)');
-    const isEven = cleanStr.includes('(双)');
-    const rangePart = cleanStr.replace(/\([单双]\)/g, '');
-    
-    rangePart.split(',').forEach(segment => {
-        if (segment.includes('-')) {
-            const [start, end] = segment.split('-').map(Number);
+    // 预处理:去掉“周”字、空格,中文逗号变英文
+    let clean = desc.replace(/\s+/g, '').replace(/,/g, ',').replace(/周/g, '');
+
+    const segments = clean.split(',');
+    segments.forEach(seg => {
+        // 检测单双周标记
+        const isOdd = seg.includes('(单)');
+        const isEven = seg.includes('(双)');
+        seg = seg.replace(/\(单\)|\(双\)/g, '');
+
+        if (seg.includes('-')) {
+            const [start, end] = seg.split('-').map(Number);
             for (let i = start; i <= end; i++) {
                 if (isOdd && i % 2 === 0) continue;
                 if (isEven && i % 2 !== 0) continue;
                 weeks.push(i);
             }
         } else {
-            const num = parseInt(segment);
+            const num = parseInt(seg);
             if (!isNaN(num)) {
                 if (isOdd && num % 2 === 0) return;
                 if (isEven && num % 2 !== 0) return;
@@ -180,264 +186,234 @@ function expandWeeks(rawStr) {
             }
         }
     });
-    return weeks;
+    return [...new Set(weeks)].sort((a, b) => a - b);
+}
+
+// ========== 4. 从HTML片段中提取教师姓名 ==========
+function extractTeacherFromHTML(html) {
+    if (!html) return null;
+    const match = html.match(/<a[^>]*>([^<]+)<\/a>/);
+    return match ? match[1].trim() : null;
 }
 
 /**
- * 2. 单行解析函数:提取核心信息
+ * 从描述文本中提取非教师、非校区的备注地点
  */
-function parseDetailLine(line) {
-    // 移除 HTML 标签
-    const cleanLine = line.replace(/<[^>]+>/g, "").trim();
-    const parts = cleanLine.split(/\s+/);
-
-    // 假设格式为:[周次] [老师] [建筑/校区] [具体地点]
-    const rawWeek = parts[0] || "";
-    const teacher = parts[1] || "未知教师";
-    const building = parts[2] || "";
-    const location = parts[3] || "";
-
-    let finalPosition = "";
-
-    if (location.includes(building)) {
-        finalPosition = location; 
-    } else {
-        finalPosition = building + " " + location;
-    }
+function extractExtraLocation(htmlText, campusName, teacher) {
+    if (!htmlText) return '';
+    // 去掉所有HTML标签
+    let clean = htmlText.replace(/<[^>]+>/g, ' ').trim();
+    // 去掉已知校区名
+    if (campusName) clean = clean.replace(new RegExp(campusName, 'g'), '');
+    // 去掉开头的周次描述
+    clean = clean.replace(/^\d+(-\d+)?周\s*/, '');
+    // 去掉教师姓名(如果传入)
+    if (teacher) clean = clean.replace(new RegExp(teacher, 'g'), '');
+    // 清理多余空格
+    clean = clean.replace(/\s+/g, ' ').trim();
+    return clean;
+}
 
-    finalPosition = finalPosition.trim();
+// ========== 5. 核心解析函数:将单条 raw item 解析为多个 course 片段 ==========
+function parseCourseItem(item) {
+    const courseName = item.courseName;
+    const day = item.dayOfWeek;
+    const beginSection = item.beginSection;
+    const endSection = item.endSection;
+    const campusName = item.campusName || '';
+    const placeName = item.placeName || '';
+    const tags = item.tags || [];
+
+    // 优先使用 cellWeekTeacherClassroomDetail,如果为空则用 multiCourseTitleDetail 或 titleDetail
+    let segmentsSource = [];
+
+    if (item.cellWeekTeacherClassroomDetail && item.cellWeekTeacherClassroomDetail.length > 0) {
+        segmentsSource = item.cellWeekTeacherClassroomDetail.map(cell => cell.text);
+    } else if (item.multiCourseTitleDetail && item.multiCourseTitleDetail.length > 1) {
+        segmentsSource = item.multiCourseTitleDetail
+            .slice(1)
+            .filter(line => {
+                const plainText = line.replace(/<[^>]+>/g, '').trim();
+                // 过滤掉纯数字/逗号/空格组成的行(班级列表)
+                if (/^[\d,\s]+$/.test(plainText)) return false;
+                // 过滤掉空行
+                return plainText.length > 0;
+            })
+            .map(line => line.trim())
+            .filter(line => line !== '');
+    } else if (item.titleDetail && item.titleDetail.length > 1) {
+        // ✅ 兜底:只有 titleDetail 时,用其中第一行教师/地点信息
+        segmentsSource = [item.titleDetail[1]];
+    }
 
-    return {
-        rawWeek,
-        teacher,
-        building,
-        location,
-        weeks: parseWeeks(rawWeek) 
-    };
-}
+    const courses = [];
+    // 保存总周次作为兜底
+    const totalWeeks = parseWeekString(item.week || '');
+
+    segmentsSource.forEach(segText => {
+        const teacher = extractTeacherFromHTML(segText) || '未知教师';
+        let weeks;
+        // 尝试从文本中提取周次描述
+        const weekDescMatch = segText.match(/^([\d\-\(\),周单双\s]+?)\s*</);
+        if (weekDescMatch && weekDescMatch[1]) {
+            const wd = weekDescMatch[1].trim();
+            weeks = parseWeeksDescription(wd);
+            if (weeks.length === 0) weeks = totalWeeks;
+        } else {
+            weeks = totalWeeks;
+        }
 
-/**
- * 3. 智能汇总函数:处理地点变动逻辑
- */
-function extractAndMergeCourse(titleDetail) {
-    if (!titleDetail || titleDetail.length === 0) return null;
-
-    const courseName = titleDetail[0];
-    // 过滤掉第一行课程名,解析后面所有的详情行
-    const rawSlots = titleDetail.slice(1).map(line => parseDetailLine(line));
-
-    const mergedMap = new Map();
-
-    rawSlots.forEach(slot => {
-        // 连堂课如果地点老师一样,就合并周次;如果不一样(比如一半在教室一半在实验室),会拆分成两个 segment
-        const identifier = `${slot.teacher}|${slot.building}|${slot.location}`;
-        
-        if (mergedMap.has(identifier)) {
-            const existing = mergedMap.get(identifier);
-            // 合并周次并去重排序
-            existing.weeks = [...new Set([...existing.weeks, ...slot.weeks])].sort((a, b) => a - b);
-            existing.rawWeeksDesc += `, ${slot.rawWeek}`;
+        // 确定地点
+        let position;
+        if (placeName) {
+            position = (campusName && !placeName.includes(campusName)) ? `${campusName} ${placeName}` : placeName;
         } else {
-            mergedMap.set(identifier, {
-                teacher: slot.teacher,
-                building: slot.building,
-                location: slot.location,
-                weeks: slot.weeks,
-                rawWeeksDesc: slot.rawWeek
-            });
+            // placeName 为空时,从描述提取备注
+            const extra = extractExtraLocation(segText, campusName, teacher);
+            position = campusName ? `${campusName} ${extra}`.trim() : extra;
         }
+        position = position || campusName || '未知地点';
+
+        courses.push({
+            name: courseName,
+            teacher: teacher,
+            position: position.trim(),
+            day: day,
+            startSection: beginSection,
+            endSection: endSection,
+            weeks: weeks,
+            campusName: campusName,
+            rawPlaceName: placeName,
+            hasExperimentTag: tags.some(t => t.text === '实')
+        });
     });
 
-    const segments = Array.from(mergedMap.values());
-
-    // --- 修复点:先计算,再打印和返回 ---
-    const allActiveWeeks = [...new Set(segments.flatMap(s => s.weeks))].sort((a, b) => a - b);
-    
-    console.log("解析课程:", courseName, "总周次:", allActiveWeeks);
-
-    return {
-        courseName,
-        allActiveWeeks,
-        segments
-    };
+    return courses;
 }
 
+// ========== 6. 聚合所有课程并映射小节编号 ==========
+function parseAllCourses(rawArrangedList, sectionList) {
+    const allCourses = [];
 
-function fixSection(section) {
-    let realSection = section;
-    if (section > 5) realSection -= 1; // 超过午餐,向上平移1格
-    if (section > 10) realSection -= 1; // 超过晚餐,再向上平移1格
-    return realSection;
-}
+    if (!rawArrangedList || !Array.isArray(rawArrangedList)) {
+        return { courses: [], timeSlots: [] };
+    }
+
+    // 构建时间 -> 小节编号 的映射
+    const startTimeToSection = {};
+    const endTimeToSection = {};
+    sectionList.forEach(slot => {
+        startTimeToSection[slot.startTime] = slot.number;
+        endTimeToSection[slot.endTime] = slot.number;
+    });
 
-/**
- * 解析所有课程并应用修正 
- */
-function parseAllCourses(rawArrangedList) {
-    const finalCourses = [];
-    if (!rawArrangedList || !Array.isArray(rawArrangedList)) return [];
-    
     rawArrangedList.forEach(item => {
-        if (item.titleDetail && item.titleDetail.length > 0) {
-            const mergedResult = extractAndMergeCourse(item.titleDetail);
-            if (!mergedResult) return;
-
-            // 1. 获取原始节次数据
-            const rawStart = parseInt(item.beginSection || item.startSection);
-            const rawEnd = parseInt(item.endSection);
-            const day = parseInt(item.dayOfWeek || item.day);
-
-            // 2. 调用 fixSection 解决错位
-            const sSection = fixSection(rawStart);
-            const eSection = fixSection(rawEnd);
-
-            mergedResult.segments.forEach(seg => {
-                if (!isNaN(sSection) && !isNaN(eSection)) {
-                    // 3. 处理地点显示问题(去重:避免出现 "励行楼 励行楼xxx")
-                    let finalPos = seg.location;
-                    if (seg.building && !seg.location.includes(seg.building)) {
-                        finalPos = seg.building + " " + seg.location;
-                    }
+        if (item.dayOfWeek === null || item.beginSection === null) return;
+
+        // 根据 beginTime 和 endTime 查找正确的小节区间
+        const realStart = startTimeToSection[item.beginTime];
+        const realEnd = endTimeToSection[item.endTime];
 
-                    finalCourses.push({
-                        name: mergedResult.courseName,
-                        teacher: seg.teacher,
-                        position: finalPos.trim(),
-                        day: day, 
-                        startSection: sSection, // 写入修正后的开始节次
-                        endSection: eSection,   // 写入修正后的结束节次
-                        weeks: seg.weeks
-                    });
-                }
-            });
+        if (realStart === undefined || realEnd === undefined) {
+            // 时间无法匹配,丢弃该课程(或使用原始值,但不推荐)
+            console.warn(`课程 ${item.courseName} 时间无法匹配时间槽: ${item.beginTime}-${item.endTime}`);
+            return;
         }
+
+        // 用正确的小节编号覆盖原始 beginSection/endSection
+        const correctedItem = {
+            ...item,
+            beginSection: realStart,
+            endSection: realEnd
+        };
+
+        const courses = parseCourseItem(correctedItem);
+        allCourses.push(...courses);
     });
 
-    return finalCourses;
-}
-/**
- * 获取所有课程信息
- */
-async function fetchAllRawData() {
-    try {
-        // 获取基础环境信息 (学期、开学日期、时间表)
-        const baseInfo = await extractCourseTime();
-        if (!baseInfo) return null;
-
-        const rawArrangedList = await getCourseData();
-        
-        if (!rawArrangedList || rawArrangedList.length === 0) {
-            AndroidBridge.showToast("未检测到当前学期的课程数据");
-            return null;
-        }
+    // 时间槽直接使用 sectionList,编号保持 1,2,3...
+    const timeSlots = sectionList.map(sec => ({
+        number: sec.number,
+        startTime: sec.startTime,
+        endTime: sec.endTime
+    }));
 
-        return { baseInfo, rawArrangedList };
-    } catch (e) {
-        console.error("抓取数据失败:", e);
-        return null;
-    }
+    console.log(`解析完成,共 ${allCourses.length} 个课程片段,${timeSlots.length} 个时间段`);
+    return { courses: allCourses, timeSlots };
 }
 
-/**
- * 保存
- */
-   async function executeSaveSequence(finalCourses, baseInfo) {
-    try {
-        // 1. 保存基础配置 (开学日期、总周数)
-        const configData = {
-            semesterStartDate: baseInfo.startDate,
-            semesterTotalWeeks: baseInfo.totalWeeks || 20,
-        };
 
-        const configSuccess = await AndroidBridge.saveCourseConfig(JSON.stringify(configData));
-        
-        if (!configSuccess) {
-            AndroidBridge.showToast("学期保存失败");
-            return false;
-        }
-
-        // 2. 保存时间段 (节次时间表
-        const slotSuccess = await AndroidBridge.savePresetTimeSlots(JSON.stringify(baseInfo.cleanSections));
-        if (!slotSuccess) return false;
+// ========== 7. 获取所有数据 ==========
+async function fetchAllRawData() {
+    const baseInfo = await extractCourseTime();
+    if (!baseInfo) return null;
 
-        // 3. 保存课程数据
-        const saveResult = await AndroidBridge.saveImportedCourses(JSON.stringify(finalCourses));
-        
-        return saveResult;
+    const rawArrangedList = await getCourseData(baseInfo.totalWeeks);
 
-    } catch (e) {
-        console.error("保存流程崩溃:", e);
-        AndroidBridge.showToast("导入过程发生意外");
-        return false;
+    if (!rawArrangedList || rawArrangedList.length === 0) {
+        AndroidBridge.showToast("未检测到当前学期的课程数据");
+        return null;
     }
+    return { baseInfo, rawArrangedList };
 }
 
-
-/**
- * 保存配置 (日期和周数)
- */
+// ========== 8. 保存配置 ==========
 async function saveConfig(baseInfo) {
     const configData = {
         semesterStartDate: baseInfo.startDate,
         semesterTotalWeeks: baseInfo.totalWeeks || 20,
-
     };
     try {
         const configSuccess = await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(configData));
-        if (configSuccess) {
-            return true;
+        if (!configSuccess) {
+            AndroidBridge.showToast("学期保存失败");
+            return false;
         }
-        return false;
+        return true;
     } catch (error) {
         AndroidBridge.showToast("保存配置失败: " + error.message);
         return false;
     }
 }
 
-
-/**
- * 主导入流
- */
+// ========== 9. 主导入流程 ==========
 async function runImportFlow() {
     try {
-        // 1. 前置确认
         const isReady = await promptUserToStart();
         if (!isReady) return;
 
-        // 2. 抓取所有必要数据
         const dataBundle = await fetchAllRawData();
         if (!dataBundle) return;
 
-        // 3. 解析原始数据
-        const finalCourses = parseAllCourses(dataBundle.rawArrangedList);
+        const { courses: finalCourses, timeSlots } = parseAllCourses(dataBundle.rawArrangedList, dataBundle.baseInfo.cleanSections);
         if (finalCourses.length === 0) {
             AndroidBridge.showToast("解析失败:未能提取到有效课程");
             return;
         }
 
-        // 4. 保存配置数据 (存日期、周数)
+        // 保存学期配置
         const configSaveResult = await saveConfig(dataBundle.baseInfo);
         if (!configSaveResult) return;
 
-        //时间段保存
+        // 保存时间段 (基于实际课程生成的大节)
         try {
-            const slotJson = JSON.stringify(dataBundle.baseInfo.cleanSections);
+            const slotJson = JSON.stringify(timeSlots);
             console.log("写入时间段数据:", slotJson);
             await window.AndroidBridgePromise.savePresetTimeSlots(slotJson);
         } catch (e) {
             console.error("时间段写入失败:", e);
-            // 这里可以选择跳过或报错
+            AndroidBridge.showToast("时间段保存失败");
+            return;
         }
 
-        // 5. 课程数据保存
+        // 保存课程数据
         const saveResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(finalCourses));
         if (!saveResult) {
             AndroidBridge.showToast("课程数据保存失败");
             return;
         }
 
-        // 6. 流程成功结束
-        AndroidBridge.showToast("Hi ~  课表导入成功!");
+        AndroidBridge.showToast("Hi ~ 课表导入成功!");
         AndroidBridge.notifyTaskCompletion();
 
     } catch (error) {