refactor: 完成项目大规模代码重构与依赖清理

这是一次综合性的重构更新,包含以下主要变更:
1.  升级Python版本到3.12,更新依赖配置
2.  替换旧的.j2模板为.jinja2格式,新增代码生成模板
3.  重构权限过滤策略,更新权限枚举与模型配置
4.  移除Prefect依赖,替换为自研拓扑并行执行引擎
5.  重构认证与上下文管理,拆分租户/请求上下文
6.  简化响应模型、CRUD与服务层代码
7.  清理废弃的支付网关模块,重构订单定时任务
8.  更新在线用户、监控等模块的接口与路由
9.  优化邮件模板与工具类,新增邮件模板文件
10. 修复数据库会话配置与类型提示
This commit is contained in:
zhangtao
2026-06-21 06:02:05 +08:00
parent 4e2b668d7b
commit 211fddd6e0
121 changed files with 2869 additions and 11379 deletions
+13 -71
View File
@@ -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(
+35 -141
View File
@@ -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)