修改了API接口定义

main
mula.liu 2025-09-26 15:47:37 +08:00
parent 91aeeca9c8
commit a243ee7e39
13 changed files with 291 additions and 766 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app.zip

Binary file not shown.

View File

@ -1,8 +1,8 @@
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, Depends
from app.core.auth import get_current_admin_user 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, MAX_IMAGE_SIZE
from app.core.response import create_api_response
from pydantic import BaseModel from pydantic import BaseModel
import os
import json import json
from pathlib import Path from pathlib import Path
@ -18,14 +18,6 @@ class SystemConfigModel(BaseModel):
MAX_FILE_SIZE: int # 字节为单位 MAX_FILE_SIZE: int # 字节为单位
MAX_IMAGE_SIZE: int # 字节为单位 MAX_IMAGE_SIZE: int # 字节为单位
class SystemConfigResponse(BaseModel):
model_name: str
system_prompt: str
DEFAULT_RESET_PASSWORD: str
MAX_FILE_SIZE: int
MAX_IMAGE_SIZE: int
message: str = ""
def load_config_from_file(): def load_config_from_file():
"""从文件加载配置,如果文件不存在则返回默认配置""" """从文件加载配置,如果文件不存在则返回默认配置"""
try: try:
@ -47,9 +39,7 @@ def load_config_from_file():
def save_config_to_file(config_data): def save_config_to_file(config_data):
"""将配置保存到文件""" """将配置保存到文件"""
try: try:
# 确保配置目录存在
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(CONFIG_FILE, 'w', encoding='utf-8') as f: with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config_data, f, ensure_ascii=False, indent=2) json.dump(config_data, f, ensure_ascii=False, indent=2)
return True return True
@ -57,28 +47,26 @@ def save_config_to_file(config_data):
print(f"保存配置文件失败: {e}") print(f"保存配置文件失败: {e}")
return False return False
@router.get("/admin/system-config", response_model=SystemConfigResponse) @router.get("/admin/system-config")
async def get_system_config(current_user=Depends(get_current_admin_user)): async def get_system_config(current_user=Depends(get_current_user)):
""" """
获取系统配置 获取系统配置
只有管理员才能访问 普通用户也可以获取
""" """
try: try:
# 优先从文件加载配置,然后从内存配置补充
config = load_config_from_file() config = load_config_from_file()
response_data = {
return SystemConfigResponse( 'model_name': config.get('model_name', LLM_CONFIG['model_name']),
model_name=config.get('model_name', LLM_CONFIG['model_name']), 'system_prompt': config.get('system_prompt', LLM_CONFIG['system_prompt']),
system_prompt=config.get('system_prompt', LLM_CONFIG['system_prompt']), 'DEFAULT_RESET_PASSWORD': config.get('DEFAULT_RESET_PASSWORD', DEFAULT_RESET_PASSWORD),
DEFAULT_RESET_PASSWORD=config.get('DEFAULT_RESET_PASSWORD', DEFAULT_RESET_PASSWORD), 'MAX_FILE_SIZE': config.get('MAX_FILE_SIZE', MAX_FILE_SIZE),
MAX_FILE_SIZE=config.get('MAX_FILE_SIZE', MAX_FILE_SIZE), 'MAX_IMAGE_SIZE': config.get('MAX_IMAGE_SIZE', MAX_IMAGE_SIZE),
MAX_IMAGE_SIZE=config.get('MAX_IMAGE_SIZE', MAX_IMAGE_SIZE), }
message="配置获取成功" return create_api_response(code="200", message="配置获取成功", data=response_data)
)
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"获取配置失败: {str(e)}") return create_api_response(code="500", message=f"获取配置失败: {str(e)}")
@router.put("/admin/system-config", response_model=SystemConfigResponse) @router.put("/admin/system-config")
async def update_system_config( async def update_system_config(
config: SystemConfigModel, config: SystemConfigModel,
current_user=Depends(get_current_admin_user) current_user=Depends(get_current_admin_user)
@ -88,7 +76,6 @@ async def update_system_config(
只有管理员才能访问 只有管理员才能访问
""" """
try: try:
# 准备要保存的配置数据
config_data = { config_data = {
'model_name': config.model_name, 'model_name': config.model_name,
'system_prompt': config.system_prompt, 'system_prompt': config.system_prompt,
@ -97,49 +84,36 @@ async def update_system_config(
'MAX_IMAGE_SIZE': config.MAX_IMAGE_SIZE 'MAX_IMAGE_SIZE': config.MAX_IMAGE_SIZE
} }
# 保存到文件
if not save_config_to_file(config_data): if not save_config_to_file(config_data):
raise HTTPException(status_code=500, detail="配置保存到文件失败") return create_api_response(code="500", message="配置保存到文件失败")
# 更新运行时配置 # 更新运行时配置
LLM_CONFIG['model_name'] = config.model_name LLM_CONFIG['model_name'] = config.model_name
LLM_CONFIG['system_prompt'] = config.system_prompt LLM_CONFIG['system_prompt'] = config.system_prompt
# 更新模块级别的配置
import app.core.config as config_module import app.core.config as config_module
config_module.DEFAULT_RESET_PASSWORD = config.DEFAULT_RESET_PASSWORD config_module.DEFAULT_RESET_PASSWORD = config.DEFAULT_RESET_PASSWORD
config_module.MAX_FILE_SIZE = config.MAX_FILE_SIZE config_module.MAX_FILE_SIZE = config.MAX_FILE_SIZE
config_module.MAX_IMAGE_SIZE = config.MAX_IMAGE_SIZE config_module.MAX_IMAGE_SIZE = config.MAX_IMAGE_SIZE
return SystemConfigResponse( return create_api_response(
model_name=config.model_name, code="200",
system_prompt=config.system_prompt, message="配置更新成功,重启服务后完全生效",
DEFAULT_RESET_PASSWORD=config.DEFAULT_RESET_PASSWORD, data=config_data
MAX_FILE_SIZE=config.MAX_FILE_SIZE,
MAX_IMAGE_SIZE=config.MAX_IMAGE_SIZE,
message="配置更新成功,重启服务后完全生效"
) )
except HTTPException:
raise
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"更新配置失败: {str(e)}") return create_api_response(code="500", message=f"更新配置失败: {str(e)}")
# 在应用启动时加载配置 # 在应用启动时加载配置
def load_system_config(): def load_system_config():
"""在应用启动时调用,加载保存的配置""" """在应用启动时调用,加载保存的配置"""
try: try:
config = load_config_from_file() config = load_config_from_file()
# 更新运行时配置
LLM_CONFIG['model_name'] = config.get('model_name', LLM_CONFIG['model_name']) LLM_CONFIG['model_name'] = config.get('model_name', LLM_CONFIG['model_name'])
LLM_CONFIG['system_prompt'] = config.get('system_prompt', LLM_CONFIG['system_prompt']) LLM_CONFIG['system_prompt'] = config.get('system_prompt', LLM_CONFIG['system_prompt'])
# 更新其他配置
import app.core.config as config_module import app.core.config as config_module
config_module.DEFAULT_RESET_PASSWORD = config.get('DEFAULT_RESET_PASSWORD', DEFAULT_RESET_PASSWORD) 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_FILE_SIZE = config.get('MAX_FILE_SIZE', MAX_FILE_SIZE)
config_module.MAX_IMAGE_SIZE = config.get('MAX_IMAGE_SIZE', MAX_IMAGE_SIZE) config_module.MAX_IMAGE_SIZE = config.get('MAX_IMAGE_SIZE', MAX_IMAGE_SIZE)
print(f"系统配置加载成功: model={config.get('model_name')}") print(f"系统配置加载成功: model={config.get('model_name')}")
except Exception as e: except Exception as e:
print(f"加载系统配置失败,使用默认配置: {e}") print(f"加载系统配置失败,使用默认配置: {e}")

View File

@ -1,11 +1,16 @@
from fastapi import APIRouter, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from app.models.models import LoginRequest, LoginResponse
from app.core.database import get_db_connection
from app.services.jwt_service import jwt_service
from app.core.auth import get_current_user
import hashlib import hashlib
from typing import Union
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from app.core.auth import get_current_user
from app.core.database import get_db_connection
from app.models.models import LoginRequest, LoginResponse
from app.services.jwt_service import jwt_service
from app.core.response import create_api_response
security = HTTPBearer() security = HTTPBearer()
@ -14,7 +19,7 @@ router = APIRouter()
def hash_password(password: str) -> str: def hash_password(password: str) -> str:
return hashlib.sha256(password.encode()).hexdigest() return hashlib.sha256(password.encode()).hexdigest()
@router.post("/auth/login", response_model=LoginResponse) @router.post("/auth/login")
def login(request: LoginRequest): def login(request: LoginRequest):
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
@ -24,11 +29,11 @@ def login(request: LoginRequest):
user = cursor.fetchone() user = cursor.fetchone()
if not user: if not user:
raise HTTPException(status_code=401, detail="用户名或密码错误") return create_api_response(code="401", message="用户名或密码错误")
hashed_input = hash_password(request.password) hashed_input = hash_password(request.password)
if user['password_hash'] != hashed_input: if user['password_hash'] != hashed_input:
raise HTTPException(status_code=401, detail="用户名或密码错误") return create_api_response(code="401", message="用户名或密码错误")
# 创建JWT token # 创建JWT token
token_data = { token_data = {
@ -39,7 +44,7 @@ def login(request: LoginRequest):
} }
token = jwt_service.create_access_token(token_data) token = jwt_service.create_access_token(token_data)
return LoginResponse( login_response_data = LoginResponse(
user_id=user['user_id'], user_id=user['user_id'],
username=user['username'], username=user['username'],
caption=user['caption'], caption=user['caption'],
@ -47,71 +52,83 @@ def login(request: LoginRequest):
token=token, token=token,
role_id=user['role_id'] role_id=user['role_id']
) )
return create_api_response(
code="200",
message="登录成功",
data=login_response_data.dict()
)
@router.post("/auth/logout") @router.post("/auth/logout")
def logout(credentials: HTTPAuthorizationCredentials = Depends(security)): def logout(credentials: HTTPAuthorizationCredentials = Depends(security)):
"""登出接口撤销当前token""" """登出接口撤销当前token"""
token = credentials.credentials token = credentials.credentials
# 验证token并获取用户信息不查询数据库
payload = jwt_service.verify_token(token) payload = jwt_service.verify_token(token)
if not payload: if not payload:
raise HTTPException(status_code=401, detail="Invalid or expired token") return create_api_response(code="401", message="无效或过期的token")
user_id = payload.get("user_id") user_id = payload.get("user_id")
if not user_id: if not user_id:
raise HTTPException(status_code=401, detail="Invalid token payload") return create_api_response(code="401", message="无效的token payload")
# 撤销当前token
revoked = jwt_service.revoke_token(token, user_id) revoked = jwt_service.revoke_token(token, user_id)
if revoked: if revoked:
return {"message": "Logged out successfully"} return create_api_response(code="200", message="登出成功")
else: else:
return {"message": "Already logged out or token not found"} return create_api_response(code="400", message="已经登出或token未找到")
@router.post("/auth/logout-all") @router.post("/auth/logout-all")
def logout_all(current_user: dict = Depends(get_current_user)): def logout_all(current_user: dict = Depends(get_current_user)):
"""登出所有设备""" """登出所有设备"""
user_id = current_user['user_id'] user_id = current_user["user_id"]
revoked_count = jwt_service.revoke_all_user_tokens(user_id) revoked_count = jwt_service.revoke_all_user_tokens(user_id)
return {"message": f"Logged out from {revoked_count} devices"} return create_api_response(code="200", message=f"{revoked_count} 个设备登出")
@router.post("/auth/admin/revoke-user-tokens/{user_id}") @router.post("/auth/admin/revoke-user-tokens/{user_id}")
def admin_revoke_user_tokens(user_id: int, credentials: HTTPAuthorizationCredentials = Depends(security)): def admin_revoke_user_tokens(
user_id: int, credentials: HTTPAuthorizationCredentials = Depends(security)
):
"""管理员功能撤销指定用户的所有token""" """管理员功能撤销指定用户的所有token"""
token = credentials.credentials token = credentials.credentials
# 验证管理员token不查询数据库
payload = jwt_service.verify_token(token) payload = jwt_service.verify_token(token)
if not payload: if not payload:
raise HTTPException(status_code=401, detail="Invalid or expired token") return create_api_response(code="401", message="无效或过期的token")
admin_user_id = payload.get("user_id") admin_user_id = payload.get("user_id")
if not admin_user_id: if not admin_user_id:
raise HTTPException(status_code=401, detail="Invalid token payload") return create_api_response(code="401", message="无效的token payload")
# 这里可以添加管理员权限检查,目前暂时允许任何登录用户操作 # 这里可以添加管理员权限检查,目前暂时允许任何登录用户操作
# if not payload.get('is_admin'): # if payload.get('role_id') != ADMIN_ROLE_ID:
# raise HTTPException(status_code=403, detail="需要管理员权限") # return create_api_response(code="403", message="需要管理员权限")
revoked_count = jwt_service.revoke_all_user_tokens(user_id) revoked_count = jwt_service.revoke_all_user_tokens(user_id)
return {"message": f"Revoked {revoked_count} tokens for user {user_id}"} return create_api_response(
code="200", message=f"为用户 {user_id} 撤销了 {revoked_count} 个token"
)
@router.get("/auth/me") @router.get("/auth/me")
def get_me(current_user: dict = Depends(get_current_user)): def get_me(current_user: dict = Depends(get_current_user)):
"""获取当前用户信息""" """获取当前用户信息"""
return current_user return create_api_response(code="200", message="获取用户信息成功", data=current_user)
@router.post("/auth/refresh") @router.post("/auth/refresh")
def refresh_token(current_user: dict = Depends(get_current_user)): def refresh_token(current_user: dict = Depends(get_current_user)):
"""刷新token""" """刷新token"""
# 这里需要从请求中获取当前token为简化先返回新token
token_data = { token_data = {
"user_id": current_user['user_id'], "user_id": current_user["user_id"],
"username": current_user['username'], "username": current_user["username"],
"caption": current_user['caption'], "caption": current_user["caption"],
"role_id": current_user['role_id'] "role_id": current_user["role_id"],
} }
new_token = jwt_service.create_access_token(token_data) new_token = jwt_service.create_access_token(token_data)
return {"token": new_token} return create_api_response(
code="200", message="Token刷新成功", data={"token": new_token}
)

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, HTTPException, Depends
from app.core.database import get_db_connection from app.core.database import get_db_connection
from app.core.response import create_api_response
from app.models.models import Tag from app.models.models import Tag
from typing import List from typing import List
import mysql.connector import mysql.connector
router = APIRouter() router = APIRouter()
@router.get("/tags/", response_model=List[Tag]) @router.get("/tags/")
def get_all_tags(): def get_all_tags():
"""_summary_ """_summary_
获取所有标签 获取所有标签
@ -18,12 +18,12 @@ def get_all_tags():
with connection.cursor(dictionary=True) as cursor: with connection.cursor(dictionary=True) as cursor:
cursor.execute(query) cursor.execute(query)
tags = cursor.fetchall() tags = cursor.fetchall()
return tags return create_api_response(code="200", message="获取标签列表成功", data=tags)
except mysql.connector.Error as err: except mysql.connector.Error as err:
print(f"Error: {err}") print(f"Error: {err}")
raise HTTPException(status_code=500, detail="Failed to retrieve tags from database.") return create_api_response(code="500", message="获取标签失败")
@router.post("/tags/", response_model=Tag) @router.post("/tags/")
def create_tag(tag_in: Tag): def create_tag(tag_in: Tag):
"""_summary_ """_summary_
创建一个新标签 创建一个新标签
@ -36,10 +36,11 @@ def create_tag(tag_in: Tag):
cursor.execute(query, (tag_in.name, tag_in.color)) cursor.execute(query, (tag_in.name, tag_in.color))
connection.commit() connection.commit()
tag_id = cursor.lastrowid tag_id = cursor.lastrowid
return {"id": tag_id, "name": tag_in.name, "color": tag_in.color} new_tag = {"id": tag_id, "name": tag_in.name, "color": tag_in.color}
return create_api_response(code="200", message="标签创建成功", data=new_tag)
except mysql.connector.IntegrityError: except mysql.connector.IntegrityError:
connection.rollback() connection.rollback()
raise HTTPException(status_code=400, detail=f"Tag '{tag_in.name}' already exists.") return create_api_response(code="400", message=f"标签 '{tag_in.name}' 已存在")
except mysql.connector.Error as err: except mysql.connector.Error as err:
print(f"Error: {err}") print(f"Error: {err}")
raise HTTPException(status_code=500, detail="Failed to create tag in database.") return create_api_response(code="500", message="创建标签失败")

View File

@ -1,8 +1,8 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, HTTPException, Depends
from app.models.models import UserInfo, PasswordChangeRequest, UserListResponse, CreateUserRequest, UpdateUserRequest, RoleInfo from app.models.models import UserInfo, PasswordChangeRequest, UserListResponse, CreateUserRequest, UpdateUserRequest, RoleInfo
from app.core.database import get_db_connection from app.core.database import get_db_connection
from app.core.auth import get_current_user from app.core.auth import get_current_user
from app.core.response import create_api_response
import app.core.config as config_module import app.core.config as config_module
import hashlib import hashlib
import datetime import datetime
@ -22,68 +22,60 @@ def hash_password(password: str) -> str:
def get_all_roles(current_user: dict = Depends(get_current_user)): def get_all_roles(current_user: dict = Depends(get_current_user)):
"""获取所有角色列表""" """获取所有角色列表"""
if current_user['role_id'] != 1: # 1 is admin if current_user['role_id'] != 1: # 1 is admin
raise HTTPException(status_code=403, detail="仅管理员有权限查看角色列表") return create_api_response(code="403", message="仅管理员有权限查看角色列表")
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
cursor.execute("SELECT role_id, role_name FROM roles ORDER BY role_id") cursor.execute("SELECT role_id, role_name FROM roles ORDER BY role_id")
roles = cursor.fetchall() roles = cursor.fetchall()
return [RoleInfo(**role) for role in roles] return create_api_response(code="200", message="获取角色列表成功", data=[RoleInfo(**role).dict() for role in roles])
@router.post("/users", status_code=201) @router.post("/users")
def create_user(request: CreateUserRequest, current_user: dict = Depends(get_current_user)): def create_user(request: CreateUserRequest, current_user: dict = Depends(get_current_user)):
if current_user['role_id'] != 1: # 1 is admin if current_user['role_id'] != 1: # 1 is admin
raise HTTPException(status_code=403, detail="仅管理员有权限创建用户") return create_api_response(code="403", message="仅管理员有权限创建用户")
# Validate email format
if not validate_email(request.email): if not validate_email(request.email):
raise HTTPException(status_code=400, detail="邮箱格式不正确") return create_api_response(code="400", message="邮箱格式不正确")
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
# Check if username exists
cursor.execute("SELECT user_id FROM users WHERE username = %s", (request.username,)) cursor.execute("SELECT user_id FROM users WHERE username = %s", (request.username,))
if cursor.fetchone(): if cursor.fetchone():
raise HTTPException(status_code=400, detail="用户名已存在") return create_api_response(code="400", message="用户名已存在")
# Use provided password or default password
password = request.password if request.password else config_module.DEFAULT_RESET_PASSWORD password = request.password if request.password else config_module.DEFAULT_RESET_PASSWORD
hashed_password = hash_password(password) hashed_password = hash_password(password)
# Insert new user
query = "INSERT INTO users (username, password_hash, caption, email, role_id, created_at) VALUES (%s, %s, %s, %s, %s, %s)" query = "INSERT INTO users (username, password_hash, caption, email, role_id, created_at) VALUES (%s, %s, %s, %s, %s, %s)"
created_at = datetime.datetime.utcnow() created_at = datetime.datetime.utcnow()
cursor.execute(query, (request.username, hashed_password, request.caption, request.email, request.role_id, created_at)) cursor.execute(query, (request.username, hashed_password, request.caption, request.email, request.role_id, created_at))
connection.commit() connection.commit()
return {"message": "用户创建成功"} return create_api_response(code="200", message="用户创建成功")
@router.put("/users/{user_id}", response_model=UserInfo) @router.put("/users/{user_id}")
def update_user(user_id: int, request: UpdateUserRequest, current_user: dict = Depends(get_current_user)): def update_user(user_id: int, request: UpdateUserRequest, current_user: dict = Depends(get_current_user)):
if current_user['role_id'] != 1: # 1 is admin if current_user['role_id'] != 1: # 1 is admin
raise HTTPException(status_code=403, detail="仅管理员有权限修改用户信息") return create_api_response(code="403", message="仅管理员有权限修改用户信息")
# Validate email format if provided
if request.email and not validate_email(request.email): if request.email and not validate_email(request.email):
raise HTTPException(status_code=400, detail="邮箱格式不正确") return create_api_response(code="400", message="邮箱格式不正确")
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
# Check if user exists
cursor.execute("SELECT user_id, username, caption, email, role_id FROM users WHERE user_id = %s", (user_id,)) cursor.execute("SELECT user_id, username, caption, email, role_id FROM users WHERE user_id = %s", (user_id,))
existing_user = cursor.fetchone() existing_user = cursor.fetchone()
if not existing_user: if not existing_user:
raise HTTPException(status_code=404, detail="用户不存在") return create_api_response(code="404", message="用户不存在")
# Check if username is being changed and if it already exists
if request.username and request.username != existing_user['username']: if request.username and request.username != existing_user['username']:
cursor.execute("SELECT user_id FROM users WHERE username = %s AND user_id != %s", (request.username, user_id)) cursor.execute("SELECT user_id FROM users WHERE username = %s AND user_id != %s", (request.username, user_id))
if cursor.fetchone(): if cursor.fetchone():
raise HTTPException(status_code=400, detail="用户名已存在") return create_api_response(code="400", message="用户名已存在")
# Prepare update data, using existing values if not provided
update_data = { update_data = {
'username': request.username if request.username else existing_user['username'], 'username': request.username if request.username else existing_user['username'],
'caption': request.caption if request.caption else existing_user['caption'], 'caption': request.caption if request.caption else existing_user['caption'],
@ -91,12 +83,10 @@ def update_user(user_id: int, request: UpdateUserRequest, current_user: dict = D
'role_id': request.role_id if request.role_id is not None else existing_user['role_id'] 'role_id': request.role_id if request.role_id is not None else existing_user['role_id']
} }
# Update user
query = "UPDATE users SET username = %s, caption = %s, email = %s, role_id = %s WHERE user_id = %s" query = "UPDATE users SET username = %s, caption = %s, email = %s, role_id = %s WHERE user_id = %s"
cursor.execute(query, (update_data['username'], update_data['caption'], update_data['email'], update_data['role_id'], user_id)) cursor.execute(query, (update_data['username'], update_data['caption'], update_data['email'], update_data['role_id'], user_id))
connection.commit() connection.commit()
# Return updated user info
cursor.execute(''' cursor.execute('''
SELECT u.user_id, u.username, u.caption, u.email, u.created_at, u.role_id, r.role_name SELECT u.user_id, u.username, u.caption, u.email, u.created_at, u.role_id, r.role_name
FROM users u FROM users u
@ -105,7 +95,7 @@ def update_user(user_id: int, request: UpdateUserRequest, current_user: dict = D
''', (user_id,)) ''', (user_id,))
updated_user = cursor.fetchone() updated_user = cursor.fetchone()
return UserInfo( user_info = UserInfo(
user_id=updated_user['user_id'], user_id=updated_user['user_id'],
username=updated_user['username'], username=updated_user['username'],
caption=updated_user['caption'], caption=updated_user['caption'],
@ -113,62 +103,56 @@ def update_user(user_id: int, request: UpdateUserRequest, current_user: dict = D
created_at=updated_user['created_at'], created_at=updated_user['created_at'],
role_id=updated_user['role_id'], role_id=updated_user['role_id'],
role_name=updated_user['role_name'], role_name=updated_user['role_name'],
meetings_created=0, # This is not accurate, but it is not displayed in the list meetings_created=0,
meetings_attended=0 meetings_attended=0
) )
return create_api_response(code="200", message="用户信息更新成功", data=user_info.dict())
@router.delete("/users/{user_id}") @router.delete("/users/{user_id}")
def delete_user(user_id: int, current_user: dict = Depends(get_current_user)): def delete_user(user_id: int, current_user: dict = Depends(get_current_user)):
if current_user['role_id'] != 1: # 1 is admin if current_user['role_id'] != 1: # 1 is admin
raise HTTPException(status_code=403, detail="仅管理员有权限删除用户") return create_api_response(code="403", message="仅管理员有权限删除用户")
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
# Check if user exists
cursor.execute("SELECT user_id FROM users WHERE user_id = %s", (user_id,)) cursor.execute("SELECT user_id FROM users WHERE user_id = %s", (user_id,))
if not cursor.fetchone(): if not cursor.fetchone():
raise HTTPException(status_code=404, detail="用户不存在") return create_api_response(code="404", message="用户不存在")
# Delete user
cursor.execute("DELETE FROM users WHERE user_id = %s", (user_id,)) cursor.execute("DELETE FROM users WHERE user_id = %s", (user_id,))
connection.commit() connection.commit()
return {"message": "用户删除成功"} return create_api_response(code="200", message="用户删除成功")
@router.post("/users/{user_id}/reset-password") @router.post("/users/{user_id}/reset-password")
def reset_password(user_id: int, current_user: dict = Depends(get_current_user)): def reset_password(user_id: int, current_user: dict = Depends(get_current_user)):
if current_user['role_id'] != 1: # 1 is admin if current_user['role_id'] != 1: # 1 is admin
raise HTTPException(status_code=403, detail="仅管理员有权限重置密码") return create_api_response(code="403", message="仅管理员有权限重置密码")
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
# Check if user exists
cursor.execute("SELECT user_id FROM users WHERE user_id = %s", (user_id,)) cursor.execute("SELECT user_id FROM users WHERE user_id = %s", (user_id,))
if not cursor.fetchone(): if not cursor.fetchone():
raise HTTPException(status_code=404, detail="用户不存在") return create_api_response(code="404", message="用户不存在")
# Hash password
hashed_password = hash_password(config_module.DEFAULT_RESET_PASSWORD) hashed_password = hash_password(config_module.DEFAULT_RESET_PASSWORD)
# Update user password
query = "UPDATE users SET password_hash = %s WHERE user_id = %s" query = "UPDATE users SET password_hash = %s WHERE user_id = %s"
cursor.execute(query, (hashed_password, user_id)) cursor.execute(query, (hashed_password, user_id))
connection.commit() connection.commit()
return {"message": f"用户 {user_id} 的密码已重置"} return create_api_response(code="200", message=f"用户 {user_id} 的密码已重置")
@router.get("/users", response_model=UserListResponse) @router.get("/users")
def get_all_users(page: int = 1, size: int = 10, current_user: dict = Depends(get_current_user)): def get_all_users(page: int = 1, size: int = 10, current_user: dict = Depends(get_current_user)):
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
# Get total count
cursor.execute("SELECT COUNT(*) as total FROM users") cursor.execute("SELECT COUNT(*) as total FROM users")
total = cursor.fetchone()['total'] total = cursor.fetchone()['total']
# Get paginated users with role names
offset = (page - 1) * size offset = (page - 1) * size
query = ''' query = '''
SELECT SELECT
@ -186,9 +170,10 @@ def get_all_users(page: int = 1, size: int = 10, current_user: dict = Depends(ge
user_list = [UserInfo(**user) for user in users] user_list = [UserInfo(**user) for user in users]
return UserListResponse(users=user_list, total=total) response_data = UserListResponse(users=user_list, total=total)
return create_api_response(code="200", message="获取用户列表成功", data=response_data.dict())
@router.get("/users/{user_id}", response_model=UserInfo) @router.get("/users/{user_id}")
def get_user_info(user_id: int, current_user: dict = Depends(get_current_user)): def get_user_info(user_id: int, current_user: dict = Depends(get_current_user)):
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
@ -203,7 +188,7 @@ def get_user_info(user_id: int, current_user: dict = Depends(get_current_user)):
user = cursor.fetchone() user = cursor.fetchone()
if not user: if not user:
raise HTTPException(status_code=404, detail="用户不存在") return create_api_response(code="404", message="用户不存在")
created_query = "SELECT COUNT(*) as count FROM meetings WHERE user_id = %s" created_query = "SELECT COUNT(*) as count FROM meetings WHERE user_id = %s"
cursor.execute(created_query, (user_id,)) cursor.execute(created_query, (user_id,))
@ -213,7 +198,7 @@ def get_user_info(user_id: int, current_user: dict = Depends(get_current_user)):
cursor.execute(attended_query, (user_id,)) cursor.execute(attended_query, (user_id,))
meetings_attended = cursor.fetchone()['count'] meetings_attended = cursor.fetchone()['count']
return UserInfo( user_info = UserInfo(
user_id=user['user_id'], user_id=user['user_id'],
username=user['username'], username=user['username'],
caption=user['caption'], caption=user['caption'],
@ -224,11 +209,12 @@ def get_user_info(user_id: int, current_user: dict = Depends(get_current_user)):
meetings_created=meetings_created, meetings_created=meetings_created,
meetings_attended=meetings_attended meetings_attended=meetings_attended
) )
return create_api_response(code="200", message="获取用户信息成功", data=user_info.dict())
@router.put("/users/{user_id}/password") @router.put("/users/{user_id}/password")
def update_password(user_id: int, request: PasswordChangeRequest, current_user: dict = Depends(get_current_user)): def update_password(user_id: int, request: PasswordChangeRequest, current_user: dict = Depends(get_current_user)):
if user_id != current_user['user_id'] and current_user['role_id'] != 1: if user_id != current_user['user_id'] and current_user['role_id'] != 1:
raise HTTPException(status_code=403, detail="没有权限修改其他用户的密码") return create_api_response(code="403", message="没有权限修改其他用户的密码")
with get_db_connection() as connection: with get_db_connection() as connection:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
@ -237,15 +223,14 @@ def update_password(user_id: int, request: PasswordChangeRequest, current_user:
user = cursor.fetchone() user = cursor.fetchone()
if not user: if not user:
raise HTTPException(status_code=404, detail="用户不存在") return create_api_response(code="404", message="用户不存在")
# If not admin, verify old password
if current_user['role_id'] != 1: if current_user['role_id'] != 1:
if user['password_hash'] != hash_password(request.old_password): if user['password_hash'] != hash_password(request.old_password):
raise HTTPException(status_code=400, detail="旧密码错误") return create_api_response(code="400", message="旧密码错误")
new_password_hash = hash_password(request.new_password) new_password_hash = hash_password(request.new_password)
cursor.execute("UPDATE users SET password_hash = %s WHERE user_id = %s", (new_password_hash, user_id)) cursor.execute("UPDATE users SET password_hash = %s WHERE user_id = %s", (new_password_hash, user_id))
connection.commit() connection.commit()
return {"message": "密码修改成功"} return create_api_response(code="200", message="密码修改成功")

View File

@ -23,7 +23,7 @@ DATABASE_CONFIG = {
'host': os.getenv('DB_HOST', '10.100.51.161'), 'host': os.getenv('DB_HOST', '10.100.51.161'),
'user': os.getenv('DB_USER', 'root'), 'user': os.getenv('DB_USER', 'root'),
'password': os.getenv('DB_PASSWORD', 'sagacity'), 'password': os.getenv('DB_PASSWORD', 'sagacity'),
'database': os.getenv('DB_NAME', 'imeeting'), 'database': os.getenv('DB_NAME', 'imeeting_dev'),
'port': int(os.getenv('DB_PORT', '3306')), 'port': int(os.getenv('DB_PORT', '3306')),
'charset': 'utf8mb4' 'charset': 'utf8mb4'
} }
@ -42,7 +42,7 @@ QINIU_DOMAIN = os.getenv('QINIU_DOMAIN', 't0vogyxkz.hn-bkt.clouddn.com')
# 应用配置 # 应用配置
APP_CONFIG = { APP_CONFIG = {
'base_url': os.getenv('BASE_URL', 'http://imeeting.unisspace.com') 'base_url': os.getenv('BASE_URL', 'http://dev.imeeting.unisspace.com')
} }
# Redis配置 # Redis配置

View File

@ -0,0 +1,14 @@
from typing import Union
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
def create_api_response(code: str, message: str, data: Union[dict, list, None] = None) -> JSONResponse:
"""Creates a standardized API JSON response."""
return JSONResponse(
status_code=200,
content={
"code": str(code),
"message": message,
"data": jsonable_encoder(data) if data is not None else {},
},
)

BIN
config/.DS_Store vendored 100644

Binary file not shown.

View File

@ -1,7 +1,7 @@
{ {
"model_name": "qwen-plus", "model_name": "qwen-plus",
"system_prompt": "你是一个专业的会议记录分析助手。请根据提供的会议转录内容,生成简洁明了的会议总结。\n\n总结应该包括以下几部分(生成MD二级目录\n1. 会议概述 - 简要说明会议的主要目的和背景(生成MD引用)\n2. 主要讨论点 - 列出会议中讨论的重要话题和内容\n3. 决策事项 - 明确记录会议中做出的决定和结论\n4. 待办事项 - 列出需要后续跟进的任务和责任人\n5. 关键信息 - 其他重要的信息点\n\n输出要求\n- 保持客观中性,不添加个人观点\n- 使用简洁、准确的中文表达\n- 按重要性排序各项内容\n- 如果某个部分没有相关内容,可以说明\"无相关内容\"\n- 总字数控制在500字以内", "system_prompt": "你是一个专业的会议记录分析助手。请根据提供的会议转录内容,生成简洁明了的会议总结。\n\n总结包括五个部分(名称严格一致,生成为MD二级目录\n1. 会议概述 - 简要说明会议的主要目的和背景(生成MD引用)\n2. 主要讨论点 - 列出会议中讨论的重要话题和内容\n3. 决策事项 - 明确记录会议中做出的决定和结论\n4. 待办事项 - 列出需要后续跟进的任务和责任人\n5. 关键信息 - 其他重要的信息点\n\n输出要求\n- 保持客观中性,不添加个人观点\n- 使用简洁、准确的中文表达\n- 按重要性排序各项内容\n- 如果某个部分没有相关内容,可以说明\"无相关内容\"\n- 总字数控制在500字以内",
"DEFAULT_RESET_PASSWORD": "123456", "DEFAULT_RESET_PASSWORD": "111111",
"MAX_FILE_SIZE": 209715200, "MAX_FILE_SIZE": 209715200,
"MAX_IMAGE_SIZE": 10485760 "MAX_IMAGE_SIZE": 10485760
} }

View File

@ -46,6 +46,7 @@ services:
volumes: volumes:
# 挂载上传目录保持数据持久化 # 挂载上传目录保持数据持久化
- ./uploads:/app/uploads - ./uploads:/app/uploads
- ./config:/app/config
restart: unless-stopped restart: unless-stopped
container_name: imeeting-backend container_name: imeeting-backend
extra_hosts: extra_hosts:

View File

@ -3,8 +3,7 @@ from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from app.api.endpoints import auth, users, meetings, tags, admin from app.api.endpoints import auth, users, meetings, tags, admin
from app.core.config import UPLOAD_DIR, API_CONFIG, MAX_FILE_SIZE from app.core.config import UPLOAD_DIR, API_CONFIG
from app.services.async_llm_service import async_llm_service
from app.api.endpoints.admin import load_system_config from app.api.endpoints.admin import load_system_config
import os import os
@ -47,7 +46,7 @@ def health_check():
return { return {
"status": "healthy", "status": "healthy",
"service": "iMeeting API", "service": "iMeeting API",
"version": "1.0.0" "version": "1.0.2"
} }
if __name__ == "__main__": if __name__ == "__main__":