| @ -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. 可以添加患者和管理员之间的关联查询 | |||||