更新后台管理消息列表;

This commit is contained in:
karlcarlo
2025-12-15 17:44:35 +08:00
parent 41d2d893d3
commit a7b3454f61
22 changed files with 1620 additions and 356 deletions

View File

@@ -3,8 +3,13 @@ GLM聊天服务,包含角色配置管理
"""
import logging
import asyncio
import os
from typing import Optional
from pymongo import AsyncMongoClient
from beanie import init_beanie
from app.models.glm_role import GLMRole
from app.models import models
logger = logging.getLogger(__name__)
@@ -14,11 +19,19 @@ class GLMService:
def __init__(self):
self._current_role: Optional[GLMRole] = None
self._max_retries = 3
self._retry_delay = 1 # 秒
self._db_client: Optional[AsyncMongoClient] = None
self._db_initialized = False
async def get_current_role(self) -> GLMRole:
"""获取当前激活的角色配置"""
if self._current_role is None:
self._current_role = await self._load_or_create_role()
logger.info("当前无缓存角色,开始加载角色配置")
self._current_role = await self._load_or_create_role_with_retry()
logger.info(f"角色加载完成: name='{self._current_role.name}'")
else:
logger.debug(f"使用缓存角色: name='{self._current_role.name}'")
return self._current_role
async def update_role(self, role_data: dict) -> GLMRole:
@@ -30,33 +43,153 @@ class GLMService:
Returns:
更新后的GLMRole实例
"""
logger.info(f"开始更新角色配置,更新字段: {list(role_data.keys())}")
role = await self.get_current_role()
# 更新字段
if "name" in role_data:
# 记录更新前的值
changes = []
if "name" in role_data and role_data["name"] != role.name:
changes.append(f"name: '{role.name}' -> '{role_data['name']}'")
role.name = role_data["name"]
if "description" in role_data:
if "description" in role_data and role_data["description"] != role.description:
changes.append(
f"description: {len(role.description)}字符 -> {len(role_data['description'])}字符"
)
role.description = role_data["description"]
if "skills" in role_data:
if "skills" in role_data and role_data["skills"] != role.skills:
old_skills_count = len(role.skills.split("\n")) if role.skills else 0
new_skills_count = (
len(role_data["skills"].split("\n")) if role_data["skills"] else 0
)
changes.append(f"skills: {old_skills_count}条 -> {new_skills_count}条")
role.skills = role_data["skills"]
if "restrictions" in role_data:
if (
"restrictions" in role_data
and role_data["restrictions"] != role.restrictions
):
old_restrictions_count = (
len(role.restrictions.split("\n")) if role.restrictions else 0
)
new_restrictions_count = (
len(role_data["restrictions"].split("\n"))
if role_data["restrictions"]
else 0
)
changes.append(
f"restrictions: {old_restrictions_count}条 -> {new_restrictions_count}条"
)
role.restrictions = role_data["restrictions"]
role.update_timestamp()
await role.save()
if changes:
logger.info(f"角色配置变更: {', '.join(changes)}")
else:
logger.info("角色配置无实际变更")
# 使用重试机制保存
await self._save_with_retry(role)
# 更新缓存
self._current_role = role
logger.info(f"角色配置已更新: {role.name}")
logger.info(f"角色配置更新完成: name='{role.name}'")
return role
async def _load_or_create_role_with_retry(self) -> GLMRole:
"""使用重试机制加载或创建默认角色"""
for attempt in range(self._max_retries):
try:
return await self._load_or_create_role()
except Exception as e:
if attempt < self._max_retries - 1:
logger.warning(f"加载角色配置失败,第{attempt + 1}次重试: {e}")
await asyncio.sleep(self._retry_delay * (attempt + 1))
else:
logger.error(f"加载角色配置失败,已达到最大重试次数: {e}")
raise
async def _ensure_db_connection(self) -> None:
"""确保数据库连接正常,如果连接已关闭则重新连接"""
if not self._db_initialized:
await self._init_db_connection()
async def _init_db_connection(self) -> None:
"""初始化数据库连接"""
try:
# 从环境变量获取数据库配置
mongodb_url = os.getenv("MONGODB_URL", "mongodb://localhost:27017")
database_name = os.getenv("DATABASE_NAME", "douba_chat_dev")
# 创建新的数据库连接
if self._db_client:
try:
await self._db_client.close()
except:
pass
self._db_client = AsyncMongoClient(mongodb_url)
await init_beanie(
database=self._db_client[database_name], document_models=models
)
self._db_initialized = True
logger.info(f"数据库连接初始化成功: {database_name}")
except Exception as e:
logger.error(f"数据库连接初始化失败: {e}")
self._db_initialized = False
raise
async def _load_or_create_role(self) -> GLMRole:
"""加载或创建默认角色"""
try:
# 确保数据库连接正常
await self._ensure_db_connection()
# 1. 尝试获取激活的角色
active_role = await GLMRole.find_one(GLMRole.is_active == True)
if active_role:
logger.info(f"加载激活角色: {active_role.name}")
return active_role
# 2. 尝试获取默认角色
default_role = await GLMRole.find_one(GLMRole.is_default == True)
if default_role:
default_role.is_active = True
await default_role.save()
logger.info(f"加载默认角色并激活: {default_role.name}")
return default_role
# 3. 创建默认角色
logger.info("创建默认角色配置")
return await self._create_default_role()
except Exception as e:
logger.error(f"加载或创建角色失败: {e}")
# 检查是否是数据库连接错误
if (
"Cannot use AsyncMongoClient after close" in str(e)
or "not connected" in str(e).lower()
):
logger.error("数据库连接已关闭,尝试重新连接...")
self._db_initialized = False
# 尝试重新连接
try:
await self._init_db_connection()
logger.info("数据库重新连接成功,重试操作...")
# 重试操作
return await self._retry_after_reconnect()
except Exception as reconnect_error:
logger.error(f"数据库重新连接失败: {reconnect_error}")
raise
async def _retry_after_reconnect(self) -> GLMRole:
"""重新连接后重试操作"""
# 1. 尝试获取激活的角色
active_role = await GLMRole.find_one(GLMRole.is_active == True)
if active_role:
logger.info(f"加载激活角色: {active_role.name}")
logger.info(f"重新连接后加载激活角色: {active_role.name}")
return active_role
# 2. 尝试获取默认角色
@@ -64,11 +197,11 @@ class GLMService:
if default_role:
default_role.is_active = True
await default_role.save()
logger.info(f"加载默认角色并激活: {default_role.name}")
logger.info(f"重新连接后加载默认角色并激活: {default_role.name}")
return default_role
# 3. 创建默认角色
logger.info("创建默认角色配置")
logger.info("重新连接后创建默认角色配置")
return await self._create_default_role()
async def _create_default_role(self) -> GLMRole:
@@ -89,10 +222,40 @@ class GLMService:
logger.info(f"默认角色创建成功: {role.name}")
return role
async def _save_with_retry(self, role: GLMRole) -> None:
"""使用重试机制保存角色"""
for attempt in range(self._max_retries):
try:
# 确保数据库连接正常
await self._ensure_db_connection()
await role.save()
return
except Exception as e:
if attempt < self._max_retries - 1:
logger.warning(f"保存角色配置失败,第{attempt + 1}次重试: {e}")
# 检查是否是数据库连接错误
if (
"Cannot use AsyncMongoClient after close" in str(e)
or "not connected" in str(e).lower()
):
logger.warning("检测到数据库连接问题,尝试重新连接...")
self._db_initialized = False
try:
await self._init_db_connection()
logger.info("数据库重新连接成功,继续重试...")
except Exception as reconnect_error:
logger.error(f"数据库重新连接失败: {reconnect_error}")
await asyncio.sleep(self._retry_delay * (attempt + 1))
else:
logger.error(f"保存角色配置失败,已达到最大重试次数: {e}")
raise
async def ensure_default_role(self):
"""确保存在默认角色配置(应用启动时调用)"""
try:
role = await self._load_or_create_role()
# 确保数据库连接正常
await self._ensure_db_connection()
role = await self._load_or_create_role_with_retry()
logger.info(f"默认角色已确保存在: {role.name}")
return role
except Exception as e:
@@ -102,6 +265,13 @@ class GLMService:
async def close(self):
"""关闭服务,清理资源"""
self._current_role = None
if self._db_client:
try:
await self._db_client.close()
logger.info("数据库连接已关闭")
except Exception as e:
logger.error(f"关闭数据库连接时出错: {e}")
self._db_initialized = False
logger.info("GLM服务已关闭")