Files
TPSchedule/docs/superpowers/specs/2026-10-14-uestc-undergraduate-design.md
T

199 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TPSchedule:电子科技大学本科生接入设计
日期:2026-10-14。
接入方案已获用户确认。本文件供书面审阅,应用目前仍为0.6.2+17;计划实现后发布0.7.0+18。软件名称TPSchedule、包名 `com.tpschedule.app`、原签名及登记冻结材料保持。
## 1. 目标与已确认要求
学校列表增加「电子科技大学(本科生)」,与「电子科技大学(研究生)」分别连接各自系统,共用现有课表、考试、设置、课程提醒及日历功能。
本科课表采用手动导入:学生先在应用内网页登录本科教务系统,再自行打开课表、选择学校网页上的学期,最后点击应用的「导入当前课表」。不在登录后自动读取或导入课表。
本科考试采用自动导入:学生在应用内选择一个学期,并多选期末、期中、补考/缓考;应用自动依次读取这些类型,全部读取成功后一次更新考试列表。学校和成功查询的选项保存在本机,后续更新沿用。
本科增加第12节,22:00–22:45;其余作息与现有研究生一致。研究生课表继续显示11节。本科查询历史考试保留当前课表,读取失败保留旧数据。
本次不增加其他学校、考试提醒、账号密码表单、后台静默学校抓取或新的服务器接口。Dock和换周动画沿用现有实现。
## 2. 附件与解析依据
用户提供的本科课表附件为 `eams.uestc.edu.cn_eams_courseTableForStd.action.html`,考试附件为 `stdExamTable!examTable.htm`。原文件保存在用户桌面;仓库测试使用脱敏的等价结构,不提交包含学生个人资料的原始网页。
课表样本中:
- 当前学期的隐藏字段 `semester.id` 为523。
- 页面包含10门课程,19条 `TaskActivity` 数据。其中18条为上课安排,1条为「停课」;3条上课安排涉及第12节。
- `unitCount = 12`,`CourseTable(2026, 84)` 对应7天、每天12节。
- `TaskActivity` 的字段包含教师、课程标识和名称、教室、周次位串。随后 `index = day * unitCount + period` 表示星期和节次,二者从0开始。
- 周次位串的第0位为保留位,第1位对应第1周。样本还包含同一课程不同周次使用不同教室、多位教师及停课信息。
考试样本中:
- 排考结果实际属于503,即2025–2026第二学期,包含4条期末考试记录。
- 排考结果的隐藏学期字段和 `params` 均为503;外层学期控件的初始化脚本仍带默认值523。不能将这个默认值当成结果所属学期。
- 表头为「课程序号、课程名称、考试日期、考试安排、考试地点、座位号、考试情况、考试类型、其它说明」。
- 类型值1、2、3分别对应期末考试、期中考试、补缓考。
解析只读取这些数据结构,不执行附件中的脚本,也不调用考试申请或取消申请操作。
## 3. 用户流程
### 3.1 选择学校
「我的 → 选择学校」提供研究生和本科生两个选项,沿用蓝色界面。取消选择不改变学校、不开始登录、不修改已导入数据。
确定选项后保存连接目标。保存失败时留在原页面,显示失败信息,不切换到新学校浏览器。仅更换连接目标不会删除现有课表或考试;数据来源在各自页面显示,避免把旧学校数据误认为新导入结果。
### 3.2 本科课表
1. 打开 `https://eams.uestc.edu.cn/eams/`。
2. 学生在学校原网页完成登录、验证码或学校要求的其他认证。
3. 应用提示「请在学校网页打开课表并选择学期,然后点击导入当前课表」。学生自行操作学校页面。
4. 点击「导入当前课表」后,读取当前页面及其同源课表框架,校验课表和实际学期,解析安排。
5. 确认成功解析后保存课表,返回应用并展示实际导入的学期及课程。跨学校或跨课表学期替换时,先说明将替换现有课表并要求确认;取消确认保留原数据。
6. 新学校或新课表学期需要重新设置学期开始日,复用现有首页提示;同一学校、同一学期更新保留开始日及周数设置。
本科课表刷新与「我的 → 更新学期」均重新打开本科网页流程,仍由学生在网页上选择学期、点击导入。不能将本科课表刷新变成自动跳转查询或自动导入。
### 3.3 本科考试
首次本科考试导入展示一个学期选择器及三个可多选类型:期末考试、期中考试、补考/缓考。至少选择一项,默认期末。已保存过本科查询时默认使用上次成功的学期和类型。
确认后,未认证时先打开学校原网页认证;确认进入已登录的本科系统后自动读取排考结果。已连接时直接读取。按类型编号1、2、3顺序访问所选类型,并展示进度,例如「正在读取期中考试(2/3)」。
全部类型校验成功后合并并保存一次,显示所选学期、类型及考试记录数。已识别的某类型空结果属于成功,该类型没有记录;所有类型均为空时显示「所选考试类型暂未安排」。登录页、未知页面或未加载完毕的内容不能当作空结果。
考试页的刷新沿用上次成功查询。增加「查询选项」入口供学生重新选择学期和类型;取消或失败不覆盖已保存查询选项。换到新的学校连接目标时,先选择该学校的查询选项,不借用另一学校的学期编号。
本科考试导入只更新考试数据及查询选项,不清空课表、不改变课表学期或学期开始日。选择历史学期后,课表仍显示已导入的课表;考试页明确显示自己的学校和学期。
## 4. 学校与学期标识
将学校目录从界面文件移至独立的学校标识模块,使用稳定字符串标识 `uestc_graduate`、`uestc_undergraduate`,不以枚举序号持久化。
学期对象带学校标识和学校自己的编号。相等比较同时检查学校和编号,避免将不同系统的编号互换。研究生编号、标签、URL规则保持;旧 `{"id":73}` 默认解释为研究生学期。
本科按用户指定规则,以523为2026–2027第一学期,每向前一个学期减20,向后加20:
| 本科编号 | 学年学期 |
| --- | --- |
| 483 | 2025–2026第一学期 |
| 503 | 2025–2026第二学期 |
| 523 | 2026–2027第一学期 |
| 543 | 2026–2027第二学期 |
| 563 | 2027–2028第一学期 |
本科有效编号满足 `id >= 3` 且 `(id - 3) % 20 == 0`;沿用编号不超过9999的输入边界。令 `n = (id - 3) / 20`,起始学年为 `2013 + floor(n / 2)`,n为偶数时第一学期、奇数时第二学期。该形式避免向前计算时负数整除导致年份错误。
本科选择器默认已保存查询学期,否则523。列表从2013–2014第一学期起,列至设备当前年份下一学年的第二学期,并始终包含有效的已保存学期;显示标签,不要求学生填写数字。手动课表导入以页面实际选中的学期为准。
本科考试请求格式:
```text
https://eams.uestc.edu.cn/eams/stdExamTable!examTable.action?semester.id={编号}&examType.id={类型}
```
本科请求由本科查询对象构建,不调用研究生学期URL构建器。
## 5. 课表解析与显示
新增本科课表适配器,使用现有HTML解析依赖及受限的JavaScript数据字面量解析。只解析 `TaskActivity` 字符串参数及可识别的节次索引赋值,不使用eval或重新执行网页脚本。在线导入、离线本科HTML导入共用此适配器。
每条安排解析课程全称、课程序号、所有教师、教室、星期、起止节次和有效周次。课程名称末尾的课程序号可单独保存,不能误删课程名称内的中文括号。
按星期、连续节次、教师、教室和周次分组;相同的重复安排去重,不同周次、教师或教室的安排分别保留。停课记录不形成课程或提醒,并从同一课程序号、星期、节次的有效安排中扣除相应周次。
课程标识包含本科来源、学期及完整安排的稳定摘要,不使用学生姓名或学号。周次有效范围仍为1–30;位串其他位置必须为0,不能截掉异常周次后假装成功。节次范围1–12;未知索引表达式、缺失教师/教室、矛盾学期或无法识别的页面报错,保留旧课表。
零安排只有在已识别课表结构、实际学期及明确空课提示后,才能请求学生确认导入空课表;不能把加载中的表格当成无课。
课表依旧七列同屏、左右滑动换周、相同节数的课程条等长。教师和教室完整显示;多位教师使用现有共享行高测量,不能截断教师名单或单独拉长一张卡片。课程全称和备注在详情中保留,卡片名称允许省略。
研究生和示例课表显示11节。本科课表显示12节;显示行数依据已导入课表的学校,而不是刚选中的连接目标。课表内容仅部分周次排至第12节时,整学期也保持12行。
现有前11节时间保持,第12节为22:00–22:45。提醒和课程日历通过现有发生时间计算使用第12节结束时间;考试不加入课程提醒或手机日历。附件中的特殊上课时间备注作为详情信息保留,提醒按本次确认的节次作息计算。
## 6. 考试解析、合并与失败处理
新增本科考试适配器,按表头定位列,保存课程序号、名称、考试日期、完整考试安排、地点、座位、情况、类型及说明。没有公布的时间或地点显示「尚未安排」,不推算补齐。
在线解析绑定正在请求的学校、学期和考试类型。优先验证实际结果表单的隐藏字段、结果参数及记录类型;忽略外层控件初始化默认值。任何明确的结果学期或类型与请求矛盾时,拒绝当前批次。离线文件根据实际结果字段确定学期和类型,不将503样本归入523。
多类型考试使用复合身份去重,包括学校、学期、类型、课程序号、日期、时间、地点及座位。同一课程的期中和期末不能互相覆盖;完全重复的记录只保留一次。研究生记录继续按原方式验证,旧记录缺失新增字段时使用兼容默认值。
每次查询拥有独立批次号。依次导航至所选排考URL,等页面完成加载并出现可识别结果,再解析该类型;不在每个类型完成后逐次写入本机。合并后的记录总数上限500。每次页面捕获的所有片段合计不超过2MB,离线单文件不超过2MB;超限报错,不能截断后导入。
自动读取单类型等待上限45秒,整个读取批次上限120秒;计时从自动访问排考页面开始,不包含学生登录及输入验证码的时间。进入等待认证状态时停止读取计时,重新认证后启动新批次并重置计时。任一类型失败、超时、结果矛盾或用户取消,丢弃本次临时结果,保持已有考试和查询选项。界面指出失败类型,允许重试或修改查询选项。
认证过期时停止读取并让学生在学校网页重新登录。重新认证后从该批次的第一个类型重读,避免将认证前后的不同账号结果合并。更换查询选项、离开页面或退出学校登录使旧批次失效;过期的网页回调不能覆盖新数据。
## 7. 本科浏览器连接
本科使用独立连接器和浏览器页面,研究生保持现有连接器及其自动导入流程。两者共用学校目录、状态展示和退出协调,不将本科的手动模式塞入研究生的自动识别条件。
浏览器仅在打开该学校连接时创建,普通离线启动不创建本科WebView。认证仍使用学校原网页,不注入或读取密码及验证码。
本科导航允许精确的HTTPS官方域名 `eams.uestc.edu.cn`、`idas.uestc.edu.cn`、`eportal.uestc.edu.cn`。数据读取只允许 `eams.uestc.edu.cn` 的 `/eams/` 页面及相同来源框架;统一认证页面只用于登录,不能作为数据来源。统一身份认证的HTTP登录跳转沿用已有HTTPS升级规则,未覆盖的回调提示重试,不扩大到任意域名。
课表捕获只在点击「导入当前课表」后发生,取实际学期字段、课表容器、所需课程列表和活动数据。复制字段时读取当前DOM属性值,避免序列化过期的默认属性。同源文档最多遍历4层、16个文档,总捕获大小不超过2MB;超限报错。检测到密码表单或不属于本科来源的框架时不捕获内容。
登录状态检测只返回状态证据,不能触发课表保存。自动考试查询只访问排考GET地址,不执行考试申请、取消申请等表单。
浏览器页提供返回、重新认证、状态提示;手动课表模式提供明确的「导入当前课表」按钮,考试模式显示所选学期、类型和读取进度。离开页面取消相应读取及计时。识别本科网页的退出地址时使本科连接和当前批次失效;应用清除共享Cookie退出时同步使两套连接状态失效,避免另一页面仍显示已连接。退出协调不为未使用的学校创建浏览器。
## 8. 本地数据与兼容
现有保存格式的 `schemaVersion: 1` 保留,添加可选字段并提供旧数据默认值:
- 连接目标:可空的 `selectedSchoolId`。旧数据根据已识别的学校学期或考试来源恢复研究生选项;无学校数据时仍要求先选学校。
- 学校学期:既有 `schoolTerm` 增加 `schoolId`,缺失时默认研究生。本科该字段记录课表实际学期。
- 考试列表:`examSchedule` 增加学校和成功请求的类型集合;自身 `termId` 仍记录考试学期。新增考试字段为兼容可选项。
本科考试刷新使用考试列表保存的学期及类型,不改课表的 `schoolTerm`。研究生已有跨学期更新规则保持兼容。
成功导入本科课表时仅替换课表相关数据及来源;新学校或新课表学期清除旧学期开始日,保留提醒时间点、颜色和课程条长度等偏好。本科考试列表独立保留并显示其来源,不混入课表来源。
更换学校连接目标只保存偏好,不提前清空课程。课表和考试页始终展示各自保存数据的学校及学期;连接目标与旧课表不同期间,明确提示当前仍为旧学校课表。
未知学校标识、无效本科学期、空考试类型集合或损坏记录拒绝加载/导入,沿用现有备份恢复入口。所有导入和偏好保存使用现有串行、原子保存机制;保存失败保留原数据。课表变更成功后协调课程提醒和日历;本科考试保存不触发课表变更的协调流程,现有应用回到前台时的服务检查保持。
## 9. 文件导入与模块划分
离线 `.html/.htm` 导入根据明确的本科/研究生页面特征分派适配器,继续先预览或确认后保存。课表JSON沿用版本1,扩展学校标识;旧JSON不需要补填新字段。
实现分为以下职责:
| 职责 | 文件或模块 |
| --- | --- |
| 学校稳定标识及可用目录 | `domain/school_identity.dart`、现有学校选择页面 |
| 学期编号和标签 | `domain/school_term.dart` |
| 本科考试类型、查询参数与URL | `domain/undergraduate_exam_query.dart` |
| 本科课表和考试数据解析 | 新本科课表/考试适配器 |
| 登录、手动捕获、自动考试批次 | 新本科连接器及浏览器页面 |
| 查询选项 | 新本科考试选择组件 |
| 保存、旧数据默认值及来源展示 | 现有Snapshot、AppController、课表/考试/我的页面 |
| 第12节、显示行数与发生时间 | 现有LessonTimes、WeekGrid及ScheduleEngine |
不修改安卓签名配置,不重建密钥,不重新生成0.5.4登记冻结或V1.0软著材料。
## 10. 验收与交付
实现阶段先为新行为写回归并观察失败,再实现和运行完整Flutter测试、静态检查及Android构建。
验收至少覆盖:
1. 两个学校选项、取消和重新进入;学校选择保存失败不导航;离线启动保持浏览器延迟创建。
2. 本科学期483/503/523/543/563标签及URL;非法编号拒绝;本科编号不进入研究生请求。
3. 附件等价结构的10门课程、18段上课安排、停课周扣除、周次位串、星期和节次索引、跨周换教室、多人教师及第12节。
4. 手动导入前没有课表读取/保存;登录页、不完整页面、空结果确认、同源框架及过期回调处理。
5. 考试样本4条归属503,不被默认523污染;期中/期末同课程保留;重复去重及未排考显示。
6. 学期和多选类型校验、自动访问顺序、全部成功后一次保存、单项失败/超时/取消不覆盖、认证恢复后整批重读。
7. 重启后复用学校和考试选项;历史本科考试不清课表;旧研究生保存文件及JSON仍正常读取。
8. 本科12行、研究生和示例11行;教师/教室完整、同节数条长一致;第12节22:45结束,提醒和课程日历使用正确时间。
9. 保留旧版签名及登记备份摘要,覆盖安装保留用户数据。
交付计划包括发布APK、调试APK、更新源码包、验证记录及关键页面截图。真实本科账号的验证码及在线抓取需在学校原网页由账户本人完成验证;离线附件和模拟回调只能验证结构与流程。本次不要求用户在聊天中提供密码。
本文确认后编写实现计划,再按选定执行方式进入代码修改。