修改了会议获取列表
parent
2f36474f4d
commit
8f332145cb
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from app.core.auth import get_current_admin_user, get_current_user
|
||||
from app.core.config import LLM_CONFIG, DEFAULT_RESET_PASSWORD, MAX_FILE_SIZE, MAX_IMAGE_SIZE
|
||||
from app.core.config import LLM_CONFIG, DEFAULT_RESET_PASSWORD, MAX_FILE_SIZE, VOICEPRINT_CONFIG, TIMELINE_PAGESIZE
|
||||
from app.core.response import create_api_response
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
|
|
@ -13,10 +13,10 @@ CONFIG_FILE = Path(__file__).parent.parent.parent.parent / "config" / "system_co
|
|||
|
||||
class SystemConfigModel(BaseModel):
|
||||
model_name: str
|
||||
system_prompt: str
|
||||
template_text: str
|
||||
DEFAULT_RESET_PASSWORD: str
|
||||
MAX_FILE_SIZE: int # 字节为单位
|
||||
MAX_IMAGE_SIZE: int # 字节为单位
|
||||
TIMELINE_PAGESIZE: int # 分页数量
|
||||
|
||||
def load_config_from_file():
|
||||
"""从文件加载配置,如果文件不存在则返回默认配置"""
|
||||
|
|
@ -30,10 +30,10 @@ def load_config_from_file():
|
|||
# 返回默认配置
|
||||
return {
|
||||
'model_name': LLM_CONFIG['model_name'],
|
||||
'system_prompt': LLM_CONFIG['system_prompt'],
|
||||
'template_text': VOICEPRINT_CONFIG['template_text'],
|
||||
'DEFAULT_RESET_PASSWORD': DEFAULT_RESET_PASSWORD,
|
||||
'MAX_FILE_SIZE': MAX_FILE_SIZE,
|
||||
'MAX_IMAGE_SIZE': MAX_IMAGE_SIZE
|
||||
'TIMELINE_PAGESIZE': TIMELINE_PAGESIZE
|
||||
}
|
||||
|
||||
def save_config_to_file(config_data):
|
||||
|
|
@ -57,10 +57,10 @@ async def get_system_config(current_user=Depends(get_current_user)):
|
|||
config = load_config_from_file()
|
||||
response_data = {
|
||||
'model_name': config.get('model_name', LLM_CONFIG['model_name']),
|
||||
'system_prompt': config.get('system_prompt', LLM_CONFIG['system_prompt']),
|
||||
'template_text': config.get('template_text', VOICEPRINT_CONFIG['template_text']),
|
||||
'DEFAULT_RESET_PASSWORD': config.get('DEFAULT_RESET_PASSWORD', DEFAULT_RESET_PASSWORD),
|
||||
'MAX_FILE_SIZE': config.get('MAX_FILE_SIZE', MAX_FILE_SIZE),
|
||||
'MAX_IMAGE_SIZE': config.get('MAX_IMAGE_SIZE', MAX_IMAGE_SIZE),
|
||||
'TIMELINE_PAGESIZE': config.get('TIMELINE_PAGESIZE', TIMELINE_PAGESIZE),
|
||||
}
|
||||
return create_api_response(code="200", message="配置获取成功", data=response_data)
|
||||
except Exception as e:
|
||||
|
|
@ -78,10 +78,10 @@ async def update_system_config(
|
|||
try:
|
||||
config_data = {
|
||||
'model_name': config.model_name,
|
||||
'system_prompt': config.system_prompt,
|
||||
'template_text': config.template_text,
|
||||
'DEFAULT_RESET_PASSWORD': config.DEFAULT_RESET_PASSWORD,
|
||||
'MAX_FILE_SIZE': config.MAX_FILE_SIZE,
|
||||
'MAX_IMAGE_SIZE': config.MAX_IMAGE_SIZE
|
||||
'TIMELINE_PAGESIZE': config.TIMELINE_PAGESIZE
|
||||
}
|
||||
|
||||
if not save_config_to_file(config_data):
|
||||
|
|
@ -89,11 +89,11 @@ async def update_system_config(
|
|||
|
||||
# 更新运行时配置
|
||||
LLM_CONFIG['model_name'] = config.model_name
|
||||
LLM_CONFIG['system_prompt'] = config.system_prompt
|
||||
VOICEPRINT_CONFIG['template_text'] = config.template_text
|
||||
import app.core.config as config_module
|
||||
config_module.DEFAULT_RESET_PASSWORD = config.DEFAULT_RESET_PASSWORD
|
||||
config_module.MAX_FILE_SIZE = config.MAX_FILE_SIZE
|
||||
config_module.MAX_IMAGE_SIZE = config.MAX_IMAGE_SIZE
|
||||
config_module.TIMELINE_PAGESIZE = config.TIMELINE_PAGESIZE
|
||||
|
||||
return create_api_response(
|
||||
code="200",
|
||||
|
|
@ -109,11 +109,11 @@ def load_system_config():
|
|||
try:
|
||||
config = load_config_from_file()
|
||||
LLM_CONFIG['model_name'] = config.get('model_name', LLM_CONFIG['model_name'])
|
||||
LLM_CONFIG['system_prompt'] = config.get('system_prompt', LLM_CONFIG['system_prompt'])
|
||||
VOICEPRINT_CONFIG['template_text'] = config.get('template_text', VOICEPRINT_CONFIG['template_text'])
|
||||
import app.core.config as config_module
|
||||
config_module.DEFAULT_RESET_PASSWORD = config.get('DEFAULT_RESET_PASSWORD', DEFAULT_RESET_PASSWORD)
|
||||
config_module.MAX_FILE_SIZE = config.get('MAX_FILE_SIZE', MAX_FILE_SIZE)
|
||||
config_module.MAX_IMAGE_SIZE = config.get('MAX_IMAGE_SIZE', MAX_IMAGE_SIZE)
|
||||
print(f"系统配置加载成功: model={config.get('model_name')}")
|
||||
config_module.TIMELINE_PAGESIZE = config.get('TIMELINE_PAGESIZE', TIMELINE_PAGESIZE)
|
||||
print(f"系统配置加载成功: model={config.get('model_name')}, pagesize={config.get('TIMELINE_PAGESIZE')}")
|
||||
except Exception as e:
|
||||
print(f"加载系统配置失败,使用默认配置: {e}")
|
||||
|
|
|
|||
|
|
@ -45,34 +45,180 @@ def _process_tags(cursor, tag_string: Optional[str], creator_id: Optional[int] =
|
|||
return [Tag(**tag) for tag in tags_data]
|
||||
|
||||
@router.get("/meetings")
|
||||
def get_meetings(current_user: dict = Depends(get_current_user), user_id: Optional[int] = None):
|
||||
def get_meetings(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
user_id: Optional[int] = None,
|
||||
page: int = 1,
|
||||
page_size: Optional[int] = None,
|
||||
search: Optional[str] = None,
|
||||
tags: Optional[str] = None,
|
||||
filter_type: str = "all"
|
||||
):
|
||||
from app.core.config import TIMELINE_PAGESIZE
|
||||
|
||||
# 使用配置的默认页面大小
|
||||
if page_size is None:
|
||||
page_size = TIMELINE_PAGESIZE
|
||||
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# 构建WHERE子句
|
||||
where_conditions = []
|
||||
params = []
|
||||
|
||||
# 用户过滤
|
||||
if user_id:
|
||||
# 需要联表查询参与者
|
||||
has_attendees_join = True
|
||||
else:
|
||||
has_attendees_join = False
|
||||
|
||||
# 按类型过滤 (created/attended/all)
|
||||
if user_id:
|
||||
if filter_type == "created":
|
||||
where_conditions.append("m.user_id = %s")
|
||||
params.append(user_id)
|
||||
elif filter_type == "attended":
|
||||
where_conditions.append("m.user_id != %s AND a.user_id = %s")
|
||||
params.extend([user_id, user_id])
|
||||
has_attendees_join = True
|
||||
else: # all
|
||||
where_conditions.append("(m.user_id = %s OR a.user_id = %s)")
|
||||
params.extend([user_id, user_id])
|
||||
has_attendees_join = True
|
||||
|
||||
# 搜索关键词过滤
|
||||
if search and search.strip():
|
||||
search_pattern = f"%{search.strip()}%"
|
||||
where_conditions.append("(m.title LIKE %s OR u.caption LIKE %s)")
|
||||
params.extend([search_pattern, search_pattern])
|
||||
|
||||
# 标签过滤
|
||||
if tags and tags.strip():
|
||||
tag_list = [t.strip() for t in tags.split(',') if t.strip()]
|
||||
if tag_list:
|
||||
# 使用JSON_CONTAINS或LIKE查询
|
||||
tag_conditions = []
|
||||
for tag in tag_list:
|
||||
tag_conditions.append("m.tags LIKE %s")
|
||||
params.append(f"%{tag}%")
|
||||
where_conditions.append(f"({' OR '.join(tag_conditions)})")
|
||||
|
||||
# 构建基础查询
|
||||
base_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
|
||||
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
|
||||
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
|
||||
'''
|
||||
if user_id:
|
||||
query = f'{base_query} LEFT JOIN attendees a ON m.meeting_id = a.meeting_id WHERE m.user_id = %s OR a.user_id = %s GROUP BY m.meeting_id ORDER BY m.meeting_time DESC, m.created_at DESC'
|
||||
cursor.execute(query, (user_id, user_id))
|
||||
|
||||
if has_attendees_join:
|
||||
base_query += " LEFT JOIN attendees a ON m.meeting_id = a.meeting_id"
|
||||
|
||||
# 添加WHERE子句
|
||||
if where_conditions:
|
||||
base_query += f" WHERE {' AND '.join(where_conditions)}"
|
||||
|
||||
# 获取总数 - 需要在添加 GROUP BY 之前
|
||||
count_base = base_query # 保存一份不含GROUP BY的查询
|
||||
if has_attendees_join:
|
||||
# 如果有联表,使用子查询计数
|
||||
count_query = f"SELECT COUNT(DISTINCT m.meeting_id) as total {count_base[count_base.find('FROM'):]}"
|
||||
else:
|
||||
query = f" {base_query} ORDER BY m.meeting_time DESC, m.created_at DESC"
|
||||
cursor.execute(query)
|
||||
# 没有联表,直接计数
|
||||
count_query = f"SELECT COUNT(*) as total {count_base[count_base.find('FROM'):]}"
|
||||
|
||||
cursor.execute(count_query, params)
|
||||
total = cursor.fetchone()['total']
|
||||
|
||||
# 添加GROUP BY(如果联表了attendees)
|
||||
if has_attendees_join:
|
||||
base_query += " GROUP BY m.meeting_id"
|
||||
|
||||
# 计算分页
|
||||
total_pages = (total + page_size - 1) // page_size
|
||||
has_more = page < total_pages
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
# 添加排序和分页
|
||||
query = f"{base_query} ORDER BY m.meeting_time DESC, m.created_at DESC LIMIT %s OFFSET %s"
|
||||
params.extend([page_size, offset])
|
||||
|
||||
cursor.execute(query, params)
|
||||
meetings = cursor.fetchall()
|
||||
|
||||
meeting_list = []
|
||||
for meeting in meetings:
|
||||
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'
|
||||
cursor.execute(attendees_query, (meeting['meeting_id'],))
|
||||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
tags = _process_tags(cursor, meeting.get('tags'))
|
||||
tags_list = _process_tags(cursor, meeting.get('tags'))
|
||||
meeting_list.append(Meeting(
|
||||
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'],
|
||||
attendees=attendees, creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags
|
||||
attendees=attendees, creator_id=meeting['creator_id'], creator_username=meeting['creator_username'], tags=tags_list
|
||||
))
|
||||
return create_api_response(code="200", message="获取会议列表成功", data=meeting_list)
|
||||
|
||||
return create_api_response(code="200", message="获取会议列表成功", data={
|
||||
"meetings": meeting_list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_pages": total_pages,
|
||||
"has_more": has_more
|
||||
})
|
||||
|
||||
@router.get("/meetings/stats")
|
||||
def get_meetings_stats(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
user_id: Optional[int] = None
|
||||
):
|
||||
"""
|
||||
获取会议统计数据:全部会议、我创建的会议、我参加的会议数量
|
||||
"""
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
if not user_id:
|
||||
return create_api_response(code="400", message="user_id is required")
|
||||
|
||||
# 获取全部会议数量(创建的 + 参加的)
|
||||
all_query = '''
|
||||
SELECT COUNT(DISTINCT m.meeting_id) as count
|
||||
FROM meetings m
|
||||
LEFT JOIN attendees a ON m.meeting_id = a.meeting_id
|
||||
WHERE m.user_id = %s OR a.user_id = %s
|
||||
'''
|
||||
cursor.execute(all_query, (user_id, user_id))
|
||||
all_count = cursor.fetchone()['count']
|
||||
|
||||
# 获取我创建的会议数量
|
||||
created_query = '''
|
||||
SELECT COUNT(*) as count
|
||||
FROM meetings m
|
||||
WHERE m.user_id = %s
|
||||
'''
|
||||
cursor.execute(created_query, (user_id,))
|
||||
created_count = cursor.fetchone()['count']
|
||||
|
||||
# 获取我参加的会议数量(不包括我创建的)
|
||||
attended_query = '''
|
||||
SELECT COUNT(DISTINCT a.meeting_id) as count
|
||||
FROM attendees a
|
||||
JOIN meetings m ON a.meeting_id = m.meeting_id
|
||||
WHERE a.user_id = %s AND m.user_id != %s
|
||||
'''
|
||||
cursor.execute(attended_query, (user_id, user_id))
|
||||
attended_count = cursor.fetchone()['count']
|
||||
|
||||
return create_api_response(code="200", message="获取会议统计成功", data={
|
||||
"all_meetings": all_count,
|
||||
"created_meetings": created_count,
|
||||
"attended_meetings": attended_count
|
||||
})
|
||||
|
||||
@router.get("/meetings/{meeting_id}")
|
||||
def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_current_user)):
|
||||
|
|
@ -477,3 +623,53 @@ def get_meeting_llm_tasks(meeting_id: int, current_user: dict = Depends(get_curr
|
|||
})
|
||||
except Exception as e:
|
||||
return create_api_response(code="500", message=f"Failed to get LLM tasks: {str(e)}")
|
||||
|
||||
@router.get("/meetings/{meeting_id}/preview-data")
|
||||
def get_meeting_preview_data(meeting_id: int):
|
||||
"""
|
||||
获取会议预览数据(无需登录认证)
|
||||
用于二维码扫描后的预览页面
|
||||
"""
|
||||
try:
|
||||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# 检查会议是否存在
|
||||
query = '''
|
||||
SELECT m.meeting_id, m.title, m.meeting_time, m.summary, m.updated_at,
|
||||
m.user_id as creator_id, u.caption as creator_username
|
||||
FROM meetings m
|
||||
JOIN users u ON m.user_id = u.user_id
|
||||
WHERE m.meeting_id = %s
|
||||
'''
|
||||
cursor.execute(query, (meeting_id,))
|
||||
meeting = cursor.fetchone()
|
||||
|
||||
if not meeting:
|
||||
return create_api_response(code="404", message="会议不存在")
|
||||
|
||||
# 检查是否已生成会议总结
|
||||
if not meeting['summary'] or not meeting['updated_at']:
|
||||
return create_api_response(code="400", message="该会议总结尚未生成")
|
||||
|
||||
# 获取参会人员信息
|
||||
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'
|
||||
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 = {
|
||||
"meeting_id": meeting['meeting_id'],
|
||||
"title": meeting['title'],
|
||||
"meeting_time": meeting['meeting_time'],
|
||||
"summary": meeting['summary'],
|
||||
"creator_username": meeting['creator_username'],
|
||||
"attendees": attendees,
|
||||
"attendees_count": len(attendees)
|
||||
}
|
||||
|
||||
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)}")
|
||||
|
|
|
|||
|
|
@ -95,3 +95,6 @@ VOICEPRINT_CONFIG = {
|
|||
"sample_rate": 16000,
|
||||
"channels": 1
|
||||
}
|
||||
|
||||
#首页TimeLine每页数量
|
||||
TIMELINE_PAGESIZE=10
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"model_name": "qwen-plus",
|
||||
"system_prompt": "你是一个专业的会议记录分析助手。请根据提供的会议转录内容,生成简洁明了的会议总结。\n\n总结包括五个部分(名称严格一致,生成为MD二级目录):\n1. 会议概述 - 简要说明会议的主要目的和背景(生成MD引用)\n2. 主要讨论点 - 列出会议中讨论的重要话题和内容\n3. 决策事项 - 明确记录会议中做出的决定和结论\n4. 待办事项 - 列出需要后续跟进的任务和责任人\n5. 关键信息 - 其他重要的信息点\n\n输出要求:\n- 保持客观中性,不添加个人观点\n- 使用简洁、准确的中文表达\n- 按重要性排序各项内容\n- 如果某个部分没有相关内容,可以说明\"无相关内容\"\n- 总字数控制在500字以内",
|
||||
"template_text": "我正在进行声纹采集,这段语音将用于身份识别和验证。\n声纹技术能够识别每个人独特的声音特征,用于人声识别应用。",
|
||||
"DEFAULT_RESET_PASSWORD": "123456",
|
||||
"MAX_FILE_SIZE": 208666624,
|
||||
"MAX_IMAGE_SIZE": 10485760
|
||||
"TIMELINE_PAGESIZE": 10
|
||||
}
|
||||
Loading…
Reference in New Issue