## 后端重构 ### 新增基础设施 - src/common/dto/ - 统一响应格式和分页查询DTO基类 - src/common/interceptors/ - 响应转换拦截器 - src/common/utils/ - JSON解析和分页计算工具函数 ### DTO规范化 - Course、Lesson、TeacherCourse、SchoolCourse、Tenant控制器添加Swagger装饰器 - 添加@ApiQuery、@ApiBody、@ApiOperation完善API文档 - 修复CourseLesson控制器路径参数问题 ## 前端重构 ### Orval API客户端生成 - 添加orval配置和生成脚本 - 生成完整的类型安全API客户端 (src/api/generated/) - 导入56个参数类型文件 ### API模块迁移 - src/api/course.ts - 迁移使用Orval生成API - src/api/school-course.ts - 修复类型错误(number vs string) - src/api/teacher.ts - 完整迁移教师端API - src/api/client.ts - 重构API客户端统一入口 - src/api/lesson.ts - 修复未使用参数 ### 文件路由配置 - 配置unplugin-vue-router插件 - 创建动态路由配置支持自动路由和传统路由切换 - 添加路由守卫保留原有权限逻辑 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.9 KiB
开发日志 - 2026-03-12
今日任务:代码重构(规范化)
根据 docs/统一开发规范.md 和 docs/前端项目规范.md 进行代码重构。
后端重构阶段
阶段 1:添加公共组件(基础设施) ✅
新增文件:
-
src/common/dto/result.dto.ts- 统一响应格式 DTOResultDto<T>- 统一响应格式{ code, message, data }PageResultDto<T>- 分页响应格式{ items, total, page, pageSize, totalPages }
-
src/common/dto/page-query.dto.ts- 分页查询 DTO 基类PageQueryDto- 基础分页查询(page, pageSize)PageSearchDto- 带搜索关键词PageWithStatusDto- 带状态筛选PageWithDateRangeDto- 带日期范围
-
src/common/interceptors/transform.interceptor.ts- 响应转换拦截器- 自动将 Controller 返回数据包装为统一格式
- 支持
@SkipTransform()跳过转换
-
src/common/utils/json.util.ts- JSON 字段解析工具parseJsonField()- 安全解析 JSON 字段parseJsonArray()- 解析 JSON 数组parseJsonObject()- 解析 JSON 对象
-
src/common/utils/pagination.util.ts- 分页计算工具calculatePagination()- 计算分页参数createPageResponse()- 创建分页响应calculateTotalPages()- 计算总页数validatePagination()- 验证分页参数
修改文件:
package.json- 添加@nestjs/swagger依赖(v11.x)src/main.ts- 配置 Swagger 文档和响应拦截器
Swagger 配置:
- 文档访问地址:
http://localhost:3000/api-docs - 添加了 JWT Bearer 认证支持
- 添加了 API 标签分类
阶段 2:DTO 规范化(Tenant 模块示例) ✅
修改文件:
-
src/modules/tenant/dto/tenant.dto.ts- 为所有 DTO 添加
@ApiProperty装饰器 - 添加 description、example、required 等属性
- 为所有 DTO 添加
-
src/modules/tenant/tenant.controller.ts- 添加
@ApiTags、@ApiOperation、@ApiResponse装饰器 - 添加
@ApiBearerAuth认证装饰器
- 添加
前端重构阶段
阶段 1:添加 Orval 配置 ✅
新增文件:
orval.config.ts- Orval 配置文件src/api/generated/mutator.ts- 自定义请求拦截器src/api/client.ts- API 客户端统一入口src/api/teacher.adapter.ts- 教师端 API 适配层
修改文件:
-
package.json- 添加 orval 依赖和生成脚本api:update- 生成 API 客户端api:watch- 监听模式生成
-
vite.config.ts- 添加文件路由插件
已安装依赖:
- orval (^8.5.3)
- unplugin-vue-router (^0.19.2)
阶段 2:配置文件路由 ✅
配置内容:
- 使用
src/views作为路由文件夹 - 支持
.vue扩展名 - 同步导入模式
待完成任务
后端
- 阶段 3:全面推广 DTO 规范化
- 添加 @ApiQuery 装饰器(Course、SchoolCourse、Lesson 等模块)
- 添加 @ApiBody 装饰器(定义请求体类型)
- Auth 模块
- Course 模块
- Lesson 模块
- SchoolCourse 模块
- 其他模块
前端
- 运行
npm run api:update生成 API 客户端 - 修复 PrepareModeView.vue 中的 API 调用错误
- 迁移课程模块(Course)使用新 API 客户端
- 迁移校本课程模块(SchoolCourse)使用新 API 客户端
- 迁移教师模块(Teacher)使用新 API 客户端
- 修复 school-course.ts 中的类型错误(string vs number)
- 清理未使用的 teacher.adapter.ts 文件
- 迁移其他 API 模块(lesson, auth 等)
新增完成
后端
-
CourseLesson 控制器重构 ✅
- 移除类级路径参数
@Controller('admin/courses/:courseId/lessons') - 改为在每个方法中显式声明完整路径
@Get(':courseId/lessons') - 修复了 Orval 验证错误(PUT/DELETE 方法的路径参数问题)
- 移除类级路径参数
-
后端 DTO 规范化 ✅
- Course 控制器:添加 @ApiQuery、@ApiBody、@ApiOperation 装饰器
- Lesson 控制器:添加 @ApiQuery、@ApiBody、@ApiOperation 装饰器
- TeacherCourse 控制器:添加 @ApiQuery、@ApiBody、@ApiOperation 装饰器
- SchoolCourse 控制器:添加 @ApiQuery、@ApiBody、@ApiOperation 装饰器
前端
-
Orval API 客户端生成成功 ✅
- 生成文件:
src/api/generated/index.ts(90KB+) - 生成模型:
src/api/generated/model/(56个参数类型文件) - Mutator:
src/api/generated/mutator.ts - TypeScript 编译通过
- API 方法现在包含完整的参数定义 ✅
- 生成文件:
-
课程模块迁移到新 API 客户端 ✅
- 更新
src/api/course.ts使用 Orval 生成的 API - 导入参数类型:
CourseControllerFindAllParams - 保持向后兼容的接口
- 更新
-
校本课程模块迁移到新 API 客户端 ✅
- 更新
src/api/school-course.ts使用 Orval 生成的 API - 支持学校端和教师端两套接口
- 更新
-
教师模块迁移到新 API 客户端 ✅
- 更新
src/api/teacher.ts使用 Orval 生成的 API - 覆盖教师课程、授课记录、首页、反馈、进度追踪、排课管理、阅读任务等所有接口
- 修复类型兼容性问题(DTO 索引签名、参数类型转换等)
- 更新
-
修复 school-course.ts 类型错误 ✅
- 移除所有
String()类型转换 - 生成的 SchoolCourse API 期望
number类型 ID,与 Teacher API 不同(期望string) - 修复所有学校端和教师端校本课程接口的类型问题
- 移除所有
-
清理 teacher.adapter.ts ✅
- 移除不存在的类型导入(TeacherCourse, Lesson, LessonFeedback, TeacherTask)
- 添加弃用注释,建议直接使用 teacher.ts 中的函数
- 改为重新导出 teacher.ts,保持向后兼容
-
修复 client.ts API 客户端 ✅
- 修复导入错误的函数名(
getReadingPlatformAPI→getApi) - 简化导出结构,直接使用
api作为 API 客户端实例 - 修复类型守卫和类型断言问题
- 改进
unwrapData和unwrapPageData函数的类型安全
- 修复导入错误的函数名(
-
修复其他 API 相关文件 ✅
- 移除 mutator.ts 中未使用的
ResultDto导入 - 修复 lesson.ts 中未使用的
lessonId参数
- 移除 mutator.ts 中未使用的
-
创建文件路由目录结构 ✅
- 更新 vite.config.ts 使用正确的
unplugin-vue-router插件导入 - 创建动态路由配置
src/router/index.ts - 保留传统路由配置作为
src/router/manual-routes.ts备份 - 支持自动路由和传统路由的平滑切换
- 更新 vite.config.ts 使用正确的
验证步骤
后端验证
cd /Users/retirado/Program/ccProgram_0312/reading-platform-backend
npm start
# 访问 http://localhost:3000/api-docs 查看 Swagger 文档
前端验证
cd /Users/retirado/Program/ccProgram_0312/reading-platform-frontend
npm run api:update
npm run dev
遇到的问题
-
@nestjs/swagger 版本冲突
- 问题:@nestjs/swagger@11.x 需要 @nestjs/common@11.x
- 解决:使用
--legacy-peer-deps安装
-
unplugin-vue-router 已弃用
- 问题:该包已合并到 vuejs/router
- 解决:暂时使用当前版本,后续可升级到 Vue Router 5
-
CourseLesson 控制器路径参数问题
- 问题:类级路径参数在 PUT/DELETE 方法中无法被 Orval 正确识别
- 解决:移除类级路径参数,在每个方法中显式声明完整路径
-
Orval 生成的 API 方法缺少参数
- 问题:后端控制器缺少 @ApiQuery、@ApiBody 装饰器,导致生成的 API 方法没有参数
- 解决方案:需要先完成后端 DTO 规范化,为所有控制器添加完整的 Swagger 装饰器
-
PrepareModeView.vue API 调用错误
- 问题:从
@/api/school-course导入getTeacherSchoolCourseFullDetail,但错误地通过teacherApi调用 - 解决:直接调用导入的函数
getTeacherSchoolCourseFullDetail()
- 问题:从
-
Teacher.ts API 迁移类型问题
- 问题:本地 DTO 接口与生成类型不兼容(缺少索引签名、参数类型不匹配)
- 解决:使用
as any类型断言进行适配,保持向后兼容的函数签名 - 问题:部分生成的 API 方法缺少参数定义(如 getTeacherTasks、getTaskTemplates)
- 解决:移除不支持的参数,在后续后端更新后恢复
- 问题:saveLessonProgress 的 lessonIds 类型为 number[] 而生成 API 需要 string[]
- 解决:在调用时进行类型转换
.map(String)
-
SchoolCourse.ts API 类型问题
- 问题:生成的 SchoolCourse API 期望
number类型 ID,但代码传递String(id) - 解决:移除所有
String()类型转换,直接传递number类型 - 注意:与 Teacher API 不同(Teacher API 期望
string),需要根据实际生成的 API 签名调整
- 问题:生成的 SchoolCourse API 期望
-
Teacher.adapter.ts 导入错误
- 问题:导入了不存在的类型(TeacherCourse, Lesson, LessonFeedback, TeacherTask)
- 解决:移除不存在的类型导入,添加弃用注释
-
Client.ts API 客户端结构错误
- 问题:尝试导入不存在的
getReadingPlatformAPI函数 - 解决:使用正确的
getApi函数,简化导出结构 - 问题:类型守卫
isSuccess导致 TypeScript 无法正确推断else分支的类型 - 解决:改用
asserts类型断言,让 TypeScript 理解类型收窄 - 问题:
unwrapData函数的类型安全问题 - 解决:添加显式类型断言和类型检查
- 问题:尝试导入不存在的
-
文件路由配置问题
- 问题:
unplugin-vue-router插件导入名称错误 (FileSystemRouter不存在) - 解决:使用默认导入
fileRouter from 'unplugin-vue-router/vite' - 问题:文件路由虚拟模块路径不确定
- 解决:创建动态路由配置,先尝试加载
vue-router/auto-routes,失败则回退到传统路由 - 保留了
manual-routes.ts作为备用,确保系统稳定性
备注
- 本次重构遵循渐进式原则,保持向后兼容
- 所有新增文件都遵循规范要求的目录结构
- 适配层确保旧代码可以继续工作