main
mula.liu 2025-12-19 17:44:51 +08:00
parent 1a15cdff88
commit 9a4686bedf
7 changed files with 204 additions and 26 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app.zip 100644

Binary file not shown.

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -20,4 +20,4 @@ python-multipart
psutil psutil
# APK Parsing # APK Parsing
androguard pyaxmlparser

View File

@ -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