diff --git a/backend/app/api/v1/__init__.py b/backend/app/api/v1/__init__.py index 39c8e11b..8195019c 100644 --- a/backend/app/api/v1/__init__.py +++ b/backend/app/api/v1/__init__.py @@ -12,6 +12,8 @@ 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_monitor.cache.controller import CacheRouter from .module_monitor.job.controller import JobRouter @@ -28,8 +30,6 @@ from .module_application.myapp.controller import MyAppRouter from .module_ai.mcp.controller import MCPRouter -from .module_resource.resource.controller import ResourceRouter - # 定义路由模块映射,按模块分组 SYSTEM_MODULES = [ @@ -43,6 +43,8 @@ SYSTEM_MODULES = [ {"router": NoticeRouter}, {"router": ParamsRouter}, {"router": DictRouter}, + {"router": TicketRouter}, + {"router": VersionRouter}, ] MONITOR_MODULES = [ diff --git a/backend/app/api/v1/module_system/position/model.py b/backend/app/api/v1/module_system/position/model.py index 4798bb57..094f455b 100644 --- a/backend/app/api/v1/module_system/position/model.py +++ b/backend/app/api/v1/module_system/position/model.py @@ -25,5 +25,4 @@ class PositionModel(CreatorMixin): # 用户关联关系 users: Mapped[List["UserModel"]] = relationship(secondary="system_user_positions", back_populates="positions", lazy="selectin") - # users: Mapped[List["UserModel"]] = relationship(secondary="system_user_positions", back_populates="positions", lazy="select") diff --git a/backend/app/api/v1/module_system/position/schema.py b/backend/app/api/v1/module_system/position/schema.py index d94172b9..2a0fbd32 100644 --- a/backend/app/api/v1/module_system/position/schema.py +++ b/backend/app/api/v1/module_system/position/schema.py @@ -23,10 +23,6 @@ class PositionOutSchema(PositionCreateSchema, BaseSchema): """岗位信息响应模型""" model_config = ConfigDict(from_attributes=True) - -class PositionOptionsOut(PositionCreateSchema): - model_config = ConfigDict(from_attributes=True) - id: int = Field(..., description="主键ID") created_at: DateTimeStr = Field(..., description="创建时间") updated_at: DateTimeStr = Field(..., description="更新时间") diff --git a/backend/app/api/v1/module_system/ticket/__init__.py b/backend/app/api/v1/module_system/ticket/__init__.py new file mode 100644 index 00000000..19efa361 --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/__init__.py @@ -0,0 +1 @@ +# 工单管理模块 \ No newline at end of file diff --git a/backend/app/api/v1/module_system/ticket/controller.py b/backend/app/api/v1/module_system/ticket/controller.py new file mode 100644 index 00000000..fadf48ad --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/controller.py @@ -0,0 +1,4 @@ +from fastapi import APIRouter + +router = APIRouter(prefix="/tickets", tags=["工单管理"]) + diff --git a/backend/app/api/v1/module_system/ticket/crud.py b/backend/app/api/v1/module_system/ticket/crud.py new file mode 100644 index 00000000..6c656d71 --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/crud.py @@ -0,0 +1,16 @@ +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 + +class CRUDTicket(CRUDBase[Ticket, TicketCreate, TicketUpdate]): + """工单 CRUD 操作""" + + def __init__(self, auth: AuthSchema) -> None: + """初始化工单CRUD""" + self.auth = auth + super().__init__(model=Ticket, auth=auth) + + pass diff --git a/backend/app/api/v1/module_system/ticket/model.py b/backend/app/api/v1/module_system/ticket/model.py new file mode 100644 index 00000000..de868290 --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/model.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey +from sqlalchemy.orm import 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="版本号") + + # 关系 + assignee = relationship("UserModel", foreign_keys=[assignee_id]) + reporter = relationship("UserModel", foreign_keys=[reporter_id]) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/ticket/param.py b/backend/app/api/v1/module_system/ticket/param.py new file mode 100644 index 00000000..e2d1aae6 --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/param.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime +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="是否可用"), + 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"), + ) -> None: + super().__init__() + + # 模糊查询字段 + self.title = ("like", title) + + # 精确查询字段 + self.creator_id = creator + self.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)) diff --git a/backend/app/api/v1/module_system/ticket/schema.py b/backend/app/api/v1/module_system/ticket/schema.py new file mode 100644 index 00000000..7c500d46 --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/schema.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +from pydantic import BaseModel +from typing import Optional, List +from datetime import datetime + + +class TicketCreate(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 + + +class TicketUpdate(BaseModel): + """更新工单""" + 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): + """工单输出""" + 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 \ No newline at end of file diff --git a/backend/app/api/v1/module_system/ticket/service.py b/backend/app/api/v1/module_system/ticket/service.py new file mode 100644 index 00000000..4799283f --- /dev/null +++ b/backend/app/api/v1/module_system/ticket/service.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from typing import Dict, List, Optional, Sequence +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 + + +class TicketService: + """工单模块服务层""" + + pass \ No newline at end of file diff --git a/backend/app/api/v1/module_system/version/__init__.py b/backend/app/api/v1/module_system/version/__init__.py new file mode 100644 index 00000000..f8a56ada --- /dev/null +++ b/backend/app/api/v1/module_system/version/__init__.py @@ -0,0 +1 @@ +# 版本管理模块 \ No newline at end of file diff --git a/backend/app/api/v1/module_system/version/controller.py b/backend/app/api/v1/module_system/version/controller.py new file mode 100644 index 00000000..4486536b --- /dev/null +++ b/backend/app/api/v1/module_system/version/controller.py @@ -0,0 +1,4 @@ +from fastapi import APIRouter + +router = APIRouter(prefix="/versions", tags=["版本管理"]) + diff --git a/backend/app/api/v1/module_system/version/crud.py b/backend/app/api/v1/module_system/version/crud.py new file mode 100644 index 00000000..bdc47881 --- /dev/null +++ b/backend/app/api/v1/module_system/version/crud.py @@ -0,0 +1,85 @@ +# -*- 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 + + +class VersionCRUD(CRUDBase[Version, VersionCreate, VersionUpdate]): + """版本模块数据层""" + + def __init__(self, auth: AuthSchema) -> None: + """初始化版本CRUD""" + self.auth = auth + super().__init__(model=Version, auth=auth) + + async def get_by_id_crud(self, id: int) -> Optional[Version]: + """ + 根据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]: + """ + 获取版本列表 + + :param search: 搜索条件 + :param order_by: 排序字段 + :return: 版本列表 + """ + return await self.list(search=search, order_by=order_by) + + async def create_crud(self, data: VersionCreate) -> Optional[Version]: + """ + 创建版本 + + :param data: 版本创建数据 + :return: 创建的版本 + """ + return await self.create(data=data) + + async def update_crud(self, id: int, data: VersionUpdate) -> Optional[Version]: + """ + 更新版本 + + :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) + + async def get_by_number_crud(self, version_number: str) -> Optional[Version]: + """ + 根据版本号获取版本 + + :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]: + """ + 根据状态获取版本列表 + + :param status: 版本状态 + :param search: 搜索条件 + :param order_by: 排序字段 + :return: 版本列表 + """ + if search is None: + search = {} + search['status'] = status + return await self.list(search=search, order_by=order_by) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/version/model.py b/backend/app/api/v1/module_system/version/model.py new file mode 100644 index 00000000..0cee9396 --- /dev/null +++ b/backend/app/api/v1/module_system/version/model.py @@ -0,0 +1,15 @@ +from sqlalchemy import Column, Integer, String, Text, DateTime +from app.core.base_model import BaseModel +from datetime import datetime + + +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="发布时间") diff --git a/backend/app/api/v1/module_system/version/param.py b/backend/app/api/v1/module_system/version/param.py new file mode 100644 index 00000000..54b0f26c --- /dev/null +++ b/backend/app/api/v1/module_system/version/param.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime +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="是否可用"), + 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"), + ) -> None: + super().__init__() + + # 模糊查询字段 + self.title = ("like", title) + + # 精确查询字段 + self.creator_id = creator + self.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)) diff --git a/backend/app/api/v1/module_system/version/schema.py b/backend/app/api/v1/module_system/version/schema.py new file mode 100644 index 00000000..0d0000dd --- /dev/null +++ b/backend/app/api/v1/module_system/version/schema.py @@ -0,0 +1,42 @@ +from pydantic import BaseModel +from typing import Optional +from datetime import datetime + + +class VersionCreate(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 + + +class VersionUpdate(BaseModel): + """更新版本""" + 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): + """版本输出""" + 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 \ No newline at end of file diff --git a/backend/app/api/v1/module_system/version/service.py b/backend/app/api/v1/module_system/version/service.py new file mode 100644 index 00000000..09d8db65 --- /dev/null +++ b/backend/app/api/v1/module_system/version/service.py @@ -0,0 +1,6 @@ + + +class VersionService: + """版本服务类""" + + pass \ No newline at end of file diff --git a/backend/app/plugin/init_app.py b/backend/app/plugin/init_app.py index 19093fcf..4b33d701 100644 --- a/backend/app/plugin/init_app.py +++ b/backend/app/plugin/init_app.py @@ -33,7 +33,7 @@ from app.core.exceptions import ( ) from app.core.database import session_connect, init_create_table from app.scripts.initialize import InitializeData -from app.api.v1.module_system.config.service import ConfigService +from app.api.v1.module_system.params.service import ParamsService from app.api.v1.module_system.dict.service import DictDataService from app.api.v1 import router @@ -52,7 +52,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]: logger.info("初始化数据完成...") await import_modules_async(modules=settings.EVENT_LIST, desc="全局事件", app=app, status=True) logger.info("初始化全局事件完成...") - await ConfigService().init_config_service(redis=app.state.redis) + await ParamsService().init_config_service(redis=app.state.redis) logger.info("初始化系统配置完成...") await DictDataService().init_dict_service(redis=app.state.redis) logger.info('初始化数据字典完成...')