Files
coursehomework-front/bug检测与修复.md
T
wanghu 44ace7c697 feat(M5-P4): mock V2 化 + 伪接口清理 + vite 代理 + 走查修复
- mock 整体重写为「V2 单一存储 + V1 适配层」:教师 19 / 学生 6 / 通知 3 全端点,
  V1 冻结 7 端点由同一存储派生(与后端同一张 homework 表一致);
  提交内核对齐后端 doSubmit(版本翻转/逾期分流/附件 dataURL 拒绝/
  仅查阅拒绝),submit/action 返回 {submissionId} 契约对齐
- 删除伪接口 mock 与客户端方法:Submissions/SubmissionDetail/Grade/Remind/
  MyHomeworks/MySubmission/Submit/Create/Edit/InitializeRed/UserPull/
  SetTopmost/Expedite/ReceiveUsers/List/GetClassTree(真实环境必 404)
- types/homework.ts 收敛为 V1 冻结契约(删 11 个孤儿类型)
- 删除被切换孤儿化的组件:HomeworkUpload/ClassCheckInput/UserCheckModal
- vite 代理指向本地 8102(M5 联调目标,远程测试环境保留注释)
- 走查修复:TeacherHomeworks 操作列改模板插槽(render 函数 Dropdown 插槽
  在 view-ui-plus 下失效导致菜单内联);ReviewModal :value/@input 改
  modelValue(Vue2 API 残留导致弹窗打不开);StudentHomeworks 重新提交
  按钮补 canSubmit 门控(已截止且不允许补交时后端必拒)
- 验收:dev:mock 冒烟 58/58 PASS;构建 EXIT=0;tsc 47 条均为存量(基线 65)
  且无引用已删符号;浏览器走查 9 页面 + 批阅保存闭环 + 3 秒阅读上报通过
2026-09-16 03:18:38 +08:00

334 lines
22 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.
# 课程作业模块 · 前后端 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 个行操作函数重复声明 | **P1** | `/list` 我布置的 | 已修复 |
| B-07 | `TeacherHomeworks.vue` `moreActions` Map 每次重渲染累积、永不回收 | **P1** | `/list` 我布置的 | 已修复 |
| B-08 | `NewHomework.vue` 编辑回显与班级树加载存在竞态,导致「全部班级」勾选态误判 | **P1** | `/list/new` 发作业 | 已修复 |
| B-09 | `src/api/index.ts` 引用未定义的 `GlobalFetch()`,且 baseURL 硬编码远程域名 | **P2** | 潜在全局 auto-import 污染 | 已修复 |
| B-10 | 7 个死组件 + 1 个 `index copy.vue` 残留,`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` 在 Node 24 下崩溃(`Search string not found: "/supportedTSExtensions = .*(?=;)/"`),类型门禁实际失效 | **P1** | 全部 TS 代码 | 已记录(需换 Node 18/20 或升级 vue-tsc) |
| C-01 | 生产构建通过(39.66s,exit 0) | — | — | 通过 |
| C-02 | TS 类型检查**无法执行**(见 B-14) | — | — | 阻塞 |
| 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`)为了让页面「跑得起来」,主动补齐了这些后端并不存在的字段,进一步掩盖了问题。
---
## 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` 两个入口):
```ts
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 【P1】`TeacherHomeworks.vue` 重复声明与无界缓存
**证据链**
- `viewDetail / viewSubmissions / toGrade / edit` 四个函数在 `<script setup>` 中**各定义两次**(同一模块作用域重复 `function` 声明)。
- `const moreActions = new Map<string, () => void>()`、`let moreSeq = 0` 位于模块级;`pubColumns` 是 `computed`,每次依赖变化重算都会调用 `mItem()` 递增 `moreSeq` 并写入 Map,**Map 只增不减**。
**后果**:重复声明在 JS 中后者覆盖前者(静默),但违反类型检查(TS `Duplicate function implementation`),也让维护者误判生效分支;Map 在长时间停留/频繁翻页的列表页持续增长(内存缓慢泄漏)。
**修复**:删除重复的 4 个函数定义;`moreActions` 改为每次 `pubColumns` 重算时重建(局部 Map),避免无界增长。
---
### B-08 【P1】`NewHomework.vue` 编辑回显与班级树竞态
**证据链**
- `onMounted(loadClasses)` 与 `onMounted(async () => { … await teacherApi.detail(); fillFromV2(d); })` 两次注册,`loadClasses()` 内部 `await teacherApi.classTree()`,**不保证先于详情回显完成**。
- `fillFromV2()` 依赖 `allClassIds.value` 判断「是否全部班级」:
```ts
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 长期未被发现的原因) | 需将 Node 降到 18/20 或升级 `vue-tsc`;建议在 CI 增加类型检查步骤 |
---
## 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 透出
- [x] `ClassHomeWork/src/main/java/com/yunhai/homework/model/v2/StudentHomeworkVo.java`:新增 `allowLate` 字段
- [x] `ClassHomeWork/src/main/java/com/yunhai/homework/service/impl/StudentHomeworkService.java`:`copyBase()` 补 `vo.setAllowLate(...)`
- [x] `src/types/homeworkV2.ts`:`StudentHomeworkV2Vo` 新增 `allowLate?: number`
- [x] `src/assets/mock/homeworkapi.ts`:学生列表 + 学生详情响应补 `allowLate`
### S2 · B-02 批阅工作台附件地址
- [x] `src/components/ReviewWorkbench.vue`:引入 `host.config`,`attUrl/attDownUrl` 统一补 `serviceHost`
### S3 · B-04 / B-05 后端 V1 VO 补字段
- [x] `HomeworkVo.java`:新增 `progress`(`model/v2/ProgressVo`)
- [x] `HomeworkDetailVo.java`:新增 `receiveText`
- [x] `HomeworkService.java`:`adminList()` 填 `progress`;`getDetailMap()` 填 `receiveText`
### S4 · B-03 统计口径
- [x] `HomeworkStatisticsVo.java`:新增 `myAssignCount/pendingGrade/avgSubmitRate/byHomework/scoreDist`
- [x] `HomeworkService.java`:`statistics()` 聚合填充
- [x] `src/views/homework/HomeworkStats.vue`:字段缺失时降级为「—/暂未提供」,不再伪装为 0
### S5 · B-06 / B-07 我布置的页
- [x] `src/views/homework/TeacherHomeworks.vue`:删除 4 个重复函数;`moreActions` 改为列重算时重建
### S6 · B-08 发作业页竞态
- [x] `src/views/homework/NewHomework.vue`:初始化改为 `await loadClasses()` → 详情回显串行
### S7 · B-09 废弃入口
- [x] 删除 `src/api/index.ts`(含 `GlobalFetch` 未定义、硬编码 baseURL)
### S8 · B-11 忽略规则
- [x] `.gitignore` 追加 `.backup/`
### S9 · 回归验证
见 §5。
---
## 5. 回归验证结果
| 验证项 | 命令 | 结果 |
|---|---|---|
| 生产构建 | `npx vite build --mode base` | 见下 |
| 端点/契约复核 | 字段对照脚本 | 见下 |
| Java 编译 | `mvn clean compile` | **未执行**(本机无 Maven/JDK) |
---
## 6. 遗留风险与后续建议
1. **类型门禁形同虚设(B-14,最高优先)**:本次 B-01/B-03/B-04 三条 P0 全部是「类型声明比后端多」的静默错配。恢复 `vue-tsc` 可运行(Node 18/20 或升级 `vue-tsc`)并纳入 CI,是防止同类问题复发的根本手段。
2. **mock 与真实后端口径漂移**:`homeworkapi.ts` 主动补齐了后端不存在的字段,使 mock 模式「看起来一切正常」,掩盖真实缺陷。建议以「后端 VO 为唯一真源」生成/校验 mock。
3. **后端改动未经编译验证**:S1/S3/S4 涉及 Java 文件,本机无 Maven/JDK,需在构建环境跑 `mvn -q clean compile` 复核;若编译不通过,回退范围仅限本次新增的字段与注入。
4. **`/stats` 与「全部作业」仍建议评估退役**:这两页属 V1 冻结面,其课程作业口径字段(提交进度/批改/成绩分布)在 V2 已有权威实现(`teacherApi.stats` / `published` 的 `progress`)。长期建议整体切 V2,避免在 V1 VO 上持续打补丁。
5. **`GitHub` 换行符噪音(B-12)**:建议统一 `core.autocrlf` 或在 `.gitattributes` 中固定 `*.ts text eol=lf`,避免无内容变更的文件反复出现在 diff 中。