You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
7.2 KiB
Python

from sqlalchemy import func, select
from app.api.v1.module_common.file.crud import FileCRUD
from app.api.v1.module_energy.chapter.model import EnergyReportChapterModel
from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from .crud import EnergyReportTemplateCRUD
from .model import EnergyReportTemplateModel
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,
)
annotation_count_map = await self._annotation_count_map(
template_ids=[item.get("id") for item in page_result.items if item.get("id")],
)
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")
item["annotation_count"] = annotation_count_map.get(item.get("id"), 0)
return page_result
async def create(self, data: EnergyReportTemplateCreateSchema) -> EnergyReportTemplateOutSchema:
await self._validate_unique_name(template_name=data.template_name, tenant_id=self._current_tenant_id())
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, tenant_id=old_template.tenant_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,
tenant_id: int | None = None,
) -> None:
if self.auth.db is None:
raise CustomException(msg="数据库会话不存在")
sql = select(EnergyReportTemplateModel.id).where(
EnergyReportTemplateModel.template_name == template_name,
EnergyReportTemplateModel.is_deleted == False,
)
if tenant_id is not None:
sql = sql.where(EnergyReportTemplateModel.tenant_id == tenant_id)
if exclude_id is not None:
sql = sql.where(EnergyReportTemplateModel.id != exclude_id)
result = await self.auth.db.execute(sql.limit(1))
if result.scalar_one_or_none() is not None:
raise CustomException(msg="能源报告模板名称已存在")
async def _annotation_count_map(self, template_ids: list[int]) -> dict[int, int]:
if not template_ids:
return {}
if self.auth.db is None:
raise CustomException(msg="数据库会话不存在")
result = await self.auth.db.execute(
select(EnergyReportChapterModel.template_id, func.count(EnergyReportChapterModel.id))
.where(
EnergyReportChapterModel.template_id.in_(template_ids),
EnergyReportChapterModel.is_deleted == False,
)
.group_by(EnergyReportChapterModel.template_id)
)
return {template_id: count for template_id, count in result.all()}
def _current_tenant_id(self) -> int | None:
if not self.auth.user:
return self.auth.tenant_id
return self.auth.tenant_id or self.auth.user.tenant_id
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)