feat:新增能源报告模板接口
parent
a1eb2315fa
commit
3f961146ed
@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.v1.module_energy.report_template.controller import EnergyReportTemplateRouter
|
||||
|
||||
energy_router = APIRouter()
|
||||
|
||||
energy_router.include_router(EnergyReportTemplateRouter)
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Path
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.common.response import ResponseSchema, SuccessResponse
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.base_schema import AuthSchema, PageResultSchema
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
|
||||
from .schema import (
|
||||
EnergyReportTemplateCreateSchema,
|
||||
EnergyReportTemplateOutSchema,
|
||||
EnergyReportTemplateQueryParam,
|
||||
EnergyReportTemplateUpdateSchema,
|
||||
)
|
||||
from .service import EnergyReportTemplateService
|
||||
|
||||
EnergyReportTemplateRouter = APIRouter(route_class=OperationLogRoute, prefix="/attachment-template", tags=["能源报告模板"])
|
||||
|
||||
|
||||
@EnergyReportTemplateRouter.get(
|
||||
"/list",
|
||||
summary="查询能源报告模板列表",
|
||||
response_model=ResponseSchema[PageResultSchema[EnergyReportTemplateOutSchema]],
|
||||
)
|
||||
async def get_energy_report_template_list_controller(
|
||||
page: Annotated[PaginationQueryParam, Depends()],
|
||||
search: Annotated[EnergyReportTemplateQueryParam, Depends()],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:query"]))],
|
||||
) -> JSONResponse:
|
||||
result = await EnergyReportTemplateService(auth).page(
|
||||
page_no=page.page_no,
|
||||
page_size=page.page_size,
|
||||
search=search,
|
||||
order_by=page.order_by or [{"order": "asc"}, {"id": "desc"}],
|
||||
)
|
||||
return SuccessResponse(data=result, msg="查询能源报告模板列表成功")
|
||||
|
||||
|
||||
@EnergyReportTemplateRouter.get(
|
||||
"/detail/{id}",
|
||||
summary="查询能源报告模板详情",
|
||||
response_model=ResponseSchema[EnergyReportTemplateOutSchema],
|
||||
)
|
||||
async def get_energy_report_template_detail_controller(
|
||||
id: Annotated[int, Path(description="能源报告模板ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:detail"]))],
|
||||
) -> JSONResponse:
|
||||
result = await EnergyReportTemplateService(auth).detail(id=id)
|
||||
return SuccessResponse(data=result, msg="查询能源报告模板详情成功")
|
||||
|
||||
|
||||
@EnergyReportTemplateRouter.post(
|
||||
"/create",
|
||||
summary="创建能源报告模板",
|
||||
response_model=ResponseSchema[EnergyReportTemplateOutSchema],
|
||||
)
|
||||
async def create_energy_report_template_controller(
|
||||
data: EnergyReportTemplateCreateSchema,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:create"]))],
|
||||
) -> JSONResponse:
|
||||
result = await EnergyReportTemplateService(auth).create(data=data)
|
||||
return SuccessResponse(data=result, msg="创建能源报告模板成功")
|
||||
|
||||
|
||||
@EnergyReportTemplateRouter.put(
|
||||
"/update/{id}",
|
||||
summary="修改能源报告模板",
|
||||
response_model=ResponseSchema[EnergyReportTemplateOutSchema],
|
||||
)
|
||||
async def update_energy_report_template_controller(
|
||||
data: EnergyReportTemplateUpdateSchema,
|
||||
id: Annotated[int, Path(description="能源报告模板ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:update"]))],
|
||||
) -> JSONResponse:
|
||||
result = await EnergyReportTemplateService(auth).update(id=id, data=data)
|
||||
return SuccessResponse(data=result, msg="修改能源报告模板成功")
|
||||
|
||||
|
||||
@EnergyReportTemplateRouter.delete(
|
||||
"/delete",
|
||||
summary="删除能源报告模板",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def delete_energy_report_template_controller(
|
||||
ids: Annotated[list[int], Body(description="ID列表")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:delete"]))],
|
||||
) -> JSONResponse:
|
||||
await EnergyReportTemplateService(auth).delete(ids=ids)
|
||||
return SuccessResponse(msg="删除能源报告模板成功")
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.core.base_schema import AuthSchema
|
||||
|
||||
from .model import EnergyReportTemplateModel
|
||||
from .schema import EnergyReportTemplateCreateSchema, EnergyReportTemplateUpdateSchema
|
||||
|
||||
|
||||
class EnergyReportTemplateCRUD(CRUDBase[EnergyReportTemplateModel, EnergyReportTemplateCreateSchema, EnergyReportTemplateUpdateSchema]):
|
||||
"""能源报告模板数据层。"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
super().__init__(model=EnergyReportTemplateModel, auth=auth)
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
from sqlalchemy import ForeignKey, Integer, String, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.core.base_model import ModelMixin, TenantMixin, UserMixin
|
||||
|
||||
|
||||
class EnergyReportTemplateModel(ModelMixin, TenantMixin, UserMixin):
|
||||
"""能源报告模板模型。"""
|
||||
|
||||
__tablename__: str = "energy_report_template"
|
||||
__table_args__ = (UniqueConstraint("tenant_id", "template_name"), {"comment": "能源报告模板表"})
|
||||
__loader_options__: list[str] = [
|
||||
"file",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"deleted_by",
|
||||
"tenant_by",
|
||||
]
|
||||
|
||||
template_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="模板名称", index=True)
|
||||
template_type: Mapped[str] = mapped_column(String(64), nullable=False, comment="模板类型", index=True)
|
||||
version: Mapped[str | None] = mapped_column(String(32), default=None, nullable=True, comment="模板版本")
|
||||
file_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("common_file.id", ondelete="RESTRICT", onupdate="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="文件记录ID",
|
||||
)
|
||||
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态 0:启用 1:停用", index=True)
|
||||
order: Mapped[int] = mapped_column(Integer, default=999, nullable=False, comment="显示排序")
|
||||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="描述")
|
||||
|
||||
file = relationship("FileModel", lazy="selectin", foreign_keys=[file_id], uselist=False)
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from fastapi import Query
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
from app.api.v1.module_common.file.schema import FileOutSchema
|
||||
from app.common.enums import QueueEnum
|
||||
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
|
||||
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
|
||||
|
||||
|
||||
class EnergyReportTemplateCreateSchema(BaseModel):
|
||||
"""能源报告模板创建模型。"""
|
||||
|
||||
template_name: str = Field(..., min_length=1, max_length=128, description="模板名称")
|
||||
template_type: str = Field(..., min_length=1, max_length=64, description="模板类型")
|
||||
version: str | None = Field(default=None, max_length=32, description="模板版本")
|
||||
file_id: int = Field(..., ge=1, description="文件记录ID")
|
||||
status: int = Field(default=0, ge=0, le=1, description="状态 0:启用 1:停用")
|
||||
order: int = Field(default=999, ge=0, description="显示排序")
|
||||
description: str | None = Field(default=None, max_length=500, description="描述")
|
||||
|
||||
@field_validator("template_name", "template_type")
|
||||
@classmethod
|
||||
def validate_required_text(cls, value: str) -> str:
|
||||
value = value.strip()
|
||||
if not value:
|
||||
raise ValueError("必填字段不能为空")
|
||||
return value
|
||||
|
||||
@field_validator("status")
|
||||
@classmethod
|
||||
def validate_status(cls, value: int) -> int:
|
||||
if value not in {0, 1}:
|
||||
raise ValueError("状态仅支持 0(启用) 或 1(停用)")
|
||||
return value
|
||||
|
||||
|
||||
class EnergyReportTemplateUpdateSchema(EnergyReportTemplateCreateSchema):
|
||||
"""能源报告模板更新模型。"""
|
||||
|
||||
|
||||
class EnergyReportTemplateOutSchema(EnergyReportTemplateCreateSchema, BaseSchema, UserBySchema, TenantBySchema):
|
||||
"""能源报告模板响应模型。"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
file: FileOutSchema | None = Field(default=None, description="文件记录")
|
||||
file_url: str | None = Field(default=None, description="文件访问地址")
|
||||
original_name: str | None = Field(default=None, description="原始文件名")
|
||||
object_name: str | None = Field(default=None, description="MinIO对象名称")
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnergyReportTemplateQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
|
||||
"""能源报告模板查询参数。"""
|
||||
|
||||
template_name: str | None = Query(None, description="模板名称")
|
||||
template_type: str | None = Query(None, description="模板类型")
|
||||
version: str | None = Query(None, description="模板版本")
|
||||
status: int | None = Query(None, ge=0, le=1, description="状态 0:启用 1:停用")
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.template_name:
|
||||
self.template_name = (QueueEnum.like.value, self.template_name)
|
||||
if self.template_type:
|
||||
self.template_type = (QueueEnum.eq.value, self.template_type)
|
||||
if self.version:
|
||||
self.version = (QueueEnum.like.value, self.version)
|
||||
if isinstance(self.status, int):
|
||||
self.status = (QueueEnum.eq.value, self.status)
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
from app.api.v1.module_common.file.crud import FileCRUD
|
||||
from app.core.base_schema import AuthSchema
|
||||
from app.core.exceptions import CustomException
|
||||
|
||||
from .crud import EnergyReportTemplateCRUD
|
||||
from .schema import (
|
||||
EnergyReportTemplateCreateSchema,
|
||||
EnergyReportTemplateOutSchema,
|
||||
EnergyReportTemplateQueryParam,
|
||||
EnergyReportTemplateUpdateSchema,
|
||||
)
|
||||
|
||||
ENERGY_REPORT_TEMPLATE_BUSINESS_TYPE = "energy_report_template"
|
||||
|
||||
|
||||
class EnergyReportTemplateService:
|
||||
"""能源报告模板服务。"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
self.auth = auth
|
||||
|
||||
async def detail(self, id: int) -> EnergyReportTemplateOutSchema:
|
||||
template = await EnergyReportTemplateCRUD(self.auth).get_or_404(id=id)
|
||||
return self._to_out_schema(template)
|
||||
|
||||
async def page(
|
||||
self,
|
||||
page_no: int,
|
||||
page_size: int,
|
||||
search: EnergyReportTemplateQueryParam | None = None,
|
||||
order_by: list[dict[str, str]] | None = None,
|
||||
) -> dict:
|
||||
offset = (page_no - 1) * page_size
|
||||
page_result = await EnergyReportTemplateCRUD(self.auth).page(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order_by or [{"order": "asc"}, {"id": "desc"}],
|
||||
search=vars(search) if search else None,
|
||||
out_schema=EnergyReportTemplateOutSchema,
|
||||
)
|
||||
for item in page_result.items:
|
||||
file_info = item.get("file") or {}
|
||||
item["file_url"] = file_info.get("file_url")
|
||||
item["original_name"] = file_info.get("original_name")
|
||||
item["object_name"] = file_info.get("object_name")
|
||||
return page_result
|
||||
|
||||
async def create(self, data: EnergyReportTemplateCreateSchema) -> EnergyReportTemplateOutSchema:
|
||||
await self._validate_unique_name(template_name=data.template_name)
|
||||
await self._validate_file(file_id=data.file_id)
|
||||
template = await EnergyReportTemplateCRUD(self.auth).create(data=data)
|
||||
await self._bind_file(file_id=data.file_id, template_id=template.id)
|
||||
return self._to_out_schema(template)
|
||||
|
||||
async def update(self, id: int, data: EnergyReportTemplateUpdateSchema) -> EnergyReportTemplateOutSchema:
|
||||
old_template = await EnergyReportTemplateCRUD(self.auth).get_or_404(id=id, msg="更新失败,能源报告模板不存在")
|
||||
await self._validate_unique_name(template_name=data.template_name, exclude_id=id)
|
||||
await self._validate_file(file_id=data.file_id, current_template_id=id)
|
||||
template = await EnergyReportTemplateCRUD(self.auth).update(id=id, data=data)
|
||||
if old_template.file_id != data.file_id:
|
||||
await self._unbind_file(file_id=old_template.file_id, template_id=id)
|
||||
await self._bind_file(file_id=data.file_id, template_id=id)
|
||||
return self._to_out_schema(template)
|
||||
|
||||
async def delete(self, ids: list[int]) -> None:
|
||||
if len(ids) < 1:
|
||||
raise CustomException(msg="删除失败,删除对象不能为空")
|
||||
templates = await EnergyReportTemplateCRUD(self.auth).get_list(search={"id": ("in", ids)})
|
||||
template_map = {template.id: template for template in templates}
|
||||
for template_id in ids:
|
||||
if template_id not in template_map:
|
||||
raise CustomException(msg="删除失败,能源报告模板不存在")
|
||||
for template in templates:
|
||||
await self._unbind_file(file_id=template.file_id, template_id=template.id)
|
||||
await EnergyReportTemplateCRUD(self.auth).delete(ids=ids)
|
||||
|
||||
def _to_out_schema(self, template) -> EnergyReportTemplateOutSchema:
|
||||
template_out = EnergyReportTemplateOutSchema.model_validate(template)
|
||||
if template_out.file:
|
||||
template_out.file_url = template_out.file.file_url
|
||||
template_out.original_name = template_out.file.original_name
|
||||
template_out.object_name = template_out.file.object_name
|
||||
return template_out
|
||||
|
||||
async def _validate_unique_name(self, template_name: str, exclude_id: int | None = None) -> None:
|
||||
template = await EnergyReportTemplateCRUD(self.auth).get(template_name=template_name)
|
||||
if template and template.id != exclude_id:
|
||||
raise CustomException(msg="能源报告模板名称已存在")
|
||||
|
||||
async def _validate_file(self, file_id: int, current_template_id: int | None = None) -> None:
|
||||
file_record = await FileCRUD(self.auth).get(id=file_id)
|
||||
if not file_record:
|
||||
raise CustomException(msg="文件记录不存在")
|
||||
if file_record.business_type and file_record.business_type != ENERGY_REPORT_TEMPLATE_BUSINESS_TYPE:
|
||||
raise CustomException(msg="文件已绑定到其他业务类型")
|
||||
if file_record.business_id and file_record.business_id != current_template_id:
|
||||
raise CustomException(msg="文件已绑定到其他能源报告模板")
|
||||
|
||||
async def _bind_file(self, file_id: int, template_id: int) -> None:
|
||||
await FileCRUD(self.auth).set(
|
||||
ids=[file_id],
|
||||
business_type=ENERGY_REPORT_TEMPLATE_BUSINESS_TYPE,
|
||||
business_id=template_id,
|
||||
)
|
||||
|
||||
async def _unbind_file(self, file_id: int, template_id: int) -> None:
|
||||
file_record = await FileCRUD(self.auth).get(id=file_id)
|
||||
if not file_record:
|
||||
return
|
||||
if file_record.business_type == ENERGY_REPORT_TEMPLATE_BUSINESS_TYPE and file_record.business_id == template_id:
|
||||
await FileCRUD(self.auth).set(ids=[file_id], business_id=None)
|
||||
|
||||
Loading…
Reference in New Issue