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

Merge pull request #222 from XingHeYuZhuan/pending

pending
星河欲转 4 недель назад
Родитель
Сommit
31b2f0560a
3 измененных файлов с 140 добавлено и 0 удалено
  1. 5 0
      index/root_index.yaml
  2. 9 0
      resources/TONGJI/adapters.yaml
  3. 126 0
      resources/TONGJI/tongji_01.js

+ 5 - 0
index/root_index.yaml

@@ -362,3 +362,8 @@ schools:
     name: "湖北医药学院"
     initial: "H"
     resource_folder: "HBMU"
+
+  - id: "TONGJI"
+    name: "同济大学"
+    initial: "T"
+    resource_folder: "TONGJI"

+ 9 - 0
resources/TONGJI/adapters.yaml

@@ -0,0 +1,9 @@
+# resources/TONGJI/adapters.yaml
+adapters:
+  - adapter_id: "TONGJI_01"
+    adapter_name: "同济大学 1 系统"
+    category: "BACHELOR_AND_ASSOCIATE"
+    asset_js_path: "tongji_01.js"
+    import_url: "https://1.tongji.edu.cn/"
+    maintainer: "Linho1219"
+    description: "同济大学教学信息管理一体化系统(1 系统)适配。无需进入课表页面,统一认证登录完成进入 1 系统后即可提取"

+ 126 - 0
resources/TONGJI/tongji_01.js

@@ -0,0 +1,126 @@
+(() => {
+  const BASE = "https://1.tongji.edu.cn/api";
+  const ENDPOINTS = {
+    authId: () => `${BASE}/sessionservice/session/currentAuthId`,
+    termList: () =>
+      `${BASE}/baseresservice/schoolCalendar/list?_t=${Date.now()}`,
+    currTerm: () =>
+      `${BASE}/baseresservice/schoolCalendar/currentTermCalendar?_t=${Date.now()}`,
+    termMetaData: (tid) =>
+      `${BASE}/baseresservice/schoolCalendar/detail?id=${tid}&_t=${Date.now()}`,
+    courseInfo: (tid) =>
+      `${BASE}/electionservice/reportManagement/findStudentTimetab?calendarId=${tid}&_t=${Date.now()}`,
+  };
+
+  async function checkAuthStatus() {
+    const response = await fetch(ENDPOINTS.authId(), {
+      method: "POST",
+      headers: { "Content-Type": "application/json; charset=utf-8" },
+      body: JSON.stringify({ authId: Math.round(Math.random() * 9000) + 1000 }),
+    });
+    if (!response.ok) throw new Error("请先登录");
+  }
+
+  function interpretTimeSlot(termMetaDataResponse) {
+    const { noWeekendWorkTimes } = termMetaDataResponse.data;
+    return noWeekendWorkTimes.map((slot, index) => ({
+      number: index + 1,
+      startTime: slot.beginTime,
+      endTime: slot.endTime,
+    }));
+  }
+
+  function interpretSemesterStartDate(courseInfoResponse) {
+    const { data } = courseInfoResponse;
+    const formatter = new Intl.DateTimeFormat("zh-CN", {
+      year: "numeric",
+      month: "2-digit",
+      day: "2-digit",
+    });
+    return formatter.format(new Date(data.beginDay)).replaceAll("/", "-");
+  }
+
+  async function determineTerm() {
+    const currTermResponse = await fetch(ENDPOINTS.currTerm()).then((res) =>
+      res.json(),
+    );
+    const currTermId = currTermResponse.data.schoolCalendar.id;
+    const useOtherTerm = await AndroidBridgePromise.showSingleSelection(
+      "是否使用当前学期",
+      JSON.stringify([
+        `使用当前学期\n(${currTermResponse.data.simpleName})`,
+        "选择其他学期",
+      ]),
+    );
+    let termId = currTermId;
+    if (useOtherTerm === null)
+      AndroidBridge.showToast("未选择学期,使用当前学期");
+    else if (useOtherTerm === 1) {
+      const termListResponse = await fetch(ENDPOINTS.termList()).then((res) =>
+        res.json(),
+      );
+      const index = await AndroidBridgePromise.showSingleSelection(
+        "请选择学期",
+        JSON.stringify(termListResponse.data.map((term) => term.fullName)),
+      );
+      const selectedId = termListResponse.data[index].id;
+      if (selectedId !== null) termId = selectedId;
+      else AndroidBridge.showToast("未选择学期,使用当前学期");
+    }
+    const termMetaDataResponse = await fetch(
+      ENDPOINTS.termMetaData(termId),
+    ).then((res) => res.json());
+    const timeSlots = interpretTimeSlot(termMetaDataResponse);
+    const semesterStartDate = interpretSemesterStartDate(termMetaDataResponse);
+    return { termId, timeSlots, semesterStartDate };
+  }
+
+  async function fetchCourseInfo(termId) {
+    const { data } = await fetch(ENDPOINTS.courseInfo(termId)).then((res) =>
+      res.json(),
+    );
+    const removeId = (str) =>
+      str.replace(/\(\d+\)$/, "").replace(/\(\d+\),/g, ", ");
+    return data.flatMap((c) =>
+      c.timeTableList.map((t) => ({
+        name: c.courseName,
+        teacher: removeId(t.teacherName),
+        position: t.roomIdI18n || t.roomLable, // typo in API
+        day: t.dayOfWeek,
+        startSection: t.timeStart,
+        endSection: t.timeEnd,
+        weeks: t.weeks,
+      })),
+    );
+  }
+
+  async function main() {
+    await checkAuthStatus();
+    const { termId, timeSlots, semesterStartDate } = await determineTerm();
+    const courseInfo = await fetchCourseInfo(termId);
+    const semesterTotalWeeks = Math.max(
+      ...courseInfo.map((c) => Math.max(...c.weeks)),
+    );
+    const result = await Promise.allSettled([
+      AndroidBridgePromise.saveImportedCourses(JSON.stringify(courseInfo)),
+      AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots)),
+      AndroidBridgePromise.saveCourseConfig(
+        JSON.stringify({ semesterStartDate, semesterTotalWeeks }),
+      ),
+    ]);
+    const rejected = [];
+    if (!result[0].value) rejected.push("课程信息");
+    if (!result[1].value) rejected.push("预设时间段");
+    if (!result[2].value) rejected.push("课表配置");
+    if (rejected.length > 0) throw new Error(`${rejected.join(", ")} 保存失败`);
+  }
+
+  main()
+    .catch((e) => {
+      AndroidBridge.showToast(`错误: ${e.message ?? e}`);
+      console.error(e);
+    })
+    .finally(() => {
+      AndroidBridge.notifyTaskCompletion();
+    });
+})();