diff --git a/.DS_Store b/.DS_Store index d8f9254..55b6e62 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/app.zip b/app.zip index 4bb133b..477099a 100644 Binary files a/app.zip and b/app.zip differ diff --git a/app/api/endpoints/meetings.py b/app/api/endpoints/meetings.py index 68f2e43..aa350ab 100644 --- a/app/api/endpoints/meetings.py +++ b/app/api/endpoints/meetings.py @@ -232,7 +232,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren cursor = connection.cursor(dictionary=True) query = ''' SELECT m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, m.tags, - m.user_id as creator_id, u.caption as creator_username, af.file_path as audio_file_path + m.user_id as creator_id, u.caption as creator_username, af.file_path as audio_file_path, + m.access_password FROM meetings m JOIN users u ON m.user_id = u.user_id LEFT JOIN audio_files af ON m.meeting_id = af.meeting_id WHERE m.meeting_id = %s ''' @@ -249,7 +250,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren meeting_data = Meeting( meeting_id=meeting['meeting_id'], title=meeting['title'], meeting_time=meeting['meeting_time'], summary=meeting['summary'], created_at=meeting['created_at'], attendees=attendees, - creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags + creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags, + access_password=meeting.get('access_password') ) if meeting['audio_file_path']: meeting_data.audio_file_path = meeting['audio_file_path'] @@ -341,7 +343,8 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre cursor = connection.cursor(dictionary=True) query = ''' SELECT m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, m.tags, - m.user_id as creator_id, u.caption as creator_username, af.file_path as audio_file_path + m.user_id as creator_id, u.caption as creator_username, af.file_path as audio_file_path, + m.access_password FROM meetings m JOIN users u ON m.user_id = u.user_id LEFT JOIN audio_files af ON m.meeting_id = af.meeting_id WHERE m.meeting_id = %s ''' @@ -358,7 +361,8 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre meeting_data = Meeting( meeting_id=meeting['meeting_id'], title=meeting['title'], meeting_time=meeting['meeting_time'], summary=meeting['summary'], created_at=meeting['created_at'], attendees=attendees, - creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags + creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags, + access_password=meeting.get('access_password') ) if meeting.get('audio_file_path'): meeting_data.audio_file_path = meeting['audio_file_path'] @@ -888,7 +892,7 @@ def get_meeting_preview_data(meeting_id: int): query = ''' SELECT m.meeting_id, m.title, m.meeting_time, m.summary, m.updated_at, m.prompt_id, m.user_id as creator_id, u.caption as creator_username, - p.name as prompt_name + p.name as prompt_name, m.access_password FROM meetings m JOIN users u ON m.user_id = u.user_id LEFT JOIN prompts p ON m.prompt_id = p.id @@ -920,10 +924,136 @@ def get_meeting_preview_data(meeting_id: int): "prompt_id": meeting['prompt_id'], "prompt_name": meeting['prompt_name'], "attendees": attendees, - "attendees_count": len(attendees) + "attendees_count": len(attendees), + "has_password": bool(meeting.get('access_password')) # 新增:是否设置了访问密码 } return create_api_response(code="200", message="获取会议预览数据成功", data=preview_data) except Exception as e: return create_api_response(code="500", message=f"Failed to get meeting preview data: {str(e)}") + +# 访问密码管理相关API + +class AccessPasswordRequest(BaseModel): + password: Optional[str] = None # None表示关闭密码 + +class VerifyPasswordRequest(BaseModel): + password: str + +@router.put("/meetings/{meeting_id}/access-password") +def update_meeting_access_password( + meeting_id: int, + request: AccessPasswordRequest, + current_user: dict = Depends(get_current_user) +): + """ + 设置或关闭会议访问密码(仅创建人可操作) + + Args: + meeting_id: 会议ID + request.password: 密码字符串(None表示关闭密码) + current_user: 当前登录用户 + + Returns: + API响应,包含操作结果 + """ + try: + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # 检查会议是否存在且当前用户是创建人 + cursor.execute( + "SELECT meeting_id, user_id FROM meetings WHERE meeting_id = %s", + (meeting_id,) + ) + meeting = cursor.fetchone() + + if not meeting: + return create_api_response(code="404", message="会议不存在") + + if meeting['user_id'] != current_user['user_id']: + return create_api_response(code="403", message="仅创建人可以设置访问密码") + + # 更新访问密码 + cursor.execute( + "UPDATE meetings SET access_password = %s WHERE meeting_id = %s", + (request.password, meeting_id) + ) + connection.commit() + + if request.password: + return create_api_response( + code="200", + message="访问密码已设置", + data={"password": request.password} + ) + else: + return create_api_response( + code="200", + message="访问密码已关闭", + data={"password": None} + ) + + except Exception as e: + return create_api_response( + code="500", + message=f"设置访问密码失败: {str(e)}" + ) + +@router.post("/meetings/{meeting_id}/verify-password") +def verify_meeting_password(meeting_id: int, request: VerifyPasswordRequest): + """ + 验证会议访问密码(无需登录认证) + + Args: + meeting_id: 会议ID + request.password: 要验证的密码 + + Returns: + API响应,包含验证结果 + """ + try: + with get_db_connection() as connection: + cursor = connection.cursor(dictionary=True) + + # 获取会议的访问密码 + cursor.execute( + "SELECT access_password FROM meetings WHERE meeting_id = %s", + (meeting_id,) + ) + meeting = cursor.fetchone() + + if not meeting: + return create_api_response(code="404", message="会议不存在") + + # 验证密码 + stored_password = meeting.get('access_password') + + if not stored_password: + # 没有设置密码,直接通过 + return create_api_response( + code="200", + message="该会议未设置访问密码", + data={"verified": True} + ) + + if request.password == stored_password: + return create_api_response( + code="200", + message="密码验证成功", + data={"verified": True} + ) + else: + return create_api_response( + code="200", + message="密码错误", + data={"verified": False} + ) + + except Exception as e: + return create_api_response( + code="500", + message=f"验证密码失败: {str(e)}" + ) + diff --git a/app/api/endpoints/prompts.py b/app/api/endpoints/prompts.py index 0244d6f..bda1a14 100644 --- a/app/api/endpoints/prompts.py +++ b/app/api/endpoints/prompts.py @@ -203,6 +203,37 @@ def delete_prompt(prompt_id: int, current_user: dict = Depends(get_current_user) if prompt['creator_id'] != current_user["user_id"]: return create_api_response(code="403", message="无权删除其他用户的提示词") + # 检查是否有会议引用了该提示词 + cursor.execute( + "SELECT COUNT(*) as count FROM meetings WHERE prompt_id = %s", + (prompt_id,) + ) + meeting_count = cursor.fetchone()['count'] + + # 检查是否有知识库引用了该提示词 + cursor.execute( + "SELECT COUNT(*) as count FROM knowledge_bases WHERE prompt_id = %s", + (prompt_id,) + ) + kb_count = cursor.fetchone()['count'] + + # 如果有引用,不允许删除 + if meeting_count > 0 or kb_count > 0: + references = [] + if meeting_count > 0: + references.append(f"{meeting_count}个会议") + if kb_count > 0: + references.append(f"{kb_count}个知识库") + + return create_api_response( + code="400", + message=f"无法删除:该提示词被{' 和 '.join(references)}引用", + data={ + "meeting_count": meeting_count, + "kb_count": kb_count + } + ) + # 删除提示词 cursor.execute("DELETE FROM prompts WHERE id = %s", (prompt_id,)) connection.commit() diff --git a/app/models/models.py b/app/models/models.py index 98d6c35..5cd9894 100644 --- a/app/models/models.py +++ b/app/models/models.py @@ -77,6 +77,7 @@ class Meeting(BaseModel): audio_file_path: Optional[str] = None transcription_status: Optional[TranscriptionTaskStatus] = None tags: Optional[List[Tag]] = [] + access_password: Optional[str] = None class TranscriptSegment(BaseModel): segment_id: int diff --git a/requirements-prod.txt b/requirements-prod.txt index 0cbca48..9bd1d4e 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -14,4 +14,7 @@ qiniu # Validation & Forms email-validator -python-multipart \ No newline at end of file +python-multipart + +# System Monitoring +psutil diff --git a/requirements.txt b/requirements.txt index 598fe20..3cdd46f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ qiniu redis>=5.0.0 dashscope PyJWT>=2.8.0 -python-jose[cryptography]>=3.3.0 \ No newline at end of file +python-jose[cryptography]>=3.3.0 +psutil