gscmxy.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. // 甘肃财贸职业学院教务系统(gscmxy.edu.cn) 拾光课程表适配脚本
  2. function parseWeeksFromSkzc(skzc) {
  3. const weeks = [];
  4. const rawSkzc = skzc || '';
  5. for (let i = 0; i < rawSkzc.length; i++) {
  6. if (rawSkzc[i] === '1') {
  7. weeks.push(i + 1);
  8. }
  9. }
  10. return weeks;
  11. }
  12. function parseSingleCourse(rawCourse) {
  13. const courseName = rawCourse.KCM;
  14. const teacherName = rawCourse.SKJS ? rawCourse.SKJS.split('/')[0] : '';
  15. const position = rawCourse.JASMC;
  16. const day = rawCourse.SKXQ;
  17. const startSection = rawCourse.KSJC;
  18. const endSection = rawCourse.JSJC;
  19. const weeks = parseWeeksFromSkzc(rawCourse.SKZC);
  20. if (!courseName || !day || !startSection || !endSection || weeks.length === 0) {
  21. return null;
  22. }
  23. const campus = rawCourse.XXXQDM_DISPLAY ? String(rawCourse.XXXQDM_DISPLAY).trim() : "";
  24. const classroom = position ? String(position).trim() : "";
  25. const finalPosition = campus && classroom ? `${classroom}(${campus})` : (classroom || '待定');
  26. const course = {
  27. "name": courseName,
  28. "teacher": teacherName,
  29. "position": finalPosition,
  30. "day": parseInt(day),
  31. "startSection": parseInt(startSection),
  32. "endSection": parseInt(endSection),
  33. "weeks": weeks
  34. };
  35. course._kbId = rawCourse.KBID;
  36. course._day = course.day;
  37. course._startSection = course.startSection;
  38. course._endSection = course.endSection;
  39. course._position = classroom;
  40. course._campus = campus;
  41. return course;
  42. }
  43. function applyCourseChanges(parsedCourses, rawChanges) {
  44. let successCount = 0;
  45. for (const change of rawChanges) {
  46. const kbID = change.KBID;
  47. const originalTeacher = change.YSKJS ? change.YSKJS.split('/')[0] : '';
  48. const weeksToRemove = parseWeeksFromSkzc(change.SKZC);
  49. let changeApplied = false;
  50. const affectedOriginalCourses = parsedCourses.filter(c =>
  51. c._kbId === kbID &&
  52. c._day === parseInt(change.SKXQ) &&
  53. c._startSection === parseInt(change.KSJC) &&
  54. c._endSection === parseInt(change.JSJC)
  55. );
  56. if (affectedOriginalCourses.length === 0) {
  57. continue;
  58. }
  59. if (weeksToRemove.length > 0) {
  60. affectedOriginalCourses.forEach(originalCourse => {
  61. const beforeLength = originalCourse.weeks.length;
  62. originalCourse.weeks = originalCourse.weeks.filter(w => !weeksToRemove.includes(w));
  63. if (originalCourse.weeks.length < beforeLength) {
  64. changeApplied = true;
  65. }
  66. });
  67. }
  68. const isTimeLocationChange = (change.TKLXDM === '01' || change.TKLXDM === '03');
  69. if (isTimeLocationChange && change.XSKZC && change.XSKXQ && change.XKSJC && change.XJSJC) {
  70. const newWeeks = parseWeeksFromSkzc(change.XSKZC);
  71. if (newWeeks.length > 0) {
  72. const newCampus = change.XXXQDM_DISPLAY ? String(change.XXXQDM_DISPLAY).trim() : "";
  73. const newClassroom = change.XJASMC ? String(change.XJASMC).trim() : (change.JASMC ? String(change.JASMC).trim() : "");
  74. const newPosition = newCampus && newClassroom ? `${newClassroom}(${newCampus})` : (newClassroom || '待定');
  75. const newCourse = {
  76. "name": change.KCM,
  77. "teacher": change.XSKJS ? change.XSKJS.split('/')[0] : originalTeacher,
  78. "position": newPosition,
  79. "day": parseInt(change.XSKXQ),
  80. "startSection": parseInt(change.XKSJC),
  81. "endSection": parseInt(change.XJSJC),
  82. "weeks": newWeeks,
  83. "_kbId": kbID,
  84. "_day": parseInt(change.XSKXQ),
  85. "_startSection": parseInt(change.XKSJC),
  86. "_endSection": parseInt(change.XJSJC)
  87. };
  88. parsedCourses.push(newCourse);
  89. changeApplied = true;
  90. }
  91. }
  92. if (changeApplied) {
  93. successCount++;
  94. }
  95. }
  96. if (successCount > 0) {
  97. AndroidBridge.showToast(`已应用 ${successCount} 条调课/停课变更,获得实际课表。`);
  98. }
  99. return parsedCourses.map(c => {
  100. delete c._kbId;
  101. delete c._day;
  102. delete c._startSection;
  103. delete c._endSection;
  104. delete c._position;
  105. delete c._campus;
  106. return c;
  107. }).filter(c => c.weeks.length > 0);
  108. }
  109. function validateYearInput(input) {
  110. if (/^[0-9]{4}$/.test(input) && parseInt(input) > 2000) {
  111. return false;
  112. } else {
  113. return "请输入有效的四位数字学年(例如:2026)!";
  114. }
  115. }
  116. async function promptUserToStart() {
  117. const confirmed = await window.AndroidBridgePromise.showAlert(
  118. "甘肃财贸职业学院课表导入",
  119. "导入前请确保您已在浏览器中成功登录教务系统,确认当前页面有显示课表。",
  120. "好的,开始导入"
  121. );
  122. if (!confirmed) {
  123. AndroidBridge.showToast("用户取消了导入。");
  124. return null;
  125. }
  126. return true;
  127. }
  128. async function getAcademicYear() {
  129. const currentYear = new Date().getFullYear();
  130. const yearSelection = await window.AndroidBridgePromise.showPrompt(
  131. "选择学年",
  132. "请输入要导入课程的起始学年(例如 2026-2027 应输入2026):",
  133. String(currentYear),
  134. "validateYearInput"
  135. );
  136. return yearSelection;
  137. }
  138. async function selectSemester() {
  139. const semesters = ["1 (秋季学期/上学期)", "2 (春季学期/下学期)"];
  140. const semesterIndex = await window.AndroidBridgePromise.showSingleSelection(
  141. "选择学期",
  142. JSON.stringify(semesters),
  143. 0
  144. );
  145. if (semesterIndex === null) return null;
  146. return String(semesterIndex + 1);
  147. }
  148. async function fetchAndParseCourses(academicYear, semesterCode) {
  149. const XNXQDM = `${academicYear}-${parseInt(academicYear) + 1}-${semesterCode}`;
  150. const headers = {
  151. "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
  152. "x-requested-with": "XMLHttpRequest",
  153. };
  154. const courseUrl = "https://jwxt.gscmxy.edu.cn/jwapp/sys/wdkb/modules/xskcb/cxxszhxqkb.do";
  155. const courseBody = `XNXQDM=${XNXQDM}`;
  156. let rawCourseData;
  157. try {
  158. const response = await fetch(courseUrl, { "headers": headers, "body": courseBody, "method": "POST", "credentials": "include" });
  159. rawCourseData = JSON.parse(await response.text());
  160. } catch (e) {
  161. AndroidBridge.showToast("请求课表 API 失败,请检查网络和登录状态。");
  162. return null;
  163. }
  164. const rawCourses = rawCourseData?.datas?.cxxszhxqkb?.rows || [];
  165. if (rawCourses.length === 0) {
  166. AndroidBridge.showToast("该学期未查询到您的课程数据。");
  167. return null;
  168. }
  169. let parsedCourses = rawCourses.map(c => parseSingleCourse(c)).filter(c => c !== null);
  170. const changeUrl = "https://jwxt.gscmxy.edu.cn/jwapp/sys/wdkb/modules/xskcb/xsdkkc.do";
  171. const changeBody = `XNXQDM=${XNXQDM}&*order=-SQSJ`;
  172. let rawChangeData;
  173. try {
  174. const response = await fetch(changeUrl, { "headers": headers, "body": changeBody, "method": "POST", "credentials": "include" });
  175. rawChangeData = JSON.parse(await response.text());
  176. } catch (e) {
  177. AndroidBridge.showToast("请求调课 API 失败,将使用未调整的课表数据。");
  178. }
  179. const rawChanges = rawChangeData?.datas?.xsdkkc?.rows || [];
  180. if (rawChanges.length > 0) {
  181. parsedCourses = applyCourseChanges(parsedCourses, rawChanges);
  182. }
  183. const courseConfig = {
  184. semesterTotalWeeks: 20
  185. };
  186. return {
  187. courses: parsedCourses,
  188. config: courseConfig
  189. };
  190. }
  191. async function saveCourses(parsedCourses) {
  192. if (parsedCourses.length === 0) {
  193. AndroidBridge.showToast("没有有效的课程数据可供保存。");
  194. return true;
  195. }
  196. try {
  197. await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(parsedCourses));
  198. AndroidBridge.showToast(`成功导入 ${parsedCourses.length} 门课程!`);
  199. return true;
  200. } catch (error) {
  201. AndroidBridge.showToast(`保存课程数据失败: ${error.message}`);
  202. return false;
  203. }
  204. }
  205. async function importPresetTimeSlots() {
  206. AndroidBridge.showToast("正在导入预设节次时间...");
  207. const presetTimeSlots = [
  208. { "number": 1, "startTime": "08:50", "endTime": "09:30" },
  209. { "number": 2, "startTime": "09:40", "endTime": "10:20" },
  210. { "number": 3, "startTime": "10:50", "endTime": "11:30" },
  211. { "number": 4, "startTime": "11:40", "endTime": "12:20" },
  212. { "number": 5, "startTime": "14:30", "endTime": "15:10" },
  213. { "number": 6, "startTime": "15:20", "endTime": "16:00" },
  214. { "number": 7, "startTime": "16:20", "endTime": "17:00" },
  215. { "number": 8, "startTime": "17:10", "endTime": "17:50" },
  216. { "number": 9, "startTime": "19:30", "endTime": "20:10" },
  217. { "number": 10, "startTime": "20:20", "endTime": "21:00" }
  218. ];
  219. try {
  220. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(presetTimeSlots));
  221. AndroidBridge.showToast("预设时间段导入成功!");
  222. return true;
  223. } catch (error) {
  224. AndroidBridge.showToast("导入时间段失败: " + error.message);
  225. return false;
  226. }
  227. }
  228. async function saveConfig(configData) {
  229. try {
  230. await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(configData));
  231. AndroidBridge.showToast("课表配置更新成功!");
  232. return true;
  233. } catch (error) {
  234. AndroidBridge.showToast("保存配置失败: " + error.message);
  235. return false;
  236. }
  237. }
  238. async function runImportFlow() {
  239. AndroidBridge.showToast("甘肃财贸职业学院课程导入流程启动...");
  240. const alertConfirmed = await promptUserToStart();
  241. if (!alertConfirmed) return;
  242. const academicYear = await getAcademicYear();
  243. if (academicYear === null) {
  244. AndroidBridge.showToast("导入已取消。");
  245. return;
  246. }
  247. const semesterCode = await selectSemester();
  248. if (semesterCode === null) {
  249. AndroidBridge.showToast("导入已取消。");
  250. return;
  251. }
  252. await importPresetTimeSlots();
  253. const courseData = await fetchAndParseCourses(academicYear, semesterCode);
  254. if (courseData === null) return;
  255. const configSaveResult = await saveConfig(courseData.config);
  256. if (!configSaveResult) return;
  257. const saveResult = await saveCourses(courseData.courses);
  258. if (!saveResult) return;
  259. AndroidBridge.showToast("所有任务已完成!课表导入成功。");
  260. AndroidBridge.notifyTaskCompletion();
  261. }
  262. runImportFlow();