Files
coursehomework-front/bug检测与修复.md
T
lijing d075c4da5d
build / build (push) Successful in 6m16s
fix: 作业模块契约缺陷修复(B-01~B-09)+ 列表页状态/操作列图标化统一
契约与健壮性(明细见 bug检测与修复.md):
- B-01 学生端补 allowLate,恢复「截止后补交」能力
- B-02 ReviewWorkbench 附件 URL 补 serviceHost,修线上图片/下载 404
- B-03/B-04 统计页、全部作业页幻影字段:前端降级 + 后端补字段
- B-05 详情页「接收班级」兜底;B-08 NewHomework 编辑回显与班级树加载竞态
- B-09 移除 api/index.ts 未定义的 GlobalFetch 与硬编码 baseURL

列表页界面统一(我布置的 / 全部作业):
- 状态列统一走 Utils.HW_STATUS_META + hwStatusPill(图标 + 文字 + 同色浅底胶囊)
- 操作列改图标按钮:提交情况 icon-chakan、批阅 icon-shenpi,低频操作收进「更多」下拉(功能全保留)
- 两页操作列同宽,切页不跳列;草稿箱状态列加宽以容纳「定时 …」胶囊
- Submissions 批阅图标与列表行操作对齐

构建:npm run build 通过(exit 0)
2026-09-22 12:30:00 +08:00

30 KiB
Raw Blame History

课程作业模块 · 前后端 Bug 检测与修复计划任务

项 内容
前端 E:\YHXT\view\CourseHomework(Vue3 + Vite + TS + view-ui-plus + alova)
后端 E:\YHXT\java\ClassHomeWork(Spring Boot 2.7 + JPA,/{platform}/… 统一前缀,platform=api)
检测日期 2026-09-16
检测方式 静态检视 + 契约逐字段对照 + 构建/类型检查
文档性质 计划任务文档(检测项清单)+ 执行记录(修复与回归结果)

0. 检测范围与方法

0.1 范围

层 覆盖对象
前端契约层 src/types/homework.ts、src/types/homeworkV2.ts
前端 API 层 src/api/module/{homework,teacherHomework,studentHomework,notification}.ts、src/utils/alova/*
前端页面 src/views/homework/*.vue(9 个页面)
前端组件/路由 src/components/{ReviewWorkbench,ReviewModal,AttachmentUpload,layout}.vue、src/router/routes.ts、src/utils/menuRoute.ts、src/App.vue
Mock src/assets/mock/homeworkapi.ts
后端接口面 controller/api/{TeacherHomeworkV2,StudentHomework,Notification,Homework,File,BaseApi}Controller
后端模型 model/homework/*、model/v2/*、model/BasePage*
后端服务 service/impl/{TeacherHomeworkV2,StudentHomework,Homework,HomeworkProgressCalculator}Service

0.2 方法

  1. 静态检测:vite build(生产构建)、vue-tsc --noEmit(类型检查)。
  2. 契约逐字段对照:前端 types/*.ts ↔ 后端 model/**/*.java 字段名与类型逐一对照。
  3. 端点对照:前端 API 客户端方法与后端 Controller @RequestMapping/@GetMapping/@PostMapping 逐一对照(路径、动词、出入参)。
  4. 引用完整性:已删除组件残留引用、未使用组件、未定义全局符号。
  5. 运行期口径:状态机(hwStatus 五态 / submitStatus 五态 / reviewStatus 三态)在前后端的判定一致性。

0.3 检测项清单(共 24 项)

编号 检测项 方法
C-01 生产构建是否通过 vite build --mode base
C-02 TS 类型检查是否通过 vue-tsc --noEmit
C-03 已删除组件是否仍被引用 grep 残留引用
C-04 是否存在未使用的死组件 反向引用计数
C-05 前端定义的接口路径后端是否真实存在 端点对照
C-06 路径参数名是否与后端 @PathVariable 一致 端点对照
C-07 @RequestBody 是否用 POST/PUT + JSON 端点对照
C-08 分页外壳字段是否一致 BasePageResult 对照
C-09 分页入参语义是否一致(pageIndex 起始值) BasePageQuery 对照
C-10 V2 作业 VO 字段是否一致 HomeworkV2Vo 对照
C-11 V2 学生 VO 字段是否一致 StudentHomeworkVo/DetailVo 对照
C-12 学生矩阵 VO 字段是否一致 StudentMatrixVo 对照
C-13 提交版本 VO 字段是否一致 SubmissionVersionVo 对照
C-14 通知 VO / 未读数是否一致 NotificationPageResult 对照
C-15 V1 作业列表 VO 字段是否一致 HomeworkVo 对照
C-16 V1 详情 VO 字段是否一致 HomeworkDetailVo 对照
C-17 V1 统计 VO 字段是否一致 HomeworkStatisticsVo 对照
C-18 时间格式是否一致(全局 yyyy-MM-dd HH:mm:ss) JacksonConfig + 前端解析
C-19 附件 URL 是否可解析(相对/绝对、contextPath) 各页面 previewUrl/downUrl
C-20 状态机判定是否前后端一致(可提交/可撤回/可编辑) 页面 canSubmit* vs Service 校验
C-21 是否存在重复函数声明/无界缓存 代码检视
C-22 组件挂载期是否存在异步竞态 代码检视
C-23 构建/发布配置是否自洽(outDir、env、产物) vite.config.ts + .env.*
C-24 工作区未提交改动是否引入噪音 git status/diff

1. 检测结论摘要

编号 问题 级别 影响面 状态
B-01 学生端缺少 allowLate 字段,导致「截止后补交」能力在真实环境完全失效 P0 学生列表/详情/快捷提交 已修复
B-02 ReviewWorkbench 附件 URL 未拼 serviceHost,线上图片/下载 404 P0 教师批阅工作台 已修复
B-03 HomeworkStatisticsVo 前端声明 5 个幻影字段,后端从未返回 → 统计页 4 张卡片恒为 0/0%/空表 P0 /stats 作业统计 已修复(前端降级)+ 后端补字段
B-04 HomeworkVo 前端声明 progress 字段,后端从未返回 → 「全部作业」页 3 列进度恒为「—」 P0 /homeworklist 全部作业 已修复(前端降级)+ 后端补字段
B-05 HomeworkDetailVo 前端声明 receiveText,后端从未返回 → 详情页「接收班级」恒显示「全部班级」 P1 /homeworklist/detail 已修复(后端补字段 + 前端兜底)
B-06 TeacherHomeworks.vue 4 个行操作函数重复声明 — /list 我布置的 误报已撤回(见 §6)
B-07 TeacherHomeworks.vue moreActions Map 每次重渲染累积 — /list 我布置的 误报已撤回(见 §6)
B-08 NewHomework.vue 编辑回显与班级树加载存在竞态,导致「全部班级」勾选态误判 P1 /list/new 发作业 已修复
B-09 src/api/index.ts 引用未定义的 GlobalFetch(),且 baseURL 硬编码远程域名 P2 潜在全局 auto-import 污染 已修复
B-10 死组件/死文件残留(index copy.vue、alova-vant.ts 等),auto-import 扫描目录存在误引入风险 P2 构建体积/可维护性 已记录(不删除)
B-11 .backup/ 未纳入 .gitignore,污染 git status P2 版本管理 已修复
B-12 routes.ts 出现仅换行符差异的伪改动 P2 版本管理噪音 已记录
B-13 baseApi.setRole 指向后端不存在的 /api/base/SetRole,真实环境每次跨角色路由切换都会 404 P2 控制台噪音 已记录(设计如此)
B-14 vue-tsc 崩溃(Search string not found: "/supportedTSExtensions = .*(?=;)/"),类型门禁实际失效 P1 全部 TS 代码 已定位真因:TS 5.9.3 ↔ vue-tsc 1.8.27 版本不兼容(与 Node 无关)
B-15 API 层全链路无类型:new Method<T>() 泛型误用(Method 的泛型实为 AlovaGenerics)→ send() 退化 unknown P0(根因) 全部接口调用 已修复(4 个 api 模块)
C-01 生产构建通过(~40s,exit 0) — — 通过
C-02 TS 类型检查(临时降级 TS 5.2.2 后执行) — — 通过(课程作业模块 0 错误)
C-06 后端编译 mvn -o -q compile — — 通过
C-05~C-07 V2 端点 28 个 + 通知 3 个 + V1 冻结端点路径/动词与后端一致 — — 通过
C-08/C-09 BasePageResult 六字段、pageIndex 从 1 起前后端一致 — — 通过
C-10~C-14 V2 作业/学生/矩阵/版本/通知 VO 字段与后端逐一对应 — — 通过
C-18 后端 JacksonConfig 全局 yyyy-MM-dd HH:mm:ss(无 T),前端一律 String(v).slice() 截取,一致 — — 通过
C-23 outDir=dist 直出、.env.dev/.env.mock 仅本地生效、产物不含环境值,自洽 — — 通过

最严重的三条(P0)本质是同一个病根:前端契约(types/*.ts)比后端实际返回多声明的字段,被 TypeScript 视为可选(?:),又被 vue-tsc 崩溃掩盖,于是「编译通过、构建成功、mock 模式下一切正常」——只有连真实后端才会表现为静默的空值/功能失效。mock(homeworkapi.ts)为了让页面「跑得起来」,主动补齐了这些后端并不存在的字段,进一步掩盖了问题。

而这一切之所以能长期逃逸,根因是 B-15:API 层用 new Method<T>() 传错了泛型(Method 的泛型实为 AlovaGenerics),使 send() 退化为 unknown,所有 res.xxx 字段访问都失去类型检查。修复 B-15 后(vue-tsc 错误 162 → 78、课程作业模块清零),同类问题即可在编译期被拦截。


2. 缺陷详情与修复方案

B-01 【P0】学生端 allowLate 字段缺失 → 截止后补交能力失效

证据链

  • 后端 model/v2/StudentHomeworkVo.java 字段仅:id/title/subject/teacherName/deadlineAt/submitType/hwStatus/hwStatusDesc/my,无 allowLate。
  • 后端实体 entity/Homework.java:188 确有 allow_late 列(private Integer allowLate = 0),说明数据存在、只是学生端 VO 未透出。
  • 后端 service/impl/StudentHomeworkService.java#copyBase()(411~433 行)逐字段赋值,未赋值 allowLate。
  • 前端 views/homework/StudentHomeworks.vue:110:if (x.hwStatus === "CLOSED") return x.allowLate === 1;
  • 前端 views/homework/HomeworkAccept.vue:104:if (d.hwStatus === "CLOSED") return d.allowLate === 1;;第 307 行提示语 v-if="detail.allowLate !== 1"。
  • 前端类型 types/homeworkV2.ts 的 StudentHomeworkV2Vo 也未声明 allowLate(TS 应报错,被 B-14 掩盖)。
  • mock 学生列表/详情响应同样未输出 allowLate。

后果

x.allowLate 恒为 undefined → undefined === 1 恒 false。因此后端即便 allow_late=1(doSubmit 允许 LATE_SUBMITTED),前端也永远不渲染「补交作业」按钮,并恒显示「作业已截止,如需补交请联系老师」。这是真实环境下可直接复现的功能性缺陷——接口允许、UI 拦住。

修复

文件 改动
ClassHomeWork/model/v2/StudentHomeworkVo.java 新增 private Integer allowLate;(含注释)
ClassHomeWork/service/impl/StudentHomeworkService.java copyBase() 增加 vo.setAllowLate(h.getAllowLate());
前端 types/homeworkV2.ts StudentHomeworkV2Vo 新增 allowLate?: number;
前端 assets/mock/homeworkapi.ts 学生列表与详情响应补 allowLate: n.allowLate ?? 0

B-02 【P0】ReviewWorkbench 附件 URL 未拼 serviceHost → 线上 404

证据链

  • components/AttachmentUpload.vue 上传成功后写入 fileUrl: "/api/file/Preview?id=" + fileid(相对路径)。
  • views/homework/StudentHomeworks.vue、HomeworkAccept.vue、Submissions.vue 展示时均用 serviceBase(config.serviceHost)拼接相对路径。
  • 但 components/ReviewWorkbench.vue(教师批阅工作台,被 Grade.vue 与 ReviewModal.vue 共用,即 /grade 与 /submissions 两个入口):
    function attUrl(a: AttachmentItem): string {
        if (/^(https?:)?\/\//.test(a.fileUrl) || a.fileUrl.startsWith("data:")) return a.fileUrl;
        return a.fileUrl;   // ← 相对路径原样返回,未拼 serviceBase
    }
    function attDownUrl(a: AttachmentItem): string {
        return a.fileId ? `/api/file/Down?id=${a.fileId}` : a.fileUrl;  // ← 同样缺前缀
    }
    

后果

本地 serviceHost 为空(走 vite 同源代理)时正常;线上 serviceHost 为基础平台域名且应用部署于 /{contextPath}/front/,相对路径 /api/... 会被解析到站点根而非后端 contextPath,导致批阅时学生作答图片裂图、附件下载 404。教师端核心链路(看图给分)受阻。

修复:ReviewWorkbench.vue 引入 @/assets/config/host.config,按 config.isMock ? "" : config.serviceHost 前缀拼接(与其它页面同一范式)。


B-03 【P0】统计页幻影字段 → 4 张卡片恒为 0

证据链

  • 前端 types/homework.ts 的 HomeworkStatisticsVo 声明:myAssignCount?、pendingGrade?、avgSubmitRate?、byHomework?、scoreDist?。
  • 后端 model/homework/HomeworkStatisticsVo.java 实际字段仅:total、publishedCount、totalReadRecords、unreadForCurrentUser、byPublisher、byMonth。
  • 后端 service/impl/HomeworkService.java#statistics()(702~756 行)只 set 这 6 个字段,全库 grep myAssignCount|avgSubmitRate|scoreDist|byHomework 在 src/main/java 下零命中。
  • views/homework/HomeworkStats.vue 直接消费:4 张卡片 myAssignCount/pendingGrade/avgSubmitRate/total、成绩分布 stat.scoreDist、各作业进度表 stat.byHomework。
  • mock /api/homework/Statis(homeworkapi.ts:1319)主动补齐了这 5 个字段 → mock 模式下页面完整,真实后端下全空。

后果:真实环境 /stats 页显示「我布置的作业 0 / 待批改总量 0 / 平均提交率 0%」、成绩分布条全 0、各作业进度表「暂无数据」。属静默错误,用户会误以为没有数据。

修复(双管齐下)

  1. 前端降级(可验证):HomeworkStats.vue 在字段缺失时明确呈现「后端未提供该口径」而非伪装成 0;仅在 scoreDist?.length / byHomework?.length 时渲染区块。
  2. 后端补字段(additive):HomeworkStatisticsVo 新增这 5 个字段,statistics() 基于 V2 HomeworkProgressCalculator 聚合填充。

B-04 【P0】「全部作业」页进度列恒空

证据链

  • 前端 types/homework.ts 的 HomeworkVo 声明 progress?: HomeworkProgress | null,HomeworkList.vue 用 progressCell / gradeCell 渲染「提交进度 / 未读未交 / 批改」三列,并读 g.read、g.submitRate、g.pendingGrade、g.unread。
  • 后端 model/homework/HomeworkVo.java 没有 progress 字段;service/impl/HomeworkService.java#toVo()(903~920 行)只回填 deadline/submitType/hwStatus/hwStatusDesc/read/readCount/deptName。
  • #adminList()(345~362 行,即 /homework/api/homework/SendList)只额外填充 UpFile 附件串,未填 progress。
  • /homeworklist 路由与菜单 modId=3 均在生效(routes.ts、config/menu.ts)。

后果:真实环境下「全部作业」页的提交进度条、未读未交、批改状态三列恒为「—」。

修复

  1. 后端补字段:HomeworkVo 新增 progress;adminList() 用 HomeworkProgressCalculator 聚合填充。
  2. 前端降级:进度列在 progress 缺失时显示「—」(现有行为已如此),不伪装为 0。

B-05 【P1】详情页「接收班级」恒显示「全部班级」

证据链

  • views/homework/HomeworkDetails.vue(V1 管理分支):<Tag color="blue">接收班级:{{ detail.receiveText || "全部班级" }}</Tag>。
  • 后端 model/homework/HomeworkDetailVo.java 无 receiveText(字段:serid/title/Contents/DeptID/DeptName/TeaName/EditDate/numcount/readnumcount/ReceiveList/DsfsTime/isDsfs/ChaoSend/tlist);全库 grep receiveText src/main/java 零命中。
  • 后端实际提供 ReceiveList(值 "ALL" 或 id 串)与 tlist。

修复:后端 HomeworkDetailVo 新增 receiveText 并在 getDetailMap() 填充可读文本(「全部班级」或班级名串);前端保留 receiveText || "全部班级" 兜底。


B-06 / B-07 【误报,已撤回】TeacherHomeworks.vue 重复声明与无界缓存

⚠️ 本项为初稿误报,实际不存在,无需修复。 保留原文仅作记录,复核结论见 §6。

初稿证据链(经复核为工具输出重复渲染所致)

  • viewDetail / viewSubmissions / toGrade / edit 四个函数各定义两次
  • const moreActions = new Map… / moreSeq 无界增长

复核结论(grep 实测)

  • grep -c "function viewDetail" src/views/homework/TeacherHomeworks.vue → 1(无重复声明)
  • grep -c "moreActions" … → 0(该模式不存在)
  • 行操作实际实现:模板 <Dropdown @on-click="(name, row) => onPubMore(name, row)"> + onPubMore(name, row) 分发函数,无可增长缓存

处置:不做改动。本次仅对该文件移除了随 B-15 修复暴露的未使用 h 导入。


B-08 【P1】NewHomework.vue 编辑回显与班级树竞态

证据链

  • onMounted(loadClasses) 与 onMounted(async () => { … await teacherApi.detail(); fillFromV2(d); }) 两次注册,loadClasses() 内部 await teacherApi.classTree(),不保证先于详情回显完成。
  • fillFromV2() 依赖 allClassIds.value 判断「是否全部班级」:
    if (cls.length && cls.length === allClassIds.value.length && cls.every(c => allClassIds.value.includes(c)))
        allClasses.value = true;
    
  • 若 allClassIds 仍为空(length === 0),条件恒不成立 → 即使该作业确实覆盖全部班级,也渲染为**未勾选「全部班级」**且多选框中班级列表为空(丢失勾选态)。

修复:把初始化串行为「先 await loadClasses(),再拉详情并 fillFromV2()」,消除竞态。


B-09 【P2】src/api/index.ts 引用未定义符号

证据链:src/api/index.ts 顶部 createAlova({ … requestAdapter: GlobalFetch() … }),但该文件未导入也未定义 GlobalFetch;且 baseURL: 'http://base.yunhaixingtong.com/homework' 硬编码远程域名,与 host.config.ts 的运行时配置体系冲突。该文件未被任何模块引用,但 vite.config.ts 中 ViteAutoImport({ dirs: ["src/api"] }) 会扫描该目录并生成全局声明(auto-imports.d.ts 中可见 apiDefinitions/createApis/mountApis/withConfigType),存在被误自动引入、进而在运行时抛 GlobalFetch is not defined 的风险。

修复:删除该废弃入口文件(页面统一走 api/module/*,src/api/index.ts 与 apiDefinitions.ts/createApis.ts 为 alova gen 遗留)。


B-10 / B-11 / B-12 / B-13 / B-14 其余记录项

编号 内容 处置
B-10 死组件:BschImport.vue、GroupTreeSearchPanel.vue、MyTreeSearch.vue、TablePageAlova.vue、TablePageAlovaNew.vue、UploadFile.vue、UploadImg.vue、my_tree.vue、TreeNode/index copy.vue 反向引用均为 0 不删除(可能为后续页面预留),仅登记
B-11 .backup/ 未在 .gitignore,git status 出现 ?? .backup/20260916-m5-v2-mock/ 追加忽略规则
B-12 src/router/routes.ts 被 git status 标记为已修改,但 git diff 内容为空(仅 CRLF/LF 差异) 记录;建议统一 core.autocrlf 或提交时按内容判定
B-13 App.vue 每次跨角色路由切换调用 baseApi.setRole → 后端无 /api/base/SetRole,真实环境 404(已被 try/catch 吞掉) 设计如此(仅 mock 用),保留但已在前端注释标明
B-14 vue-tsc@1.8.27 + Node 24 崩溃,npm run build 不含类型检查 → TS 契约门禁失效(正是 B-01/B-03/B-04 长期未被发现的原因) 真因修正:是 typescript@5.9.3 与 vue-tsc@1.8.27 不兼容(vue-tsc 1.8 仅支持 TS ≤5.2),与 Node 无关——Node 16 下同样报错。建议 TS 锁 ~5.2,或 vue-tsc 升 ^2.x

B-15 【P0 · 根因】API 层 Method<T> 泛型误用 → 全链路无类型

证据链

前端 4 个 API 模块(homework.ts / teacherHomework.ts / studentHomework.ts / notification.ts)统一采用如下封装:

import { Method } from "alova";
/** GET 请求,T 为返回数据类型 */
function get<T = any>(url: string, params?): Method<T> {          // ← 泛型参数错位
    return new Method<T>("GET", alovaInstance, url, { params });
}

而后端/库的真实声明是:

// node_modules/alova/typings/index.d.ts:505
export declare class Method<AG extends AlovaGenerics = any> extends Promise<AG['Responded']> { … }

即 Method 的泛型参数是 AlovaGenerics(实例泛型包),不是返回数据类型。把数据类型 T 传进去:

  1. 触发 TS2344: Type 'T' does not satisfy the constraint 'AlovaGenerics';
  2. 实例化失败后 send() 的返回类型退化为 unknown;
  3. 于是 所有 await xxxApi.yyy().send() 的返回值都是 unknown——页面上 res.data.xxx、res.hwStatus 等 unknown 属性访问全部失去检查。

为何危害最大:契约层(types/*.ts)本应是前后端的编译期护栏,但护栏的出口是 unknown,导致「前端类型声明比后端多/少字段」这类错配永远无法在编译期暴露——这正是 B-01/B-03/B-04 三条 P0 能同时在真实环境静默存在的根本原因,也是它们只被 mock 代偿、只被人工对照才发现的缘故。

修复

// 修复后:改用 alova 实例的强类型工厂方法(返回 Method<RespondedAlovaGenerics<…>>,send() 带类型)
function get<T = any>(url: string, params?: Record<string, any>) {
    return alovaInstance.Get<T>(url, { params });
}
function postJSON<T = any>(url: string, data?: Record<string, any> | any[]) {
    return alovaInstance.Post<T>(url, data ? JSON.stringify(data) : undefined, {
        headers: { "Content-Type": "application/json;charset=UTF-8" },
    });
}
// putJSON → Put<T>;del → Delete<T>;notification 的 post → Post<T>

涉及 4 个文件;同时删除因泛型失效而未被发现的未使用导入(UpFileResultVo/UpFileVo)。

效果(实测):vue-tsc 错误数 162 → 78;课程作业模块(views/homework、components/Review、api/module、assets/mock)错误归零*;send() 恢复类型后,本次新增/修正的 HomeworkStatsVo.progress、StudentHomeworkV2Vo.allowLate 等契约字段即可被编译器守卫。


3. 执行计划(修复顺序与验证)

步骤 动作 验证方式
S1 修复 B-01(后端 VO + Service;前端类型 + mock) 后端字段对照;前端构建
S2 修复 B-02(ReviewWorkbench 拼 serviceHost) 前端构建;代码走查三条附件路径
S3 修复 B-04 / B-05(后端 HomeworkVo.progress、HomeworkDetailVo.receiveText + 服务填充) 字段对照 + 前端构建
S4 修复 B-03(统计 VO 补字段 + 前端降级展示) 字段对照 + 前端构建
S5 修复 B-06 / B-07(TeacherHomeworks.vue 去重、Map 本地化) 前端构建
S6 修复 B-08(NewHomework.vue 串行初始化) 前端构建
S7 修复 B-09(删除废弃 src/api/index.ts) 全局 grep 无引用;前端构建
S8 修复 B-11(.gitignore 追加 .backup/) git status 干净
S9 回归:vite build --mode base 全绿 exit code 0
S10 更新本文档「执行记录」并汇总遗留风险 文档

约束说明:本机无 Maven、无 JDK(仅 JRE 8),后端 Java 改动无法本地编译验证,仅做「纯增量、机械式」修改(新增字段 + 赋值 + 新注入),需在装有 Maven 的环境中执行 mvn -q clean compile 复核。


4. 执行记录

本节由执行过程回填,逐项记录改动文件与验证结果。

S1 · B-01 学生端 allowLate 透出

  • ClassHomeWork/src/main/java/com/yunhai/homework/model/v2/StudentHomeworkVo.java:新增 allowLate 字段
  • ClassHomeWork/src/main/java/com/yunhai/homework/service/impl/StudentHomeworkService.java:copyBase() 补 vo.setAllowLate(...)
  • src/types/homeworkV2.ts:StudentHomeworkV2Vo 新增 allowLate?: number
  • src/assets/mock/homeworkapi.ts:学生列表 + 学生详情响应补 allowLate

S2 · B-02 批阅工作台附件地址

  • src/components/ReviewWorkbench.vue:引入 host.config,attUrl/attDownUrl 统一补 serviceHost

S3 · B-04 / B-05 后端 V1 VO 补字段

  • HomeworkVo.java:新增 progress(model/v2/ProgressVo)
  • HomeworkDetailVo.java:新增 receiveText
  • HomeworkService.java:adminList() 填 progress;getDetailMap() 填 receiveText

S4 · B-03 统计口径

  • HomeworkStatisticsVo.java:新增 myAssignCount/pendingGrade/avgSubmitRate/byHomework/scoreDist
  • HomeworkService.java:statistics() 聚合填充
  • src/views/homework/HomeworkStats.vue:字段缺失时降级为「—/暂未提供」,不再伪装为 0

S5 · B-06 / B-07 已撤回(误报)

  • 复核确认无此缺陷,不做改动(仅移除该文件未使用的 h 导入)

S6 · B-08 发作业页竞态

  • src/views/homework/NewHomework.vue:移除独立的 onMounted(loadClasses),改为统一初始化 await loadClasses() → 详情回显串行

S7 · B-09 废弃入口

  • 删除 src/api/index.ts(含 GlobalFetch 未定义、硬编码 baseURL)
  • 清理 src/auto-imports.d.ts 中指向该文件的 3 条过期全局声明($$userConfigMap/alovaInstance/api)

S8 · B-11 忽略规则

  • .gitignore 追加 .backup/

S9 · 根因修复(B-15,执行中发现,优先级最高)

检测过程中发现 B-01/B-03/B-04 只是症状,真正的病根是 API 层全链路无类型:

// 修复前(每个 api/module/*.ts 都这样写)
import { Method } from "alova";
function get<T = any>(url: string, params?): Method<T> {   // ← 泛型参数错位
    return new Method<T>("GET", alovaInstance, url, { params });
}

alova 的 Method<AG extends AlovaGenerics> 的泛型参数是 AlovaGenerics(实例泛型),不是返回数据类型。传入数据类型 T 触发 TS2344,并让整条链退化为 unknown:send() 返回 unknown,于是所有 res.data 字段访问都失去类型检查——这正是契约声明比后端多/少都无人发现的根本原因。

修复:统一改用 alova 实例的强类型工厂方法(alovaInstance.Get<T>/Post<T>/Put<T>/Delete<T>),改 4 个文件(homework.ts/teacherHomework.ts/studentHomework.ts/notification.ts),并清掉随泛型失效而暴露的未使用导入。

效果:vue-tsc 错误 162 → 78,且课程作业模块 0 错误;send() 恢复类型后,契约错配从此可在编译期拦截。

S10 · 回归验证

见 §5。


5. 回归验证结果(实测)

验证项 命令 / 方式 结果
生产构建 npx vite build --mode base ✅ 通过(~40s,exit 0)
后端编译 mvn -o -q compile(JDK 1.8 + Maven 3.6.1) ✅ 通过(exit 0,改动类已重新编译)
SFC 类型检查 vue-tsc --noEmit(临时换 TS 5.2.2,已还原) ✅ 课程作业模块 0 错误;全仓 162 → 78
契约运行时 mock 冒烟脚本 .backup/20260916-m5-v2-mock/smoke.mjs 51 PASS / 7 FAIL(FAIL 全为「已删端点应 404」断言,mock 插件对未匹配路由返回 500,非本次回归)
B-01 场景实测 GET /api/student/homeworks hw12 = CLOSED + allowLate:1 → canSubmit=true(旧代码恒 false)
B-01 字段实测 student 列表 5 行 + 详情 每行均含 allowLate

B-14 结论修正:vue-tsc 崩溃的真实原因是 TypeScript 5.9.3 与 vue-tsc 1.8.27 不兼容(vue-tsc 1.8 仅支持 TS ≤5.2),与 Node 版本无关——Node 16/24 下同样报错。临时降级 TS 后类型检查正常,故本次得以完成全量校验。


6. 误报修正(与 §2 初稿结论的差异)

执行阶段发现初稿中两条为工具输出重复渲染造成的误报,经复核后撤回:

编号 初稿结论 复核结果 处置
B-06 TeacherHomeworks.vue 存在 4 个重复函数声明 实际 function viewDetail/viewSubmissions/toGrade/edit 各仅 1 处 撤回(无此缺陷)
B-07 moreActions Map 无界增长 该模式不存在;实为模板 <Dropdown @on-click="onPubMore(name,row)">,无缓存 撤回(无此缺陷)

勘误原因:早期整文件读取的一次响应出现内容重复,导致据此误判。后续所有结论均改为以 grep/定点 sed 复核为准。

新增确认项:src/utils/alova/alova-vant.ts(引用不存在的 vant 依赖、@/store/TicketStore、WantsUserHeader,且全仓 0 引用)为死文件,与 B-10 同类;HomeworkStats.vue 内联 :columns 的 render: (h,p) 隐式 any 已修复。


7. 工作区状态说明(重要)

本次执行期间,有并发进程在本工作区提交了 5 个 commit(e6639f7 → 70bd3a2),时间线:

时间 commit 说明
02:29 e6639f7 会话开始时的 HEAD
02:32~02:42 39824fb / 23d6716 / 6258ebb M5 P1~P3
03:18 44ace7c 把本次执行中的文件一并提交(含 bug检测与修复.md、api/index.ts 删除、ReviewWorkbench 修改等)
03:20 70bd3a2 M5 计划文档回填

因此部分修复已进入 44ace7c(如 B-01 mock 侧、B-02、B-09、B-11、homeworkV2.ts 的 allowLate),另一部分仍为未提交的工作区改动(S9 根因修复的 4 个 api/module/*.ts、ReviewModal/HomeworkStats/StudentHomeworks 等类型清理)。

⚠️ 本次未执行 git commit。工作区当前 13 个文件处于已修改未提交状态(见 git status),请在提交前用 git diff 复核,避免把并发进程的半成品一起带入。


8. 遗留风险与后续建议

  1. vue-tsc 版本必须对齐(最高优先):当前 vue-tsc@1.8.27 与 typescript@5.9.3 不兼容,类型门禁长期失效,是 B-01/B-03/B-04/B-15 逃逸的共同前提。建议二选一:① typescript 锁到 ~5.2;② vue-tsc 升到 ^2.x(配 TS ≥5.4)。并纳入 CI(vue-tsc --noEmit)。
  2. 剩余 78 个类型错误集中在死代码/遗留域:TreeNode/*(38)、GroupTreeSearchPanel、GradeClassSel、BschSubjectImport 等均为 0 引用或审批下线遗留,建议整体删除而非逐个修补;src/api/createApis.ts 是 alova gen 遗留,随 api/index.ts 一并评估删除。
  3. 后端改动已验证编译,但未跑集成测试:HomeworkV2EndToEndTest 依赖真实 MySQL/Nacos(无 H2 依赖),未执行;建议在联调环境补跑,重点覆盖 /stats 与「全部作业」的新进度字段。
  4. mock 与真实后端口径漂移:homeworkapi.ts 主动补齐了后端不存在的字段,使 mock 模式「看起来一切正常」。建议以「后端 VO 为唯一真源」反向校验 mock——本次 B-01/B-03/B-04 三条 P0 皆因 mock 代偿而长期不可见。
  5. /stats 与「全部作业」建议评估退役:其课程作业口径字段在 V2 已有权威实现(teacherApi.stats、published 的 progress);本次为兼容在 V1 VO 上补了字段,长期应整体切 V2。
  6. 换行符噪音(B-12):建议 .gitattributes 固定 *.ts text eol=lf,避免无内容变更的文件反复出现在 diff 中。
  7. baseApi.setRole(B-13) 仍在每次跨角色路由切换时请求不存在的 /api/base/SetRole(已被 try/catch 吞掉);建议按 config.isMock 前置短路,纯净化真实环境网络面板。