278 lines
8.1 KiB
Python
278 lines
8.1 KiB
Python
|
|
"""
|
|||
|
|
Bot 服务层
|
|||
|
|
实现 Bot 的 CRUD、权限控制、Token 管理、Agent 绑定等功能
|
|||
|
|
"""
|
|||
|
|
import secrets
|
|||
|
|
import hashlib
|
|||
|
|
from datetime import datetime
|
|||
|
|
from typing import Optional, List, Dict, Any, Tuple
|
|||
|
|
|
|||
|
|
from app.extensions import db
|
|||
|
|
from app.models import Bot, User, Agent
|
|||
|
|
|
|||
|
|
|
|||
|
|
class BotService:
|
|||
|
|
"""Bot 服务类"""
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def generate_token() -> str:
|
|||
|
|
"""生成 Bot API Token"""
|
|||
|
|
return f"bot_{secrets.token_urlsafe(32)}"
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def hash_token(token: str) -> str:
|
|||
|
|
"""Hash Token 用于存储"""
|
|||
|
|
return hashlib.sha256(token.encode()).hexdigest()
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def verify_token(bot: Bot, token: str) -> bool:
|
|||
|
|
"""验证 Token"""
|
|||
|
|
if not bot.token_hash:
|
|||
|
|
return False
|
|||
|
|
return bot.token_hash == BotService.hash_token(token)
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def check_permission(user: User, bot: Bot, action: str) -> bool:
|
|||
|
|
"""
|
|||
|
|
检查用户对 Bot 的权限
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user: 当前用户
|
|||
|
|
bot: Bot 对象
|
|||
|
|
action: 操作类型 ('view', 'use', 'edit', 'delete', 'bind')
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
是否有权限
|
|||
|
|
"""
|
|||
|
|
# 管理员拥有所有权限
|
|||
|
|
if user.role == 'admin':
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
# 查看/使用权限
|
|||
|
|
if action in ['view', 'use']:
|
|||
|
|
# 系统级 Bot 所有用户可用
|
|||
|
|
if bot.is_system:
|
|||
|
|
return True
|
|||
|
|
# 自己的 Bot
|
|||
|
|
return bot.owner_id == user.id
|
|||
|
|
|
|||
|
|
# 编辑/删除/绑定权限
|
|||
|
|
if action in ['edit', 'delete', 'bind']:
|
|||
|
|
# 只有所有者可以编辑/删除/绑定
|
|||
|
|
return bot.owner_id == user.id
|
|||
|
|
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def create_bot(
|
|||
|
|
name: str,
|
|||
|
|
owner_id: str,
|
|||
|
|
display_name: Optional[str] = None,
|
|||
|
|
avatar: Optional[str] = None,
|
|||
|
|
description: Optional[str] = None,
|
|||
|
|
is_system: bool = False,
|
|||
|
|
agent_id: Optional[str] = None,
|
|||
|
|
capabilities: Optional[List[str]] = None,
|
|||
|
|
config: Optional[Dict[str, Any]] = None
|
|||
|
|
) -> Tuple[Bot, str]:
|
|||
|
|
"""
|
|||
|
|
创建 Bot
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
(Bot 对象, 明文 Token)
|
|||
|
|
"""
|
|||
|
|
# 生成 Token
|
|||
|
|
token = BotService.generate_token()
|
|||
|
|
token_hash = BotService.hash_token(token)
|
|||
|
|
|
|||
|
|
bot = Bot(
|
|||
|
|
name=name,
|
|||
|
|
display_name=display_name or name,
|
|||
|
|
avatar=avatar,
|
|||
|
|
description=description,
|
|||
|
|
owner_id=owner_id,
|
|||
|
|
agent_id=agent_id,
|
|||
|
|
token_hash=token_hash,
|
|||
|
|
is_system=is_system,
|
|||
|
|
capabilities=capabilities or ['chat'],
|
|||
|
|
config=config or BotService.get_default_config(),
|
|||
|
|
status='offline',
|
|||
|
|
created_at=datetime.utcnow()
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
db.session.add(bot)
|
|||
|
|
db.session.commit()
|
|||
|
|
|
|||
|
|
return bot, token
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_default_config() -> Dict[str, Any]:
|
|||
|
|
"""获取默认 Bot 配置"""
|
|||
|
|
return {
|
|||
|
|
"model": "gpt-4o",
|
|||
|
|
"temperature": 0.7,
|
|||
|
|
"max_tokens": 4096,
|
|||
|
|
"system_prompt": "",
|
|||
|
|
"rate_limit": {
|
|||
|
|
"requests_per_minute": 60,
|
|||
|
|
"tokens_per_day": 100000
|
|||
|
|
},
|
|||
|
|
"features": {
|
|||
|
|
"streaming": True,
|
|||
|
|
"markdown": True,
|
|||
|
|
"code_highlight": True,
|
|||
|
|
"file_upload": False
|
|||
|
|
},
|
|||
|
|
"context": {
|
|||
|
|
"max_history": 20,
|
|||
|
|
"summary_threshold": 10
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_bot_by_id(bot_id: str) -> Optional[Bot]:
|
|||
|
|
"""根据 ID 获取 Bot"""
|
|||
|
|
return Bot.query.get(bot_id)
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_bot_by_name(name: str) -> Optional[Bot]:
|
|||
|
|
"""根据名称获取 Bot"""
|
|||
|
|
return Bot.query.filter_by(name=name).first()
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_bots_by_owner(owner_id: str) -> List[Bot]:
|
|||
|
|
"""获取用户的所有 Bot"""
|
|||
|
|
return Bot.query.filter_by(owner_id=owner_id).order_by(Bot.created_at.desc()).all()
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_system_bots() -> List[Bot]:
|
|||
|
|
"""获取所有系统级 Bot"""
|
|||
|
|
return Bot.query.filter_by(is_system=True).order_by(Bot.created_at.desc()).all()
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_available_bots(user: User) -> List[Bot]:
|
|||
|
|
"""
|
|||
|
|
获取用户可用的所有 Bot
|
|||
|
|
- 自己的 Bot
|
|||
|
|
- 系统级 Bot
|
|||
|
|
"""
|
|||
|
|
if user.role == 'admin':
|
|||
|
|
return Bot.query.order_by(Bot.created_at.desc()).all()
|
|||
|
|
|
|||
|
|
return Bot.query.filter(
|
|||
|
|
(Bot.owner_id == user.id) | (Bot.is_system == True)
|
|||
|
|
).order_by(Bot.created_at.desc()).all()
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def update_bot(
|
|||
|
|
bot: Bot,
|
|||
|
|
display_name: Optional[str] = None,
|
|||
|
|
avatar: Optional[str] = None,
|
|||
|
|
description: Optional[str] = None,
|
|||
|
|
capabilities: Optional[List[str]] = None,
|
|||
|
|
config: Optional[Dict[str, Any]] = None
|
|||
|
|
) -> Bot:
|
|||
|
|
"""更新 Bot 信息"""
|
|||
|
|
if display_name is not None:
|
|||
|
|
bot.display_name = display_name
|
|||
|
|
if avatar is not None:
|
|||
|
|
bot.avatar = avatar
|
|||
|
|
if description is not None:
|
|||
|
|
bot.description = description
|
|||
|
|
if capabilities is not None:
|
|||
|
|
bot.capabilities = capabilities
|
|||
|
|
if config is not None:
|
|||
|
|
bot.config = {**bot.config, **config} if bot.config else config
|
|||
|
|
|
|||
|
|
db.session.commit()
|
|||
|
|
return bot
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def bind_agent(bot: Bot, agent_id: Optional[str]) -> Bot:
|
|||
|
|
"""
|
|||
|
|
绑定/解绑 Agent
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
bot: Bot 对象
|
|||
|
|
agent_id: Agent ID,None 表示解绑
|
|||
|
|
"""
|
|||
|
|
bot.agent_id = agent_id
|
|||
|
|
|
|||
|
|
# 更新状态
|
|||
|
|
if agent_id:
|
|||
|
|
agent = Agent.query.get(agent_id)
|
|||
|
|
if agent and agent.status == 'online':
|
|||
|
|
bot.status = 'online'
|
|||
|
|
else:
|
|||
|
|
bot.status = 'offline'
|
|||
|
|
else:
|
|||
|
|
bot.status = 'offline'
|
|||
|
|
|
|||
|
|
db.session.commit()
|
|||
|
|
return bot
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def regenerate_token(bot: Bot) -> str:
|
|||
|
|
"""重新生成 Token"""
|
|||
|
|
token = BotService.generate_token()
|
|||
|
|
bot.token_hash = BotService.hash_token(token)
|
|||
|
|
db.session.commit()
|
|||
|
|
return token
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def delete_bot(bot: Bot) -> bool:
|
|||
|
|
"""删除 Bot"""
|
|||
|
|
try:
|
|||
|
|
db.session.delete(bot)
|
|||
|
|
db.session.commit()
|
|||
|
|
return True
|
|||
|
|
except Exception as e:
|
|||
|
|
db.session.rollback()
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def update_status(bot: Bot, status: str) -> Bot:
|
|||
|
|
"""更新 Bot 状态"""
|
|||
|
|
bot.status = status
|
|||
|
|
bot.last_active_at = datetime.utcnow()
|
|||
|
|
db.session.commit()
|
|||
|
|
return bot
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def sync_agent_status(bot: Bot) -> Bot:
|
|||
|
|
"""
|
|||
|
|
同步 Agent 状态到 Bot
|
|||
|
|
当 Agent 状态变化时调用
|
|||
|
|
"""
|
|||
|
|
if bot.agent_id:
|
|||
|
|
agent = Agent.query.get(bot.agent_id)
|
|||
|
|
if agent:
|
|||
|
|
bot.status = agent.status
|
|||
|
|
else:
|
|||
|
|
bot.status = 'offline'
|
|||
|
|
else:
|
|||
|
|
bot.status = 'offline'
|
|||
|
|
|
|||
|
|
bot.last_active_at = datetime.utcnow()
|
|||
|
|
db.session.commit()
|
|||
|
|
return bot
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_bot_stats(bot: Bot) -> Dict[str, Any]:
|
|||
|
|
"""获取 Bot 统计信息"""
|
|||
|
|
from app.models import Session, Message
|
|||
|
|
|
|||
|
|
session_count = Session.query.filter_by(bot_id=bot.id).count()
|
|||
|
|
message_count = Message.query.filter_by(bot_id=bot.id).count()
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
'bot_id': bot.id,
|
|||
|
|
'name': bot.name,
|
|||
|
|
'status': bot.status,
|
|||
|
|
'session_count': session_count,
|
|||
|
|
'message_count': message_count,
|
|||
|
|
'is_system': bot.is_system,
|
|||
|
|
'created_at': bot.created_at.isoformat() if bot.created_at else None,
|
|||
|
|
'last_active_at': bot.last_active_at.isoformat() if bot.last_active_at else None,
|
|||
|
|
}
|