feat:新增能源报告模板添加标注接口

master
HuangHuiKang 3 days ago
parent 3f961146ed
commit 2470c31f64

@ -1,8 +1,13 @@
from fastapi import APIRouter
from app.api.v1.module_energy.chapter.controller import ChapterRouter
from app.api.v1.module_energy.config.controller import TemplateConfigRouter
from app.api.v1.module_energy.report_template.controller import EnergyReportTemplateRouter
from app.api.v1.module_energy.variable.controller import VariableRouter
energy_router = APIRouter()
energy_router.include_router(EnergyReportTemplateRouter)
energy_router.include_router(TemplateConfigRouter)
energy_router.include_router(ChapterRouter)
energy_router.include_router(VariableRouter)

@ -0,0 +1,101 @@
from typing import Annotated
from fastapi import APIRouter, Body, Depends, Path, Query
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 ChapterCreateSchema, ChapterOutSchema, ChapterQueryParam, ChapterUpdateSchema
from .service import ChapterService
ChapterRouter = APIRouter(route_class=OperationLogRoute, prefix="/chapter", tags=["能源报告章节"])
@ChapterRouter.get(
"/list",
summary="查询章节列表",
response_model=ResponseSchema[PageResultSchema[ChapterOutSchema]],
)
async def get_chapter_list_controller(
page: Annotated[PaginationQueryParam, Depends()],
search: Annotated[ChapterQueryParam, Depends()],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:chapter:query"]))],
) -> JSONResponse:
result = await ChapterService(auth).page(
page_no=page.page_no,
page_size=page.page_size,
search=search,
order_by=page.order_by or [{"chapter_order": "asc"}, {"id": "asc"}],
)
return SuccessResponse(data=result, msg="查询章节列表成功")
@ChapterRouter.get(
"/list_by_template",
summary="查询模板下全部章节(不分页)",
response_model=ResponseSchema[list[ChapterOutSchema]],
)
async def get_chapter_list_by_template_controller(
template_id: Annotated[int, Query(ge=1, description="模板ID")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:chapter:query"]))],
) -> JSONResponse:
result = await ChapterService(auth).list_by_template(template_id=template_id)
return SuccessResponse(data=result, msg="查询模板章节成功")
@ChapterRouter.get(
"/detail/{id}",
summary="查询章节详情",
response_model=ResponseSchema[ChapterOutSchema],
)
async def get_chapter_detail_controller(
id: Annotated[int, Path(description="章节ID")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:chapter:detail"]))],
) -> JSONResponse:
result = await ChapterService(auth).detail(id=id)
return SuccessResponse(data=result, msg="查询章节详情成功")
@ChapterRouter.post(
"/create",
summary="创建章节",
response_model=ResponseSchema[ChapterOutSchema],
)
async def create_chapter_controller(
data: ChapterCreateSchema,
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:chapter:create"]))],
) -> JSONResponse:
result = await ChapterService(auth).create(data=data)
return SuccessResponse(data=result, msg="创建章节成功")
@ChapterRouter.put(
"/update/{id}",
summary="修改章节",
response_model=ResponseSchema[ChapterOutSchema],
)
async def update_chapter_controller(
data: ChapterUpdateSchema,
id: Annotated[int, Path(description="章节ID")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:chapter:update"]))],
) -> JSONResponse:
result = await ChapterService(auth).update(id=id, data=data)
return SuccessResponse(data=result, msg="修改章节成功")
@ChapterRouter.delete(
"/delete",
summary="删除章节",
response_model=ResponseSchema[None],
)
async def delete_chapter_controller(
ids: Annotated[list[int], Body(description="ID列表")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:chapter:delete"]))],
) -> JSONResponse:
await ChapterService(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 EnergyReportChapterModel
from .schema import ChapterCreateSchema, ChapterUpdateSchema
class ChapterCRUD(CRUDBase[EnergyReportChapterModel, ChapterCreateSchema, ChapterUpdateSchema]):
"""能源报告章节数据层。"""
def __init__(self, auth: AuthSchema) -> None:
super().__init__(model=EnergyReportChapterModel, auth=auth)

@ -0,0 +1,40 @@
from sqlalchemy import ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.base_model import ModelMixin, TenantMixin, UserMixin
class EnergyReportChapterModel(ModelMixin, TenantMixin, UserMixin):
"""能源报告章节模型。"""
__tablename__: str = "energy_report_chapter"
__table_args__: dict[str, str] = {"comment": "能源报告章节标注表"}
__loader_options__: list[str] = [
"template",
"rule",
"created_by",
"updated_by",
"deleted_by",
"tenant_by",
]
template_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("energy_report_template.id", ondelete="CASCADE", onupdate="CASCADE"),
nullable=False,
index=True,
comment="能源报告模板ID",
)
chapter_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="章节名称", index=True)
chapter_order: Mapped[int] = mapped_column(Integer, default=999, nullable=False, comment="章节排序")
rule_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("rule_list.id", ondelete="RESTRICT", onupdate="CASCADE"),
nullable=False,
index=True,
comment="关联规则ID",
)
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="描述")
template = relationship("EnergyReportTemplateModel", lazy="selectin", foreign_keys=[template_id], uselist=False)
rule = relationship("RuleModel", lazy="selectin", foreign_keys=[rule_id], uselist=False)

@ -0,0 +1,69 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, CommonSchema, TenantBySchema, UserBySchema
class ChapterCreateSchema(BaseModel):
"""章节创建模型。"""
template_id: int = Field(..., ge=1, description="能源报告模板ID")
chapter_name: str = Field(..., min_length=1, max_length=128, description="章节名称")
chapter_order: int = Field(default=999, ge=0, description="章节排序")
rule_id: int = Field(..., ge=1, description="关联规则ID")
description: str | None = Field(default=None, max_length=500, description="描述")
@field_validator("chapter_name")
@classmethod
def validate_chapter_name(cls, value: str) -> str:
value = value.strip()
if not value:
raise ValueError("章节名称不能为空")
return value
class ChapterUpdateSchema(BaseModel):
"""章节更新模型。"""
chapter_name: str = Field(..., min_length=1, max_length=128, description="章节名称")
chapter_order: int = Field(default=999, ge=0, description="章节排序")
rule_id: int = Field(..., ge=1, description="关联规则ID")
description: str | None = Field(default=None, max_length=500, description="描述")
@field_validator("chapter_name")
@classmethod
def validate_chapter_name(cls, value: str) -> str:
value = value.strip()
if not value:
raise ValueError("章节名称不能为空")
return value
class ChapterOutSchema(ChapterCreateSchema, BaseSchema, UserBySchema, TenantBySchema):
"""章节响应模型。"""
model_config = ConfigDict(from_attributes=True)
rule_name: str | None = Field(default=None, description="关联规则名称")
@dataclass
class ChapterQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""章节查询参数。"""
template_id: int | None = Query(None, ge=1, description="模板ID")
chapter_name: str | None = Query(None, description="章节名称")
rule_id: int | None = Query(None, ge=1, description="规则ID")
def __post_init__(self) -> None:
if isinstance(self.template_id, int):
self.template_id = (QueueEnum.eq.value, self.template_id)
if self.chapter_name:
self.chapter_name = (QueueEnum.like.value, self.chapter_name)
if isinstance(self.rule_id, int):
self.rule_id = (QueueEnum.eq.value, self.rule_id)

@ -0,0 +1,76 @@
from app.api.v1.module_rule.rule.crud import RuleCRUD
from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from .crud import ChapterCRUD
from .schema import ChapterOutSchema, ChapterQueryParam, ChapterCreateSchema, ChapterUpdateSchema
class ChapterService:
"""能源报告章节服务。"""
def __init__(self, auth: AuthSchema) -> None:
self.auth = auth
async def detail(self, id: int) -> ChapterOutSchema:
chapter = await ChapterCRUD(self.auth).get_or_404(id=id)
return self._to_out_schema(chapter)
async def list_by_template(self, template_id: int) -> list[ChapterOutSchema]:
chapters = await ChapterCRUD(self.auth).get_list(
search={"template_id": template_id},
order_by=[{"chapter_order": "asc"}, {"id": "asc"}],
)
return [self._to_out_schema(chapter) for chapter in chapters]
async def page(
self,
page_no: int,
page_size: int,
search: ChapterQueryParam | None = None,
order_by: list[dict[str, str]] | None = None,
) -> dict:
offset = (page_no - 1) * page_size
page_result = await ChapterCRUD(self.auth).page(
offset=offset,
limit=page_size,
order_by=order_by or [{"chapter_order": "asc"}, {"id": "asc"}],
search=vars(search) if search else None,
out_schema=ChapterOutSchema,
)
for item in page_result.items:
rule_info = item.get("rule") or {}
item["rule_name"] = rule_info.get("name")
return page_result
async def create(self, data: ChapterCreateSchema) -> ChapterOutSchema:
await self._validate_rule(rule_id=data.rule_id)
chapter = await ChapterCRUD(self.auth).create(data=data)
return self._to_out_schema(chapter)
async def update(self, id: int, data: ChapterUpdateSchema) -> ChapterOutSchema:
await ChapterCRUD(self.auth).get_or_404(id=id, msg="更新失败,章节标注不存在")
await self._validate_rule(rule_id=data.rule_id)
chapter = await ChapterCRUD(self.auth).update(id=id, data=data)
return self._to_out_schema(chapter)
async def delete(self, ids: list[int]) -> None:
if len(ids) < 1:
raise CustomException(msg="删除失败,删除对象不能为空")
chapters = await ChapterCRUD(self.auth).get_list(search={"id": ("in", ids)})
chapter_map = {chapter.id: chapter for chapter in chapters}
for chapter_id in ids:
if chapter_id not in chapter_map:
raise CustomException(msg="删除失败,章节标注不存在")
await ChapterCRUD(self.auth).delete(ids=ids)
def _to_out_schema(self, chapter) -> ChapterOutSchema:
chapter_out = ChapterOutSchema.model_validate(chapter)
if hasattr(chapter, "rule") and chapter.rule:
chapter_out.rule_name = chapter.rule.name
return chapter_out
async def _validate_rule(self, rule_id: int) -> None:
rule = await RuleCRUD(self.auth).get(id=rule_id)
if not rule:
raise CustomException(msg="关联规则不存在")

@ -0,0 +1,40 @@
from typing import Annotated
from fastapi import APIRouter, Depends, Query
from fastapi.responses import JSONResponse
from app.common.response import ResponseSchema, SuccessResponse
from app.core.base_schema import AuthSchema
from app.core.dependencies import AuthPermission
from app.core.router_class import OperationLogRoute
from .schema import TemplateConfigSaveSchema
from .service import TemplateConfigService
TemplateConfigRouter = APIRouter(route_class=OperationLogRoute, prefix="/attachment-template/config", tags=["能源报告模板配置"])
@TemplateConfigRouter.get(
"/detail",
summary="查询模板章节规则和变量关联配置",
response_model=ResponseSchema[dict],
)
async def get_template_config_detail_controller(
template_id: Annotated[int, Query(ge=1, description="模板ID")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:detail"]))],
) -> JSONResponse:
result = await TemplateConfigService(auth).detail(template_id=template_id)
return SuccessResponse(data=result, msg="查询模板配置成功")
@TemplateConfigRouter.post(
"/save",
summary="统一保存模板章节规则和变量关联配置",
response_model=ResponseSchema[dict],
)
async def save_template_config_controller(
data: TemplateConfigSaveSchema,
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:update"]))],
) -> JSONResponse:
result = await TemplateConfigService(auth).save(data=data)
return SuccessResponse(data=result, msg="保存模板配置成功")

@ -0,0 +1,29 @@
from pydantic import BaseModel, Field, field_validator
from app.api.v1.module_energy.variable.schema import VariableItemSchema
class TemplateChapterRuleSchema(BaseModel):
"""模板章节规则配置项。"""
id: int | None = Field(default=None, ge=1, description="章节规则标注ID存在则更新不传则新增")
chapter_name: str = Field(..., min_length=1, max_length=128, description="章节名称")
chapter_order: int = Field(default=999, ge=0, description="章节排序")
rule_id: int = Field(..., ge=1, description="关联规则ID")
description: str | None = Field(default=None, max_length=500, description="描述")
@field_validator("chapter_name")
@classmethod
def validate_chapter_name(cls, value: str) -> str:
value = value.strip()
if not value:
raise ValueError("章节名称不能为空")
return value
class TemplateConfigSaveSchema(BaseModel):
"""模板章节规则和变量关联统一保存模型。"""
template_id: int = Field(..., ge=1, description="模板ID")
chapters: list[TemplateChapterRuleSchema] = Field(default_factory=list, description="章节规则标注列表")
variables: list[VariableItemSchema] = Field(default_factory=list, description="变量关联列表")

@ -0,0 +1,82 @@
from app.api.v1.module_energy.chapter.crud import ChapterCRUD
from app.api.v1.module_energy.report_template.crud import EnergyReportTemplateCRUD
from app.api.v1.module_energy.variable.service import VariableService
from app.api.v1.module_rule.rule.crud import RuleCRUD
from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from .schema import TemplateChapterRuleSchema, TemplateConfigSaveSchema
class TemplateConfigService:
"""能源报告模板配置服务。"""
def __init__(self, auth: AuthSchema) -> None:
self.auth = auth
async def save(self, data: TemplateConfigSaveSchema) -> dict:
await self._validate_template(template_id=data.template_id)
await self._validate_rules(chapters=data.chapters)
await self._save_chapters(template_id=data.template_id, chapters=data.chapters)
variable_result = await VariableService(self.auth).save(data=data)
return {
"template_id": data.template_id,
"chapters": await self._list_chapters(template_id=data.template_id),
"variables": variable_result,
}
async def detail(self, template_id: int) -> dict:
await self._validate_template(template_id=template_id)
return {
"template_id": template_id,
"chapters": await self._list_chapters(template_id=template_id),
"variables": await VariableService(self.auth).list_by_template(template_id=template_id),
}
async def _save_chapters(self, template_id: int, chapters: list[TemplateChapterRuleSchema]) -> None:
for chapter in chapters:
chapter_data = chapter.model_dump(exclude={"id"})
chapter_data["template_id"] = template_id
if chapter.id:
old_chapter = await ChapterCRUD(self.auth).get(id=chapter.id, template_id=template_id)
if not old_chapter:
raise CustomException(msg=f"章节规则标注不存在或不属于当前模板: {chapter.id}")
await ChapterCRUD(self.auth).update(id=chapter.id, data=chapter_data)
else:
await ChapterCRUD(self.auth).create(data=chapter_data)
async def _validate_template(self, template_id: int) -> None:
template = await EnergyReportTemplateCRUD(self.auth).get(id=template_id)
if not template:
raise CustomException(msg="能源报告模板不存在")
async def _validate_rules(self, chapters: list[TemplateChapterRuleSchema]) -> None:
rule_ids = list(dict.fromkeys(chapter.rule_id for chapter in chapters))
if not rule_ids:
return
rules = await RuleCRUD(self.auth).get_list(search={"id": ("in", rule_ids)})
exists_ids = {rule.id for rule in rules}
invalid_ids = [rule_id for rule_id in rule_ids if rule_id not in exists_ids]
if invalid_ids:
raise CustomException(msg=f"关联规则不存在: {invalid_ids}")
async def _list_chapters(self, template_id: int) -> list[dict]:
chapters = await ChapterCRUD(self.auth).get_list(
search={"template_id": template_id},
order_by=[{"chapter_order": "asc"}, {"id": "asc"}],
)
result: list[dict] = []
for chapter in chapters:
rule_name = chapter.rule.name if hasattr(chapter, "rule") and chapter.rule else None
result.append(
{
"id": chapter.id,
"template_id": chapter.template_id,
"chapter_name": chapter.chapter_name,
"chapter_order": chapter.chapter_order,
"rule_id": chapter.rule_id,
"rule_name": rule_name,
"description": chapter.description,
}
)
return result

@ -13,14 +13,14 @@ 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="模板类型")
template_type: str = Field(default="", 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")
@field_validator("template_name")
@classmethod
def validate_required_text(cls, value: str) -> str:
value = value.strip()

@ -0,0 +1,67 @@
from typing import Annotated
from fastapi import APIRouter, Depends, Path, Query
from fastapi.responses import JSONResponse
from app.common.response import ResponseSchema, SuccessResponse
from app.core.base_schema import AuthSchema
from app.core.dependencies import AuthPermission
from app.core.router_class import OperationLogRoute
from .schema import VariableSaveSchema
from .service import VariableService
VariableRouter = APIRouter(route_class=OperationLogRoute, prefix="/variable", tags=["能源报告变量关联"])
@VariableRouter.get(
"/list",
summary="查询模板的变量-章节关联列表",
response_model=ResponseSchema[list[dict]],
)
async def get_variable_list_controller(
template_id: Annotated[int, Query(ge=1, description="模板ID")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:variable:query"]))],
) -> JSONResponse:
result = await VariableService(auth).list_by_template(template_id=template_id)
return SuccessResponse(data=result, msg="查询变量关联列表成功")
@VariableRouter.get(
"/chapter/{chapter_id}",
summary="查询章节标注详情和变量勾选状态",
response_model=ResponseSchema[dict],
)
async def get_variable_list_by_chapter_controller(
chapter_id: Annotated[int, Path(ge=1, description="章节标注ID")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:variable:query"]))],
) -> JSONResponse:
result = await VariableService(auth).list_by_chapter(chapter_id=chapter_id)
return SuccessResponse(data=result, msg="查询章节标注变量配置成功")
@VariableRouter.post(
"/save",
summary="批量保存变量-章节关联",
response_model=ResponseSchema[list[dict]],
)
async def save_variable_controller(
data: VariableSaveSchema,
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:variable:save"]))],
) -> JSONResponse:
result = await VariableService(auth).save(data=data)
return SuccessResponse(data=result, msg="保存变量关联成功")
@VariableRouter.delete(
"/delete",
summary="删除某个变量的全部关联",
response_model=ResponseSchema[None],
)
async def delete_variable_controller(
template_id: Annotated[int, Query(ge=1, description="模板ID")],
variable_name: Annotated[str, Query(description="变量名称")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:variable:delete"]))],
) -> JSONResponse:
await VariableService(auth).delete_by_variable(template_id=template_id, variable_name=variable_name)
return SuccessResponse(msg="删除变量关联成功")

@ -0,0 +1,25 @@
from sqlalchemy import delete
from app.core.base_crud import CRUDBase
from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from .model import EnergyReportVariableModel
class VariableCRUD(CRUDBase[EnergyReportVariableModel, dict, dict]):
"""能源报告变量关联数据层。"""
def __init__(self, auth: AuthSchema) -> None:
super().__init__(model=EnergyReportVariableModel, auth=auth)
async def hard_delete(self, ids: list[int]) -> None:
try:
pk = self._get_pk_col()
sql = self._tenant_dml_where(delete(self.model).where(pk.in_(ids)))
await self.db.execute(sql)
await self.db.flush()
except CustomException:
raise
except Exception as e:
raise CustomException(msg=f"删除变量关联失败: {e!s}")

@ -0,0 +1,30 @@
from sqlalchemy import ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.base_model import ModelMixin, TenantMixin, UserMixin
class EnergyReportVariableModel(ModelMixin, TenantMixin, UserMixin):
"""能源报告变量-章节关联模型。"""
__tablename__: str = "energy_report_variable"
__table_args__ = (UniqueConstraint("tenant_id", "template_id", "variable_name", "chapter_name"), {"comment": "能源报告变量章节关联表"})
__loader_options__: list[str] = [
"template",
"created_by",
"updated_by",
"deleted_by",
"tenant_by",
]
template_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("energy_report_template.id", ondelete="CASCADE", onupdate="CASCADE"),
nullable=False,
index=True,
comment="能源报告模板ID",
)
variable_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="变量名称")
chapter_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="章节名称", index=True)
template = relationship("EnergyReportTemplateModel", lazy="selectin", foreign_keys=[template_id], uselist=False)

@ -0,0 +1,53 @@
from pydantic import BaseModel, ConfigDict, Field, field_validator
class VariableItemSchema(BaseModel):
"""单个变量-章节关联项。"""
variable_name: str = Field(..., min_length=1, max_length=128, description="变量名称")
chapter_names: list[str] = Field(default_factory=list, description="关联章节名称列表")
@field_validator("variable_name")
@classmethod
def validate_required(cls, value: str) -> str:
value = value.strip()
if not value:
raise ValueError("必填字段不能为空")
return value
@field_validator("chapter_names")
@classmethod
def validate_chapter_names(cls, values: list[str]) -> list[str]:
result: list[str] = []
for value in values:
chapter_name = value.strip()
if not chapter_name:
raise ValueError("章节名称不能为空")
result.append(chapter_name)
return result
class VariableSaveSchema(BaseModel):
"""批量保存变量-章节关联。"""
template_id: int = Field(..., ge=1, description="模板ID")
variables: list[VariableItemSchema] = Field(default_factory=list, description="变量列表")
class VariableOutSchema(BaseModel):
"""变量-章节关联响应模型。"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(description="主键ID")
template_id: int = Field(description="模板ID")
variable_name: str = Field(description="变量名称")
chapter_name: str = Field(description="章节名称")
class VariableGroupOutSchema(BaseModel):
"""按变量分组的响应模型。"""
variable_name: str = Field(description="变量名称")
chapter_names: list[str] = Field(default_factory=list, description="关联章节名称列表")
chapters: list[dict] = Field(default_factory=list, description="关联章节详情列表")

@ -0,0 +1,135 @@
from app.api.v1.module_energy.chapter.crud import ChapterCRUD
from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from .crud import VariableCRUD
from .schema import VariableGroupOutSchema, VariableSaveSchema
class VariableService:
"""能源报告变量关联服务。"""
def __init__(self, auth: AuthSchema) -> None:
self.auth = auth
async def list_by_template(self, template_id: int) -> list[dict]:
records = await VariableCRUD(self.auth).get_list(
search={"template_id": template_id},
order_by=[{"variable_name": "asc"}, {"id": "asc"}],
)
chapter_map = await self._chapter_summary_map(template_id=template_id)
group_map: dict[str, VariableGroupOutSchema] = {}
for record in records:
key = record.variable_name
if key not in group_map:
group_map[key] = VariableGroupOutSchema(
variable_name=record.variable_name,
chapter_names=[],
chapters=[],
)
group_map[key].chapter_names.append(record.chapter_name)
group_map[key].chapters.append(
chapter_map.get(record.chapter_name, {"chapter_name": record.chapter_name})
)
return [group.model_dump() for group in group_map.values()]
async def list_by_chapter(self, chapter_id: int) -> dict:
chapter = await ChapterCRUD(self.auth).get_or_404(id=chapter_id, msg="章节标注不存在")
records = await VariableCRUD(self.auth).get_list(
search={"template_id": chapter.template_id},
order_by=[{"variable_name": "asc"}, {"id": "asc"}],
)
rule_name = chapter.rule.name if hasattr(chapter, "rule") and chapter.rule else None
variable_map: dict[str, dict] = {}
for record in records:
if record.variable_name not in variable_map:
variable_map[record.variable_name] = {
"variable_name": record.variable_name,
"chapter_names": [],
"checked": False,
}
variable_map[record.variable_name]["chapter_names"].append(record.chapter_name)
for variable in variable_map.values():
variable["chapter_names"] = self._unique_names(variable["chapter_names"])
variable["checked"] = chapter.chapter_name in variable["chapter_names"]
return {
"chapter_annotation": {
"id": chapter.id,
"chapter_name": chapter.chapter_name,
"rule_id": chapter.rule_id,
"rule_name": rule_name,
},
"variables": list(variable_map.values()),
}
async def save(self, data: VariableSaveSchema) -> list[dict]:
chapter_names = self._collect_chapter_names(data)
await self._validate_chapters(template_id=data.template_id, chapter_names=chapter_names)
existing = await VariableCRUD(self.auth).get_list(search={"template_id": data.template_id})
if existing:
await VariableCRUD(self.auth).hard_delete(ids=[record.id for record in existing])
for variable in data.variables:
for chapter_name in self._unique_names(variable.chapter_names):
await VariableCRUD(self.auth).create(
data={
"template_id": data.template_id,
"variable_name": variable.variable_name,
"chapter_name": chapter_name,
}
)
return await self.list_by_template(template_id=data.template_id)
async def delete_by_variable(self, template_id: int, variable_name: str) -> None:
records = await VariableCRUD(self.auth).get_list(
search={"template_id": template_id, "variable_name": variable_name},
)
if not records:
raise CustomException(msg="变量关联不存在")
await VariableCRUD(self.auth).hard_delete(ids=[record.id for record in records])
def _collect_chapter_names(self, data: VariableSaveSchema) -> list[str]:
chapter_names: list[str] = []
for variable in data.variables:
chapter_names.extend(variable.chapter_names)
return self._unique_names(chapter_names)
def _unique_names(self, names: list[str]) -> list[str]:
return list(dict.fromkeys(name.strip() for name in names if name.strip()))
async def _validate_chapters(self, template_id: int, chapter_names: list[str]) -> None:
if not chapter_names:
return
chapters = await ChapterCRUD(self.auth).get_list(search={"template_id": template_id})
exists_names = {chapter.chapter_name for chapter in chapters}
invalid_names = [chapter_name for chapter_name in chapter_names if chapter_name not in exists_names]
if invalid_names:
raise CustomException(msg=f"章节不存在或不属于当前模板: {invalid_names}")
async def _chapter_summary_map(self, template_id: int) -> dict[str, dict]:
chapters = await ChapterCRUD(self.auth).get_list(
search={"template_id": template_id},
order_by=[{"chapter_order": "asc"}, {"id": "asc"}],
)
result: dict[str, dict] = {}
for chapter in chapters:
if chapter.chapter_name not in result:
result[chapter.chapter_name] = {
"chapter_name": chapter.chapter_name,
"annotation_ids": [],
"rules": [],
}
rule_name = chapter.rule.name if hasattr(chapter, "rule") and chapter.rule else None
result[chapter.chapter_name]["annotation_ids"].append(chapter.id)
result[chapter.chapter_name]["rules"].append(
{
"chapter_annotation_id": chapter.id,
"rule_id": chapter.rule_id,
"rule_name": rule_name,
}
)
return result

@ -200,8 +200,13 @@ class Settings(BaseSettings):
".svg",
".xls",
".xlsx",
".doc",
".docx",
".pdf",
".txt",
".csv",
]
MAX_FILE_SIZE: int = 10 * 1024 * 1024 # 最大文件大小(10MB)
MAX_FILE_SIZE: int = 100 * 1024 * 1024 # 最大文件大小(100MB)
# ================================================= #
# ***************** MinIO对象存储配置 **************** #

@ -15,7 +15,9 @@ from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.v1.module_common.file.model import FileModel
from app.api.v1.module_energy.chapter.model import EnergyReportChapterModel
from app.api.v1.module_energy.report_template.model import EnergyReportTemplateModel
from app.api.v1.module_energy.variable.model import EnergyReportVariableModel
from app.api.v1.module_platform.email.model import EmailConfigModel, EmailTemplateModel
from app.api.v1.module_platform.invoice.model import InvoiceModel
from app.api.v1.module_platform.menu.model import MenuModel
@ -80,6 +82,8 @@ class InitializeData:
TicketModel,
FileModel,
EnergyReportTemplateModel,
EnergyReportChapterModel,
EnergyReportVariableModel,
# ── 日志表(追加写入) ──
LoginLogModel,
OperationLogModel,

Loading…
Cancel
Save