| @ -0,0 +1,413 @@ | |||
| # 前端开发对接文档 | |||
| ## 1. 简介 | |||
| 本文档旨在为前端开发人员提供与后端API交互所需的所有信息。 | |||
| - **技术栈**: 前端 (Vue) + 后端 (FastAPI) | |||
| - **API基础路径**: 所有API路由都以 `/api/v3` 为前缀 (具体URL需根据部署情况配置)。 | |||
| - **认证**: 系统采用JWT (JSON Web Token) 进行认证。 | |||
| --- | |||
| ## 2. 核心流程 | |||
| ### 2.1 认证流程 | |||
| 1. **注册/登录**: | |||
| - 新用户通过 `POST /api/v3/register` 接口进行注册。 | |||
| - 已注册用户通过 `POST /api/v3/login` 接口进行登录。 | |||
| 2. **获取Token**: 注册或登录成功后,后端会返回一个 `access_token` 和 `user_info` 对象。 | |||
| 3. **存储Token**: 前端需要安全地存储 `access_token` (例如,使用 `localStorage`、`sessionStorage` 或 `cookie`)。 | |||
| 4. **发送认证请求**: 对于需要认证的接口,前端必须在HTTP请求头中携带 `access_token`。 | |||
| ``` | |||
| Authorization: Bearer {your_access_token} | |||
| ``` | |||
| ### 2.2 关键数据对象:`user_info` | |||
| 注册或登录成功后,前端会收到一个 `user_info` 对象,建议将其保存在全局状态管理器中 (如 Pinia/Vuex),以便在各个页面和组件中使用。 | |||
| ```json | |||
| "user_info": { | |||
| "user_id": "user-abc-123", // 用户的唯一、固定ID,安全 | |||
| "medical_record_number": "J0012345", // 就诊号,用于登录 | |||
| "name": "张三", | |||
| "gender": "男", | |||
| "dominant_hand": "右", | |||
| "diagnosis": "无", | |||
| "dob": "1960-01-15", | |||
| "height_cm": 175, | |||
| "weight_kg": 70, | |||
| "contact": "13800138000", | |||
| "inpatient_number": "Z67890", | |||
| "bed_number": "503", | |||
| "education_level": "本科", | |||
| "education_years": 16, | |||
| "remarks": "" | |||
| } | |||
| ``` | |||
| ### 2.3 错误处理 | |||
| API会返回标准化的错误响应,前端可以根据 `code` 和 `error_details.type` 来实现精细化的错误提示。 | |||
| ```json | |||
| // 示例:参数验证失败 | |||
| { | |||
| "status": "error", | |||
| "code": 400, | |||
| "message": "Invalid Parameters", | |||
| "error_details": { | |||
| "type": "validation_error", | |||
| "description": "参数验证失败", | |||
| "fields": { | |||
| "field_name": "错误描述" | |||
| } | |||
| } | |||
| } | |||
| ``` | |||
| --- | |||
| ## 3. API 接口参考 | |||
| ### 页面1: 用户注册 (扫码) | |||
| #### `POST /api/v3/register` | |||
| - **说明**: 创建一个新患者用户。 | |||
| - **认证**: 无需 | |||
| - **请求体**: | |||
| ```json | |||
| { | |||
| "medical_record_number": "string", // 就诊号, 将作为登录用户名 | |||
| "id_last_six": "string", // 身份证后六位, 将作为登录密码 | |||
| "name": "string", | |||
| "gender": "string", | |||
| "dominant_hand": "string", | |||
| "diagnosis": "string", | |||
| "dob": "string", // 格式: YYYY-MM-DD | |||
| "height_cm": "number", | |||
| "weight_kg": "number", | |||
| "contact": "string", | |||
| "inpatient_number": "string", // 可选 | |||
| "bed_number": "string", // 可选 | |||
| "education_level": "string", // 可选 | |||
| "education_years": "number", // 可选 | |||
| "remarks": "string" // 可选 | |||
| } | |||
| ``` | |||
| - **成功响应 (200 OK)**: 返回 `access_token` 和 `user_info`。 | |||
| ```json | |||
| { | |||
| "status": "success", | |||
| "code": 200, | |||
| "data": { | |||
| "access_token": "...", | |||
| "token_type": "Bearer", | |||
| "is_admin": false, | |||
| "user_info": { ... } | |||
| }, | |||
| "message": "注册成功并自动登录" | |||
| } | |||
| ``` | |||
| --- | |||
| ### 页面2: 登录 | |||
| #### `POST /api/v3/login` | |||
| - **说明**: 用户登录。 | |||
| - **认证**: 无需 | |||
| - **请求体**: | |||
| ```json | |||
| { | |||
| "username": "string", // medical_record_number 或管理员用户名 | |||
| "password": "string" // id_last_six 或管理员密码 | |||
| } | |||
| ``` | |||
| - **成功响应 (200 OK)**: | |||
| - **普通用户**: 返回 `access_token` 和 `user_info`。 | |||
| - **管理员**: 返回 `access_token` 和 `is_admin: true`。 | |||
| --- | |||
| ### 页面3: 基本信息确认 | |||
| - **说明**: 纯前端页面,用于展示从登录/注册接口获取的 `user_info`。用户确认后,前端负责路由跳转到下一页。 | |||
| --- | |||
| ### 页面4: 姿态识别 | |||
| #### `POST /api/v3/streams` (开始推流) | |||
| - **说明**: 启动姿态识别服务,并获取WebSocket连接地址。 | |||
| - **认证**: **需要** | |||
| - **成功响应 (200 OK)**: | |||
| ```json | |||
| { | |||
| "status": "success", | |||
| "code": 200, | |||
| "data": { | |||
| "stream_id": "stream_123456", | |||
| "ws_url": "ws://{host}/api/v3/ws/streams/{stream_id}?token={access_token}", | |||
| "message": "推流服务已启动,请建立WebSocket连接" | |||
| } | |||
| } | |||
| ``` | |||
| - **前端工作**: 前端获取 `ws_url` 后,立即建立WebSocket连接。 | |||
| #### `DELETE /api/v3/streams/{stream_id}` (停止推流) | |||
| - **说明**: 停止指定的姿态识别流。 | |||
| - **认证**: **需要** | |||
| #### WebSocket 通信 | |||
| - **地址**: 由 `POST /api/v3/streams` 接口返回。 | |||
| - **后端 -> 前端**: 后端会持续推送视频分析数据。 | |||
| ```json | |||
| { | |||
| "video_frame": "base64_encoded_image", // Base64编码的视频帧 | |||
| "timestamp": "...", | |||
| "stream_id": "...", | |||
| "analysis_data": { | |||
| "stage": "stand_up" // 当前阶段标识 | |||
| } | |||
| } | |||
| ``` | |||
| - **前端职责**: 接收到新的 `stage` 后,根据该标识显示对应的提示文本和播放语音。所有提示内容(文本、音频文件)预置在前端。 | |||
| --- | |||
| ### 页面5: 视频交互 | |||
| #### `GET /api/v3/videos` (获取视频及问卷列表) | |||
| - **说明**: 一次性获取所有视频及其关联的问题。 | |||
| - **认证**: **需要** | |||
| - **成功响应 (200 OK)**: | |||
| ```json | |||
| { | |||
| "status": "success", | |||
| "code": 200, | |||
| "data": { | |||
| "videos": [ | |||
| { | |||
| "video_id": "video_123456", | |||
| "title": "...", | |||
| "description": "...", | |||
| "questions": [ ... ] | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| ``` | |||
| #### `GET /api/v3/videos/{video_id}.mp4` (获取视频文件) | |||
| - **说明**: 用于HTML `<video>` 标签的 `src` 属性,播放视频。 | |||
| - **认证**: 无需 | |||
| #### 多模态实时交互 (WebSocket) | |||
| - **说明**: 此页面的眼动、面部表情、手写数据采集是并行的。前端主要通过WebSocket与 **手写服务** 交互。 | |||
| - **WebSocket URL**: 由 `POST /api/v3/video-sessions` 接口动态返回(**注意:此API尚未在文档中明确定义,需与后端确认**)。 | |||
| - **前端 -> 后端**: | |||
| - 发送手写数据: `{"type": "handwriting_stroke", "payload": ...}` | |||
| - 结束会话: `{"command": "end_session"}` | |||
| --- | |||
| ### 页面6: 语音交互 (异步流程) | |||
| 此页面交互是异步的,前端需要使用 **SSE (Server-Sent Events)** 来接收实时进度。 | |||
| #### 1. `POST /api/v3/audio-sessions` (开始会话) | |||
| - **说明**: 创建一个语音交互会话。 | |||
| - **认证**: **需要** | |||
| - **成功响应 (200 OK)**: 返回 `session_id`。 | |||
| #### 2. `POST /api/v3/audio-sessions/{session_id}/interact` (上传音频并发起处理) | |||
| - **说明**: 上传用户录音,这是一个异步接口,会立即返回。 | |||
| - **认证**: **需要** | |||
| - **请求**: `multipart/form-data`,包含名为 `audio` 的文件。 | |||
| - **成功响应 (202 Accepted)**: | |||
| ```json | |||
| { | |||
| "status": "accepted", | |||
| "code": 202, | |||
| "data": { | |||
| "request_id": "req_abc123", | |||
| "events_url": "/api/v3/audio-sessions/{session_id}/events/{request_id}", | |||
| "message": "请求已接收,请连接到events_url获取实时进度" | |||
| } | |||
| } | |||
| ``` | |||
| #### 3. `GET /api/v3/audio-sessions/{session_id}/events/{request_id}` (监听事件) | |||
| - **说明**: 前端使用上一步返回的 `events_url` 建立SSE连接。 | |||
| - **协议**: `text/event-stream` | |||
| - **服务端推送事件**: | |||
| - **语音识别(ASR)完成**: | |||
| ``` | |||
| event: asr_result | |||
| data: {"status": "success", "text": "用户说的话"} | |||
| ``` | |||
| - **AI回复完成**: | |||
| ``` | |||
| event: ai_result | |||
| data: {"status": "success", "text": "AI的回复文本"} | |||
| ``` | |||
| - **语音合成(TTS)完成**: | |||
| ``` | |||
| event: tts_result | |||
| data: {"status": "success", "audio_url": "/api/v3/audios/audio_xyz.wav"} | |||
| ``` | |||
| - **任务结束**: | |||
| ``` | |||
| event: done | |||
| data: {"status": "success", "message": "处理完成"} | |||
| ``` | |||
| - **错误**: | |||
| ``` | |||
| event: error | |||
| data: {"status": "error", "stage": "asr", "message": "ASR处理失败"} | |||
| ``` | |||
| #### 4. `GET /api/v3/audios/{audio_id}.wav` (获取音频文件) | |||
| - **说明**: 用于播放TTS合成的语音。 | |||
| - **认证**: 无需 | |||
| --- | |||
| ### 页面7: 历史测试记录查询 | |||
| #### `GET /api/v3/admin/logs` | |||
| - **说明**: 获取所有历史测试记录的批次列表。仅管理员可访问。 | |||
| - **认证**: **需要** | |||
| - **成功响应 (200 OK)**: | |||
| ```json | |||
| { | |||
| "status": "success", | |||
| "code": 200, | |||
| "data": { | |||
| "tables": [ | |||
| { | |||
| "table_name": "test_20240315_1", | |||
| "created_at": "2024-03-15T14:30:00Z", | |||
| "description": "2024年3月15日第一次测试" | |||
| }, | |||
| { | |||
| "table_name": "test_20240315_2", | |||
| "created_at": "2024-03-15T16:30:00Z", | |||
| "description": "2024年3月15日第二次测试" | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| ``` | |||
| --- | |||
| ### 页面8: 单次测试记录列表 | |||
| #### `GET /api/v3/admin/logs/{table_name}` | |||
| - **说明**: 查询指定批次下的所有测试者记录。 | |||
| - **认证**: **需要** | |||
| - **Query参数**: | |||
| - `page` (number, optional, default: 1): 页码 | |||
| - `page_size` (number, optional, default: 50): 每页数量 | |||
| - **成功响应 (200 OK)**: | |||
| ```json | |||
| { | |||
| "status": "success", | |||
| "code": 200, | |||
| "data": { | |||
| "total": 100, | |||
| "total_pages": 2, | |||
| "current_page": 1, | |||
| "page_size": 50, | |||
| "records": [ | |||
| { | |||
| "log_id": 1, | |||
| "medical_record_number": "J0012345", | |||
| "name": "张三", | |||
| "created_at": "2024-03-15T08:30:00Z", | |||
| "status": "completed" | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| ``` | |||
| - **`status` 字段说明**: | |||
| - `initialized`: 已初始化 | |||
| - `processing`: 数据处理中 | |||
| - `pending_review`: 待专家审核 | |||
| - `completed`: 已完成 | |||
| --- | |||
| ### 页面9: 分析模块详情与编辑 | |||
| 所有此部分的API都需要认证,并使用路径参数 `/api/v3/admin/logs/{table_name}/{log_id}/...` | |||
| #### 步态分析 (Gait) | |||
| - **`GET .../gait`**: 获取步态分析数据(包含事件、分段和计算指标)。 | |||
| - **`POST .../gait/events`**: 提交修正后的事件标记,后端将重新计算并返回更新后的数据。 | |||
| - **请求体**: | |||
| ```json | |||
| { | |||
| "events": [ | |||
| { "event": "stand_up", "frame": 85 }, | |||
| { "event": "walk_start", "frame": 180 }, | |||
| { "event": "walk_end", "frame": 840 } | |||
| ] | |||
| } | |||
| ``` | |||
| #### 面部表情分析 (Facial) | |||
| - **`GET .../facial`**: 获取面部表情视频URL及当前UPDRS评级。 | |||
| - **`POST .../facial/rating`**: 提交或更新UPDRS评级。 | |||
| - **请求体**: `{"rating": 2}` (评级范围: 0-4) | |||
| #### 眼动分析 (Eyetracking) | |||
| - **`GET .../eyetracking`**: 获取眼动分析视频URL及当前评级。 | |||
| - **`POST .../eyetracking/rating`**: 提交或更新评级。 | |||
| - **请求体**: `{"rating": 3}` (评级范围: 0-4) | |||
| #### 手写笔迹分析 (Handwriting) | |||
| - **`GET .../handwriting`**: 获取手写数据视频URL及当前评级。 | |||
| - **`POST .../handwriting/rating`**: 提交或更新评级。 | |||
| - **请求体**: `{"rating": 1}` (评级范围: 0-4) | |||
| #### 语音分析 (Speech) | |||
| - **`GET .../speech`**: 获取语音样本URL及当前评级。 | |||
| - **`POST .../speech/rating`**: 提交或更新评级。 | |||
| - **请求体**: `{"rating": 3}` (评级范围: 0-4) | |||
| #### 语义分析 (Semantics) | |||
| - **`GET .../semantics`**: 获取文本内容及当前评级。 | |||
| - **`POST .../semantics/rating`**: 提交或更新评级。 | |||
| - **请求体**: `{"rating": 1}` (评级范围: 0-4) | |||
| #### 原始数据下载 | |||
| - **`GET .../raw`**: 下载原始时序数据 (CSV文件)。 | |||
| - **说明**: 后端直接返回文件流,前端需处理下载。 | |||
| - **响应头**: `Content-Type: text/csv`, `Content-Disposition: attachment; filename="..."` | |||
| @ -0,0 +1,262 @@ | |||
| # 数据库表结构定义 | |||
| ## `user_info` 表 | |||
| 用于存储管理员用户的基本信息、认证凭据和权限。**注意:此表只存储管理员用户,患者用户存储在 `patient_info` 表中。** | |||
| ### 表结构 (SQL DDL) | |||
| ```sql | |||
| CREATE TABLE user_info ( | |||
| id SERIAL PRIMARY KEY, | |||
| username VARCHAR(50) UNIQUE NOT NULL, | |||
| email VARCHAR(255) UNIQUE NOT NULL, | |||
| hashed_password VARCHAR(255) NOT NULL, | |||
| department VARCHAR(100), | |||
| position VARCHAR(100), | |||
| is_admin BOOLEAN DEFAULT TRUE, | |||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | |||
| updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| ``` | |||
| ### 字段说明 | |||
| | 字段名 | 类型 | 约束 | 说明 | | |||
| | --------------- | ------------- | --------------- | ---------------------------------------- | | |||
| | `id` | SERIAL | PRIMARY KEY | 管理员唯一标识,自增主键。 | | |||
| | `username` | VARCHAR(50) | UNIQUE, NOT NULL| 管理员用户名,用于登录,必须唯一。 | | |||
| | `email` | VARCHAR(255) | UNIQUE, NOT NULL| 管理员邮箱,必须唯一。 | | |||
| | `hashed_password` | VARCHAR(255) | NOT NULL | 使用 bcrypt 哈希算法加密后的密码。 | | |||
| | `department` | VARCHAR(100) | | 管理员所属部门。 | | |||
| | `position` | VARCHAR(100) | | 管理员职位。 | | |||
| | `is_admin` | BOOLEAN | DEFAULT TRUE | 标识用户是否为管理员,默认为 `TRUE`。 | | |||
| | `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录创建时间。 | | |||
| | `updated_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录最后更新时间。 | | |||
| ### 示例数据 | |||
| 为了方便测试,可以插入以下数据: | |||
| ```sql | |||
| -- 密码: admin123 | |||
| INSERT INTO user_info (username, email, hashed_password, department, position, is_admin) | |||
| VALUES ('admin', 'admin@example.com', '$2b$12$EixZaYVK1eCjG4n6b5D1zuR8yA5d1E0n.3Jd9G.Yk.H8zJ.p7.wI.', '系统管理', '管理员', TRUE); | |||
| -- 密码: manager123 | |||
| INSERT INTO user_info (username, email, hashed_password, department, position, is_admin) | |||
| VALUES ('manager', 'manager@example.com', '$2b$12$xG.Vn2G7f2wJdDGkQxGjA.w0q.K6.Qj3S.h2Z/d.aY5q.w2K.X5U.', '医疗管理', '主管', TRUE); | |||
| ``` | |||
| ## `patient_info` 表 | |||
| 用于存储患者用户的基本信息和认证凭据。患者通过扫码注册,使用就诊号+身份证后六位登录。 | |||
| ### 表结构 (SQL DDL) | |||
| ```sql | |||
| CREATE TABLE patient_info ( | |||
| id SERIAL PRIMARY KEY, | |||
| user_id VARCHAR(50) UNIQUE NOT NULL, | |||
| medical_record_number VARCHAR(50) UNIQUE NOT NULL, | |||
| id_last_six VARCHAR(6) NOT NULL, | |||
| name VARCHAR(100) NOT NULL, | |||
| gender VARCHAR(10) NOT NULL, | |||
| dominant_hand VARCHAR(10) NOT NULL, | |||
| diagnosis TEXT, | |||
| dob DATE, | |||
| height_cm INTEGER, | |||
| weight_kg DECIMAL(5,2), | |||
| contact VARCHAR(20), | |||
| inpatient_number VARCHAR(50), | |||
| bed_number VARCHAR(20), | |||
| education_level VARCHAR(50), | |||
| education_years INTEGER, | |||
| remarks TEXT, | |||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | |||
| updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| ``` | |||
| ### 字段说明 | |||
| | 字段名 | 类型 | 约束 | 说明 | | |||
| | --------------------- | ------------- | --------------- | ---------------------------------------- | | |||
| | `id` | SERIAL | PRIMARY KEY | 患者唯一标识,自增主键。 | | |||
| | `user_id` | VARCHAR(50) | UNIQUE, NOT NULL| 系统生成的用户ID,用于内部标识。 | | |||
| | `medical_record_number` | VARCHAR(50) | UNIQUE, NOT NULL| 就诊号,用作登录用户名,必须唯一。 | | |||
| | `id_last_six` | VARCHAR(6) | NOT NULL | 身份证后六位,用作登录密码。 | | |||
| | `name` | VARCHAR(100) | NOT NULL | 患者姓名。 | | |||
| | `gender` | VARCHAR(10) | NOT NULL | 性别(男/女)。 | | |||
| | `dominant_hand` | VARCHAR(10) | NOT NULL | 利手(左/右)。 | | |||
| | `diagnosis` | TEXT | | 诊断信息。 | | |||
| | `dob` | DATE | | 出生日期(格式:YYYY-MM-DD)。 | | |||
| | `height_cm` | INTEGER | | 身高(厘米)。 | | |||
| | `weight_kg` | DECIMAL(5,2) | | 体重(公斤)。 | | |||
| | `contact` | VARCHAR(20) | | 联系方式。 | | |||
| | `inpatient_number` | VARCHAR(50) | | 住院号(可选)。 | | |||
| | `bed_number` | VARCHAR(20) | | 床号(可选)。 | | |||
| | `education_level` | VARCHAR(50) | | 教育程度(可选)。 | | |||
| | `education_years` | INTEGER | | 教育年数(可选)。 | | |||
| | `remarks` | TEXT | | 备注信息(可选)。 | | |||
| | `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录创建时间。 | | |||
| | `updated_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录最后更新时间。 | | |||
| ### 示例数据 | |||
| ```sql | |||
| INSERT INTO patient_info ( | |||
| user_id, medical_record_number, id_last_six, name, gender, dominant_hand, | |||
| diagnosis, dob, height_cm, weight_kg, contact, inpatient_number, | |||
| bed_number, education_level, education_years, remarks | |||
| ) VALUES ( | |||
| 'user-abc123', 'J0012345', '123456', '张三', '男', '右', | |||
| '无', '1960-01-15', 175, 70.5, '13800138000', 'Z67890', | |||
| '503', '本科', 16, '测试患者' | |||
| ); | |||
| ``` | |||
| ## `logs_metadata` 表 | |||
| 用于记录所有历史日志表的元信息。管理员通过查询此表来获取历史日志列表。**此表在管理员登录时由系统自动更新。** | |||
| ### 表结构 (SQL DDL) | |||
| ```sql | |||
| CREATE TABLE logs_metadata ( | |||
| id SERIAL PRIMARY KEY, | |||
| table_name VARCHAR(100) UNIQUE NOT NULL, | |||
| description TEXT, | |||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | |||
| updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| ``` | |||
| ### 字段说明 | |||
| | 字段名 | 类型 | 约束 | 说明 | | |||
| | ------------ | ----------- | --------------- | ---------------------------------------- | | |||
| | `id` | SERIAL | PRIMARY KEY | 元数据记录的唯一标识。 | | |||
| | `table_name` | VARCHAR(100)| UNIQUE, NOT NULL| 实际存储日志数据的表名。 | | |||
| | `description`| TEXT | | 对该日志的描述。 | | |||
| | `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录创建的时间,默认为当前时间。 | | |||
| | `updated_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录最后更新时间。 | | |||
| ### 示例数据 | |||
| ```sql | |||
| INSERT INTO logs_metadata (table_name, description, created_at) VALUES | |||
| ('log_20240315_1', '2024年3月15日第一次交互日志', '2024-03-15T14:30:00Z'), | |||
| ('log_20240315_2', '2024年3月15日第二次交互日志', '2024-03-15T16:30:00Z'); | |||
| ``` | |||
| ## 动态测试日志表 (`log_...` 表) | |||
| 这种表由系统在管理员登录时动态创建,用于存储一个特定测试批次的所有用户测试数据。表名格式为 `log_YYYYMMDD_N` (例如 `log_20240401_1`)。 | |||
| ### 表结构 (SQL DDL) | |||
| ```sql | |||
| CREATE TABLE log_YYYYMMDD_N ( | |||
| log_id SERIAL PRIMARY KEY, | |||
| user_id VARCHAR(255) UNIQUE NOT NULL, | |||
| medical_record_number VARCHAR(100) UNIQUE NOT NULL, | |||
| name VARCHAR(100), | |||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | |||
| status VARCHAR(50) DEFAULT 'initialized', | |||
| gait_analysis JSONB, | |||
| facial_expression INTEGER, | |||
| eye_tracking INTEGER, | |||
| handwriting INTEGER, | |||
| speech_analysis INTEGER, | |||
| semantic_analysis INTEGER, | |||
| raw_data_path VARCHAR(255) | |||
| ); | |||
| ``` | |||
| ### 字段说明 | |||
| | 字段名 | 类型 | 说明 | | |||
| | --------------------- | ------------- | ------------------------------------------------------------ | | |||
| | `log_id` | SERIAL | 本次测试日志的唯一ID,主键。 | | |||
| | `user_id` | VARCHAR(255) | 患者的用户ID,关联 `patient_info` 表。 | | |||
| | `medical_record_number`| VARCHAR(100) | 患者就诊号。 | | |||
| | `name` | VARCHAR(100) | 患者姓名。 | | |||
| | `created_at` | TIMESTAMPTZ | 记录创建时间。 | | |||
| | `status` | VARCHAR(50) | 测试日志的状态。默认为 `initialized` (已初始化)。流程: `initialized` -> `processing` (数据处理中) -> `pending_review` (待专家审核) -> `completed` (已完成确认)。 | | |||
| | `gait_analysis` | JSONB | 存储步态分析结果的JSON对象。 | | |||
| | `facial_expression` | INTEGER | 存储面部表情的UPDRS评级(0-4),可为空。 | | |||
| | `eye_tracking` | INTEGER | 存储眼动功能的UPDRS评级(0-4),可为空。 | | |||
| | `handwriting` | INTEGER | 存储手写功能的UPDRS评级(0-4),可为空。 | | |||
| | `speech_analysis` | INTEGER | 存储言语功能的UPDRS评级(0-4),可为空。 | | |||
| | `semantic_analysis` | INTEGER | 存储认知与语义连贯性的评级(0-4),可为空。 | | |||
| | `raw_data_path` | VARCHAR(255) | 指向原始时序数据文件(如CSV)的路径。 | | |||
| ### 示例数据 | |||
| ```sql | |||
| -- 此表示例数据为概念性展示,实际数据结构嵌套在JSONB字段中 | |||
| INSERT INTO log_20240401_1 (user_id, medical_record_number, name, status, gait_analysis, facial_expression) VALUES | |||
| ('user-abc123', 'J0012345', '张三', 'completed', '{ | |||
| "procedural_events": [{"event": "stand_up", "frame": 85}], | |||
| "walk_segments": [{"segment_id": 1, "gait_speed": 1.2}] | |||
| }', 2); | |||
| ``` | |||
| ## `questions` 表 | |||
| 用于存储视频问卷的所有问题。 | |||
| ### 表结构 (SQL DDL) | |||
| ```sql | |||
| CREATE TABLE questions ( | |||
| id SERIAL PRIMARY KEY, | |||
| type VARCHAR(50) NOT NULL, | |||
| content TEXT NOT NULL, | |||
| required BOOLEAN DEFAULT TRUE, | |||
| max_length INTEGER, | |||
| -- 可以增加一个 category 字段,用于关联特定的视频或场景 | |||
| category VARCHAR(100) | |||
| ); | |||
| ``` | |||
| ### 字段说明 | |||
| | 字段名 | 类型 | 说明 | | |||
| | ---------- | ----------- | -------------------------------------------------- | | |||
| | `id` | SERIAL | 问题的唯一ID。 | | |||
| | `type` | VARCHAR(50) | 问题类型,例如 `text`。 | | |||
| | `content` | TEXT | 问题的主要内容。 | | |||
| | `required` | BOOLEAN | 该问题是否为必答题,默认为 `TRUE`。 | | |||
| | `max_length`| INTEGER | 答案的最大长度限制。 | | |||
| | `category` | VARCHAR(100)| 问题分类,可用于从特定类别的题库中抽题。 | | |||
| ### 示例数据 | |||
| ```sql | |||
| INSERT INTO questions (type, content, required, max_length, category) VALUES | |||
| ('text', '请描述视频中展示的主要问题是什么?', TRUE, 500, 'general'), | |||
| ('text', '您认为应该如何解决这个问题?', TRUE, 1000, 'general'), | |||
| ('text', '您对这个解决方案有什么建议?', FALSE, 800, 'general'), | |||
| ('text', '视频中的场景让您联想到了什么?', TRUE, 500, 'emotion'), | |||
| ('text', '您是否在现实生活中遇到过类似的情况?', FALSE, 1000, 'experience'), | |||
| ('text', '如果您是视频中的主角,您会怎么做?', TRUE, 1000, 'decision'), | |||
| ('text', '这个事件对您的触动是什么?', TRUE, 800, 'emotion'), | |||
| ('text', '请用三个词来形容您的观看感受。', TRUE, 100, 'emotion'); | |||
| ``` | |||
| ## 用户类型说明 | |||
| ### 管理员用户 (user_info表) | |||
| - **注册方式**: 管理后台创建 | |||
| - **登录方式**: 用户名 + 密码 | |||
| - **权限**: 管理员权限,可查看所有测试记录 | |||
| - **特点**: 使用bcrypt加密密码,支持邮箱验证 | |||
| ### 患者用户 (patient_info表) | |||
| - **注册方式**: 扫码自助注册 | |||
| - **登录方式**: 就诊号 + 身份证后六位 | |||
| - **权限**: 普通用户权限,可进行测试 | |||
| - **特点**: 使用就诊号作为用户名,身份证后六位作为密码 | |||
| @ -0,0 +1,247 @@ | |||
| # 患者注册路由说明 | |||
| ## 概述 | |||
| 根据软件框架V2文档的要求,我们为第一个页面(用户注册页面-扫码)创建了独立的路由。这个路由专门处理患者的自助注册功能,适用于现场排队等候的场景。 | |||
| **重要说明**:系统中只有两种用户类型: | |||
| - **患者用户**:通过扫码注册,使用就诊号+身份证后六位登录 | |||
| - **管理员用户**:通过管理后台创建,用于系统管理 | |||
| ## 路由信息 | |||
| - **路由**: `/api/v3/register` | |||
| - **方法**: POST | |||
| - **文件位置**: `app/routers/register.py` | |||
| ## 功能特点 | |||
| ### 1. 独立的路由系统 | |||
| - 患者注册功能完全独立于管理员注册 | |||
| - 使用独立的数据库表 `patient_info` | |||
| - 独立的JWT令牌生成和验证 | |||
| ### 2. 完整的患者信息字段 | |||
| 根据软件框架V2文档,支持以下字段: | |||
| **必需字段**: | |||
| - `medical_record_number`: 就诊号(将作为登录用户名) | |||
| - `id_last_six`: 身份证后六位(将作为登录密码) | |||
| - `name`: 姓名 | |||
| - `gender`: 性别 | |||
| - `dominant_hand`: 利手 | |||
| - `diagnosis`: 诊断 | |||
| - `dob`: 出生年月(格式: YYYY-MM-DD) | |||
| - `height_cm`: 身高(cm) | |||
| - `weight_kg`: 体重(kg) | |||
| - `contact`: 联系方式 | |||
| **可选字段**: | |||
| - `inpatient_number`: 住院号 | |||
| - `bed_number`: 床号 | |||
| - `education_level`: 教育程度 | |||
| - `education_years`: 教育年数 | |||
| - `remarks`: 备注 | |||
| ### 3. 数据验证 | |||
| - 必需字段验证 | |||
| - 数据类型验证(数字、日期格式) | |||
| - 重复注册检查(就诊号唯一性) | |||
| ### 4. 自动登录 | |||
| - 注册成功后自动生成JWT令牌 | |||
| - 返回完整的用户信息 | |||
| - 无需额外登录步骤 | |||
| ## 数据库设计 | |||
| ### patient_info 表结构 | |||
| ```sql | |||
| CREATE TABLE patient_info ( | |||
| id SERIAL PRIMARY KEY, | |||
| user_id VARCHAR(50) UNIQUE NOT NULL, | |||
| medical_record_number VARCHAR(50) UNIQUE NOT NULL, | |||
| id_last_six VARCHAR(6) NOT NULL, | |||
| name VARCHAR(100) NOT NULL, | |||
| gender VARCHAR(10) NOT NULL, | |||
| dominant_hand VARCHAR(10) NOT NULL, | |||
| diagnosis TEXT, | |||
| dob DATE, | |||
| height_cm INTEGER, | |||
| weight_kg DECIMAL(5,2), | |||
| contact VARCHAR(20), | |||
| inpatient_number VARCHAR(50), | |||
| bed_number VARCHAR(20), | |||
| education_level VARCHAR(50), | |||
| education_years INTEGER, | |||
| remarks TEXT, | |||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | |||
| updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| ``` | |||
| ### user_info 表结构(简化后) | |||
| ```sql | |||
| CREATE TABLE user_info ( | |||
| id SERIAL PRIMARY KEY, | |||
| username VARCHAR(50) UNIQUE NOT NULL, | |||
| email VARCHAR(255) UNIQUE NOT NULL, | |||
| hashed_password VARCHAR(255) NOT NULL, | |||
| department VARCHAR(100), | |||
| position VARCHAR(100), | |||
| is_admin BOOLEAN DEFAULT TRUE, | |||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | |||
| updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| ``` | |||
| ## 用户类型说明 | |||
| ### 患者用户 | |||
| - **注册方式**: 扫码自助注册 | |||
| - **登录方式**: 就诊号 + 身份证后六位 | |||
| - **数据存储**: `patient_info` 表 | |||
| - **权限**: 普通用户权限,可进行测试 | |||
| ### 管理员用户 | |||
| - **注册方式**: 管理后台创建 | |||
| - **登录方式**: 用户名 + 密码 | |||
| - **数据存储**: `user_info` 表(简化后只存储管理员) | |||
| - **权限**: 管理员权限,可查看所有测试记录 | |||
| ## 登录支持 | |||
| 患者用户可以通过以下方式登录: | |||
| - **用户名**: 就诊号 | |||
| - **密码**: 身份证后六位 | |||
| 登录接口 `/api/v3/login` 已更新,支持患者用户登录。 | |||
| ## 响应格式 | |||
| ### 成功响应 | |||
| ```json | |||
| { | |||
| "status": "success", | |||
| "code": 200, | |||
| "data": { | |||
| "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", | |||
| "token_type": "Bearer", | |||
| "is_admin": false, | |||
| "user_info": { | |||
| "user_id": "user-abc-123", | |||
| "medical_record_number": "J0012345", | |||
| "name": "张三", | |||
| "gender": "男", | |||
| "dominant_hand": "右", | |||
| "diagnosis": "无", | |||
| "dob": "1960-01-15", | |||
| "height_cm": 175, | |||
| "weight_kg": 70.5, | |||
| "contact": "13800138000", | |||
| "inpatient_number": "Z67890", | |||
| "bed_number": "503", | |||
| "education_level": "本科", | |||
| "education_years": 16, | |||
| "remarks": "" | |||
| } | |||
| }, | |||
| "message": "注册成功并自动登录" | |||
| } | |||
| ``` | |||
| ### 错误响应示例 | |||
| **重复注册**: | |||
| ```json | |||
| { | |||
| "status": "error", | |||
| "code": 400, | |||
| "message": "Username Already Exists", | |||
| "error_details": { | |||
| "type": "duplicate_username", | |||
| "description": "该就诊号已被注册" | |||
| } | |||
| } | |||
| ``` | |||
| **参数验证失败**: | |||
| ```json | |||
| { | |||
| "status": "error", | |||
| "code": 400, | |||
| "message": "Invalid Parameters", | |||
| "error_details": { | |||
| "type": "validation_error", | |||
| "description": "参数验证失败", | |||
| "fields": { | |||
| "dob": "出生日期格式必须是 YYYY-MM-DD", | |||
| "height_cm": "身高必须是数字" | |||
| } | |||
| } | |||
| } | |||
| ``` | |||
| ## 测试 | |||
| 使用提供的测试脚本 `test_register.py` 可以验证注册功能: | |||
| ```bash | |||
| python test_register.py | |||
| ``` | |||
| 测试包括: | |||
| 1. 患者注册功能 | |||
| 2. 患者登录功能 | |||
| 3. 重复注册检查 | |||
| ## 集成说明 | |||
| ### 1. 路由注册 | |||
| 在 `app/main.py` 中已添加: | |||
| ```python | |||
| from app.routers import register | |||
| app.include_router(register.router, prefix="/api/v3") | |||
| ``` | |||
| ### 2. 数据库初始化 | |||
| 在应用启动时会自动初始化患者数据库表: | |||
| ```python | |||
| await register.init_register_database() | |||
| ``` | |||
| ### 3. 登录支持 | |||
| `app/routers/auth.py` 中的登录接口已更新,支持患者用户登录。 | |||
| ## 数据库架构优势 | |||
| ### 1. 表结构分离 | |||
| - `user_info` 表:专门存储管理员用户,结构简化 | |||
| - `patient_info` 表:专门存储患者用户,包含完整的医疗信息 | |||
| ### 2. 安全性提升 | |||
| - 管理员和患者数据完全分离 | |||
| - 不同的认证方式(bcrypt vs 身份证后六位) | |||
| - 独立的权限管理 | |||
| ### 3. 维护性增强 | |||
| - 表结构清晰,职责明确 | |||
| - 便于后续功能扩展 | |||
| - 数据查询和统计更方便 | |||
| ## 注意事项 | |||
| 1. **安全性**: 身份证后六位作为密码,在生产环境中应考虑更安全的认证方式 | |||
| 2. **数据验证**: 前端应进行适当的数据验证,后端提供二次验证 | |||
| 3. **错误处理**: 所有错误都有详细的错误信息,便于前端处理 | |||
| 4. **令牌管理**: JWT令牌有效期为7天,可根据需要调整 | |||
| 5. **用户类型**: 系统中只有患者和管理员两种用户类型,概念要清晰 | |||
| 6. **数据库**: 确保两个表都正确创建和初始化 | |||
| ## 后续开发 | |||
| 1. 可以考虑添加患者信息的更新接口 | |||
| 2. 可以添加患者信息的查询接口(管理员权限) | |||
| 3. 可以添加患者数据的导出功能 | |||
| 4. 可以添加更复杂的密码策略 | |||
| 5. 可以添加患者和管理员之间的关联查询 | |||