hnzy.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // ====================== 工具函数 ======================
  2. /**
  3. * 检查用户是否已登录。
  4. * 如果当前URL包含登录关键字,说明用户未登录,返回false。
  5. * 如果不包含登录关键字,说明用户已登录,返回true。
  6. */
  7. function isUserLoggedIn() {
  8. const url = window.location.href;
  9. const loginKeywords = [
  10. "https://we.hnzj.edu.cn/sso/login"
  11. ];
  12. // 检查URL是否包含登录关键字
  13. for (const keyword of loginKeywords) {
  14. if (url.includes(keyword)) {
  15. return false; // 包含登录关键字,说明用户未登录
  16. }
  17. }
  18. // 不包含登录关键字,说明用户已登录
  19. return true;
  20. }
  21. // 展开 weeks 字符串 -> 数字数组
  22. function parseWeeks(weeksStr) {
  23. const weeks = new Set();
  24. if (!weeksStr) return [];
  25. const parts = weeksStr.split(",");
  26. for (const part of parts) {
  27. if (part.includes("-")) {
  28. const [start, end] = part.split("-").map(n => parseInt(n));
  29. for (let i = start; i <= end && i <= 20; i++) {
  30. weeks.add(i);
  31. }
  32. } else {
  33. const n = parseInt(part);
  34. if (n >= 1 && n <= 20) weeks.add(n);
  35. }
  36. }
  37. return Array.from(weeks).sort((a, b) => a - b);
  38. }
  39. // 合并重复课程
  40. function mergeDuplicateCourses(courses) {
  41. const merged = [];
  42. const keyMap = {}; // key = name+day+startSection+endSection+position
  43. for (const c of courses) {
  44. const key = `${c.name}|${c.day}|${c.startSection}|${c.endSection}|${c.position}`;
  45. if (!keyMap[key]) {
  46. keyMap[key] = { ...c, weeks: [...c.weeks] };
  47. } else {
  48. keyMap[key].weeks = Array.from(new Set([...keyMap[key].weeks, ...c.weeks]));
  49. }
  50. }
  51. for (const k in keyMap) merged.push(keyMap[k]);
  52. return merged;
  53. }
  54. // ====================== 弹窗选择学年学期 ======================
  55. async function selectYearAndTerm(schoolYears, schoolTerms) {
  56. try {
  57. // 构造所有学年 x 学期组合
  58. const options = [];
  59. const mapping = []; // 用于根据选项索引找到学年和学期
  60. schoolYears.forEach(y => {
  61. schoolTerms.forEach(t => {
  62. options.push(`${y.label} ${t.label}`);
  63. mapping.push({ year: y.value, term: t.value });
  64. });
  65. });
  66. const selectedIndex = await window.AndroidBridgePromise.showSingleSelection(
  67. "请选择学年和学期",
  68. JSON.stringify(options),
  69. 0
  70. );
  71. if (selectedIndex === null) return null;
  72. return mapping[selectedIndex];
  73. } catch (err) {
  74. console.error("选择学年学期失败:", err);
  75. return null;
  76. }
  77. }
  78. // ====================== 异步获取学年学期 ======================
  79. async function fetchSchoolYearTerms() {
  80. try {
  81. const res = await fetch("https://one.hnzj.edu.cn/kcb/api/schoolyearTerms");
  82. const json = await res.json();
  83. return {
  84. schoolYears: json.response.schoolYears,
  85. schoolTerms: json.response.schoolTerms
  86. };
  87. } catch (err) {
  88. console.error("获取学年学期失败:", err);
  89. AndroidBridge.showToast("获取学年学期失败:" + err.message);
  90. return null;
  91. }
  92. }
  93. // ====================== 异步获取课程并处理 ======================
  94. async function fetchCoursesForAllWeeks(year, term) {
  95. let allCourses = [];
  96. for (let week = 1; week <= 20; week++) {
  97. try {
  98. const url = `https://one.hnzj.edu.cn/kcb/api/course?schoolYear=${year}&schoolTerm=${term}&week=${week}`;
  99. const res = await fetch(url);
  100. const json = await res.json();
  101. json.response.forEach(dayInfo => {
  102. dayInfo.data.forEach(c => {
  103. const weeks = parseWeeks(c.weeks);
  104. if (!weeks.length) return;
  105. allCourses.push({
  106. name: c.courseName,
  107. teacher: c.teacherName,
  108. position: c.classRoom,
  109. day: dayInfo.week,
  110. startSection: parseInt(c.startSection),
  111. endSection: parseInt(c.endSection),
  112. weeks
  113. });
  114. });
  115. });
  116. } catch (err) {
  117. console.error(`第 ${week} 周课程获取失败:`, err);
  118. }
  119. }
  120. const mergedCourses = mergeDuplicateCourses(allCourses);
  121. return mergedCourses.length ? mergedCourses : null;
  122. }
  123. // ====================== 保存课程 ======================
  124. async function saveCourses(courses) {
  125. try {
  126. const result = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
  127. if (result === true) {
  128. AndroidBridge.showToast("课程导入成功!");
  129. return true;
  130. } else {
  131. AndroidBridge.showToast("课程导入失败,请查看日志!");
  132. return false;
  133. }
  134. } catch (err) {
  135. console.error("保存课程失败:", err);
  136. AndroidBridge.showToast("保存课程失败:" + err.message);
  137. return false;
  138. }
  139. }
  140. // ====================== 导入预设时间段(实际大节时间) ======================
  141. async function importPresetTimeSlots() {
  142. // 调整后的课程大节时间,端点衔接,总时长不变
  143. const presetTimeSlots = [
  144. { number: 1, startTime: "08:30", endTime: "09:15" }, // 第一大节第一部分
  145. { number: 2, startTime: "09:15", endTime: "10:00" }, // 第一大节第二部分
  146. { number: 3, startTime: "10:20", endTime: "11:05" }, // 第二大节第一部分
  147. { number: 4, startTime: "11:05", endTime: "11:50" }, // 第二大节第二部分
  148. { number: 5, startTime: "14:20", endTime: "15:05" }, // 第三大节第一部分
  149. { number: 6, startTime: "15:05", endTime: "15:50" }, // 第三大节第二部分
  150. { number: 7, startTime: "16:10", endTime: "16:55" }, // 第四大节第一部分
  151. { number: 8, startTime: "16:55", endTime: "17:40" }, // 第四大节第二部分
  152. { number: 9, startTime: "18:00", endTime: "18:45" }, // 第五大节第一部分
  153. { number: 10, startTime: "18:45", endTime: "19:00" }, // 第五大节第二部分
  154. { number: 11, startTime: "19:00", endTime: "19:45" }, // 第六大节第一部分
  155. { number: 12, startTime: "19:45", endTime: "20:30" } // 第六大节第二部分
  156. ];
  157. try {
  158. const result = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(presetTimeSlots));
  159. if (result === true) {
  160. AndroidBridge.showToast("时间段导入成功!");
  161. } else {
  162. AndroidBridge.showToast("时间段导入失败,请查看日志!");
  163. }
  164. } catch (err) {
  165. console.error("时间段导入失败:", err);
  166. AndroidBridge.showToast("时间段导入失败:" + err.message);
  167. }
  168. }
  169. // ====================== 主流程 ======================
  170. async function runImportFlow() {
  171. // 检查用户是否已登录
  172. if (!isUserLoggedIn()) {
  173. AndroidBridge.showToast("检测到未登录状态,请先登录后再使用课程导入功能!");
  174. return;
  175. }
  176. AndroidBridge.showToast("课程导入流程即将开始...");
  177. // 1️⃣ 获取学年学期
  178. const yearTermData = await fetchSchoolYearTerms();
  179. if (!yearTermData) {
  180. return;
  181. }
  182. // 2️⃣ 用户选择
  183. const selection = await selectYearAndTerm(yearTermData.schoolYears, yearTermData.schoolTerms);
  184. if (!selection) {
  185. AndroidBridge.showToast("用户取消选择!");
  186. return;
  187. }
  188. // 3️⃣ 异步获取课程并处理
  189. const courses = await fetchCoursesForAllWeeks(selection.year, selection.term);
  190. if (!courses) {
  191. return;
  192. }
  193. // 4️⃣ 保存课程
  194. const saveResult = await saveCourses(courses);
  195. if (!saveResult) {
  196. return;
  197. }
  198. // 5️⃣ 导入预设时间段
  199. await importPresetTimeSlots();
  200. // ✅ 只有所有步骤都成功完成,才通知任务完成
  201. AndroidBridge.showToast(`课程导入成功,共导入 ${courses.length} 门课程!`);
  202. console.log("JS:整个导入流程执行完毕并成功。");
  203. AndroidBridge.notifyTaskCompletion();
  204. }
  205. // 启动流程
  206. runImportFlow();