1.2.1
parent
1a15cdff88
commit
9a4686bedf
|
|
@ -50,6 +50,88 @@ def _process_tags(cursor, tag_string: Optional[str], creator_id: Optional[int] =
|
||||||
tags_data = cursor.fetchall()
|
tags_data = cursor.fetchall()
|
||||||
return [Tag(**tag) for tag in tags_data]
|
return [Tag(**tag) for tag in tags_data]
|
||||||
|
|
||||||
|
def _get_meeting_overall_status(meeting_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
获取会议的整体进度状态(包含转译和LLM两个阶段)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {
|
||||||
|
"overall_status": "pending" | "transcribing" | "summarizing" | "completed" | "failed",
|
||||||
|
"overall_progress": 0-100,
|
||||||
|
"current_stage": "transcription" | "llm" | "completed",
|
||||||
|
"transcription": {status, progress, task_id, error_message, created_at},
|
||||||
|
"llm": {status, progress, task_id, error_message, created_at}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# 获取转译状态
|
||||||
|
transcription_status = transcription_service.get_meeting_transcription_status(meeting_id)
|
||||||
|
trans_data = {
|
||||||
|
"status": transcription_status.get('status', 'pending') if transcription_status else 'pending',
|
||||||
|
"progress": transcription_status.get('progress', 0) if transcription_status else 0,
|
||||||
|
"task_id": transcription_status.get('task_id') if transcription_status else None,
|
||||||
|
"error_message": transcription_status.get('error_message') if transcription_status else None,
|
||||||
|
"created_at": transcription_status.get('created_at') if transcription_status else None
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取LLM状态
|
||||||
|
llm_status = async_meeting_service.get_meeting_llm_status(meeting_id)
|
||||||
|
llm_data = {
|
||||||
|
"status": llm_status.get('status', 'pending') if llm_status else 'pending',
|
||||||
|
"progress": llm_status.get('progress', 0) if llm_status else 0,
|
||||||
|
"task_id": llm_status.get('task_id') if llm_status else None,
|
||||||
|
"error_message": llm_status.get('error_message') if llm_status else None,
|
||||||
|
"created_at": llm_status.get('created_at') if llm_status else None
|
||||||
|
}
|
||||||
|
|
||||||
|
# 计算整体状态和进度
|
||||||
|
trans_status_val = trans_data["status"]
|
||||||
|
llm_status_val = llm_data["status"]
|
||||||
|
|
||||||
|
# 判断是否有失败
|
||||||
|
if trans_status_val == 'failed':
|
||||||
|
overall_status = "failed"
|
||||||
|
current_stage = "transcription"
|
||||||
|
overall_progress = 0
|
||||||
|
elif llm_status_val == 'failed':
|
||||||
|
overall_status = "failed"
|
||||||
|
current_stage = "llm"
|
||||||
|
overall_progress = 50 # 转译已完成
|
||||||
|
# 判断当前阶段
|
||||||
|
elif trans_status_val == 'completed' and llm_status_val == 'completed':
|
||||||
|
overall_status = "completed"
|
||||||
|
current_stage = "completed"
|
||||||
|
overall_progress = 100
|
||||||
|
elif trans_status_val == 'completed':
|
||||||
|
# 转译完成,进入LLM阶段
|
||||||
|
if llm_status_val in ['pending', 'processing']:
|
||||||
|
overall_status = "summarizing"
|
||||||
|
current_stage = "llm"
|
||||||
|
overall_progress = 50 + int(llm_data["progress"] * 0.5)
|
||||||
|
else:
|
||||||
|
# llm还未开始
|
||||||
|
overall_status = "summarizing"
|
||||||
|
current_stage = "llm"
|
||||||
|
overall_progress = 50
|
||||||
|
else:
|
||||||
|
# 还在转译阶段
|
||||||
|
if trans_status_val in ['pending', 'processing']:
|
||||||
|
overall_status = "transcribing"
|
||||||
|
current_stage = "transcription"
|
||||||
|
overall_progress = int(trans_data["progress"] * 0.5)
|
||||||
|
else:
|
||||||
|
# 转译还未开始
|
||||||
|
overall_status = "pending"
|
||||||
|
current_stage = "transcription"
|
||||||
|
overall_progress = 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
"overall_status": overall_status,
|
||||||
|
"overall_progress": overall_progress,
|
||||||
|
"current_stage": current_stage,
|
||||||
|
"transcription": trans_data,
|
||||||
|
"llm": llm_data
|
||||||
|
}
|
||||||
|
|
||||||
@router.get("/meetings")
|
@router.get("/meetings")
|
||||||
def get_meetings(
|
def get_meetings(
|
||||||
current_user: dict = Depends(get_current_user),
|
current_user: dict = Depends(get_current_user),
|
||||||
|
|
@ -164,7 +246,8 @@ def get_meetings(
|
||||||
meeting_list.append(Meeting(
|
meeting_list.append(Meeting(
|
||||||
meeting_id=meeting['meeting_id'], title=meeting['title'], meeting_time=meeting['meeting_time'],
|
meeting_id=meeting['meeting_id'], title=meeting['title'], meeting_time=meeting['meeting_time'],
|
||||||
summary=meeting['summary'], created_at=meeting['created_at'], audio_file_path=meeting['audio_file_path'],
|
summary=meeting['summary'], created_at=meeting['created_at'], audio_file_path=meeting['audio_file_path'],
|
||||||
attendees=attendees, creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags_list
|
attendees=attendees, creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags_list,
|
||||||
|
access_password=meeting.get('access_password')
|
||||||
))
|
))
|
||||||
|
|
||||||
return create_api_response(code="200", message="获取会议列表成功", data={
|
return create_api_response(code="200", message="获取会议列表成功", data={
|
||||||
|
|
@ -882,12 +965,18 @@ def get_meeting_preview_data(meeting_id: int):
|
||||||
"""
|
"""
|
||||||
获取会议预览数据(无需登录认证)
|
获取会议预览数据(无需登录认证)
|
||||||
用于二维码扫描后的预览页面
|
用于二维码扫描后的预览页面
|
||||||
|
|
||||||
|
返回状态码说明:
|
||||||
|
- 200: 会议已完成(summary已生成)
|
||||||
|
- 400: 会议处理中(转译或总结阶段)
|
||||||
|
- 503: 处理失败(转译或总结失败)
|
||||||
|
- 404: 会议不存在
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with get_db_connection() as connection:
|
with get_db_connection() as connection:
|
||||||
cursor = connection.cursor(dictionary=True)
|
cursor = connection.cursor(dictionary=True)
|
||||||
|
|
||||||
# 检查会议是否存在,并获取模版信息
|
# 检查会议是否存在,并获取基本信息
|
||||||
query = '''
|
query = '''
|
||||||
SELECT m.meeting_id, m.title, m.meeting_time, m.summary, m.updated_at, m.prompt_id,
|
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,
|
m.user_id as creator_id, u.caption as creator_username,
|
||||||
|
|
@ -903,15 +992,55 @@ def get_meeting_preview_data(meeting_id: int):
|
||||||
if not meeting:
|
if not meeting:
|
||||||
return create_api_response(code="404", message="会议不存在")
|
return create_api_response(code="404", message="会议不存在")
|
||||||
|
|
||||||
# 检查是否已生成会议总结
|
# 获取整体进度状态(两阶段)
|
||||||
if not meeting['summary'] or not meeting['updated_at']:
|
progress_info = _get_meeting_overall_status(meeting_id)
|
||||||
return create_api_response(code="400", message="该会议总结尚未生成")
|
overall_status = progress_info["overall_status"]
|
||||||
|
|
||||||
|
# 根据整体状态返回不同响应
|
||||||
|
|
||||||
|
# 情况1: 任一阶段失败 → 返回503
|
||||||
|
if overall_status == "failed":
|
||||||
|
failed_stage = progress_info["current_stage"]
|
||||||
|
error_info = progress_info["transcription"] if failed_stage == "transcription" else progress_info["llm"]
|
||||||
|
error_message = error_info["error_message"] or "处理失败"
|
||||||
|
|
||||||
|
stage_name = "转译" if failed_stage == "transcription" else "总结"
|
||||||
|
return create_api_response(
|
||||||
|
code="503",
|
||||||
|
message=f"会议{stage_name}失败: {error_message}",
|
||||||
|
data={
|
||||||
|
"meeting_id": meeting_id,
|
||||||
|
"title": meeting['title'],
|
||||||
|
"processing_status": progress_info
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 情况2: 处理中(转译或总结阶段)→ 返回400
|
||||||
|
if overall_status in ["pending", "transcribing", "summarizing"]:
|
||||||
|
stage_descriptions = {
|
||||||
|
"pending": "等待开始",
|
||||||
|
"transcribing": "正在转译音频",
|
||||||
|
"summarizing": "正在生成总结"
|
||||||
|
}
|
||||||
|
return create_api_response(
|
||||||
|
code="400",
|
||||||
|
message=f"会议正在处理中: {stage_descriptions[overall_status]}",
|
||||||
|
data={
|
||||||
|
"meeting_id": meeting_id,
|
||||||
|
"title": meeting['title'],
|
||||||
|
"processing_status": progress_info
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 情况3: 全部完成 → 返回200,提供完整预览数据
|
||||||
|
if overall_status == "completed" and meeting['summary']:
|
||||||
# 获取参会人员信息
|
# 获取参会人员信息
|
||||||
attendees_query = 'SELECT u.user_id, u.caption FROM attendees a JOIN users u ON a.user_id = u.user_id WHERE a.meeting_id = %s'
|
with get_db_connection() as connection:
|
||||||
cursor.execute(attendees_query, (meeting_id,))
|
cursor = connection.cursor(dictionary=True)
|
||||||
attendees_data = cursor.fetchall()
|
attendees_query = 'SELECT u.user_id, u.caption FROM attendees a JOIN users u ON a.user_id = u.user_id WHERE a.meeting_id = %s'
|
||||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
cursor.execute(attendees_query, (meeting_id,))
|
||||||
|
attendees_data = cursor.fetchall()
|
||||||
|
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||||
|
|
||||||
# 组装返回数据
|
# 组装返回数据
|
||||||
preview_data = {
|
preview_data = {
|
||||||
|
|
@ -924,7 +1053,8 @@ def get_meeting_preview_data(meeting_id: int):
|
||||||
"prompt_name": meeting['prompt_name'],
|
"prompt_name": meeting['prompt_name'],
|
||||||
"attendees": attendees,
|
"attendees": attendees,
|
||||||
"attendees_count": len(attendees),
|
"attendees_count": len(attendees),
|
||||||
"has_password": bool(meeting.get('access_password')) # 新增:是否设置了访问密码
|
"has_password": bool(meeting.get('access_password')),
|
||||||
|
"processing_status": progress_info # 附带进度信息供调试
|
||||||
}
|
}
|
||||||
|
|
||||||
return create_api_response(code="200", message="获取会议预览数据成功", data=preview_data)
|
return create_api_response(code="200", message="获取会议预览数据成功", data=preview_data)
|
||||||
|
|
|
||||||
|
|
@ -321,6 +321,57 @@ class AsyncMeetingService:
|
||||||
print(f"Error getting meeting LLM tasks: {e}")
|
print(f"Error getting meeting LLM tasks: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def get_meeting_llm_status(self, meeting_id: int) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取会议的最新LLM任务状态(与transcription对齐)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
meeting_id: 会议ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[Dict]: 任务状态信息,如果没有任务返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with get_db_connection() as connection:
|
||||||
|
cursor = connection.cursor(dictionary=True)
|
||||||
|
|
||||||
|
# 查询最新的LLM任务
|
||||||
|
query = """
|
||||||
|
SELECT task_id, status, progress, created_at, completed_at, error_message
|
||||||
|
FROM llm_tasks
|
||||||
|
WHERE meeting_id = %s
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
cursor.execute(query, (meeting_id,))
|
||||||
|
task_record = cursor.fetchone()
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
if not task_record:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 如果任务还在进行中,获取最新状态
|
||||||
|
if task_record['status'] in ['pending', 'processing']:
|
||||||
|
try:
|
||||||
|
return self.get_task_status(task_record['task_id'])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to get latest LLM task status for meeting {meeting_id}, returning DB status. Error: {e}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'task_id': task_record['task_id'],
|
||||||
|
'status': task_record['status'],
|
||||||
|
'progress': task_record['progress'] or 0,
|
||||||
|
'meeting_id': meeting_id,
|
||||||
|
'created_at': task_record['created_at'].isoformat() if task_record['created_at'] else None,
|
||||||
|
'completed_at': task_record['completed_at'].isoformat() if task_record['completed_at'] else None,
|
||||||
|
'error_message': task_record['error_message']
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting meeting LLM status: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
def _update_task_status_in_redis(self, task_id: str, status: str, progress: int, message: str = None, result: str = None, error_message: str = None):
|
def _update_task_status_in_redis(self, task_id: str, status: str, progress: int, message: str = None, result: str = None, error_message: str = None):
|
||||||
"""更新Redis中的任务状态"""
|
"""更新Redis中的任务状态"""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,20 @@
|
||||||
"""
|
"""
|
||||||
APK解析工具
|
APK解析工具
|
||||||
用于从APK文件中提取版本信息
|
用于从APK文件中提取版本信息(使用 pyaxmlparser)
|
||||||
"""
|
"""
|
||||||
import zipfile
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
import struct
|
|
||||||
|
|
||||||
|
|
||||||
# 如果安装了 androguard,使用更可靠的解析方法
|
|
||||||
def parse_apk_with_androguard(apk_path):
|
def parse_apk_with_androguard(apk_path):
|
||||||
"""
|
"""
|
||||||
使用 androguard 库解析 APK
|
解析APK文件,提取版本信息
|
||||||
需要先安装: pip install androguard
|
使用 pyaxmlparser 库(轻量级,~1MB)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from androguard.core.apk import APK
|
from pyaxmlparser import APK
|
||||||
|
|
||||||
apk = APK(apk_path)
|
apk = APK(apk_path)
|
||||||
version_code = apk.get_androidversion_code()
|
version_code = apk.version_code
|
||||||
version_name = apk.get_androidversion_name()
|
version_name = apk.version_name
|
||||||
|
|
||||||
print(f"APK解析成功: version_code={version_code}, version_name={version_name}")
|
print(f"APK解析成功: version_code={version_code}, version_name={version_name}")
|
||||||
|
|
||||||
|
|
@ -26,11 +22,12 @@ def parse_apk_with_androguard(apk_path):
|
||||||
'version_code': int(version_code) if version_code else None,
|
'version_code': int(version_code) if version_code else None,
|
||||||
'version_name': version_name
|
'version_name': version_name
|
||||||
}
|
}
|
||||||
except ImportError as ie:
|
except ImportError:
|
||||||
print(f"androguard 导入失败: {str(ie)}")
|
print("错误: pyaxmlparser 未安装")
|
||||||
return parse_apk(apk_path)
|
print("请运行: pip install pyaxmlparser")
|
||||||
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"使用androguard解析APK失败: {str(e)}")
|
print(f"APK解析失败: {str(e)}")
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,4 @@ python-multipart
|
||||||
psutil
|
psutil
|
||||||
|
|
||||||
# APK Parsing
|
# APK Parsing
|
||||||
androguard
|
pyaxmlparser
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,4 @@ dashscope
|
||||||
PyJWT>=2.8.0
|
PyJWT>=2.8.0
|
||||||
python-jose[cryptography]>=3.3.0
|
python-jose[cryptography]>=3.3.0
|
||||||
psutil
|
psutil
|
||||||
androguard
|
pyaxmlparser
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue