# 本文件由 @asteasolutions/zod-to-openapi 从 packages/shared/src/schemas/*.schema.ts 的 zod schema 自动反推生成。 # 若修改字段/端点,请修改 zod schema 后重新运行 openapi 生成脚本(见 architecture.md §二 / §5.1),勿手改本文件。 # 契约与 zod schema、请求运行时校验、z.infer 类型三者同源,永不失同步。 openapi: 3.0.3 info: title: 备考中枢 API(公务员考试备考数据中枢) version: 1.0.0 description: > 个人单用户公务员备考工具 REST API。数据底层为本地 JSON 文件,但对外保持 REST semantic(资源化 + HTTP 动词 + 版本号)。统一响应 ApiResponse,code=0 成功, code!=0 失败。全 TypeScript,无 any。本契约由 zod-openapi 从 zod schema 自动生成, 前端据此生成 TS 类型(orval / @hey-api/openapi-ts)与 MSW Mock。 servers: - url: http://localhost:3000 description: 本地开发 tags: - name: questions description: 题库(真题录入 / 管理 / 导入 / 导出) - name: practice description: 刷题(抽题 / 提交作答判定) - name: wrong-questions description: 错题本(自动入本 / 错因标签 / 恢复) - name: mock-exams description: 模考记录与分模块分析 - name: shenlun description: 申论写作 / 自评 - name: tasks description: 备考计划任务 / 打卡 - name: stats description: 数据中枢统计聚合 - name: settings description: 偏好设置(含 AI 开关) - name: data description: 数据资产(导出 / 备份 / 还原) paths: # ============ 题库 Questions ============ /api/v1/questions: get: tags: [questions] summary: 题库列表(支持分页 / 按模块 / 关键字筛选) parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Limit' - name: module in: query schema: $ref: '#/components/schemas/ModuleKey' - name: subject in: query schema: $ref: '#/components/schemas/SubjectKey' - name: q in: query schema: type: string - name: sort in: query schema: type: string enum: [created_at, difficulty] description: 排序字段 - name: order in: query schema: type: string enum: [asc, desc] responses: '200': description: 分页列表 content: application/json: schema: $ref: '#/components/schemas/QuestionListResponse' '400': $ref: '#/components/responses/BadRequest' post: tags: [questions] summary: 录入题(单题) requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/QuestionCreate' responses: '201': description: 创建成功 content: application/json: schema: $ref: '#/components/schemas/QuestionResponse' '400': $ref: '#/components/responses/BadRequest' '422': $ref: '#/components/responses/ValidationError' /api/v1/questions/import: post: tags: [questions] summary: 批量导入真题(数组) requestBody: required: true content: application/json: schema: type: array items: $ref: '#/components/schemas/QuestionCreate' responses: '201': description: 导入结果 content: application/json: schema: $ref: '#/components/schemas/ImportResponse' '422': $ref: '#/components/responses/ValidationError' /api/v1/questions/export: get: tags: [questions] summary: 导出题库(JSON 全量) responses: '200': description: 导出的题库 JSON content: application/json: schema: type: array items: $ref: '#/components/schemas/Question' /api/v1/questions/{id}: get: tags: [questions] summary: 题目详情 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 题目详情 content: application/json: schema: $ref: '#/components/schemas/QuestionResponse' '404': $ref: '#/components/responses/NotFound' patch: tags: [questions] summary: 更新题目 parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/QuestionUpdate' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/QuestionResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' delete: tags: [questions] summary: 删除题目 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 删除成功 content: application/json: schema: $ref: '#/components/schemas/DeleteResponse' '404': $ref: '#/components/responses/NotFound' # ============ 刷题 Practice ============ /api/v1/practice/draw: post: tags: [practice] summary: 抽题(按模块 / 错题优先 / 随机) requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DrawRequest' responses: '200': description: 抽题结果(题目列表) content: application/json: schema: $ref: '#/components/schemas/DrawResponse' '400': $ref: '#/components/responses/BadRequest' /api/v1/practice/submit: post: tags: [practice] summary: 提交作答并判定(答错自动进错题本) requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SubmitAnswerRequest' responses: '201': description: 判定结果(含是否入错题本) content: application/json: schema: $ref: '#/components/schemas/SubmitAnswerResponse' '400': $ref: '#/components/responses/BadRequest' '422': $ref: '#/components/responses/ValidationError' # ============ 错题本 WrongQuestions ============ /api/v1/wrong-questions: get: tags: [wrong-questions] summary: 错题本列表(按模块 / 状态 / 错因筛选) parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Limit' - name: module in: query schema: $ref: '#/components/schemas/ModuleKey' - name: status in: query schema: $ref: '#/components/schemas/WrongQuestionStatus' - name: reason in: query schema: $ref: '#/components/schemas/WrongReason' responses: '200': description: 错题本分页 content: application/json: schema: $ref: '#/components/schemas/WrongQuestionListResponse' /api/v1/wrong-questions/{id}: get: tags: [wrong-questions] summary: 错题详情 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 错题详情 content: application/json: schema: $ref: '#/components/schemas/WrongQuestionResponse' '404': $ref: '#/components/responses/NotFound' patch: tags: [wrong-questions] summary: 更新错题(打错因标签 / 批注 / 状态) parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WrongQuestionUpdate' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/WrongQuestionResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /api/v1/wrong-questions/{id}/resolve: post: tags: [wrong-questions] summary: 标记错题已掌握(移除待复习清单) parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 更新状态为 mastered content: application/json: schema: $ref: '#/components/schemas/WrongQuestionResponse' '404': $ref: '#/components/responses/NotFound' # ============ 模考 MockExams ============ /api/v1/mock-exams: get: tags: [mock-exams] summary: 模考列表 parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Limit' - name: examType in: query schema: $ref: '#/components/schemas/ExamType' responses: '200': description: 模考分页 content: application/json: schema: $ref: '#/components/schemas/MockExamListResponse' post: tags: [mock-exams] summary: 录入模考成绩 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MockExamCreate' responses: '201': description: 创建成功 content: application/json: schema: $ref: '#/components/schemas/MockExamResponse' '422': $ref: '#/components/responses/ValidationError' /api/v1/mock-exams/{id}: get: tags: [mock-exams] summary: 模考详情(含分模块分析) parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 模考详情 content: application/json: schema: $ref: '#/components/schemas/MockExamResponse' '404': $ref: '#/components/responses/NotFound' patch: tags: [mock-exams] summary: 更新模考记录 parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MockExamUpdate' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/MockExamResponse' '404': $ref: '#/components/responses/NotFound' delete: tags: [mock-exams] summary: 删除模考记录 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 删除成功 content: application/json: schema: $ref: '#/components/schemas/DeleteResponse' '404': $ref: '#/components/responses/NotFound' # ============ 申论 ShenLun ============ /api/v1/shenlun: get: tags: [shenlun] summary: 申论列表 parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Limit' - name: module in: query schema: $ref: '#/components/schemas/ShenLunModuleKey' responses: '200': description: 申论分页 content: application/json: schema: $ref: '#/components/schemas/ShenLunListResponse' post: tags: [shenlun] summary: 新建申论写作 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ShenLunCreate' responses: '201': description: 创建成功 content: application/json: schema: $ref: '#/components/schemas/ShenLunResponse' '422': $ref: '#/components/responses/ValidationError' /api/v1/shenlun/{id}: patch: tags: [shenlun] summary: 更新申论(自评 / 正文) parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ShenLunUpdate' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/ShenLunResponse' '404': $ref: '#/components/responses/NotFound' delete: tags: [shenlun] summary: 删除申论 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 删除成功 content: application/json: schema: $ref: '#/components/schemas/DeleteResponse' '404': $ref: '#/components/responses/NotFound' # ============ 备考计划 Tasks ============ /api/v1/tasks: get: tags: [tasks] summary: 任务列表 parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Limit' - name: status in: query schema: $ref: '#/components/schemas/TaskStatus' - name: planDate in: query schema: type: string format: date responses: '200': description: 任务分页 content: application/json: schema: $ref: '#/components/schemas/TaskListResponse' post: tags: [tasks] summary: 创建任务 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TaskCreate' responses: '201': description: 创建成功 content: application/json: schema: $ref: '#/components/schemas/TaskResponse' '422': $ref: '#/components/responses/ValidationError' /api/v1/tasks/{id}: patch: tags: [tasks] summary: 更新任务(状态 / 完成量 / 打卡) parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TaskUpdate' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/TaskResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' delete: tags: [tasks] summary: 删除任务 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: 删除成功 content: application/json: schema: $ref: '#/components/schemas/DeleteResponse' '404': $ref: '#/components/responses/NotFound' # ============ 统计 Stats ============ /api/v1/stats/overview: get: tags: [stats] summary: 数据中枢总览(总答题数 / 正确率 / 错因分布 / 连续打卡) responses: '200': description: 统计总览 content: application/json: schema: $ref: '#/components/schemas/StatsOverviewResponse' /api/v1/stats/daily: get: tags: [stats] summary: 日粒度趋势(近 N 天) parameters: - name: days in: query schema: type: integer minimum: 1 maximum: 365 default: 30 responses: '200': description: 日趋势 content: application/json: schema: $ref: '#/components/schemas/StatsDailyResponse' /api/v1/stats/module: get: tags: [stats] summary: 分模块表现(考点×错因归因输入) responses: '200': description: 模块统计 content: application/json: schema: $ref: '#/components/schemas/StatsModuleResponse' /api/v1/stats/monthly-report: get: tags: [stats] summary: 月报复盘 parameters: - name: month in: query schema: type: string format: yyyy-MM responses: '200': description: 月报 content: application/json: schema: $ref: '#/components/schemas/StatsMonthlyResponse' # ============ 设置 Settings ============ /api/v1/settings: get: tags: [settings] summary: 获取偏好设置 responses: '200': description: 设置 content: application/json: schema: $ref: '#/components/schemas/SettingsResponse' patch: tags: [settings] summary: 更新偏好设置 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SettingsUpdate' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/SettingsResponse' '422': $ref: '#/components/responses/ValidationError' # ============ 数据资产 Data ============ /api/v1/data/export: post: tags: [data] summary: 导出全部数据为备份 JSON responses: '200': description: 备份 JSON(含 schema 版本) content: application/json: schema: $ref: '#/components/schemas/BackupPayload' get: tags: [data] summary: 导出指定实体为 CSV parameters: - name: entity in: query required: true schema: type: string enum: [questions, wrong-questions, mock-exams, shenlun, tasks] responses: '200': description: CSV 文本 content: text/csv: schema: type: string /api/v1/data/backup: post: tags: [data] summary: 创建备份副本 responses: '200': description: 备份结果 content: application/json: schema: $ref: '#/components/schemas/BackupResult' /api/v1/data/restore: post: tags: [data] summary: 从备份还原 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BackupPayload' responses: '200': description: 还原成功 content: application/json: schema: $ref: '#/components/schemas/RestoreResult' '400': $ref: '#/components/responses/BadRequest' '422': $ref: '#/components/responses/ValidationError' components: parameters: Id: name: id in: path required: true description: 实体 id(UUID) schema: type: string format: uuid Page: name: page in: query schema: type: integer minimum: 1 default: 1 Limit: name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 20 schemas: # ---------- 枚举 ---------- SubjectKey: type: string enum: [xingce, shenlun] ModuleKey: type: string enum: - xingce-shuli - xingce-panduan - xingce-yanyu - xingce-changshi - xingce-ziliao - shenlun-zhuizong - shenlun-zonghe - shenlun-shenlun ShenLunModuleKey: type: string enum: [shenlun-zhuizong, shenlun-zonghe, shenlun-shenlun] QuestionType: type: string enum: [single, multiple, judge, blank, essay] Correctness: type: string enum: [correct, wrong, partial, blank] WrongReason: type: string enum: - knowledge-gap - concept-confusion - careless - time-pressure - method-unfamiliar - calculation-error - reading-error WrongQuestionStatus: type: string enum: [open, resolved, mastered] ExamType: type: string enum: [national, province, self] TaskStatus: type: string enum: [todo, doing, done, skipped] TaskRecurrence: type: string enum: [once, daily, weekly] DayOfWeek: type: string enum: [mon, tue, wed, thu, fri, sat, sun] # ---------- Question ---------- Question: type: object required: [id, subject, module, type, stem, answer, tags, createdAt, updatedAt] properties: id: type: string format: uuid subject: $ref: '#/components/schemas/SubjectKey' module: $ref: '#/components/schemas/ModuleKey' type: $ref: '#/components/schemas/QuestionType' stem: type: string minLength: 1 options: type: array items: type: string answer: oneOf: - type: string - type: array items: type: string analysis: type: string source: type: string difficulty: type: integer minimum: 1 maximum: 5 tags: type: array items: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time aiExplanation: type: string aiConfidence: type: number minimum: 0 maximum: 1 QuestionCreate: allOf: - $ref: '#/components/schemas/Question' - type: object properties: id: type: string format: uuid description: 可省略,服务端生成 QuestionUpdate: type: object minProperties: 1 properties: stem: type: string minLength: 1 options: type: array items: type: string answer: oneOf: - type: string - type: array items: type: string analysis: type: string source: type: string difficulty: type: integer minimum: 1 maximum: 5 tags: type: array items: type: string module: $ref: '#/components/schemas/ModuleKey' QuestionResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/Question' QuestionListResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/PageData-Question' # ---------- Practice ---------- DrawRequest: type: object properties: subject: $ref: '#/components/schemas/SubjectKey' module: $ref: '#/components/schemas/ModuleKey' count: type: integer minimum: 1 maximum: 50 default: 10 strategy: type: string enum: [random, module, wrong-first] default: module DrawResponse: type: object properties: questions: type: array items: $ref: '#/components/schemas/Question' SubmitAnswerRequest: type: object required: [questionId, userAnswer, source] properties: questionId: type: string format: uuid userAnswer: oneOf: - type: string - type: array items: type: string source: type: string enum: [practice, mock-exam] tookMs: type: integer minimum: 0 wrongReasons: type: array items: $ref: '#/components/schemas/WrongReason' SubmitAnswerResponse: type: object properties: questionId: type: string format: uuid correctness: $ref: '#/components/schemas/Correctness' isWrong: type: boolean correctAnswer: oneOf: - type: string - type: array items: type: string enteredWrongBook: type: boolean wrongQuestionId: type: string format: uuid nullable: true # ---------- AnswerRecord(作答记录,驱动统计)---------- AnswerRecord: type: object required: [id, questionId, module, correctness, isWrong, userAnswer, source, practiceDate, createdAt, updatedAt] properties: id: type: string format: uuid questionId: type: string format: uuid module: $ref: '#/components/schemas/ModuleKey' correctness: $ref: '#/components/schemas/Correctness' isWrong: type: boolean userAnswer: oneOf: - type: string - type: array items: type: string wrongReasons: type: array items: $ref: '#/components/schemas/WrongReason' tookMs: type: integer minimum: 0 source: type: string enum: [practice, mock-exam] practiceDate: type: string format: date-time createdAt: type: string format: date-time updatedAt: type: string format: date-time # ---------- WrongQuestion ---------- WrongQuestion: type: object required: [id, questionId, module, wrongReasons, wrongCount, lastWrongAt, reviewCount, status, createdAt, updatedAt] properties: id: type: string format: uuid questionId: type: string format: uuid module: $ref: '#/components/schemas/ModuleKey' wrongReasons: type: array items: $ref: '#/components/schemas/WrongReason' wrongCount: type: integer minimum: 1 lastWrongAt: type: string format: date-time reviewCount: type: integer minimum: 0 status: $ref: '#/components/schemas/WrongQuestionStatus' note: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time WrongQuestionUpdate: type: object minProperties: 1 properties: wrongReasons: type: array items: $ref: '#/components/schemas/WrongReason' note: type: string status: $ref: '#/components/schemas/WrongQuestionStatus' reviewCount: type: integer minimum: 0 WrongQuestionResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/WrongQuestion' WrongQuestionListResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/PageData-WrongQuestion' # ---------- MockExam ---------- MockExam: type: object required: [id, title, examType, fullScore, score, durationMin, examDate, createdAt, updatedAt] properties: id: type: string format: uuid title: type: string examType: $ref: '#/components/schemas/ExamType' fullScore: type: number score: type: number rank: type: integer durationMin: type: integer minimum: 0 moduleScores: type: object additionalProperties: type: number moduleCorrectRate: type: object additionalProperties: type: number examDate: type: string format: date note: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time MockExamCreate: type: object required: [title, examType, fullScore, score, durationMin, examDate] properties: title: type: string examType: $ref: '#/components/schemas/ExamType' fullScore: type: number score: type: number rank: type: integer durationMin: type: integer minimum: 0 moduleScores: type: object additionalProperties: type: number moduleCorrectRate: type: object additionalProperties: type: number examDate: type: string format: date note: type: string MockExamUpdate: type: object minProperties: 1 properties: title: type: string score: type: number rank: type: integer durationMin: type: integer minimum: 0 moduleScores: type: object additionalProperties: type: number moduleCorrectRate: type: object additionalProperties: type: number note: type: string MockExamResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/MockExam' MockExamListResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/PageData-MockExam' # ---------- ShenLun ---------- ShenLunEssay: type: object required: [id, topic, module, content, wordCount, createdAt, updatedAt] properties: id: type: string format: uuid examId: type: string format: uuid topic: type: string module: $ref: '#/components/schemas/ShenLunModuleKey' content: type: string wordCount: type: integer minimum: 0 durationMin: type: integer minimum: 0 selfRating: type: integer minimum: 1 maximum: 5 aiFeedback: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time ShenLunCreate: type: object required: [topic, module, content] properties: examId: type: string format: uuid topic: type: string module: $ref: '#/components/schemas/ShenLunModuleKey' content: type: string durationMin: type: integer minimum: 0 selfRating: type: integer minimum: 1 maximum: 5 ShenLunUpdate: type: object minProperties: 1 properties: content: type: string durationMin: type: integer minimum: 0 selfRating: type: integer minimum: 1 maximum: 5 ShenLunResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/ShenLunEssay' ShenLunListResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/PageData-ShenLunEssay' # ---------- Task ---------- StudyTask: type: object required: [id, title, planDate, status, recurrence, createdAt, updatedAt] properties: id: type: string format: uuid title: type: string module: $ref: '#/components/schemas/ModuleKey' planDate: type: string format: date status: $ref: '#/components/schemas/TaskStatus' recurrence: $ref: '#/components/schemas/TaskRecurrence' targetCount: type: integer minimum: 0 completedCount: type: integer minimum: 0 note: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time TaskCreate: type: object required: [title, planDate, recurrence] properties: title: type: string module: $ref: '#/components/schemas/ModuleKey' planDate: type: string format: date recurrence: $ref: '#/components/schemas/TaskRecurrence' targetCount: type: integer minimum: 0 note: type: string TaskUpdate: type: object minProperties: 1 properties: title: type: string status: $ref: '#/components/schemas/TaskStatus' targetCount: type: integer minimum: 0 completedCount: type: integer minimum: 0 note: type: string TaskResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/StudyTask' TaskListResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/PageData-StudyTask' # ---------- Stats ---------- ModuleStat: type: object required: [module, total, correct, correctRate, wrong] properties: module: $ref: '#/components/schemas/ModuleKey' total: type: integer correct: type: integer correctRate: type: number wrong: type: integer StatsOverview: type: object properties: totalAnswered: type: integer totalCorrect: type: integer totalWrong: type: integer overallCorrectRate: type: number practiceStreak: type: integer moduleStats: type: array items: $ref: '#/components/schemas/ModuleStat' wrongByReason: type: object additionalProperties: type: integer StatsDailyPoint: type: object properties: date: type: string format: date answered: type: integer correctRate: type: number StatsModuleGroup: type: object properties: module: $ref: '#/components/schemas/ModuleKey' total: type: integer correct: type: integer wrong: type: integer correctRate: type: number wrongByReason: type: object additionalProperties: type: integer StatsMonthly: type: object properties: month: type: string format: yyyy-MM totalAnswered: type: integer totalCorrect: type: integer totalWrong: type: integer overallCorrectRate: type: number moduleStats: type: array items: $ref: '#/components/schemas/ModuleStat' wrongByReason: type: object additionalProperties: type: integer streakSummary: type: object properties: maxStreak: type: integer currentStreak: type: integer StatsOverviewResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/StatsOverview' StatsDailyResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: type: array items: $ref: '#/components/schemas/StatsDailyPoint' StatsModuleResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: type: array items: $ref: '#/components/schemas/StatsModuleGroup' StatsMonthlyResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/StatsMonthly' # ---------- StatsSnapshot(统计聚合快照,持久化实体)---------- StatsSnapshot: type: object required: [id, date, totalAnswered, totalCorrect, totalWrong, overallCorrectRate, moduleStats, wrongByReason, createdAt, updatedAt] properties: id: type: string format: uuid date: type: string format: date totalAnswered: type: integer totalCorrect: type: integer totalWrong: type: integer overallCorrectRate: type: number practiceStreak: type: integer moduleStats: type: array items: $ref: '#/components/schemas/ModuleStat' wrongByReason: type: object additionalProperties: type: integer createdAt: type: string format: date-time updatedAt: type: string format: date-time # ---------- Settings ---------- AppSettings: type: object required: [id, uiTheme, aiEnabled, createdAt, updatedAt] properties: id: type: string enum: ['app'] targetScore: type: number dailyQuestionTarget: type: integer dailyStudyMinutes: type: integer uiTheme: type: string enum: [light, dark] primaryColor: type: string aiEnabled: type: boolean createdAt: type: string format: date-time updatedAt: type: string format: date-time SettingsUpdate: type: object minProperties: 1 properties: targetScore: type: number dailyQuestionTarget: type: integer dailyStudyMinutes: type: integer uiTheme: type: string enum: [light, dark] aiEnabled: type: boolean SettingsResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: $ref: '#/components/schemas/AppSettings' # ---------- Data 备份 ---------- BackupPayload: type: object required: [schemaVersion, exportedAt] properties: schemaVersion: type: integer exportedAt: type: string format: date-time questions: type: array items: $ref: '#/components/schemas/Question' answerRecords: type: array items: $ref: '#/components/schemas/AnswerRecord' wrongQuestions: type: array items: $ref: '#/components/schemas/WrongQuestion' mockExams: type: array items: $ref: '#/components/schemas/MockExam' shenlun: type: array items: $ref: '#/components/schemas/ShenLunEssay' tasks: type: array items: $ref: '#/components/schemas/StudyTask' stats: type: array items: $ref: '#/components/schemas/StatsSnapshot' settings: $ref: '#/components/schemas/AppSettings' BackupResult: type: object properties: backupId: type: string createdAt: type: string format: date-time filePath: type: string RestoreResult: type: object properties: restoredAt: type: string format: date-time entities: type: array items: type: string preBackupId: type: string description: 还原前自动生成的一次备份 id # ---------- 通用 ---------- DeleteResponse: allOf: - $ref: '#/components/schemas/ApiSuccess' - type: object properties: data: type: object properties: deleted: type: boolean id: type: string format: uuid ImportResponse: type: object properties: imported: type: integer skips: type: array items: type: object properties: index: type: integer reason: type: string ApiSuccess: type: object required: [code, data] properties: code: type: integer enum: [0] data: description: 业务数据,由各响应的 data 属性具体定义 message: type: string ApiFailure: type: object required: [code, data, message] properties: code: type: integer description: 非 0 错误码 data: type: object nullable: true message: type: string PageData-Question: type: object required: [items, total, page, limit, hasMore] properties: items: type: array items: $ref: '#/components/schemas/Question' total: type: integer page: type: integer limit: type: integer hasMore: type: boolean PageData-WrongQuestion: type: object required: [items, total, page, limit, hasMore] properties: items: type: array items: $ref: '#/components/schemas/WrongQuestion' total: type: integer page: type: integer limit: type: integer hasMore: type: boolean PageData-MockExam: type: object required: [items, total, page, limit, hasMore] properties: items: type: array items: $ref: '#/components/schemas/MockExam' total: type: integer page: type: integer limit: type: integer hasMore: type: boolean PageData-ShenLunEssay: type: object required: [items, total, page, limit, hasMore] properties: items: type: array items: $ref: '#/components/schemas/ShenLunEssay' total: type: integer page: type: integer limit: type: integer hasMore: type: boolean PageData-StudyTask: type: object required: [items, total, page, limit, hasMore] properties: items: type: array items: $ref: '#/components/schemas/StudyTask' total: type: integer page: type: integer limit: type: integer hasMore: type: boolean responses: BadRequest: description: 请求参数错误 content: application/json: schema: $ref: '#/components/schemas/ApiFailure' ValidationError: description: 校验失败(422 不可处理) content: application/json: schema: $ref: '#/components/schemas/ApiFailure' NotFound: description: 资源不存在 content: application/json: schema: $ref: '#/components/schemas/ApiFailure'