该架构是一个单机前后端BS架构,没有并发,而且也不可能并发,因为设备层没有那么多冗余资源。 软件采用前后端分离的架构,前端采用Vue,后端采用FastAPI,数据库选用向量数据库Postgresql。
patient_info表单。{
"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", // 身高(cm)
"weight_kg": "number", // 体重(kg)
"contact": "string", // 联系方式
"inpatient_number": "string", // 住院号 (可选)
"bed_number": "string", // 床号 (可选)
"education_level": "string", // 教育程度 (可选)
"education_years": "number", // 教育年数 (可选)
"remarks": "string" // 备注 (可选)
}
// 注册成功响应
{
"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,
"contact": "13800138000",
"inpatient_number": "Z67890",
"bed_number": "503",
"education_level": "本科",
"education_years": 16,
"remarks": ""
}
},
"message": "注册成功并自动登录"
}
// 用户名已存在响应
{
"status": "error",
"code": 400,
"message": "Username Already Exists",
"error_details": {
"type": "duplicate_username",
"description": "该用户名已被注册"
}
}
// 邮箱已存在响应
{
"status": "error",
"code": 400,
"message": "Email Already Exists",
"error_details": {
"type": "duplicate_email",
"description": "该邮箱已被注册"
}
}
// 参数验证失败响应
{
"status": "error",
"code": 400,
"message": "Invalid Parameters",
"error_details": {
"type": "validation_error",
"description": "参数验证失败",
"fields": {
"email": "邮箱格式不正确",
"password": "密码长度必须大于6位"
}
}
}
{
"username": "string", // 用户名
"password": "string" // 密码
}
// 管理员用户响应
{
"status": "success",
"code": 200,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"is_admin": true,
"active_log_table": "log_20240401_1"
},
"message": "管理员登录成功,已创建新的测试会话日志表"
}
// 普通用户响应
{
"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,
"contact": "13800138000",
"inpatient_number": "Z67890",
"bed_number": "503",
"education_level": "本科",
"education_years": 16,
"remarks": ""
}
},
"message": "登录成功,令牌长期有效,用户信息已包含在令牌中"
}
// 错误响应
{
"status": "error",
"code": 401,
"message": "Unauthorized",
"error_details": {
"type": "invalid_credentials",
"description": "Invalid username or password"
}
}
user_info,用户确认后前端负责路由跳转。Authorization: Bearer {access_token} // 登录接口获取的token
// 开始推流成功响应
{
"status": "success",
"code": 200,
"data": {
"stream_id": "stream_123456", // 流ID,用于后续停止推流
"ws_url": "ws://{host}/api/v3/ws/streams/{stream_id}?token={access_token}",
"message": "推流服务已启动,请建立WebSocket连接"
}
}
// 错误响应 - 摄像头未就绪
{
"status": "error",
"code": 503,
"message": "摄像头未就绪",
"error_details": {
"type": "camera_not_ready",
"description": "请检查摄像头连接"
}
}
// 错误响应 - 服务繁忙
{
"status": "error",
"code": 503,
"message": "服务繁忙",
"error_details": {
"type": "service_busy",
"description": "当前服务负载过高,请稍后重试"
}
}
// 错误响应 - 未授权
{
"status": "error",
"code": 401,
"message": "未授权访问",
"error_details": {
"type": "unauthorized",
"description": "请先登录"
}
}
Authorization: Bearer {access_token} // 登录接口获取的token
// 停止推流成功响应
{
"status": "success",
"code": 200,
"data": {
"message": "推流服务已停止"
}
}
// 错误响应 - 无效的流ID
{
"status": "error",
"code": 400,
"message": "无效的流ID",
"error_details": {
"type": "invalid_stream_id",
"description": "指定的流ID不存在或已过期"
}
}
// 错误响应 - 未授权
{
"status": "error",
"code": 401,
"message": "未授权访问",
"error_details": {
"type": "unauthorized",
"description": "请先登录"
}
}
POST /api/v3/streams响应中的ws_url提供stage) 推送给前端。stage。stage后,负责根据该标识查找并显示对应的提示文本,以及播放对应的语音提示。所有提示内容(文本、音频文件名)都预置在前端。{
"video_frame": "base64_encoded_image",
"timestamp": "2024-03-15T14:30:00Z",
"stream_id": "stream_123456",
"analysis_data": {
"stage": "stand_up" // 当前阶段。可能的值: 'initial', 'stand_up', 'walk_to_target', 'turn', 'walk_back', 'sit_down', 'complete', 'error'
}
}
Authorization: Bearer {access_token}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"videos": [
{
"video_id": "video_123456",
"title": "场景一:公园散步",
"description": "一段描述在公园里散步的视频。",
"duration": 300,
"questions": [
{ "id": "q1", "type": "text", "content": "请描述视频中展示的主要问题是什么?", "required": true, "max_length": 500 },
{ "id": "q2", "type": "text", "content": "您认为应该如何解决这个问题?", "required": true, "max_length": 1000 }
]
},
{
"video_id": "video_789012",
"title": "场景二:超市购物",
"description": "一段模拟在超市购物场景的视频。",
"duration": 240,
"questions": [
{ "id": "q3", "type": "text", "content": "您对这个解决方案有什么建议?", "required": false, "max_length": 800 }
]
}
]
}
}
video/mp4)session_manager.py),该进程会进一步启动三个独立的子进程:
eye_data_collector.py: 专职采集眼动数据,并实时写入本地CSV文件,不受其他进程干扰。face_data_collector.py: 专职采集面部表情数据,并实时写入本地CSV文件,不受其他进程干扰。handwriting_server.py: 专职运行WebSocket服务器,用于处理与前端的手写交互。POST /api/v3/video-sessions 接口动态返回,格式类似于 ws://127.0.0.1:{port}。前端必须使用此接口返回的URL进行连接。POST /video-sessions) 启动后端的 session_manager.py,后者立即启动 eye_data_collector 和 face_data_collector 进程,开始在后台静默采集数据。ws_url与 handwriting_server.py 建立WebSocket连接。{"type": "handwriting_stroke", "payload": ...})。handwriting_server 负责接收。{"command": "end_session"}) 给WebSocket。handwriting_server 收到结束指令后,通过进程间通信通知 session_manager。session_manager 随即安全地关闭所有相关的子进程(眼动、面部和WebSocket),完成数据保存和资源清理。{"command": "end_session"}{"type": "handwriting_stroke", "payload": ...}POST /api/v3/audio-sessions 创建新会话。POST /api/v3/audio-sessions/{session_id}/interact 上传音频。request_id 和一个SSE连接地址,并开始在后台异步处理音频。asr_result 事件。前端接收到后,在界面上显示识别出的用户文本。ai_result 事件。前端接收后,显示AI的文本回复。tts_result 事件,包含音频文件的URL。done 事件,关闭SSE连接。Authorization: Bearer {access_token}
{
"system_prompt": "你是一个聊天机器人,请根据用户需求进行回应。",
"max_history": 10
}
// 开始会话成功响应
{
"status": "success",
"code": 200,
"data": {
"session_id": "session_123456",
"message": "语音交互会话已启动"
}
}
Authorization: Bearer {access_token}
Content-Type: multipart/form-data
audio: 用户录制的音频文件 (例如 blob 或 .wav 格式)// 成功接收请求
{
"status": "accepted",
"code": 202,
"data": {
"request_id": "req_abc123",
"events_url": "/api/v3/audio-sessions/{session_id}/events/{request_id}",
"message": "请求已接收,请连接到events_url获取实时进度"
}
}
text/event-streamevent: asr_result
data: {"status": "success", "text": "用户说的话", "confidence": 0.95}
event: ai_result
data: {"status": "success", "text": "AI的回复文本"}
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处理失败"}
Authorization: Bearer {access_token}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"conversation_history": [
{ "role": "user", "content": "你好" },
{ "role": "assistant", "content": "你好,有什么可以帮你的吗?" }
]
}
}
Authorization: Bearer {access_token}
// 结束会话成功响应
{
"status": "success",
"code": 200,
"data": { "message": "语音交互会话已结束" }
}
audio/wav)Authorization: Bearer {access_token}// 成功响应
{
"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日第二次测试"
}
]
}
}
// 无数据响应
{
"status": "success",
"code": 200,
"data": {
"tables": []
}
}
// 失败响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You don't have permission to access test records"
}
}
Authorization: Bearer {access_token}page (number, optional, default: 1): 页码,从1开始page_size (number, optional, default: 50): 每页记录数/api/v3/admin/logs/log_20240315_1?page=1&page_size=50// 成功响应
{
"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"
},
{
"log_id": 2,
"medical_record_number": "J0012346",
"name": "李四",
"created_at": "2024-03-15T08:31:00Z",
"status": "pending_review"
},
{
"log_id": 3,
"medical_record_number": "J0012347",
"name": "王五",
"created_at": "2024-03-15T08:32:00Z",
"status": "initialized"
}
]
}
}
- **`status` 字段说明**:
- `initialized`: 已初始化。记录已创建,等待用户录入数据。
- `processing`: 数据处理中。用户已完成记录,系统正在后台分析数据。
- `pending_review`: 待专家审核。系统分析完成,等待专家进行评级和确认。
- `completed`: 已完成。专家已完成确认,记录归档。
// 查询失败响应
{
"status": "error",
"code": 404,
"message": "Not Found",
"error_details": {
"type": "invalid_table",
"description": "Log table does not exist"
}
}
// 权限失败响应
{
"status": "error",
"code": 403,
"message": "Access Denied",
"error_details": {
"type": "permission_denied",
"description": "You don't have permission to access this test record"
}
}
本部分包含所有针对单个测试记录的具体分析模块。用户从记录列表页选择具体项目后,前端访问对应的页面URL进入编辑与查看。
GET /api/v3/admin/logs/{table_name}/{log_id}/gaitAuthorization: Bearer {access_token}{
"status": "success",
"code": 200,
"data": {
"procedural_events": [
{ "event": "stand_up", "frame": 85 },
{ "event": "turn_around", "frame": 755 },
{ "event": "sit_down", "frame": 1560 }
],
"walk_segments": [
{
"segment_id": 1,
"start_frame": 153,
"end_frame": 750,
"calculated_metrics": {
"gait_speed": 1.2,
"stride_length": 0.75,
"step_speed": 1.1,
"swing_speed": 2.5,
"cadence": 110.0,
"single_support_phase_ratio": 0.4,
"swing_phase_ratio": 0.4,
"double_support_ratio": 0.2,
"step_height": 0.15,
"step_width": 0.2
},
"events": [
{ "event": "left_heel_strike", "frame": 165 },
{ "event": "right_toe_off", "frame": 168 },
{ "event": "double_support", "frame": 170 },
{ "event": "right_heel_strike", "frame": 180 },
{ "event": "left_toe_off", "frame": 183 },
]
},
...
]
}
}
POST /api/v3/admin/logs/{table_name}/{log_id}/gait/eventsAuthorization: Bearer {access_token}stand_up, turn_around, sit_down等过程性事件,以及定义行走分段的walk_start, walk_end事件)。后端接收到新的事件列表后,将立即执行重新计算,并返回更新后的完整步态分析数据。{
"events": [
{ "event": "stand_up", "frame": 85 },
{ "event": "walk_start", "frame": 180 },
{ "event": "walk_end", "frame": 840 },
{ "event": "turn_around", "frame": 900 },
{ "event": "sit_down", "frame": 1600 }
]
}
- 成功响应:返回更新后的完整步态分析数据,结构同`功能1:获取步态分析数据`的响应。
- 请求无效响应
{
"status": "error",
"code": 400,
"message": "Bad Request",
"error_details": {
"type": "invalid_event_format",
"description": "The submitted event data is incorrectly formatted or frames are invalid."
}
}
- 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to modify this resource."
}
}
GET /api/v3/admin/logs/{table_name}/{log_id}/rawAuthorization: Bearer {access_token}text/csvattachment; filename="raw_data_{log_id}.csv"此部分提供对视频中的整体面部表情进行UPDRS评级的功能。评级是针对整个视频的单个分数。
GET /api/v3/admin/logs/{table_name}/{log_id}/facialAuthorization: Bearer {access_token}// 成功响应
{
"status": "success",
"code": 200,
"data": {
"log_id": 1,
"video_url": "/api/v3/videos/facial_expression_video_abc.mp4",
"updrs_rating": 1 // 当前UPDRS分数,如果未评级则为null
}
}
// 记录不存在响应
{
"status": "error",
"code": 404,
"message": "Not Found",
"error_details": {
"type": "record_not_found",
"description": "The specified record does not exist."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to access this resource."
}
}
POST /api/v3/admin/logs/{table_name}/{log_id}/facial/ratingAuthorization: Bearer {access_token}{
"rating": 2
}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"message": "UPDRS rating updated successfully."
}
}
// 请求无效响应
{
"status": "error",
"code": 400,
"message": "Bad Request",
"error_details": {
"type": "invalid_rating_value",
"description": "Rating must be an integer between 0 and 4."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to modify this resource."
}
}
此部分提供对眼动追踪视频进行评级的功能。评级主要关注扫视和凝视的稳定性。
GET /api/v3/admin/logs/{table_name}/{log_id}/eyetrackingAuthorization: Bearer {access_token}// 成功响应
{
"status": "success",
"code": 200,
"data": {
"video_url": "/api/v3/videos/eyetracking_video_abc.mp4",
"rating": 2 // 当前评级分数,如果未评级则为null
}
}
// 记录不存在响应
{
"status": "error",
"code": 404,
"message": "Not Found",
"error_details": {
"type": "record_not_found",
"description": "The specified record does not exist."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to access this resource."
}
}
POST /api/v3/admin/logs/{table_name}/{log_id}/eyetracking/ratingAuthorization: Bearer {access_token}{
"rating": 3
}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"message": "Eyetracking rating updated successfully."
}
}
// 请求无效响应
{
"status": "error",
"code": 400,
"message": "Bad Request",
"error_details": {
"type": "invalid_rating_value",
"description": "Rating must be an integer between 0 and 4."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to modify this resource."
}
}
此部分提供对手写笔迹(如螺旋测试)的评级功能,主要评估运动震颤和书写过小症(Micrographia)。
GET /api/v3/admin/logs/{table_name}/{log_id}/handwritingAuthorization: Bearer {access_token}// 成功响应
{
"status": "success",
"code": 200,
"data": {
"video_url": "/api/v3/videos/handwriting_spiral_abc.mp4",
"rating": 1 // 当前评级分数,如果未评级则为null
}
}
// 记录不存在响应
{
"status": "error",
"code": 404,
"message": "Not Found",
"error_details": {
"type": "record_not_found",
"description": "The specified record does not exist."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to access this resource."
}
}
POST /api/v3/admin/logs/{table_name}/{log_id}/handwriting/ratingAuthorization: Bearer {access_token}{
"rating": 2
}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"message": "Handwriting rating updated successfully."
}
}
// 请求无效响应
{
"status": "error",
"code": 400,
"message": "Bad Request",
"error_details": {
"type": "invalid_rating_value",
"description": "Rating must be an integer between 0 and 4."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to modify this resource."
}
}
此部分提供对语音样本的评级,主要评估音量、音调、清晰度等言语功能。
GET /api/v3/admin/logs/{table_name}/{log_id}/speechAuthorization: Bearer {access_token}// 成功响应
{
"status": "success",
"code": 200,
"data": {
"audio_url": "/api/v3/audios/speech_sample_abc.wav",
"rating": 3 // 当前评级分数,如果未评级则为null
}
}
// 记录不存在响应
{
"status": "error",
"code": 404,
"message": "Not Found",
"error_details": {
"type": "record_not_found",
"description": "The specified record does not exist."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to access this resource."
}
}
POST /api/v3/admin/logs/{table_name}/{log_id}/speech/ratingAuthorization: Bearer {access_token}{
"rating": 3
}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"message": "Speech rating updated successfully."
}
}
// 请求无效响应
{
"status": "error",
"code": 400,
"message": "Bad Request",
"error_details": {
"type": "invalid_rating_value",
"description": "Rating must be an integer between 0 and 4."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to modify this resource."
}
}
此部分提供对文本(例如,语音识别结果)的评级,主要评估思维连贯性、逻辑性和复杂性。
GET /api/v3/admin/logs/{table_name}/{log_id}/semanticsAuthorization: Bearer {access_token}// 成功响应
{
"status": "success",
"code": 200,
"data": {
"text_content": "今天天气真好,我感觉心情非常愉快,虽然昨天有点不舒服,但现在好多了。",
"rating": 0 // 当前评级分数,如果未评级则为null
}
}
// 记录不存在响应
{
"status": "error",
"code": 404,
"message": "Not Found",
"error_details": {
"type": "record_not_found",
"description": "The specified record does not exist."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to access this resource."
}
}
POST /api/v3/admin/logs/{table_name}/{log_id}/semantics/ratingAuthorization: Bearer {access_token}{
"rating": 1
}
// 成功响应
{
"status": "success",
"code": 200,
"data": {
"message": "Semantic rating updated successfully."
}
}
// 请求无效响应
{
"status": "error",
"code": 400,
"message": "Bad Request",
"error_details": {
"type": "invalid_rating_value",
"description": "Rating must be an integer between 0 and 4."
}
}
// 未授权响应
{
"status": "error",
"code": 403,
"message": "Forbidden",
"error_details": {
"type": "permission_denied",
"description": "You do not have permission to modify this resource."
}
}