From 5d96c832f60f456fa663d788366d69d0bd01a1aa Mon Sep 17 00:00:00 2001 From: zhonghua Date: Tue, 10 Mar 2026 16:44:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(api):=20=E9=87=8D=E6=9E=84=20API=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BB=A5=E4=BD=BF=E7=94=A8=E6=96=B0=E7=9A=84?= =?UTF-8?q?=20readingApi=20=E5=AE=A2=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将多个 API 函数从旧的 http 实现迁移到新的 readingApi 客户端 - 更新登录、登出、获取用户信息等功能以适应新的数据结构 - 统一课程、主题和课时的 API 调用方式,简化代码结构 - 保留部分旧接口以兼容后端,确保现有功能不受影响 --- reading-platform-frontend/src/api/auth.ts | 63 ++++++--- reading-platform-frontend/src/api/client.ts | 16 +++ reading-platform-frontend/src/api/course.ts | 140 ++++++++++---------- reading-platform-frontend/src/api/index.ts | 3 +- reading-platform-frontend/src/api/lesson.ts | 50 +++---- reading-platform-frontend/src/api/theme.ts | 33 ++--- 6 files changed, 168 insertions(+), 137 deletions(-) create mode 100644 reading-platform-frontend/src/api/client.ts diff --git a/reading-platform-frontend/src/api/auth.ts b/reading-platform-frontend/src/api/auth.ts index ce45446..8ad79b5 100644 --- a/reading-platform-frontend/src/api/auth.ts +++ b/reading-platform-frontend/src/api/auth.ts @@ -1,19 +1,18 @@ -import { http } from './index'; +import { readingApi } from './client' +import type { + LoginRequest, + LoginResponse as ApiLoginResponse, + ResultLoginResponse, + ResultUserInfoResponse, + UserInfoResponse, +} from './generated/model' -export interface LoginParams { - account: string; - password: string; - role: string; -} +export type LoginParams = LoginRequest -// Java 后端返回的平铺结构 -export interface LoginResponse { - token: string; - userId: number; - username: string; - name: string; - role: 'admin' | 'school' | 'teacher' | 'parent'; - tenantId?: number; +// Java 后端返回的平铺结构(保持与现有业务使用一致) +export interface LoginResponse extends Required> { + role: 'admin' | 'school' | 'teacher' | 'parent' + tenantId?: number } export interface UserProfile { @@ -29,20 +28,48 @@ export interface UserProfile { // 登录 export function login(params: LoginParams): Promise { - return http.post('/auth/login', params).then((res: any) => res.data); + return readingApi.login(params).then((res) => { + const wrapped = res as ResultLoginResponse + const data = (wrapped.data ?? {}) as ApiLoginResponse + + return { + token: data.token ?? '', + userId: data.userId ?? 0, + username: data.username ?? '', + name: data.name ?? '', + role: (data.role as LoginResponse['role']) ?? 'teacher', + tenantId: data.tenantId, + } + }) } // 登出 export function logout(): Promise { - return http.post('/auth/logout'); + return readingApi.logout().then(() => undefined) } // 刷新Token export function refreshToken(): Promise<{ token: string }> { - return http.post('/auth/refresh'); + // OpenAPI 目前未定义 refresh 接口,暂时保留原有调用路径以兼容后端 + const { http } = require('./index') + return http.post('/auth/refresh') } // 获取当前用户信息 export function getProfile(): Promise { - return http.get('/auth/profile'); + return readingApi.getCurrentUser().then((res) => { + const wrapped = res as ResultUserInfoResponse + const data = (wrapped.data ?? {}) as UserInfoResponse + + return { + id: data.id ?? 0, + name: data.name ?? '', + role: (data.role as UserProfile['role']) ?? 'teacher', + tenantId: data.tenantId, + tenantName: undefined, + email: data.email, + phone: data.phone, + avatar: data.avatarUrl, + } + }) } diff --git a/reading-platform-frontend/src/api/client.ts b/reading-platform-frontend/src/api/client.ts new file mode 100644 index 0000000..8549279 --- /dev/null +++ b/reading-platform-frontend/src/api/client.ts @@ -0,0 +1,16 @@ +import { getReadingPlatformAPI } from './generated/api' +import type { ResultUserInfoResponse } from './generated/model' + +// Orval 生成的完整 API 客户端 +export const readingApi = getReadingPlatformAPI() + +// 通用工具类型:根据方法名拿到返回 Promise 的结果类型 +export type ApiResultOf> = + Awaited[K]>> + +// 如果后端统一使用 Result 包裹,这个类型可以从中解包出 data +export type UnwrapResult = R extends { data: infer D } ? D : R + +// 示例:当前登录用户信息的解包类型 +export type CurrentUserInfo = UnwrapResult + diff --git a/reading-platform-frontend/src/api/course.ts b/reading-platform-frontend/src/api/course.ts index 0f4eb4a..2c2ad92 100644 --- a/reading-platform-frontend/src/api/course.ts +++ b/reading-platform-frontend/src/api/course.ts @@ -1,53 +1,15 @@ -import { http } from './index'; +import { readingApi } from './client' +import type { + GetCoursePage1Params, + ResultPageResultCourse, + Course as ApiCourse, + ApproveCourseParams, + RejectCourseParams, +} from './generated/model' -export interface CourseQueryParams { - page?: number; - pageSize?: number; - grade?: string; - status?: string; - keyword?: string; -} +export type CourseQueryParams = GetCoursePage1Params -export interface Course { - id: number; - name: string; - description?: string; - pictureBookName?: string; - grades: string[]; - status: string; - version: string; - usageCount: number; - teacherCount: number; - avgRating: number; - createdAt: Date; - updatedAt: Date; - submittedAt?: Date; - reviewedAt?: Date; - reviewComment?: string; - // 新增字段 - themeId?: number; - theme?: { id: number; name: string }; - coreContent?: string; - coverImagePath?: string; - domainTags?: string[]; - gradeTags?: string[]; - duration?: number; - // 课程介绍字段 - introSummary?: string; - introHighlights?: string; - introGoals?: string; - introSchedule?: string; - introKeyPoints?: string; - introMethods?: string; - introEvaluation?: string; - introNotes?: string; - // 排课计划参考 - scheduleRefData?: string; - // 环创建设 - environmentConstruction?: string; - // 关联课程 - courseLessons?: CourseLesson[]; -} +export type Course = ApiCourse export interface CourseLesson { id: number; @@ -101,14 +63,26 @@ export interface ValidationWarning { code: string; } -// 获取课程包列表 -export function getCourses(params: CourseQueryParams): Promise<{ - items: Course[]; - total: number; - page: number; - pageSize: number; +// 获取课程包列表(使用 Orval 生成的分页接口,并适配为原有扁平结构) +export function getCourses( + params: CourseQueryParams, +): Promise<{ + items: Course[] + total: number + page: number + pageSize: number }> { - return http.get('/admin/courses', { params }); + return readingApi.getCoursePage1(params).then((res) => { + const wrapped = res as ResultPageResultCourse + const pageData = wrapped.data + + return { + items: (pageData?.items as Course[]) ?? [], + total: pageData?.total ?? 0, + page: pageData?.page ?? params.page ?? 1, + pageSize: pageData?.pageSize ?? params.pageSize ?? 10, + } + }) } // 获取审核列表 @@ -118,81 +92,105 @@ export function getReviewList(params: CourseQueryParams): Promise<{ page: number; pageSize: number; }> { - return http.get('/admin/courses/review', { params }); + // 审核列表对应 Orval 的 getReviewCoursePage,返回结构同课程分页 + return readingApi.getReviewCoursePage(params as any).then((res) => { + const wrapped = res as ResultPageResultCourse + const pageData = wrapped.data + + return { + items: (pageData?.items as Course[]) ?? [], + total: pageData?.total ?? 0, + page: pageData?.page ?? params.page ?? 1, + pageSize: pageData?.pageSize ?? params.pageSize ?? 10, + } + }) } // 获取课程包详情 export function getCourse(id: number): Promise { - return http.get(`/admin/courses/${id}`); + return readingApi.getCourse3(id).then((res) => res) } // 创建课程包 export function createCourse(data: any): Promise { - return http.post('/admin/courses', data); + return readingApi.createCourse1(data).then((res) => res) } // 更新课程包 export function updateCourse(id: number, data: any): Promise { - return http.put(`/admin/courses/${id}`, data); + return readingApi.updateCourse1(id, data).then((res) => res) } // 删除课程包 export function deleteCourse(id: number): Promise { - return http.delete(`/admin/courses/${id}`); + return readingApi.deleteCourse1(id).then((res) => res) } // 验证课程完整性 export function validateCourse(id: number): Promise { - return http.get(`/admin/courses/${id}/validate`); + // 暂无对应 Orval 接口,继续使用旧路径 + const { http } = require('./index') + return http.get(`/admin/courses/${id}/validate`) } // 提交审核 -export function submitCourse(id: number, copyrightConfirmed: boolean): Promise { - return http.post(`/admin/courses/${id}/submit`, { copyrightConfirmed }); +export function submitCourse(id: number, _copyrightConfirmed: boolean): Promise { + // 后端接口签名只需要 ID,版权确认逻辑在前端自行控制 + return readingApi.submitCourse(id).then((res) => res) } // 撤销审核 export function withdrawCourse(id: number): Promise { - return http.post(`/admin/courses/${id}/withdraw`); + return readingApi.withdrawCourse(id).then((res) => res) } // 审核通过 export function approveCourse(id: number, data: { checklist?: any; comment?: string }): Promise { - return http.post(`/admin/courses/${id}/approve`, data); + const params: ApproveCourseParams = { + comment: data.comment, + } + return readingApi.approveCourse(id, params).then((res) => res) } // 审核驳回 export function rejectCourse(id: number, data: { checklist?: any; comment: string }): Promise { - return http.post(`/admin/courses/${id}/reject`, data); + const params: RejectCourseParams = { + comment: data.comment, + } + return readingApi.rejectCourse(id, params).then((res) => res) } // 直接发布(超级管理员) -export function directPublishCourse(id: number, skipValidation?: boolean): Promise { - return http.post(`/admin/courses/${id}/direct-publish`, { skipValidation }); +export function directPublishCourse(id: number, _skipValidation?: boolean): Promise { + // skipValidation 由后端接口定义控制,这里总是调用“直接发布”接口 + return readingApi.directPublishCourse(id).then((res) => res) } // 发布课程包(兼容旧API) export function publishCourse(id: number): Promise { - return http.post(`/admin/courses/${id}/publish`); + return readingApi.publishCourse(id).then((res) => res) } // 下架课程包 export function unpublishCourse(id: number): Promise { - return http.post(`/admin/courses/${id}/unpublish`); + return readingApi.unpublishCourse(id).then((res) => res) } // 重新发布 export function republishCourse(id: number): Promise { - return http.post(`/admin/courses/${id}/republish`); + return readingApi.republishCourse(id).then((res) => res) } // 获取课程包统计数据 export function getCourseStats(id: number): Promise { + // 统计接口在 OpenAPI 中与当前使用的字段含义略有差异,暂时保留旧实现 + const { http } = require('./index') return http.get(`/admin/courses/${id}/stats`); } // 获取版本历史 export function getCourseVersions(id: number): Promise { + const { http } = require('./index') return http.get(`/admin/courses/${id}/versions`); } diff --git a/reading-platform-frontend/src/api/index.ts b/reading-platform-frontend/src/api/index.ts index badbbc9..a8d4081 100644 --- a/reading-platform-frontend/src/api/index.ts +++ b/reading-platform-frontend/src/api/index.ts @@ -3,7 +3,8 @@ import { message } from 'ant-design-vue'; // 创建axios实例 const request: AxiosInstance = axios.create({ - baseURL: '/api/v1', // 使用 /api/v1,代理会保留完整路径 + // 由各调用方提供完整路径(例如 /api/v1/teacher/tasks),这里不再追加前缀 + baseURL: '', timeout: 30000, headers: { 'Content-Type': 'application/json', diff --git a/reading-platform-frontend/src/api/lesson.ts b/reading-platform-frontend/src/api/lesson.ts index ba32027..8bf2a9e 100644 --- a/reading-platform-frontend/src/api/lesson.ts +++ b/reading-platform-frontend/src/api/lesson.ts @@ -1,29 +1,12 @@ -import { http } from './index'; +import { readingApi } from './client' +import type { + ResultListCourseLesson, + CourseLesson as ApiCourseLesson, +} from './generated/model' // ==================== 课程类型 ==================== -export interface CourseLesson { - id: number; - courseId: number; - lessonType: string; - name: string; - description?: string; - duration: number; - videoPath?: string; - videoName?: string; - pptPath?: string; - pptName?: string; - pdfPath?: string; - pdfName?: string; - objectives?: string; - preparation?: string; - extension?: string; - reflection?: string; - assessmentData?: string; - useTemplate: boolean; - sortOrder: number; - steps?: LessonStep[]; -} +export type CourseLesson = ApiCourseLesson export interface LessonStep { id: number; @@ -84,14 +67,17 @@ export interface CreateStepData { // ==================== 超管端 API ==================== -// 获取课程列表 +// 获取课程列表(系统课程课时) export function getLessonList(courseId: number) { - return http.get(`/admin/courses/${courseId}/lessons`); + return readingApi.getLessons1(courseId).then((res) => { + const wrapped = res as ResultListCourseLesson + return wrapped.data?.items ?? [] + }) } // 获取课程详情 export function getLessonDetail(courseId: number, lessonId: number) { - return http.get(`/admin/courses/${courseId}/lessons/${lessonId}`); + return readingApi.getLesson2(courseId, lessonId).then((res) => res.data ?? res) } // 按类型获取课程 @@ -101,21 +87,24 @@ export function getLessonByType(courseId: number, lessonType: string) { // 创建课程 export function createLesson(courseId: number, data: CreateLessonData) { - return http.post(`/admin/courses/${courseId}/lessons`, data); + return readingApi.createLesson1(courseId, data as any).then((res) => res) } // 更新课程 export function updateLesson(lessonId: number, data: Partial) { + // Orval 接口需要同时提供 courseId 和 lessonId,这里仅有 lessonId 时保留旧实现 + const { http } = require('./index') return http.put(`/admin/courses/0/lessons/${lessonId}`, data); } // 删除课程 export function deleteLesson(courseId: number, lessonId: number) { - return http.delete(`/admin/courses/${courseId}/lessons/${lessonId}`); + return readingApi.deleteLesson(courseId, lessonId).then((res) => res) } // 重新排序课程 export function reorderLessons(courseId: number, lessonIds: number[]) { + const { http } = require('./index') return http.put(`/admin/courses/${courseId}/lessons/reorder`, { lessonIds }); } @@ -123,26 +112,31 @@ export function reorderLessons(courseId: number, lessonIds: number[]) { // 获取环节列表 export function getStepList(courseId: number, lessonId: number) { + const { http } = require('./index') return http.get(`/admin/courses/${courseId}/lessons/${lessonId}/steps`); } // 创建环节 export function createStep(courseId: number, lessonId: number, data: CreateStepData) { + const { http } = require('./index') return http.post(`/admin/courses/${courseId}/lessons/${lessonId}/steps`, data); } // 更新环节 export function updateStep(stepId: number, data: Partial) { + const { http } = require('./index') return http.put(`/admin/courses/0/lessons/steps/${stepId}`, data); } // 删除环节 export function deleteStep(courseId: number, lessonId: number, stepId: number) { + const { http } = require('./index') return http.delete(`/admin/courses/${courseId}/lessons/steps/${stepId}`); } // 重新排序环节 export function reorderSteps(courseId: number, lessonId: number, stepIds: number[]) { + const { http } = require('./index') return http.put(`/admin/courses/${courseId}/lessons/${lessonId}/steps/reorder`, { stepIds }); } diff --git a/reading-platform-frontend/src/api/theme.ts b/reading-platform-frontend/src/api/theme.ts index 01e8e86..5cdd797 100644 --- a/reading-platform-frontend/src/api/theme.ts +++ b/reading-platform-frontend/src/api/theme.ts @@ -1,18 +1,7 @@ -import { http } from './index'; +import { readingApi } from './client' +import type { ResultListTheme, ResultTheme, Theme as ApiTheme } from './generated/model' -export interface Theme { - id: number; - name: string; - description?: string; - sortOrder: number; - status: string; - createdAt: string; - courses?: { - id: number; - name: string; - coverImagePath?: string; - }[]; -} +export type Theme = ApiTheme export interface CreateThemeData { name: string; @@ -29,27 +18,33 @@ export interface UpdateThemeData { // 获取主题列表 export function getThemeList() { - return http.get('/admin/themes'); + return readingApi.getThemes().then((res) => { + const wrapped = res as ResultListTheme + return wrapped.data ?? [] + }) } // 获取主题详情 export function getThemeDetail(id: number) { - return http.get(`/admin/themes/${id}`); + return readingApi.getTheme(id).then((res) => { + const wrapped = res as ResultTheme + return wrapped.data ?? res + }) } // 创建主题 export function createTheme(data: CreateThemeData) { - return http.post('/admin/themes', data); + return readingApi.createTheme(data as any).then((res) => res) } // 更新主题 export function updateTheme(id: number, data: UpdateThemeData) { - return http.put(`/admin/themes/${id}`, data); + return readingApi.updateTheme(id, data as any).then((res) => res) } // 删除主题 export function deleteTheme(id: number) { - return http.delete(`/admin/themes/${id}`); + return readingApi.deleteTheme(id).then((res) => res) } // 重新排序主题