refactor(api): 重构API路由组织结构并简化模块导入

- 将系统管理、监控管理、通用、资源管理、示例、应用及AI模块路由封装为统一Router
- 精简顶层路由注册逻辑,提升维护性和可读性
- 按模块重构AI、应用、通用、示例、监控、资源、系统子模块初始化文件
- 采用SQLAlchemy 2.0新语法重写工单和版本模型,兼容MySQL和PostgreSQL
- 完善工单和版本模块CRUD、服务、参数校验、Schema和控制器代码
- 增加工单和版本的权限验证、日志记录及分页支持
- 统一使用pydantic的新配置类ConfigDict增强Schema定义
- 删除废弃的工单和版本旧代码,提升代码一致性和健壮性
This commit is contained in:
zhangtao
2025-09-16 02:06:54 +08:00
parent a42feeee30
commit d36b86e446
23 changed files with 571 additions and 248 deletions
+23 -88
View File
@@ -2,101 +2,36 @@
from fastapi import APIRouter
from .module_system.auth.controller import AuthRouter
from .module_system.menu.controller import MenuRouter
from .module_system.dept.controller import DeptRouter
from .module_system.role.controller import RoleRouter
from .module_system.user.controller import UserRouter
from .module_system.log.controller import LogRouter
from .module_system.position.controller import PositionRouter
from .module_system.notice.controller import NoticeRouter
from .module_system.params.controller import ParamsRouter
from .module_system.dict.controller import DictRouter
from .module_system.ticket.controller import router as TicketRouter
from .module_system.version.controller import router as VersionRouter
# 系统管理模块
from .module_system import SystemRouter
from .module_monitor.cache.controller import CacheRouter
from .module_monitor.job.controller import JobRouter
from .module_monitor.online.controller import OnlineRouter
from .module_monitor.server.controller import ServerRouter
# 监控管理模块
from .module_monitor import MonitorRouter
from .module_common.file.controller import FileRouter
# 通用模块
from .module_common import CommonRouter
from .module_resource.resource.controller import ResourceRouter
# 资源管理模块
from .module_resource import ResourceRouter
from .module_example.demo.controller import DemoRouter
# 示例模块
from .module_example import ExampleRouter
from .module_application.myapp.controller import MyAppRouter
# 应用模块
from .module_application import ApplicationRouter
from .module_ai.mcp.controller import MCPRouter
# 定义路由模块映射,按模块分组
SYSTEM_MODULES = [
{"router": AuthRouter},
{"router": MenuRouter},
{"router": DeptRouter},
{"router": RoleRouter},
{"router": UserRouter},
{"router": LogRouter},
{"router": PositionRouter},
{"router": NoticeRouter},
{"router": ParamsRouter},
{"router": DictRouter},
{"router": TicketRouter},
{"router": VersionRouter},
]
MONITOR_MODULES = [
{"router": JobRouter},
{"router": CacheRouter},
{"router": OnlineRouter},
{"router": ServerRouter},
]
COMMON_MODULES = [{"router": FileRouter}]
RESOURCE_MODULES = [{"router": ResourceRouter}]
EXAMPLE_MODULES = [{"router": DemoRouter}]
APPLICATION_MODULES = [{"router": MyAppRouter}]
AI_MODULES = [{"router": MCPRouter}]
# AI模块
from .module_ai import AIRouter
# 创建主路由
router = APIRouter()
for module in SYSTEM_MODULES:
router.include_router(
router=module["router"], prefix="/system"
)
for module in MONITOR_MODULES:
router.include_router(
router=module["router"], prefix="/monitor"
)
for module in COMMON_MODULES:
router.include_router(
router=module["router"], prefix="/common"
)
for module in EXAMPLE_MODULES:
router.include_router(
router=module["router"], prefix="/example"
)
for module in APPLICATION_MODULES:
router.include_router(
router=module["router"], prefix="/application"
)
for module in RESOURCE_MODULES:
router.include_router(
router=module["router"], prefix="/resource"
)
for module in AI_MODULES:
router.include_router(
router=module["router"], prefix="/ai"
)
# 注册各模块路由
router.include_router(SystemRouter)
router.include_router(MonitorRouter)
router.include_router(CommonRouter)
router.include_router(ResourceRouter)
router.include_router(ExampleRouter)
router.include_router(ApplicationRouter)
router.include_router(AIRouter)
+10
View File
@@ -2,3 +2,13 @@
"""
AI模块初始化文件
"""
from fastapi import APIRouter
from .mcp.controller import MCPRouter
AIRouter = APIRouter(prefix="/ai")
# 包含所有子路由
AIRouter.include_router(MCPRouter)
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from .myapp.controller import MyAppRouter
ApplicationRouter = APIRouter(prefix="/application")
# 包含所有子路由
ApplicationRouter.include_router(MyAppRouter)
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from .file.controller import FileRouter
CommonRouter = APIRouter(prefix="/common")
# 包含所有子路由
CommonRouter.include_router(FileRouter)
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from .demo.controller import DemoRouter
ExampleRouter = APIRouter(prefix="/example")
# 包含所有子路由
ExampleRouter.include_router(DemoRouter)
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from .cache.controller import CacheRouter
from .job.controller import JobRouter
from .online.controller import OnlineRouter
from .server.controller import ServerRouter
MonitorRouter = APIRouter(prefix="/monitor")
# 包含所有子路由
MonitorRouter.include_router(CacheRouter)
MonitorRouter.include_router(JobRouter)
MonitorRouter.include_router(OnlineRouter)
MonitorRouter.include_router(ServerRouter)
@@ -1,5 +1,11 @@
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from .resource import ResourceRouter
__all__ = ['ResourceRouter']
ResourceRouter = APIRouter(prefix="/resource")
# 包含所有子路由
ResourceRouter.include_router(ResourceRouter)
@@ -1 +1,32 @@
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from .auth.controller import AuthRouter
from .user.controller import UserRouter
from .role.controller import RoleRouter
from .menu.controller import MenuRouter
from .dept.controller import DeptRouter
from .position.controller import PositionRouter
from .dict.controller import DictRouter
from .params.controller import ParamsRouter
from .notice.controller import NoticeRouter
from .log.controller import LogRouter
from .version.controller import VersionRouter
from .ticket.controller import TicketRouter
SystemRouter = APIRouter(prefix="/system")
# 包含所有子路由
SystemRouter.include_router(AuthRouter)
SystemRouter.include_router(UserRouter)
SystemRouter.include_router(RoleRouter)
SystemRouter.include_router(MenuRouter)
SystemRouter.include_router(DeptRouter)
SystemRouter.include_router(PositionRouter)
SystemRouter.include_router(DictRouter)
SystemRouter.include_router(ParamsRouter)
SystemRouter.include_router(NoticeRouter)
SystemRouter.include_router(LogRouter)
SystemRouter.include_router(VersionRouter)
SystemRouter.include_router(TicketRouter)
@@ -22,7 +22,4 @@ class PositionUpdateSchema(PositionCreateSchema):
class PositionOutSchema(PositionCreateSchema, BaseSchema):
"""岗位信息响应模型"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="主键ID")
created_at: DateTimeStr = Field(..., description="创建时间")
updated_at: DateTimeStr = Field(..., description="更新时间")
...
@@ -1 +0,0 @@
# 工单管理模块
@@ -1,4 +1,71 @@
from fastapi import APIRouter
# -*- coding: utf-8 -*-
router = APIRouter(prefix="/tickets", tags=["工单管理"])
from fastapi import APIRouter, Body, Depends, Path
from fastapi.responses import JSONResponse
from app.common.response import SuccessResponse
from app.common.request import PaginationService
from app.core.router_class import OperationLogRoute
from app.core.base_params import PaginationQueryParams
from app.core.dependencies import AuthPermission
from app.core.logger import logger
from app.api.v1.module_system.auth.schema import AuthSchema
from .param import TicketQueryParams
from .service import TicketService
from .schema import TicketCreateSchema, TicketUpdateSchema
TicketRouter = APIRouter(route_class=OperationLogRoute, prefix="/tickets", tags=["工单管理"])
@TicketRouter.get("/detail/{id}", summary="获取工单详情", description="获取工单详情")
async def get_ticket_detail_controller(
id: int = Path(..., description="工单ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:query"]))
) -> JSONResponse:
result_dict = await TicketService.get_ticket_detail_service(auth=auth, id=id)
logger.info(f"获取工单详情成功 {id}")
return SuccessResponse(data=result_dict, msg="获取工单详情成功")
@TicketRouter.get("/list", summary="查询工单列表", description="查询工单列表")
async def get_ticket_list_controller(
page: PaginationQueryParams = Depends(),
search: TicketQueryParams = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:query"]))
) -> JSONResponse:
result_dict_list = await TicketService.get_ticket_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.get_page_obj(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
logger.info("查询工单列表成功")
return SuccessResponse(data=result_dict, msg="查询工单列表成功")
@TicketRouter.post("/create", summary="创建工单", description="创建工单")
async def create_ticket_controller(
data: TicketCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:create"]))
) -> JSONResponse:
result_dict = await TicketService.create_ticket_service(auth=auth, data=data)
logger.info(f"创建工单成功: {result_dict}")
return SuccessResponse(data=result_dict, msg="创建工单成功")
@TicketRouter.put("/update/{id}", summary="修改工单", description="修改工单")
async def update_ticket_controller(
data: TicketUpdateSchema,
id: int = Path(..., description="工单ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:update"]))
) -> JSONResponse:
result_dict = await TicketService.update_ticket_service(auth=auth, id=id, data=data)
logger.info(f"修改工单成功: {result_dict}")
return SuccessResponse(data=result_dict, msg="修改工单成功")
@TicketRouter.delete("/delete", summary="删除工单", description="删除工单")
async def delete_ticket_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:delete"]))
) -> JSONResponse:
await TicketService.delete_ticket_service(auth=auth, ids=ids)
logger.info(f"删除工单成功: {ids}")
return SuccessResponse(msg="删除工单成功")
+50 -10
View File
@@ -1,16 +1,56 @@
from sqlalchemy.orm import Session
from typing import List
from app.api.v1.module_system.ticket.model import Ticket
from app.api.v1.module_system.ticket.schema import TicketCreate, TicketUpdate
from app.core.base_crud import CRUDBase
from ..auth.schema import AuthSchema
from typing import Dict, List, Optional, Sequence
class CRUDTicket(CRUDBase[Ticket, TicketCreate, TicketUpdate]):
from app.core.base_crud import CRUDBase
from app.api.v1.module_system.auth.schema import AuthSchema
from .model import TicketModel
from .schema import TicketCreateSchema, TicketUpdateSchema
class TicketCRUD(CRUDBase[TicketModel, TicketCreateSchema, TicketUpdateSchema]):
"""工单 CRUD 操作"""
def __init__(self, auth: AuthSchema) -> None:
"""初始化工单CRUD"""
self.auth = auth
super().__init__(model=Ticket, auth=auth)
pass
super().__init__(model=TicketModel, auth=auth)
async def get_by_id_crud(self, id: int) -> Optional[TicketModel]:
"""根据id获取工单信息
:param id: 工单ID
:return: 工单信息
"""
return await self.get(id=id)
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[TicketModel]:
"""获取工单列表
:param search: 搜索条件
:param order_by: 排序字段
:return: 工单列表
"""
return await self.list(search=search or {}, order_by=order_by or [])
async def create_crud(self, data: TicketCreateSchema) -> Optional[TicketModel]:
"""创建工单
:param data: 工单创建数据
:return: 创建的工单
"""
return await self.create(data=data)
async def update_crud(self, id: int, data: TicketUpdateSchema) -> Optional[TicketModel]:
"""更新工单
:param id: 工单ID
:param data: 工单更新数据
:return: 更新的工单
"""
return await self.update(id=id, data=data)
async def delete_crud(self, ids: List[int]) -> None:
"""批量删除工单
:param ids: 工单ID列表
"""
return await self.delete(ids=ids)
@@ -1,23 +1,30 @@
# -*- coding: utf-8 -*-
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from typing import Optional
from sqlalchemy import String, Text, DateTime, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from datetime import datetime
from app.core.base_model import MappedBase, CreatorMixin
class Ticket(CreatorMixin):
"""工单模型"""
__tablename__ = "ticket"
title = Column(String(255), nullable=False, comment="工单标题")
status = Column(String(50), default="1", comment="工单状态(1:待处理 2:处理中 3:已解决 4:已关闭)")
priority = Column(String(50), default="medium", comment="优先级(low, medium, high, urgent)")
type = Column(String(50), comment="工单类型(bug, feature, task)")
assignee_id = Column(Integer, ForeignKey("system_user.id"), comment="指派给用户ID")
reporter_id = Column(Integer, ForeignKey("system_user.id"), comment="报告人ID")
project = Column(String(100), comment="所属项目")
version = Column(String(50), comment="版本号")
from app.core.base_model import CreatorMixin
class TicketModel(CreatorMixin):
"""
工单模型 - SQLAlchemy 2.0 语法
兼容 MySQL 和 PostgreSQL
"""
__tablename__ = 'system_ticket'
__table_args__ = ({'comment': '工单表'})
title: Mapped[str] = mapped_column(String(255), nullable=False, comment='工单标题')
ticket_status: Mapped[str] = mapped_column(String(50), default='1', comment='工单状态(1:待处理 2:处理中 3:已解决 4:已关闭)')
priority: Mapped[str] = mapped_column(String(50), default='medium', comment='优先级(low, medium, high, urgent)')
type: Mapped[Optional[str]] = mapped_column(String(50), comment='工单类型(bug, feature, task)')
assignee_id: Mapped[Optional[int]] = mapped_column(ForeignKey('system_users.id'), comment='指派给用户ID')
reporter_id: Mapped[Optional[int]] = mapped_column(ForeignKey('system_users.id'), comment='报告人ID')
project: Mapped[Optional[str]] = mapped_column(String(100), comment='所属项目')
version: Mapped[Optional[str]] = mapped_column(String(50), comment='版本号')
# 关系
assignee = relationship("UserModel", foreign_keys=[assignee_id])
reporter = relationship("UserModel", foreign_keys=[reporter_id])
assignee = relationship('UserModel', foreign_keys=[assignee_id])
reporter = relationship('UserModel', foreign_keys=[reporter_id])
@@ -5,13 +5,17 @@ from typing import Optional
from fastapi import Query
from app.core.validator import DateTimeStr
class TicketQueryParams:
"""工单管理查询参数"""
def __init__(
self,
title: Optional[str] = Query(None, description="工单标题"),
status: Optional[bool] = Query(None, description="是否可用"),
status: Optional[bool] = Query(None, description="状态"),
ticket_status: Optional[str] = Query(None, description="工单状态"),
priority: Optional[str] = Query(None, description="优先级"),
creator: Optional[int] = Query(None, description="创建人"),
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
@@ -24,9 +28,11 @@ class TicketQueryParams:
# 精确查询字段
self.creator_id = creator
self.status = status
self.ticket_status = ticket_status # 使用 ticket_status 而不是 status
self.priority = priority
# 时间范围查询
if start_time and end_time:
start_datetime = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
end_datetime = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
self.created_at = ("between", (start_datetime, end_datetime))
start_datetime = datetime.strptime(str(start_time), '%Y-%m-%d %H:%M:%S')
end_datetime = datetime.strptime(str(end_time), '%Y-%m-%d %H:%M:%S')
self.created_at = ("between", (start_datetime, end_datetime))
@@ -1,49 +1,32 @@
# -*- coding: utf-8 -*-
from pydantic import BaseModel
from typing import Optional, List
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field
from datetime import datetime
from app.core.base_schema import BaseSchema
class TicketCreate(BaseModel):
class TicketCreateSchema(BaseModel):
"""创建工单"""
title: str
description: Optional[str] = None
status: Optional[str] = "open"
priority: Optional[str] = "medium"
type: Optional[str] = "bug"
assignee_id: Optional[int] = None
reporter_id: int
project: Optional[str] = None
version: Optional[str] = None
title: str = Field(..., max_length=255, description='工单标题')
description: Optional[str] = Field(default=None, description='工单描述')
status: Optional[bool] = Field(default=True, description='1、启动 2、停止')
ticket_status: Optional[str] = Field(default='1', description='工单处理状态(1:待处理 2:处理中 3:已解决 4:已关闭)')
priority: Optional[str] = Field(default='medium', description='优先级(low, medium, high, urgent)')
type: Optional[str] = Field(default='bug', description='工单类型(bug, feature, task)')
assignee_id: Optional[int] = Field(default=None, description='指派给用户ID')
reporter_id: int = Field(..., description='报告人ID')
project: Optional[str] = Field(default=None, max_length=100, description='所属项目')
version: Optional[str] = Field(default=None, max_length=50, description='版本号')
class TicketUpdate(BaseModel):
class TicketUpdateSchema(TicketCreateSchema):
"""更新工单"""
title: Optional[str] = None
description: Optional[str] = None
status: Optional[str] = None
priority: Optional[str] = None
type: Optional[str] = None
assignee_id: Optional[int] = None
project: Optional[str] = None
version: Optional[str] = None
...
class TicketOut(BaseModel):
class TicketOutSchema(TicketCreateSchema, BaseSchema):
"""工单输出"""
id: int
title: str
description: Optional[str] = None
status: str
priority: str
type: str
assignee_id: Optional[int] = None
reporter_id: int
project: Optional[str] = None
version: Optional[str] = None
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
...
@@ -1,12 +1,60 @@
# -*- coding: utf-8 -*-
from typing import Dict, List, Optional, Sequence
from typing import Dict, List, Optional, Union
from app.api.v1.module_system.auth.schema import AuthSchema
from app.api.v1.module_system.ticket.crud import TicketCRUD
from app.api.v1.module_system.ticket.schema import TicketCreate, TicketUpdate
from app.core.exceptions import CustomException
from .crud import TicketCRUD
from .schema import TicketCreateSchema, TicketUpdateSchema, TicketOutSchema
from .param import TicketQueryParams
class TicketService:
"""工单模块服务层"""
pass
@classmethod
async def get_ticket_detail_service(cls, auth: AuthSchema, id: int) -> Dict:
"""获取工单详情"""
obj = await TicketCRUD(auth).get_by_id_crud(id=id)
return TicketOutSchema.model_validate(obj).model_dump()
@classmethod
async def get_ticket_list_service(cls, auth: AuthSchema, search: Optional[TicketQueryParams] = None, order_by: Optional[Union[str, List[Dict[str, str]]]] = None) -> List[Dict]:
"""获取工单列表"""
# 处理排序参数
processed_order_by = None
if order_by:
if isinstance(order_by, str):
processed_order_by = eval(order_by)
else:
processed_order_by = order_by
obj_list = await TicketCRUD(auth).get_list_crud(search=search.__dict__ if search else None, order_by=processed_order_by)
return [TicketOutSchema.model_validate(obj).model_dump() for obj in obj_list]
@classmethod
async def create_ticket_service(cls, auth: AuthSchema, data: TicketCreateSchema) -> Dict:
"""创建工单"""
obj = await TicketCRUD(auth).create_crud(data=data)
return TicketOutSchema.model_validate(obj).model_dump()
@classmethod
async def update_ticket_service(cls, auth: AuthSchema, id: int, data: TicketUpdateSchema) -> Dict:
"""更新工单"""
# 检查工单是否存在
obj = await TicketCRUD(auth).get_by_id_crud(id=id)
if not obj:
raise CustomException(msg='更新失败,该工单不存在')
obj = await TicketCRUD(auth).update_crud(id=id, data=data)
return TicketOutSchema.model_validate(obj).model_dump()
@classmethod
async def delete_ticket_service(cls, auth: AuthSchema, ids: List[int]) -> None:
"""删除工单"""
if len(ids) < 1:
raise CustomException(msg='删除失败,删除对象不能为空')
for id in ids:
obj = await TicketCRUD(auth).get_by_id_crud(id=id)
if not obj:
raise CustomException(msg='删除失败,该工单不存在')
await TicketCRUD(auth).delete_crud(ids=ids)
@@ -1 +0,0 @@
# 版本管理模块
@@ -1,4 +1,71 @@
from fastapi import APIRouter
# -*- coding: utf-8 -*-
router = APIRouter(prefix="/versions", tags=["版本管理"])
from fastapi import APIRouter, Body, Depends, Path
from fastapi.responses import JSONResponse
from app.common.response import SuccessResponse
from app.common.request import PaginationService
from app.core.router_class import OperationLogRoute
from app.core.base_params import PaginationQueryParams
from app.core.dependencies import AuthPermission
from app.core.logger import logger
from app.api.v1.module_system.auth.schema import AuthSchema
from .param import VersionQueryParams
from .service import VersionService
from .schema import VersionCreateSchema, VersionUpdateSchema
VersionRouter = APIRouter(route_class=OperationLogRoute, prefix="/versions", tags=["版本管理"])
@VersionRouter.get("/detail/{id}", summary="获取版本详情", description="获取版本详情")
async def get_version_detail_controller(
id: int = Path(..., description="版本ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:query"]))
) -> JSONResponse:
result_dict = await VersionService.get_version_detail_service(auth=auth, id=id)
logger.info(f"获取版本详情成功 {id}")
return SuccessResponse(data=result_dict, msg="获取版本详情成功")
@VersionRouter.get("/list", summary="查询版本列表", description="查询版本列表")
async def get_version_list_controller(
page: PaginationQueryParams = Depends(),
search: VersionQueryParams = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:query"]))
) -> JSONResponse:
result_dict_list = await VersionService.get_version_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.get_page_obj(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
logger.info("查询版本列表成功")
return SuccessResponse(data=result_dict, msg="查询版本列表成功")
@VersionRouter.post("/create", summary="创建版本", description="创建版本")
async def create_version_controller(
data: VersionCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:create"]))
) -> JSONResponse:
result_dict = await VersionService.create_version_service(auth=auth, data=data)
logger.info(f"创建版本成功: {result_dict}")
return SuccessResponse(data=result_dict, msg="创建版本成功")
@VersionRouter.put("/update/{id}", summary="修改版本", description="修改版本")
async def update_version_controller(
data: VersionUpdateSchema,
id: int = Path(..., description="版本ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:update"]))
) -> JSONResponse:
result_dict = await VersionService.update_version_service(auth=auth, id=id, data=data)
logger.info(f"修改版本成功: {result_dict}")
return SuccessResponse(data=result_dict, msg="修改版本成功")
@VersionRouter.delete("/delete", summary="删除版本", description="删除版本")
async def delete_version_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:delete"]))
) -> JSONResponse:
await VersionService.delete_version_service(auth=auth, ids=ids)
logger.info(f"删除版本成功: {ids}")
return SuccessResponse(msg="删除版本成功")
@@ -1,51 +1,48 @@
# -*- coding: utf-8 -*-
from typing import Dict, List, Optional, Sequence
from app.core.base_crud import CRUDBase
from app.api.v1.module_system.auth.schema import AuthSchema
from app.api.v1.module_system.version.model import Version
from app.api.v1.module_system.version.schema import VersionCreate, VersionUpdate
from .model import VersionModel
from .schema import VersionCreateSchema, VersionUpdateSchema
class VersionCRUD(CRUDBase[Version, VersionCreate, VersionUpdate]):
class VersionCRUD(CRUDBase[VersionModel, VersionCreateSchema, VersionUpdateSchema]):
"""版本模块数据层"""
def __init__(self, auth: AuthSchema) -> None:
"""初始化版本CRUD"""
self.auth = auth
super().__init__(model=Version, auth=auth)
super().__init__(model=VersionModel, auth=auth)
async def get_by_id_crud(self, id: int) -> Optional[Version]:
"""
根据id获取版本信息
async def get_by_id_crud(self, id: int) -> Optional[VersionModel]:
"""根据id获取版本信息
:param id: 版本ID
:return: 版本信息
"""
return await self.get(id=id)
async def get_list_crud(self, search: Dict = None, order_by: List[Dict[str, str]] = None) -> Sequence[Version]:
"""
获取版本列表
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[VersionModel]:
"""获取版本列表
:param search: 搜索条件
:param order_by: 排序字段
:return: 版本列表
"""
return await self.list(search=search, order_by=order_by)
return await self.list(search=search or {}, order_by=order_by or [])
async def create_crud(self, data: VersionCreate) -> Optional[Version]:
"""
创建版本
async def create_crud(self, data: VersionCreateSchema) -> Optional[VersionModel]:
"""创建版本
:param data: 版本创建数据
:return: 创建的版本
"""
return await self.create(data=data)
async def update_crud(self, id: int, data: VersionUpdate) -> Optional[Version]:
"""
更新版本
async def update_crud(self, id: int, data: VersionUpdateSchema) -> Optional[VersionModel]:
"""更新版本
:param id: 版本ID
:param data: 版本更新数据
@@ -54,25 +51,22 @@ class VersionCRUD(CRUDBase[Version, VersionCreate, VersionUpdate]):
return await self.update(id=id, data=data)
async def delete_crud(self, ids: List[int]) -> None:
"""
批量删除版本
"""批量删除版本
:param ids: 版本ID列表
"""
return await self.delete(ids=ids)
async def get_by_number_crud(self, version_number: str) -> Optional[Version]:
"""
根据版本号获取版本
async def get_by_number_crud(self, version_number: str) -> Optional[VersionModel]:
"""根据版本号获取版本
:param version_number: 版本号
:return: 版本信息
"""
return await self.get(version_number=version_number)
async def get_by_status_crud(self, status: str, search: Dict = None, order_by: List[Dict[str, str]] = None) -> Sequence[Version]:
"""
根据状态获取版本列表
async def get_by_status_crud(self, status: str, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[VersionModel]:
"""根据状态获取版本列表
:param status: 版本状态
:param search: 搜索条件
@@ -81,5 +75,5 @@ class VersionCRUD(CRUDBase[Version, VersionCreate, VersionUpdate]):
"""
if search is None:
search = {}
search['status'] = status
return await self.list(search=search, order_by=order_by)
search['version_status'] = status
return await self.list(search=search, order_by=order_by or [])
@@ -1,15 +1,24 @@
from sqlalchemy import Column, Integer, String, Text, DateTime
from app.core.base_model import BaseModel
# -*- coding: utf-8 -*-
from typing import Optional
from sqlalchemy import String, Text, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from datetime import datetime
from app.core.base_model import CreatorMixin
class Version(BaseModel):
"""版本模型"""
__tablename__ = "version"
version_number = Column(String(50), nullable=False, comment="版本号")
title = Column(String(255), nullable=False, comment="版本标题")
release_notes = Column(Text, comment="发布说明")
status = Column(String(50), default="draft", comment="版本状态(draft, released, archived)")
project = Column(String(100), comment="所属项目")
released_at = Column(DateTime, comment="发布时间")
class VersionModel(CreatorMixin):
"""
版本模型 - SQLAlchemy 2.0 语法
兼容 MySQL 和 PostgreSQL
"""
__tablename__ = 'system_version'
__table_args__ = ({'comment': '版本表'})
version_number: Mapped[str] = mapped_column(String(50), nullable=False, comment='版本号')
title: Mapped[str] = mapped_column(String(255), nullable=False, comment='版本标题')
release_notes: Mapped[Optional[str]] = mapped_column(Text, comment='发布说明')
version_status: Mapped[str] = mapped_column(String(50), default='draft', comment='版本状态(draft, released, archived)')
project: Mapped[Optional[str]] = mapped_column(String(100), comment='所属项目')
released_at: Mapped[Optional[datetime]] = mapped_column(DateTime, comment='发布时间')
@@ -5,13 +5,16 @@ from typing import Optional
from fastapi import Query
from app.core.validator import DateTimeStr
class VersionQueryParams:
"""版本管理查询参数"""
def __init__(
self,
title: Optional[str] = Query(None, description="版本标题"),
status: Optional[bool] = Query(None, description="是否可用"),
status: Optional[bool] = Query(None, description="状态"),
version_status: Optional[str] = Query(None, description="版本状态"),
creator: Optional[int] = Query(None, description="创建人"),
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
@@ -23,10 +26,12 @@ class VersionQueryParams:
# 精确查询字段
self.creator_id = creator
self.status = status
self.version_status = version_status # 使用 version_status 而不是 status
# 时间范围查询
if start_time and end_time:
start_datetime = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
end_datetime = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
self.created_at = ("between", (start_datetime, end_datetime))
start_datetime = datetime.strptime(str(start_time), '%Y-%m-%d %H:%M:%S')
end_datetime = datetime.strptime(str(end_time), '%Y-%m-%d %H:%M:%S')
self.created_at = ("between", (start_datetime, end_datetime))
@@ -1,42 +1,30 @@
from pydantic import BaseModel
# -*- coding: utf-8 -*-
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field
from datetime import datetime
from app.core.base_schema import BaseSchema
class VersionCreate(BaseModel):
class VersionCreateSchema(BaseModel):
"""创建版本"""
version_number: str
title: str
description: Optional[str] = None
release_notes: Optional[str] = None
status: Optional[str] = "draft"
project: Optional[str] = None
released_at: Optional[datetime] = None
version_number: str = Field(..., max_length=50, description='版本号')
title: str = Field(..., max_length=255, description='版本标题')
release_notes: Optional[str] = Field(default=None, description='发布说明')
description: Optional[str] = Field(default=None, description='工单描述')
status: Optional[bool] = Field(default=True, description='1、启动 2、停止')
version_status: Optional[str] = Field(default='draft', description='版本状态(draft, released, archived)')
project: Optional[str] = Field(default=None, max_length=100, description='所属项目')
released_at: Optional[datetime] = Field(default=None, description='发布时间')
class VersionUpdate(BaseModel):
class VersionUpdateSchema(VersionCreateSchema):
"""更新版本"""
version_number: Optional[str] = None
title: Optional[str] = None
description: Optional[str] = None
release_notes: Optional[str] = None
status: Optional[str] = None
project: Optional[str] = None
released_at: Optional[datetime] = None
...
class VersionOut(BaseModel):
class VersionOutSchema(VersionCreateSchema, BaseSchema):
"""版本输出"""
id: int
version_number: str
title: str
description: Optional[str] = None
release_notes: Optional[str] = None
status: str
project: Optional[str] = None
released_at: Optional[datetime] = None
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
...
@@ -1,6 +1,88 @@
# -*- coding: utf-8 -*-
from typing import Dict, List, Optional, Union
from app.api.v1.module_system.auth.schema import AuthSchema
from app.core.exceptions import CustomException
from .crud import VersionCRUD
from .schema import VersionCreateSchema, VersionUpdateSchema, VersionOutSchema
from .param import VersionQueryParams
class VersionService:
"""版本服务"""
pass
"""版本模块服务"""
@classmethod
async def get_version_detail_service(cls, auth: AuthSchema, id: int) -> Dict:
"""获取版本详情"""
obj = await VersionCRUD(auth).get_by_id_crud(id=id)
return VersionOutSchema.model_validate(obj).model_dump()
@classmethod
async def get_version_list_service(cls, auth: AuthSchema, search: Optional[VersionQueryParams] = None, order_by: Optional[Union[str, List[Dict[str, str]]]] = None) -> List[Dict]:
"""获取版本列表"""
# 处理排序参数
processed_order_by = None
if order_by:
if isinstance(order_by, str):
processed_order_by = eval(order_by)
else:
processed_order_by = order_by
obj_list = await VersionCRUD(auth).get_list_crud(search=search.__dict__ if search else None, order_by=processed_order_by)
return [VersionOutSchema.model_validate(obj).model_dump() for obj in obj_list]
@classmethod
async def create_version_service(cls, auth: AuthSchema, data: VersionCreateSchema) -> Dict:
"""创建版本"""
# 检查版本号是否已存在
exist_obj = await VersionCRUD(auth).get_by_number_crud(version_number=data.version_number)
if exist_obj:
raise CustomException(msg='创建失败,版本号已存在')
obj = await VersionCRUD(auth).create_crud(data=data)
return VersionOutSchema.model_validate(obj).model_dump()
@classmethod
async def update_version_service(cls, auth: AuthSchema, id: int, data: VersionUpdateSchema) -> Dict:
"""更新版本"""
# 检查版本是否存在
obj = await VersionCRUD(auth).get_by_id_crud(id=id)
if not obj:
raise CustomException(msg='更新失败,该版本不存在')
# 检查版本号是否重复(如果提供了版本号)
if data.version_number:
exist_obj = await VersionCRUD(auth).get_by_number_crud(version_number=data.version_number)
if exist_obj and exist_obj.id != id:
raise CustomException(msg='更新失败,版本号已存在')
obj = await VersionCRUD(auth).update_crud(id=id, data=data)
return VersionOutSchema.model_validate(obj).model_dump()
@classmethod
async def delete_version_service(cls, auth: AuthSchema, ids: List[int]) -> None:
"""删除版本"""
if len(ids) < 1:
raise CustomException(msg='删除失败,删除对象不能为空')
for id in ids:
obj = await VersionCRUD(auth).get_by_id_crud(id=id)
if not obj:
raise CustomException(msg='删除失败,该版本不存在')
await VersionCRUD(auth).delete_crud(ids=ids)
@classmethod
async def get_version_by_status_service(cls, auth: AuthSchema, status: str, search: Optional[VersionQueryParams] = None, order_by: Optional[Union[str, List[Dict[str, str]]]] = None) -> List[Dict]:
"""根据状态获取版本列表"""
# 处理排序参数
processed_order_by = None
if order_by:
if isinstance(order_by, str):
processed_order_by = eval(order_by)
else:
processed_order_by = order_by
obj_list = await VersionCRUD(auth).get_by_status_crud(
status=status,
search=search.__dict__ if search else None,
order_by=processed_order_by
)
return [VersionOutSchema.model_validate(obj).model_dump() for obj in obj_list]