mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 04:46:26 +00:00
refactor(ai): 重构AI模块目录结构并优化代码
将AI模块从module_application迁移到module_ai目录 新增聊天会话和消息的CRUD、服务和控制器 实现WebSocket聊天接口和前端组件 优化代码结构和性能,修复已知问题
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Path
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.common.response import ResponseSchema, SuccessResponse
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.logger import log
|
||||
from app.core.router_class import OperationLogRoute
|
||||
|
||||
from .schema import (
|
||||
ChatSessionCreateSchema,
|
||||
ChatSessionOutSchema,
|
||||
ChatSessionQueryParam,
|
||||
ChatSessionUpdateSchema,
|
||||
)
|
||||
from .service import ChatSessionService
|
||||
|
||||
ChatSessionRouter = APIRouter(route_class=OperationLogRoute, prefix="/chat_session", tags=["AI模块"])
|
||||
|
||||
|
||||
@ChatSessionRouter.get(
|
||||
"/detail/{id}",
|
||||
summary="获取聊天会话详情",
|
||||
description="获取聊天会话详情",
|
||||
response_model=ResponseSchema[ChatSessionOutSchema],
|
||||
)
|
||||
async def get_session_detail_controller(
|
||||
id: Annotated[int, Path(description="会话ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat_session:detail"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
获取聊天会话详情
|
||||
|
||||
参数:
|
||||
- id (str): 会话ID
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含会话详情的JSON响应
|
||||
"""
|
||||
result_dict = await ChatSessionService.detail_service(id=id, auth=auth)
|
||||
log.info(f"获取聊天会话详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取聊天会话详情成功")
|
||||
|
||||
|
||||
@ChatSessionRouter.get(
|
||||
"/list",
|
||||
summary="查询聊天会话列表",
|
||||
description="查询聊天会话列表",
|
||||
response_model=ResponseSchema[list[ChatSessionOutSchema]],
|
||||
)
|
||||
async def get_session_list_controller(
|
||||
page: Annotated[PaginationQueryParam, Depends()],
|
||||
search: Annotated[ChatSessionQueryParam, Depends()],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat_session:query"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
查询聊天会话列表
|
||||
|
||||
参数:
|
||||
- page (PaginationQueryParam): 分页查询参数
|
||||
- search (ChatSessionQueryParam): 查询参数
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含会话列表分页信息的JSON响应
|
||||
"""
|
||||
result_dict = await ChatSessionService.page_service(
|
||||
auth=auth,
|
||||
page_no=page.page_no,
|
||||
page_size=page.page_size,
|
||||
search=search,
|
||||
order_by=page.order_by,
|
||||
)
|
||||
log.info("查询聊天会话列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询聊天会话列表成功")
|
||||
|
||||
|
||||
@ChatSessionRouter.post(
|
||||
"/create",
|
||||
summary="创建聊天会话",
|
||||
description="创建聊天会话",
|
||||
response_model=ResponseSchema[ChatSessionOutSchema],
|
||||
)
|
||||
async def create_session_controller(
|
||||
data: ChatSessionCreateSchema,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat_session:create"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
创建聊天会话
|
||||
|
||||
参数:
|
||||
- data (ChatSessionCreateSchema): 会话创建模型
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含创建会话详情的JSON响应
|
||||
"""
|
||||
result_dict = await ChatSessionService.create_service(auth=auth, data=data)
|
||||
log.info(f"创建聊天会话成功: {result_dict.get('title')}")
|
||||
return SuccessResponse(data=result_dict, msg="创建聊天会话成功")
|
||||
|
||||
|
||||
@ChatSessionRouter.put(
|
||||
"/update/{id}",
|
||||
summary="修改聊天会话",
|
||||
description="修改聊天会话",
|
||||
response_model=ResponseSchema[ChatSessionOutSchema],
|
||||
)
|
||||
async def update_session_controller(
|
||||
data: ChatSessionUpdateSchema,
|
||||
id: Annotated[int, Path(description="会话ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat_session:update"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
修改聊天会话
|
||||
|
||||
参数:
|
||||
- data (ChatSessionUpdateSchema): 会话更新模型
|
||||
- id (int): 会话ID
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含修改会话详情的JSON响应
|
||||
"""
|
||||
result_dict = await ChatSessionService.update_service(auth=auth, id=id, data=data)
|
||||
log.info(f"修改聊天会话成功: {result_dict.get('title')}")
|
||||
return SuccessResponse(data=result_dict, msg="修改聊天会话成功")
|
||||
|
||||
|
||||
@ChatSessionRouter.delete(
|
||||
"/delete",
|
||||
summary="删除聊天会话",
|
||||
description="删除聊天会话",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def delete_session_controller(
|
||||
ids: Annotated[list[int], Body(description="会话ID列表")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_ai:chat_session:delete"]))],
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
删除聊天会话
|
||||
|
||||
参数:
|
||||
- ids (list[int]): 会话ID列表
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
|
||||
返回:
|
||||
- JSONResponse: 包含删除会话详情的JSON响应
|
||||
"""
|
||||
await ChatSessionService.delete_service(auth=auth, ids=ids)
|
||||
log.info(f"删除聊天会话成功: {ids}")
|
||||
return SuccessResponse(msg="删除聊天会话成功")
|
||||
@@ -0,0 +1,149 @@
|
||||
from collections.abc import Sequence
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.core.exceptions import CustomException
|
||||
|
||||
from .model import ChatSessionModel
|
||||
from .schema import (
|
||||
ChatSessionCreateSchema,
|
||||
ChatSessionOutSchema,
|
||||
ChatSessionUpdateSchema,
|
||||
)
|
||||
|
||||
|
||||
class ChatSessionCRUD(CRUDBase[ChatSessionModel, ChatSessionCreateSchema, ChatSessionUpdateSchema]):
|
||||
"""聊天会话数据层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""
|
||||
初始化CRUD数据层
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
"""
|
||||
super().__init__(model=ChatSessionModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int, preload: list[str] | None = None) -> ChatSessionModel | None:
|
||||
"""
|
||||
详情
|
||||
|
||||
参数:
|
||||
- id (int): 会话ID
|
||||
- preload (list[str] | None): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- ChatSessionModel | None: 会话模型实例或None
|
||||
"""
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def list_crud(
|
||||
self,
|
||||
search: dict | None = None,
|
||||
order_by: list[dict] | None = None,
|
||||
preload: list[str] | None = None,
|
||||
) -> Sequence[ChatSessionModel]:
|
||||
"""
|
||||
列表查询
|
||||
|
||||
参数:
|
||||
- search (dict | None): 查询参数
|
||||
- order_by (list[dict] | None): 排序参数
|
||||
- preload (list[str] | None): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[ChatSessionModel]: 会话模型实例序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_crud(self, data: ChatSessionCreateSchema) -> ChatSessionModel:
|
||||
"""
|
||||
创建
|
||||
|
||||
参数:
|
||||
- data (ChatSessionCreateSchema): 会话创建模型
|
||||
|
||||
返回:
|
||||
- ChatSessionModel: 会话模型实例
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def update_crud(self, id: int, data: ChatSessionUpdateSchema) -> ChatSessionModel:
|
||||
"""
|
||||
更新
|
||||
|
||||
参数:
|
||||
- id (int): 会话ID
|
||||
- data (ChatSessionUpdateSchema): 会话更新模型
|
||||
|
||||
返回:
|
||||
- ChatSessionModel: 会话模型实例
|
||||
"""
|
||||
obj = await self.get(id=id, preload=[])
|
||||
if not obj:
|
||||
raise CustomException(msg="更新对象不存在")
|
||||
|
||||
obj_dict = data.model_dump(exclude_unset=True) if not isinstance(data, dict) else data
|
||||
|
||||
if self.auth.user and hasattr(obj, "updated_id"):
|
||||
setattr(obj, "updated_id", self.auth.user.id)
|
||||
|
||||
for key, value in obj_dict.items():
|
||||
if hasattr(obj, key):
|
||||
setattr(obj, key, value)
|
||||
|
||||
await self.auth.db.flush()
|
||||
await self.auth.db.refresh(obj)
|
||||
return obj
|
||||
|
||||
async def delete_crud(self, ids: list[int]) -> None:
|
||||
"""
|
||||
批量删除
|
||||
|
||||
参数:
|
||||
- ids (list[int]): 会话ID列表
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
from sqlalchemy import delete
|
||||
|
||||
if not ids:
|
||||
raise CustomException(msg="删除失败,删除对象不能为空")
|
||||
|
||||
sql = delete(self.model).where(self.model.id.in_(ids))
|
||||
await self.auth.db.execute(sql)
|
||||
await self.auth.db.flush()
|
||||
|
||||
async def page_crud(
|
||||
self,
|
||||
offset: int,
|
||||
limit: int,
|
||||
order_by: list[dict] | None = None,
|
||||
search: dict | None = None,
|
||||
preload: list | None = None,
|
||||
) -> dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- offset (int): 偏移量
|
||||
- limit (int): 每页数量
|
||||
- order_by (list[dict] | None): 排序参数
|
||||
- search (dict | None): 查询参数
|
||||
- preload (list | None): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- dict: 分页数据
|
||||
"""
|
||||
order_by_list = order_by or [{"id": "desc"}]
|
||||
search_dict = search or {}
|
||||
|
||||
return await self.page(
|
||||
offset=offset,
|
||||
limit=limit,
|
||||
order_by=order_by_list,
|
||||
search=search_dict,
|
||||
out_schema=ChatSessionOutSchema,
|
||||
preload=preload,
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
from sqlalchemy import String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import ModelMixin, UserMixin
|
||||
|
||||
|
||||
class ChatSessionModel(ModelMixin, UserMixin):
|
||||
"""
|
||||
聊天会话表
|
||||
"""
|
||||
|
||||
__tablename__: str = "ai_chat_session"
|
||||
__table_args__: dict[str, str] = {"comment": "聊天会话表"}
|
||||
__loader_options__: list[str] = ["created_by", "updated_by"]
|
||||
|
||||
title: Mapped[str] = mapped_column(String(200), nullable=False, comment="会话标题")
|
||||
@@ -0,0 +1,53 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from fastapi import Query
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.common.enums import QueueEnum
|
||||
from app.core.base_schema import BaseSchema, UserBySchema
|
||||
|
||||
|
||||
class ChatSessionCreateSchema(BaseModel):
|
||||
"""新增聊天会话"""
|
||||
|
||||
title: str = Field(..., description="会话标题")
|
||||
|
||||
|
||||
class ChatSessionUpdateSchema(BaseModel):
|
||||
"""更新聊天会话"""
|
||||
|
||||
title: str | None = Field(None, description="会话标题")
|
||||
|
||||
|
||||
class ChatSessionOutSchema(ChatSessionCreateSchema, BaseSchema, UserBySchema):
|
||||
"""聊天会话响应模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChatSessionQueryParam:
|
||||
"""聊天会话查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
title: str | None = Query(None, description="会话标题"),
|
||||
status: str | None = Query(None, description="是否启用"),
|
||||
created_time: list[str] | None = Query(
|
||||
None,
|
||||
description="创建时间范围",
|
||||
examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"],
|
||||
),
|
||||
updated_time: list[str] | None = Query(
|
||||
None,
|
||||
description="更新时间范围",
|
||||
examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"],
|
||||
),
|
||||
) -> None:
|
||||
self.title = (QueueEnum.like.value, f"%{title}%") if title else None
|
||||
self.status = (QueueEnum.eq.value, status) if status else None
|
||||
|
||||
if created_time and len(created_time) == 2:
|
||||
self.created_time = (QueueEnum.between.value, (created_time[0], created_time[1]))
|
||||
if updated_time and len(updated_time) == 2:
|
||||
self.updated_time = (QueueEnum.between.value, (updated_time[0], updated_time[1]))
|
||||
@@ -0,0 +1,153 @@
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import log
|
||||
|
||||
from .crud import ChatSessionCRUD
|
||||
from .model import ChatSessionModel
|
||||
from .schema import (
|
||||
ChatSessionCreateSchema,
|
||||
ChatSessionOutSchema,
|
||||
ChatSessionQueryParam,
|
||||
ChatSessionUpdateSchema,
|
||||
)
|
||||
|
||||
|
||||
class ChatSessionService:
|
||||
"""
|
||||
聊天会话管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def detail_service(cls, auth: AuthSchema, id: int) -> dict:
|
||||
"""
|
||||
详情
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- id (str): 会话ID
|
||||
|
||||
返回:
|
||||
- dict: 会话模型实例字典
|
||||
"""
|
||||
obj: ChatSessionModel | None = await ChatSessionCRUD(auth).get_by_id_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg="该会话不存在")
|
||||
result = ChatSessionOutSchema.model_validate(obj).model_dump()
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def list_service(
|
||||
cls,
|
||||
auth: AuthSchema,
|
||||
search: ChatSessionQueryParam | None = None,
|
||||
order_by: list[dict[str, str]] | None = None,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
列表查询
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- search (ChatSessionQueryParam | None): 查询参数
|
||||
- order_by (list[dict[str, str]] | None): 排序参数
|
||||
|
||||
返回:
|
||||
- list[dict]: 会话模型实例字典列表
|
||||
"""
|
||||
search_dict = search.__dict__ if search else None
|
||||
obj_list = await ChatSessionCRUD(auth).list_crud(search=search_dict, order_by=order_by)
|
||||
result_list = [ChatSessionOutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
return result_list
|
||||
|
||||
@classmethod
|
||||
async def page_service(
|
||||
cls,
|
||||
auth: AuthSchema,
|
||||
page_no: int,
|
||||
page_size: int,
|
||||
search: ChatSessionQueryParam | None = None,
|
||||
order_by: list[dict[str, str]] | None = None,
|
||||
) -> dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- page_no (int): 页码
|
||||
- page_size (int): 每页数量
|
||||
- search (ChatSessionQueryParam | None): 查询参数
|
||||
- order_by (list[dict[str, str]] | None): 排序参数
|
||||
|
||||
返回:
|
||||
- dict: 分页数据
|
||||
"""
|
||||
search_dict = search.__dict__ if search else {}
|
||||
order_by_list = order_by or [{"id": "desc"}]
|
||||
offset = (page_no - 1) * page_size
|
||||
|
||||
result = await ChatSessionCRUD(auth).page_crud(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order_by_list,
|
||||
search=search_dict,
|
||||
)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def create_service(cls, auth: AuthSchema, data: ChatSessionCreateSchema) -> dict:
|
||||
"""
|
||||
创建
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- data (ChatSessionCreateSchema): 会话创建模型
|
||||
|
||||
返回:
|
||||
- dict: 会话模型实例字典
|
||||
"""
|
||||
obj = await ChatSessionCRUD(auth).create_crud(data=data)
|
||||
result = ChatSessionOutSchema.model_validate(obj).model_dump()
|
||||
log.info(f"创建聊天会话成功: {result.get('title')}")
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def update_service(cls, auth: AuthSchema, id: int, data: ChatSessionUpdateSchema) -> dict:
|
||||
"""
|
||||
更新
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- id (int): 会话ID
|
||||
- data (ChatSessionUpdateSchema): 会话更新模型
|
||||
|
||||
返回:
|
||||
- dict: 会话模型实例字典
|
||||
"""
|
||||
obj = await ChatSessionCRUD(auth).update_crud(id=id, data=data)
|
||||
if not obj:
|
||||
raise CustomException(msg="更新失败,该会话不存在")
|
||||
result = ChatSessionOutSchema.model_validate(obj).model_dump()
|
||||
log.info(f"更新聊天会话成功: {result.get('title')}")
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def delete_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
||||
"""
|
||||
删除
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- ids (list[int]): 会话ID列表
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
if len(ids) < 1:
|
||||
raise CustomException(msg="删除失败,删除对象不能为空")
|
||||
|
||||
for id in ids:
|
||||
obj = await ChatSessionCRUD(auth).get_by_id_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg=f"删除失败,ID为{id}的会话不存在")
|
||||
|
||||
await ChatSessionCRUD(auth).delete_crud(ids=ids)
|
||||
log.info(f"删除聊天会话成功: {ids}")
|
||||
Reference in New Issue
Block a user