openapi: 3.0.3 info: title: Kids AI Posture Platform API version: 1.0.0 description: | 儿童AI体态管理与运动康复平台 V1.0 API 契约。 所有报告用于健康管理建议,不构成医疗诊断。接口返回统一包含 requestId 以便追踪。 servers: - url: https://api.example.com description: production - url: https://staging-api.example.com description: staging security: - bearerAuth: [] x-error-model: &error-model code: integer message: string details: type: object additionalProperties: true requestId: string tags: - name: Auth - name: Children - name: Videos - name: Analysis - name: Reports - name: Training - name: Admin paths: /api/auth/login: post: tags: [Auth] summary: 手机号或微信授权登录 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' examples: phone: summary: 手机验证码 value: loginType: phone_code credential: "186xxxx0000" code: "682139" wechat: summary: 微信授权码 value: loginType: wechat credential: "wx_auth_code" responses: '200': description: 登录成功 content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '401': description: 登录失败 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/children: get: tags: [Children] summary: 获取当前用户可访问的儿童档案 parameters: - name: page in: query schema: { type: integer, minimum: 1, default: 1 } - name: pageSize in: query schema: { type: integer, minimum: 1, maximum: 100, default: 20 } responses: '200': description: 儿童档案分页列表 content: application/json: schema: $ref: '#/components/schemas/ChildrenPagedResponse' post: tags: [Children] summary: 创建儿童档案 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChildCreateRequest' responses: '201': description: 创建成功 headers: Idempotency-Key: description: 冪等键回显,用于重试去重核对 schema: { type: string } content: application/json: schema: $ref: '#/components/schemas/ChildResponse' '400': description: 请求参数错误 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/children/{childId}: parameters: - $ref: '#/components/parameters/ChildId' get: tags: [Children] summary: 获取儿童档案详情 responses: '200': description: 档案详情 content: application/json: schema: $ref: '#/components/schemas/ChildResponse' '404': description: 资源不存在 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' patch: tags: [Children] summary: 更新儿童档案 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChildUpdateRequest' responses: '200': description: 更新成功 content: application/json: schema: $ref: '#/components/schemas/ChildResponse' '409': description: 更新冲突(数据并发) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' delete: tags: [Children] summary: 注销/归档儿童档案 responses: '204': description: 归档成功 /api/videos/upload-token: post: tags: [Videos] summary: 获取视频直传凭证 description: 建议请求头携带 Idempotency-Key,避免重试产生重复录像对象。 parameters: - name: Idempotency-Key in: header required: false schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UploadTokenRequest' responses: '200': description: 上传凭证 content: application/json: schema: $ref: '#/components/schemas/UploadTokenResponse' '429': description: 请求过于频繁 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/videos: post: tags: [Videos] summary: 登记已上传视频 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VideoCreateRequest' responses: '201': description: 视频登记成功 content: application/json: schema: $ref: '#/components/schemas/VideoResponse' '409': description: 去重/重复提交 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/analysis/tasks: post: tags: [Analysis] summary: 创建AI分析任务 description: 同一 childId+videoId+taskType 可复用幂等键创建。 parameters: - name: Idempotency-Key in: header required: false schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AnalysisTaskCreateRequest' responses: '201': description: 任务创建成功 content: application/json: schema: $ref: '#/components/schemas/AnalysisTaskResponse' '409': description: 重复任务,返回已有任务 content: application/json: schema: $ref: '#/components/schemas/AnalysisTaskResponse' /api/analysis/tasks/{taskId}: get: tags: [Analysis] summary: 查询分析任务状态 parameters: - $ref: '#/components/parameters/TaskId' responses: '200': description: 任务状态 content: application/json: schema: $ref: '#/components/schemas/AnalysisTaskResponse' '404': description: 任务不存在 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/analysis/tasks/{taskId}/cancel: post: tags: [Analysis] summary: 取消分析任务 parameters: - $ref: '#/components/parameters/TaskId' responses: '200': description: 取消成功 content: application/json: schema: $ref: '#/components/schemas/StandardResponse' /api/analysis/webhook: post: tags: [Analysis] summary: 分析结果回调(异步) description: 系统内部回调给后端事件网关,需使用 HMAC 签名。 security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AnalysisTaskWebhook' responses: '204': description: 接收成功 /api/reports/{reportId}: get: tags: [Reports] summary: 获取体态分析报告 parameters: - $ref: '#/components/parameters/ReportId' responses: '200': description: 报告详情 content: application/json: schema: $ref: '#/components/schemas/ReportResponse' /api/training/plans: post: tags: [Training] summary: 创建训练计划 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TrainingPlanCreateRequest' responses: '201': description: 训练计划创建成功 content: application/json: schema: $ref: '#/components/schemas/TrainingPlanResponse' /api/training/plans/{planId}: parameters: - name: planId in: path required: true schema: type: integer get: tags: [Training] summary: 获取训练计划 responses: '200': description: 训练计划详情 content: application/json: schema: $ref: '#/components/schemas/TrainingPlanResponse' /api/training/plans/{planId}/records: post: tags: [Training] summary: 提交训练打卡记录 parameters: - name: planId in: path required: true schema: { type: integer } requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TrainingRecordCreateRequest' responses: '201': description: 打卡成功 content: application/json: schema: $ref: '#/components/schemas/TrainingRecordResponse' /api/admin/dashboard: get: tags: [Admin] summary: 获取机构运营看板 responses: '200': description: 看板指标 content: application/json: schema: $ref: '#/components/schemas/AdminDashboardResponse' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: ChildId: name: childId in: path required: true schema: { type: integer } TaskId: name: taskId in: path required: true schema: { type: integer } ReportId: name: reportId in: path required: true schema: { type: integer } schemas: ApiMeta: type: object properties: requestId: { type: string } timestamp: { type: string, format: date-time } traceId: { type: string } StandardResponse: type: object properties: code: { type: integer } message: { type: string } data: { type: object, nullable: true } meta: { $ref: '#/components/schemas/ApiMeta' } ErrorResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: code: { type: integer, example: 10003 } message: { type: string, example: parameter_validation_failed } data: type: object properties: errorCode: { type: string } path: { type: string } PagedMeta: type: object properties: page: { type: integer } pageSize: { type: integer } total: { type: integer } hasMore: { type: boolean } LoginRequest: type: object required: [loginType, credential] properties: loginType: { type: string, enum: [phone_code, wechat] } credential: { type: string } code: { type: string } LoginResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: token: { type: string } user: { $ref: '#/components/schemas/User' } User: type: object properties: id: { type: integer } phoneMasked: { type: string } role: { type: string, enum: [parent, coach, org_admin, platform_admin] } ChildCreateRequest: type: object required: [name, birthday] properties: name: { type: string } birthday: { type: string, format: date } gender: { type: string, enum: [male, female, unknown] } height: { type: number } weight: { type: number } contraindications: { type: string } ChildUpdateRequest: allOf: - $ref: '#/components/schemas/ChildCreateRequest' Child: type: object properties: id: { type: integer } name: { type: string } birthday: { type: string, format: date } age: { type: integer } height: { type: number } weight: { type: number } status: { type: string } ChildResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: { $ref: '#/components/schemas/Child' } ChildrenPagedResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: list: type: array items: { $ref: '#/components/schemas/Child' } page: { $ref: '#/components/schemas/PagedMeta' } UploadTokenRequest: type: object required: [childId, fileName, contentType, size] properties: childId: { type: integer } fileName: { type: string } contentType: { type: string, example: video/mp4 } size: { type: integer } UploadTokenResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: uploadUrl: { type: string } objectKey: { type: string } expireAt: { type: string, format: date-time } VideoCreateRequest: type: object required: [childId, objectKey, scene] properties: childId: { type: integer } objectKey: { type: string } scene: type: string enum: [front_posture, side_posture, squat, balance, gait] captureHint: { type: string } VideoResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: id: { type: integer } status: { type: string, enum: [uploaded, rejected, archived] } AnalysisTaskCreateRequest: type: object required: [childId, videoId, taskType] properties: childId: { type: integer } videoId: { type: integer } taskType: { type: string, enum: [posture_screening, movement_scoring, reassessment] } AnalysisTaskResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: id: { type: integer } status: type: string enum: [CREATED, QUEUED, PROCESSING, SUCCEEDED, FAILED, CANCELLED] progress: { type: integer, minimum: 0, maximum: 100 } errorCode: { type: string } reportId: { type: integer } retryCount: { type: integer } AnalysisTaskWebhook: type: object required: [taskId, status] properties: taskId: { type: integer } status: type: string enum: [SUCCEEDED, FAILED, CANCELLED] progress: { type: integer } errorCode: { type: string } result: { type: object } signature: { type: string } ReportResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: id: { type: integer } childId: { type: integer } taskId: { type: integer } riskLevel: { type: string, enum: [low, medium, high, review_required] } summary: { type: string } metrics: type: array items: type: object properties: name: { type: string } value: { type: number } level: { type: string } confidence: { type: number } recommendations: type: array items: { type: string } disclaimer: { type: string } reviewedBy: { type: integer, nullable: true } TrainingPlanCreateRequest: type: object required: [childId, goal, cycleDays] properties: childId: { type: integer } reportId: { type: integer } goal: { type: string } cycleDays: { type: integer, minimum: 7 } exerciseIds: type: array items: { type: integer } constraints: type: object properties: maxDailyMinutes: { type: integer } coachNotes: { type: string } TrainingPlanResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: id: { type: integer } status: { type: string, enum: [draft, active, completed, paused] } detail: { type: object } startedAt: { type: string, format: date-time } endedAt: { type: string, format: date-time, nullable: true } TrainingRecordCreateRequest: type: object required: [exerciseId, completed] properties: exerciseId: { type: integer } completed: { type: boolean } score: { type: integer, minimum: 0, maximum: 100 } durationSeconds: { type: integer } note: { type: string } media: type: array items: type: string TrainingRecordResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: id: { type: integer } planId: { type: integer } createdAt: { type: string, format: date-time } AdminDashboardResponse: allOf: - $ref: '#/components/schemas/StandardResponse' - type: object properties: data: type: object properties: newChildren: { type: integer } uploadedVideos: { type: integer } completedReports: { type: integer } activePlans: { type: integer } conversionRate: { type: number } reassessmentCompletionRate: { type: number }