添加了会议标签

main
mula.liu 2025-09-19 16:51:07 +08:00
parent f6e1fd6d94
commit 9fb07bb435
5 changed files with 112 additions and 15 deletions

BIN
app.zip

Binary file not shown.

View File

@ -1,5 +1,6 @@
from fastapi import APIRouter, HTTPException, UploadFile, File, Form, Depends, BackgroundTasks
from app.models.models import Meeting, TranscriptSegment, TranscriptionTaskStatus, CreateMeetingRequest, UpdateMeetingRequest, SpeakerTagUpdateRequest, BatchSpeakerTagUpdateRequest, TranscriptUpdateRequest, BatchTranscriptUpdateRequest
from app.models.models import Meeting, TranscriptSegment, TranscriptionTaskStatus, CreateMeetingRequest, UpdateMeetingRequest, SpeakerTagUpdateRequest, BatchSpeakerTagUpdateRequest, TranscriptUpdateRequest, BatchTranscriptUpdateRequest, Tag
from app.core.database import get_db_connection
from app.core.config import BASE_DIR, UPLOAD_DIR, AUDIO_DIR, MARKDOWN_DIR, ALLOWED_EXTENSIONS, ALLOWED_IMAGE_EXTENSIONS, MAX_FILE_SIZE, MAX_IMAGE_SIZE
from app.services.qiniu_service import qiniu_service
@ -7,7 +8,7 @@ from app.services.llm_service import LLMService
from app.services.async_transcription_service import AsyncTranscriptionService
from app.services.async_llm_service import async_llm_service
from app.core.auth import get_current_user, get_optional_current_user
from typing import Optional
from typing import List, Optional
from datetime import datetime
from pydantic import BaseModel
import os
@ -27,6 +28,24 @@ transcription_service = AsyncTranscriptionService()
class GenerateSummaryRequest(BaseModel):
user_prompt: Optional[str] = ""
def _process_tags(cursor, tag_string: Optional[str]) -> List[Tag]:
if not tag_string:
return []
tag_names = [name.strip() for name in tag_string.split(',') if name.strip()]
if not tag_names:
return []
# Ensure all tags exist in the 'tags' table
insert_ignore_query = "INSERT IGNORE INTO tags (name) VALUES (%s)"
cursor.executemany(insert_ignore_query, [(name,) for name in tag_names])
# Fetch the full tag objects
format_strings = ', '.join(['%s'] * len(tag_names))
cursor.execute(f"SELECT id, name, color FROM tags WHERE name IN ({format_strings})", tuple(tag_names))
tags_data = cursor.fetchall()
return [Tag(**tag) for tag in tags_data]
@router.get("/meetings", response_model=list[Meeting])
def get_meetings(current_user: dict = Depends(get_current_user), user_id: Optional[int] = None):
with get_db_connection() as connection:
@ -34,7 +53,7 @@ def get_meetings(current_user: dict = Depends(get_current_user), user_id: Option
base_query = '''
SELECT
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
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
FROM meetings m
JOIN users u ON m.user_id = u.user_id
@ -67,6 +86,8 @@ def get_meetings(current_user: dict = Depends(get_current_user), user_id: Option
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'))
meeting_list.append(Meeting(
meeting_id=meeting['meeting_id'],
title=meeting['title'],
@ -75,7 +96,8 @@ def get_meetings(current_user: dict = Depends(get_current_user), user_id: Option
created_at=meeting['created_at'],
attendees=attendees,
creator_id=meeting['creator_id'],
creator_username=meeting['creator_username']
creator_username=meeting['creator_username'],
tags=tags
))
return meeting_list
@ -87,7 +109,7 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
query = '''
SELECT
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
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
@ -112,6 +134,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
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'))
# 关闭游标,避免与转录服务的数据库连接冲突
cursor.close()
@ -123,7 +147,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
created_at=meeting['created_at'],
attendees=attendees,
creator_id=meeting['creator_id'],
creator_username=meeting['creator_username']
creator_username=meeting['creator_username'],
tags=tags
)
# Add audio file path if exists
@ -182,16 +207,24 @@ def create_meeting(meeting_request: CreateMeetingRequest, current_user: dict = D
with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True)
# Process tags
if meeting_request.tags:
tag_names = [name.strip() for name in meeting_request.tags.split(',') if name.strip()]
if tag_names:
insert_ignore_query = "INSERT IGNORE INTO tags (name) VALUES (%s)"
cursor.executemany(insert_ignore_query, [(name,) for name in tag_names])
# Create meeting
meeting_query = '''
INSERT INTO meetings (user_id, title, meeting_time, summary,created_at)
VALUES (%s, %s, %s, %s, %s)
INSERT INTO meetings (user_id, title, meeting_time, summary, tags, created_at)
VALUES (%s, %s, %s, %s, %s, %s)
'''
cursor.execute(meeting_query, (
meeting_request.user_id,
meeting_request.title,
meeting_request.meeting_time,
None,
meeting_request.tags,
datetime.now().isoformat()
))
@ -221,17 +254,25 @@ def update_meeting(meeting_id: int, meeting_request: UpdateMeetingRequest, curre
if meeting['user_id'] != current_user['user_id']:
raise HTTPException(status_code=403, detail="Permission denied")
# Process tags
if meeting_request.tags:
tag_names = [name.strip() for name in meeting_request.tags.split(',') if name.strip()]
if tag_names:
insert_ignore_query = "INSERT IGNORE INTO tags (name) VALUES (%s)"
cursor.executemany(insert_ignore_query, [(name,) for name in tag_names])
# Update meeting
update_query = '''
UPDATE meetings
SET title = %s, meeting_time = %s, summary = %s
SET title = %s, meeting_time = %s, summary = %s, tags = %s
WHERE meeting_id = %s
'''
cursor.execute(update_query, (
meeting_request.title,
meeting_request.meeting_time,
meeting_request.summary,
meeting_request.tags,
meeting_id
))
@ -282,7 +323,7 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
query = '''
SELECT
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
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
@ -308,6 +349,8 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
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'))
# 关闭游标,避免与转录服务的数据库连接冲突
cursor.close()
@ -319,7 +362,8 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
created_at=meeting['created_at'],
attendees=attendees,
creator_id=meeting['creator_id'],
creator_username=meeting['creator_username']
creator_username=meeting['creator_username'],
tags=tags
)
# Add audio file path if exists
@ -906,4 +950,4 @@ def get_meeting_llm_tasks(meeting_id: int, current_user: dict = Depends(get_curr
# }
# except Exception as e:
# raise HTTPException(status_code=500, detail=f"Failed to get latest LLM task: {str(e)}")
# raise HTTPException(status_code=500, detail=f"Failed to get latest LLM task: {str(e)}")

View File

@ -0,0 +1,45 @@
from fastapi import APIRouter, HTTPException, Depends
from app.core.database import get_db_connection
from app.models.models import Tag
from typing import List
import mysql.connector
router = APIRouter()
@router.get("/tags/", response_model=List[Tag])
def get_all_tags():
"""_summary_
获取所有标签
"""
query = "SELECT id, name, color FROM tags ORDER BY name"
try:
with get_db_connection() as connection:
with connection.cursor(dictionary=True) as cursor:
cursor.execute(query)
tags = cursor.fetchall()
return tags
except mysql.connector.Error as err:
print(f"Error: {err}")
raise HTTPException(status_code=500, detail="Failed to retrieve tags from database.")
@router.post("/tags/", response_model=Tag)
def create_tag(tag_in: Tag):
"""_summary_
创建一个新标签
"""
query = "INSERT INTO tags (name, color) VALUES (%s, %s)"
try:
with get_db_connection() as connection:
with connection.cursor(dictionary=True) as cursor:
try:
cursor.execute(query, (tag_in.name, tag_in.color))
connection.commit()
tag_id = cursor.lastrowid
return {"id": tag_id, "name": tag_in.name, "color": tag_in.color}
except mysql.connector.IntegrityError:
connection.rollback()
raise HTTPException(status_code=400, detail=f"Tag '{tag_in.name}' already exists.")
except mysql.connector.Error as err:
print(f"Error: {err}")
raise HTTPException(status_code=500, detail="Failed to create tag in database.")

View File

@ -1,4 +1,3 @@
from pydantic import BaseModel, EmailStr
from typing import Optional, Union, List
import datetime
@ -51,6 +50,11 @@ class AttendeeInfo(BaseModel):
user_id: int
caption: str
class Tag(BaseModel):
id: int
name: str
color: str
class TranscriptionTaskStatus(BaseModel):
task_id: str
status: str # 'pending', 'processing', 'completed', 'failed'
@ -72,6 +76,7 @@ class Meeting(BaseModel):
creator_username: str
audio_file_path: Optional[str] = None
transcription_status: Optional[TranscriptionTaskStatus] = None
tags: Optional[List[Tag]] = []
class TranscriptSegment(BaseModel):
segment_id: int
@ -87,12 +92,14 @@ class CreateMeetingRequest(BaseModel):
title: str
meeting_time: Optional[datetime.datetime]
attendee_ids: list[int]
tags: Optional[str] = None
class UpdateMeetingRequest(BaseModel):
title: str
meeting_time: Optional[datetime.datetime]
summary: Optional[str]
attendee_ids: list[int]
tags: Optional[str] = None
class SpeakerTagUpdateRequest(BaseModel):
speaker_id: int # 使用原始speaker_id整数
@ -110,4 +117,4 @@ class BatchTranscriptUpdateRequest(BaseModel):
class PasswordChangeRequest(BaseModel):
old_password: str
new_password: str
new_password: str

View File

@ -2,7 +2,7 @@ import uvicorn
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from app.api.endpoints import auth, users, meetings
from app.api.endpoints import auth, users, meetings, tags
from app.core.config import UPLOAD_DIR, API_CONFIG, MAX_FILE_SIZE
from app.services.async_llm_service import async_llm_service
import os
@ -30,6 +30,7 @@ if UPLOAD_DIR.exists():
app.include_router(auth.router, prefix="/api", tags=["Authentication"])
app.include_router(users.router, prefix="/api", tags=["Users"])
app.include_router(meetings.router, prefix="/api", tags=["Meetings"])
app.include_router(tags.router, prefix="/api", tags=["Tags"])
@app.get("/")
def read_root():