mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-23 05:10:57 +00:00
refactor: 完成项目大规模代码重构与依赖清理
这是一次综合性的重构更新,包含以下主要变更: 1. 升级Python版本到3.12,更新依赖配置 2. 替换旧的.j2模板为.jinja2格式,新增代码生成模板 3. 重构权限过滤策略,更新权限枚举与模型配置 4. 移除Prefect依赖,替换为自研拓扑并行执行引擎 5. 重构认证与上下文管理,拆分租户/请求上下文 6. 简化响应模型、CRUD与服务层代码 7. 清理废弃的支付网关模块,重构订单定时任务 8. 更新在线用户、监控等模块的接口与路由 9. 优化邮件模板与工具类,新增邮件模板文件 10. 修复数据库会话配置与类型提示
This commit is contained in:
@@ -18,7 +18,7 @@ from .schema import (
|
||||
)
|
||||
from .service import ChatService
|
||||
|
||||
ChatRouter = APIRouter(route_class=OperationLogRoute, prefix="/chat", tags=["AI对话"])
|
||||
ChatRouter = APIRouter(route_class=OperationLogRoute, prefix="/chat", tags=["AI管理", "AI对话"])
|
||||
|
||||
|
||||
@ChatRouter.get(
|
||||
@@ -30,17 +30,8 @@ async def get_session_detail_controller(
|
||||
session_id: Annotated[str, Path(description="会话ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat:detail"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
获取会话详情
|
||||
|
||||
参数:
|
||||
- session_id (str): 会话ID
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含会话详情的JSON响应
|
||||
"""
|
||||
result = await ChatService.get_session_service(auth=auth, session_id=session_id)
|
||||
service = ChatService(auth)
|
||||
result = await service.get_session(session_id=session_id)
|
||||
return SuccessResponse(data=result, msg="获取会话详情成功")
|
||||
|
||||
|
||||
@@ -54,19 +45,8 @@ async def get_session_list_controller(
|
||||
search: Annotated[ChatSessionQueryParam, Depends()],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat:query"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
查询会话列表
|
||||
|
||||
参数:
|
||||
- page (PaginationQueryParam): 分页查询参数
|
||||
- search (ChatSessionQueryParam): 查询参数
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含会话列表分页信息的JSON响应
|
||||
"""
|
||||
result_dict = await ChatService.page_service(
|
||||
auth=auth,
|
||||
service = ChatService(auth)
|
||||
result_dict = await service.page(
|
||||
page_no=page.page_no,
|
||||
page_size=page.page_size,
|
||||
search=search,
|
||||
@@ -84,17 +64,8 @@ async def create_session_controller(
|
||||
data: ChatSessionCreateSchema,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat:create"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
创建会话
|
||||
|
||||
参数:
|
||||
- data (ChatSessionCreateSchema): 会话创建模型
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含创建会话详情的JSON响应
|
||||
"""
|
||||
result = await ChatService.create_service(auth=auth, data=data)
|
||||
service = ChatService(auth)
|
||||
result = await service.create(data=data)
|
||||
return SuccessResponse(data=result, msg="创建会话成功")
|
||||
|
||||
|
||||
@@ -108,18 +79,8 @@ async def update_session_controller(
|
||||
data: ChatSessionUpdateSchema,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat:update"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
更新会话
|
||||
|
||||
参数:
|
||||
- session_id (str): 会话ID
|
||||
- data (ChatSessionUpdateSchema): 会话更新模型
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含更新会话详情的JSON响应
|
||||
"""
|
||||
await ChatService.update_service(auth=auth, session_id=session_id, data=data)
|
||||
service = ChatService(auth)
|
||||
await service.update(session_id=session_id, data=data)
|
||||
return SuccessResponse(data=None, msg="更新会话成功")
|
||||
|
||||
|
||||
@@ -132,17 +93,8 @@ async def delete_session_controller(
|
||||
session_ids: list[str],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat:delete"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
删除会话
|
||||
|
||||
参数:
|
||||
- session_ids (list[str]): 会话ID列表
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含删除结果的JSON响应
|
||||
"""
|
||||
await ChatService.delete_service(auth=auth, session_ids=session_ids)
|
||||
service = ChatService(auth)
|
||||
await service.delete(session_ids=session_ids)
|
||||
return SuccessResponse(data=None, msg="删除会话成功")
|
||||
|
||||
|
||||
@@ -155,20 +107,10 @@ async def ai_chat_controller(
|
||||
data: AiChatRequestSchema,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat:query"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
AI 对话(非流式)
|
||||
|
||||
参数:
|
||||
- data (AiChatRequestSchema): 对话请求数据
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含 AI 回复、会话ID和函数调用信息的JSON响应
|
||||
"""
|
||||
result = await ChatService.chat_non_stream(
|
||||
service = ChatService(auth)
|
||||
result = await service.chat_non_stream(
|
||||
message=data.message,
|
||||
session_id=data.session_id,
|
||||
auth=auth,
|
||||
)
|
||||
return SuccessResponse(
|
||||
data=AiChatResponseSchema(
|
||||
|
||||
@@ -66,10 +66,10 @@ async def _format_session_data(session: TeamSession, auth: AuthSchema | None = N
|
||||
try:
|
||||
team_id = session_dict.get("team_id")
|
||||
if isinstance(team_id, str):
|
||||
dept_name = await DeptService.detail_service(auth=auth, id=int(team_id))
|
||||
dept_name = await DeptService(auth).detail(id=int(team_id))
|
||||
result["team_name"] = dept_name.get("name")
|
||||
elif isinstance(team_id, int):
|
||||
dept_name = await DeptService.detail_service(auth=auth, id=team_id)
|
||||
dept_name = await DeptService(auth).detail(id=team_id)
|
||||
result["team_name"] = dept_name.get("name")
|
||||
else:
|
||||
result["team_name"] = None
|
||||
@@ -128,26 +128,16 @@ def _extract_messages(runs: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
class ChatService:
|
||||
"""聊天会话管理模块服务层"""
|
||||
|
||||
@classmethod
|
||||
async def chat_query(cls, query: ChatQuerySchema, auth: AuthSchema) -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
处理聊天查询并流式返回文本片段。
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
self.auth = auth
|
||||
|
||||
参数:
|
||||
- query (ChatQuerySchema): 用户消息与会话等查询参数。
|
||||
- auth (AuthSchema): 当前用户认证信息。
|
||||
|
||||
返回:
|
||||
- AsyncGenerator[str, None]: 逐段输出的回复文本。
|
||||
"""
|
||||
async def chat_query(self, query: ChatQuerySchema) -> AsyncGenerator[str, None]:
|
||||
"""流式 AI 对话"""
|
||||
try:
|
||||
# 创建 CRUD 实例获取数据库连接
|
||||
crud = ChatSessionCRUD(auth)
|
||||
crud = ChatSessionCRUD(self.auth)
|
||||
|
||||
# 获取或创建会话
|
||||
session_id = query.session_id
|
||||
if not session_id:
|
||||
# 创建新会话
|
||||
import uuid
|
||||
|
||||
session_id = str(uuid.uuid4())
|
||||
@@ -156,17 +146,15 @@ class ChatService:
|
||||
raise CustomException(msg="创建会话失败")
|
||||
session_id = session.session_id
|
||||
|
||||
# 创建 AgnoFactory 实例并创建 Team,传入数据库连接
|
||||
agno_factory = AgnoFactory()
|
||||
dept_id = str(auth.user.dept_id) if auth and auth.user and hasattr(auth.user, "dept_id") and auth.user.dept_id else "default"
|
||||
dept_id = str(self.auth.user.dept_id) if self.auth and self.auth.user and hasattr(self.auth.user, "dept_id") and self.auth.user.dept_id else "default"
|
||||
agent = agno_factory.create_agent(
|
||||
user_id=auth.user.username if auth and auth.user else "user",
|
||||
user_id=self.auth.user.username if self.auth and self.auth.user else "user",
|
||||
dept_id=dept_id,
|
||||
session_id=session_id,
|
||||
db=crud.db,
|
||||
)
|
||||
|
||||
# 执行聊天查询 - 使用流式输出
|
||||
async for chunk in agent.arun(input=query.message, stream=True):
|
||||
if chunk and chunk.content:
|
||||
yield chunk.content
|
||||
@@ -175,26 +163,12 @@ class ChatService:
|
||||
logger.error(f"聊天查询失败: {e}")
|
||||
yield f"抱歉,处理您的请求时出现错误:{str(e)}"
|
||||
|
||||
@classmethod
|
||||
async def chat_non_stream(cls, message: str, session_id: str | None, auth: AuthSchema) -> dict[str, Any]:
|
||||
"""
|
||||
处理聊天查询并返回非流式 JSON 结构(含 session_id、操作建议等)。
|
||||
|
||||
参数:
|
||||
- message (str): 用户输入文本。
|
||||
- session_id (str | None): 已有会话 ID;为空则新建会话。
|
||||
- auth (AuthSchema): 当前用户认证信息。
|
||||
|
||||
返回:
|
||||
- dict[str, Any]: 包含 response、session_id、action 等字段的字典。
|
||||
"""
|
||||
async def chat_non_stream(self, message: str, session_id: str | None) -> dict[str, Any]:
|
||||
"""非流式 AI 对话"""
|
||||
try:
|
||||
# 创建 CRUD 实例获取数据库连接
|
||||
crud = ChatSessionCRUD(auth)
|
||||
crud = ChatSessionCRUD(self.auth)
|
||||
|
||||
# 获取或创建会话
|
||||
if not session_id:
|
||||
# 创建新会话
|
||||
import uuid
|
||||
|
||||
session_id = str(uuid.uuid4())
|
||||
@@ -203,35 +177,28 @@ class ChatService:
|
||||
raise CustomException(msg="创建会话失败")
|
||||
session_id = session.session_id
|
||||
|
||||
# 创建 AgnoFactory 实例并创建 Team,传入数据库连接
|
||||
agno_factory = AgnoFactory()
|
||||
dept_id = str(auth.user.dept_id) if auth and auth.user and hasattr(auth.user, "dept_id") and auth.user.dept_id else "default"
|
||||
dept_id = str(self.auth.user.dept_id) if self.auth and self.auth.user and hasattr(self.auth.user, "dept_id") and self.auth.user.dept_id else "default"
|
||||
agent: Team = agno_factory.create_agent(
|
||||
user_id=auth.user.username if auth and auth.user else "user",
|
||||
user_id=self.auth.user.username if self.auth and self.auth.user else "user",
|
||||
dept_id=dept_id,
|
||||
session_id=session_id,
|
||||
db=crud.db,
|
||||
)
|
||||
|
||||
# 执行聊天查询
|
||||
response: TeamRunOutput = await agent.arun(input=message)
|
||||
|
||||
# 解析响应内容和操作建议
|
||||
response_text = ""
|
||||
action = None
|
||||
|
||||
if response and response.content:
|
||||
response_text = response.content
|
||||
# 尝试从 response 中解析操作建议
|
||||
# 如果 AI 返回了 JSON 格式的操作建议
|
||||
import json
|
||||
|
||||
try:
|
||||
# 检查响应是否包含 JSON 格式的操作建议
|
||||
if response_text.strip().startswith("{") and response_text.strip().endswith("}"):
|
||||
action = json.loads(response_text)
|
||||
elif "```json" in response_text:
|
||||
# 提取 JSON 代码块
|
||||
json_start = response_text.find("```json") + 7
|
||||
json_end = response_text.find("```", json_start)
|
||||
if json_end > json_start:
|
||||
@@ -240,9 +207,8 @@ class ChatService:
|
||||
except (json.JSONDecodeError, Exception):
|
||||
pass
|
||||
|
||||
# 如果没有解析到 JSON,尝试从文本中提取操作信息
|
||||
if not action:
|
||||
action = cls._parse_action_from_response(response_text)
|
||||
action = self._parse_action_from_response(response_text)
|
||||
|
||||
return {
|
||||
"response": response_text,
|
||||
@@ -264,7 +230,6 @@ class ChatService:
|
||||
def _parse_action_from_response(response_text: str) -> dict[str, Any] | None:
|
||||
"""从响应文本中解析操作建议"""
|
||||
|
||||
# 定义路由配置
|
||||
route_config = {
|
||||
"用户管理": {"path": "/system/user", "name": "用户管理"},
|
||||
"角色管理": {"path": "/system/role", "name": "角色管理"},
|
||||
@@ -274,14 +239,12 @@ class ChatService:
|
||||
"系统日志": {"path": "/system/log", "name": "系统日志"},
|
||||
}
|
||||
|
||||
# 检查是否包含导航意图
|
||||
navigation_keywords = ["跳转", "打开", "进入", "前往", "去", "浏览", "查看"]
|
||||
has_navigation = any(keyword in response_text for keyword in navigation_keywords)
|
||||
|
||||
if not has_navigation:
|
||||
return None
|
||||
|
||||
# 查找页面名称
|
||||
for page_name, route_info in route_config.items():
|
||||
if page_name in response_text:
|
||||
return {
|
||||
@@ -290,7 +253,6 @@ class ChatService:
|
||||
"name": route_info["name"],
|
||||
}
|
||||
|
||||
# 尝试从关键词匹配
|
||||
keyword_mapping = {
|
||||
"用户": {"path": "/system/user", "name": "用户管理"},
|
||||
"角色": {"path": "/system/role", "name": "角色管理"},
|
||||
@@ -310,107 +272,39 @@ class ChatService:
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def create_service(cls, auth: AuthSchema, data: ChatSessionCreateSchema) -> dict[str, Any] | None:
|
||||
"""
|
||||
创建会话。
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息。
|
||||
- data (ChatSessionCreateSchema): 创建参数。
|
||||
|
||||
返回:
|
||||
- dict[str, Any] | None: 格式化后的会话字典;失败为 None。
|
||||
"""
|
||||
crud = ChatSessionCRUD(auth)
|
||||
session = await crud.create_crud(data=data)
|
||||
if session:
|
||||
return await _format_session_data(session, auth)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_session_service(cls, auth: AuthSchema, session_id: str) -> dict[str, Any] | None:
|
||||
"""
|
||||
获取单个会话详情。
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息。
|
||||
- session_id (str): 会话 ID。
|
||||
|
||||
返回:
|
||||
- dict[str, Any] | None: 格式化后的会话字典;不存在为 None。
|
||||
"""
|
||||
crud = ChatSessionCRUD(auth)
|
||||
async def get_session(self, session_id: str) -> dict[str, Any] | None:
|
||||
crud = ChatSessionCRUD(self.auth)
|
||||
session: TeamSession | None = await crud.get_by_id_crud(session_id=session_id)
|
||||
if session:
|
||||
return await _format_session_data(session, auth)
|
||||
return await _format_session_data(session, self.auth)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def page_service(
|
||||
cls,
|
||||
auth: AuthSchema,
|
||||
async def create(self, data: ChatSessionCreateSchema) -> dict[str, Any] | None:
|
||||
crud = ChatSessionCRUD(self.auth)
|
||||
session = await crud.create_crud(data=data)
|
||||
if session:
|
||||
return await _format_session_data(session, self.auth)
|
||||
return None
|
||||
|
||||
async def page(
|
||||
self,
|
||||
page_no: int,
|
||||
page_size: int,
|
||||
search: ChatSessionQueryParam,
|
||||
order_by: list[dict[str, str]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
分页获取会话列表。会话由 Agno 存储,无统一 SQL 分页,仅对内存列表切片。
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息。
|
||||
- page_no (int): 页码。
|
||||
- page_size (int): 每页条数。
|
||||
- search (ChatSessionQueryParam): 查询条件。
|
||||
- order_by (list[dict[str, str]] | None): 排序,可选。
|
||||
|
||||
返回:
|
||||
- dict[str, Any]: 分页结果(含 items、total 等)。
|
||||
"""
|
||||
crud = ChatSessionCRUD(auth)
|
||||
# 获取所有会话
|
||||
crud = ChatSessionCRUD(self.auth)
|
||||
sessions = await crud.list_crud()
|
||||
|
||||
# 转换为响应模型 - 使用 TeamSession 内置的 to_dict 方法并格式化
|
||||
items = [await _format_session_data(s, auth) for s in sessions]
|
||||
|
||||
# 非关系型会话存储,沿用内存分页
|
||||
result = await PaginationService.paginate(
|
||||
items = [await _format_session_data(s, self.auth) for s in sessions]
|
||||
return await PaginationService.paginate(
|
||||
data_list=items,
|
||||
page_no=page_no,
|
||||
page_size=page_size,
|
||||
)
|
||||
|
||||
return result
|
||||
async def update(self, session_id: str, data: ChatSessionUpdateSchema) -> bool:
|
||||
crud = ChatSessionCRUD(self.auth)
|
||||
return await crud.update_crud(session_id=session_id, data=data)
|
||||
|
||||
@classmethod
|
||||
async def update_service(cls, auth: AuthSchema, session_id: str, data: ChatSessionUpdateSchema) -> bool:
|
||||
"""
|
||||
更新会话。
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息。
|
||||
- session_id (str): 会话 ID。
|
||||
- data (ChatSessionUpdateSchema): 更新数据。
|
||||
|
||||
返回:
|
||||
- bool: 是否成功。
|
||||
"""
|
||||
crud = ChatSessionCRUD(auth)
|
||||
success = await crud.update_crud(session_id=session_id, data=data)
|
||||
return success
|
||||
|
||||
@classmethod
|
||||
async def delete_service(cls, auth: AuthSchema, session_ids: list[str]) -> None:
|
||||
"""
|
||||
删除会话。
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息。
|
||||
- session_ids (list[str]): 待删除会话 ID 列表。
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
await ChatSessionCRUD(auth).delete_crud(session_ids=session_ids)
|
||||
async def delete(self, session_ids: list[str]) -> None:
|
||||
await ChatSessionCRUD(self.auth).delete_crud(session_ids=session_ids)
|
||||
|
||||
Reference in New Issue
Block a user