From 5d52158c3e22aaa63239d613bb850020d81eddc7 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Thu, 9 Jul 2026 14:13:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=E8=83=BD=E6=BA=90?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E6=A8=A1=E6=9D=BF=E6=A8=A1=E5=9D=97=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/v1/module_energy/config/controller.py | 31 ++++++- app/api/v1/module_energy/config/schema.py | 11 +++ app/api/v1/module_energy/config/service.py | 92 ++++++++++++++++++- app/api/v1/module_energy/variable/schema.py | 1 + app/api/v1/module_energy/variable/service.py | 6 ++ 5 files changed, 138 insertions(+), 3 deletions(-) diff --git a/app/api/v1/module_energy/config/controller.py b/app/api/v1/module_energy/config/controller.py index 1188ad1..e231689 100644 --- a/app/api/v1/module_energy/config/controller.py +++ b/app/api/v1/module_energy/config/controller.py @@ -1,6 +1,6 @@ from typing import Annotated -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Path, Query from fastapi.responses import JSONResponse from app.common.response import ResponseSchema, SuccessResponse @@ -8,7 +8,7 @@ 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 .schema import TemplateConfigSaveSchema, TemplateSingleChapterConfigSaveSchema from .service import TemplateConfigService TemplateConfigRouter = APIRouter(route_class=OperationLogRoute, prefix="/attachment-template/config", tags=["能源报告模板配置"]) @@ -38,3 +38,30 @@ async def save_template_config_controller( ) -> JSONResponse: result = await TemplateConfigService(auth).save(data=data) return SuccessResponse(data=result, msg="保存模板配置成功") + + +@TemplateConfigRouter.post( + "/chapter/create", + summary="创建单个章节标注并保存变量关联", + response_model=ResponseSchema[dict], +) +async def create_template_chapter_config_controller( + data: TemplateSingleChapterConfigSaveSchema, + auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:update"]))], +) -> JSONResponse: + result = await TemplateConfigService(auth).create_chapter_config(data=data) + return SuccessResponse(data=result, msg="创建章节标注变量配置成功") + + +@TemplateConfigRouter.put( + "/chapter/update/{chapter_id}", + summary="更新单个章节标注并保存变量关联", + response_model=ResponseSchema[dict], +) +async def update_template_chapter_config_controller( + data: TemplateSingleChapterConfigSaveSchema, + chapter_id: Annotated[int, Path(ge=1, description="章节标注ID")], + auth: Annotated[AuthSchema, Depends(AuthPermission(["module_energy:attachment_template:update"]))], +) -> JSONResponse: + result = await TemplateConfigService(auth).update_chapter_config(chapter_id=chapter_id, data=data) + return SuccessResponse(data=result, msg="更新章节标注变量配置成功") diff --git a/app/api/v1/module_energy/config/schema.py b/app/api/v1/module_energy/config/schema.py index 4de940a..e972741 100644 --- a/app/api/v1/module_energy/config/schema.py +++ b/app/api/v1/module_energy/config/schema.py @@ -27,3 +27,14 @@ 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="变量关联列表") + + +class TemplateSingleChapterConfigSaveSchema(TemplateConfigSaveSchema): + """单个章节标注和变量关联保存模型。""" + + @field_validator("chapters") + @classmethod + def validate_single_chapter(cls, value: list[TemplateChapterRuleSchema]) -> list[TemplateChapterRuleSchema]: + if len(value) != 1: + raise ValueError("chapters 只能传入一条章节规则标注") + return value diff --git a/app/api/v1/module_energy/config/service.py b/app/api/v1/module_energy/config/service.py index dc0c949..856da49 100644 --- a/app/api/v1/module_energy/config/service.py +++ b/app/api/v1/module_energy/config/service.py @@ -1,11 +1,12 @@ from app.api.v1.module_energy.chapter.crud import ChapterCRUD +from app.api.v1.module_energy.variable.crud import VariableCRUD 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 +from .schema import TemplateChapterRuleSchema, TemplateConfigSaveSchema, TemplateSingleChapterConfigSaveSchema class TemplateConfigService: @@ -33,6 +34,32 @@ class TemplateConfigService: "variables": await VariableService(self.auth).list_by_template(template_id=template_id), } + async def create_chapter_config(self, data: TemplateSingleChapterConfigSaveSchema) -> dict: + await self._validate_template(template_id=data.template_id) + await self._validate_rules(chapters=data.chapters) + chapter = await self._create_chapter(template_id=data.template_id, chapter=data.chapters[0]) + await self._save_variables( + template_id=data.template_id, + chapter_name=chapter.chapter_name, + variables=data.variables, + ) + return await VariableService(self.auth).list_by_chapter(chapter_id=chapter.id) + + async def update_chapter_config(self, chapter_id: int, data: TemplateSingleChapterConfigSaveSchema) -> dict: + await self._validate_template(template_id=data.template_id) + await self._validate_rules(chapters=data.chapters) + chapter = await self._update_chapter( + template_id=data.template_id, + chapter_id=chapter_id, + chapter=data.chapters[0], + ) + await self._save_variables( + template_id=data.template_id, + chapter_name=chapter.chapter_name, + variables=data.variables, + ) + return await VariableService(self.auth).list_by_chapter(chapter_id=chapter.id) + async def _save_chapters(self, template_id: int, chapters: list[TemplateChapterRuleSchema]) -> None: for chapter in chapters: chapter_data = chapter.model_dump(exclude={"id"}) @@ -45,6 +72,69 @@ class TemplateConfigService: else: await ChapterCRUD(self.auth).create(data=chapter_data) + async def _create_chapter(self, template_id: int, chapter: TemplateChapterRuleSchema): + if chapter.id: + raise CustomException(msg="新增章节标注时 chapters[0].id 不能传值") + chapter_data = chapter.model_dump(exclude={"id"}) + chapter_data["template_id"] = template_id + return await ChapterCRUD(self.auth).create(data=chapter_data) + + async def _update_chapter(self, template_id: int, chapter_id: int, chapter: TemplateChapterRuleSchema): + old_chapter = await ChapterCRUD(self.auth).get(id=chapter_id, template_id=template_id) + if not old_chapter: + raise CustomException(msg=f"章节规则标注不存在或不属于当前模板: {chapter_id}") + if chapter.id and chapter.id != chapter_id: + raise CustomException(msg="请求体 chapters[0].id 必须与路径标注ID一致") + chapter_data = chapter.model_dump(exclude={"id"}) + return await ChapterCRUD(self.auth).update(id=chapter_id, data=chapter_data) + + async def _save_variables(self, template_id: int, chapter_name: str, variables: list) -> None: + chapter_names = self._collect_variable_chapter_names(chapter_name=chapter_name, variables=variables) + await self._validate_variable_chapters(template_id=template_id, chapter_names=chapter_names) + for variable in variables: + existing = await VariableCRUD(self.auth).get_list( + search={"template_id": template_id, "variable_name": variable.variable_name}, + ) + if existing: + await VariableCRUD(self.auth).hard_delete(ids=[record.id for record in existing]) + for variable_chapter_name in self._resolve_variable_chapter_names( + current_chapter_name=chapter_name, + variable=variable, + ): + await VariableCRUD(self.auth).create( + data={ + "template_id": template_id, + "variable_name": variable.variable_name, + "chapter_name": variable_chapter_name, + } + ) + + def _collect_variable_chapter_names(self, chapter_name: str, variables: list) -> list[str]: + chapter_names: list[str] = [] + for variable in variables: + chapter_names.extend(self._resolve_variable_chapter_names(current_chapter_name=chapter_name, variable=variable)) + return self._unique_names(chapter_names) + + def _resolve_variable_chapter_names(self, current_chapter_name: str, variable) -> list[str]: + chapter_names = list(variable.chapter_names) + if variable.checked is True and current_chapter_name not in chapter_names: + chapter_names.append(current_chapter_name) + if variable.checked is False: + chapter_names = [chapter_name for chapter_name in chapter_names if chapter_name != current_chapter_name] + 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_variable_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 _validate_template(self, template_id: int) -> None: template = await EnergyReportTemplateCRUD(self.auth).get(id=template_id) if not template: diff --git a/app/api/v1/module_energy/variable/schema.py b/app/api/v1/module_energy/variable/schema.py index 814e997..5d26ee6 100644 --- a/app/api/v1/module_energy/variable/schema.py +++ b/app/api/v1/module_energy/variable/schema.py @@ -6,6 +6,7 @@ class VariableItemSchema(BaseModel): variable_name: str = Field(..., min_length=1, max_length=128, description="变量名称") chapter_names: list[str] = Field(default_factory=list, description="关联章节名称列表") + checked: bool | None = Field(default=None, description="当前标注是否勾选该变量,单标注保存接口使用") @field_validator("variable_name") @classmethod diff --git a/app/api/v1/module_energy/variable/service.py b/app/api/v1/module_energy/variable/service.py index 0564068..8f9b229 100644 --- a/app/api/v1/module_energy/variable/service.py +++ b/app/api/v1/module_energy/variable/service.py @@ -46,12 +46,18 @@ class VariableService: variable_map[record.variable_name] = { "variable_name": record.variable_name, "chapter_names": [], + "chapters": [], "checked": False, } variable_map[record.variable_name]["chapter_names"].append(record.chapter_name) + chapter_map = await self._chapter_summary_map(template_id=chapter.template_id) for variable in variable_map.values(): variable["chapter_names"] = self._unique_names(variable["chapter_names"]) + variable["chapters"] = [ + chapter_map.get(chapter_name, {"chapter_name": chapter_name}) + for chapter_name in variable["chapter_names"] + ] variable["checked"] = chapter.chapter_name in variable["chapter_names"] return {