certflow.services.caliber_mapping_service 源代码

"""口径映射服务

收口 CaliberMapping 的 ORM 类与 CRUD 逻辑,供控制器层(Controller)调用,
消除控制器直接依赖 ``certflow.models.*`` 的跨层边。视图(View)只经控制器门面
访问,不直接触碰本服务或模型层。

架构层次:View → Controller (CaliberMappingController)
         → Service (CaliberMappingService) → Model (CaliberMapping)
"""

from __future__ import annotations

from typing import Any

from sqlalchemy.orm import Session

from certflow.models.caliber_mapping import CaliberMapping
from certflow.services.dict_csv_sync import DictCsvSync


[文档] class CaliberMappingService: """口径映射服务 — 控制器与模型之间的业务调度层。 CRUD 逻辑从原 ``CaliberMappingController`` 下沉至此,控制器仅做事务提交。 """ # CSV 列序 / 类型 / 唯一键(与 dictionaries.yaml 的 caliber_mappings 段一致) CSV_COLUMNS = ["raw_text", "caliber_value", "is_imperial"] CSV_FIELD_TYPES = { "raw_text": "text", "caliber_value": "text", "is_imperial": "bool", } CSV_UNIQUE_KEYS = ["raw_text"] def __init__(self, session: Session) -> None: """初始化服务;绑定数据库会话。 Args: session: SQLAlchemy 会话(通常由 BaseController.get_service 注入)。 """ self.session = session self._csv_sync = DictCsvSync( CaliberMapping, csv_columns=self.CSV_COLUMNS, field_types=self.CSV_FIELD_TYPES, unique_keys=self.CSV_UNIQUE_KEYS, )
[文档] def list_all(self) -> list[CaliberMapping]: """返回全部口径映射记录(按 id 升序)。""" return self.session.query(CaliberMapping).order_by(CaliberMapping.id).all()
[文档] def get_by_id(self, mapping_id: int) -> CaliberMapping | None: """按主键获取记录。""" return self.session.get(CaliberMapping, mapping_id)
[文档] def create(self, data: dict[str, Any]) -> CaliberMapping: """新增记录(不提交,交由控制器统一 commit)。""" obj = CaliberMapping(**data) self.session.add(obj) self.session.flush() return obj
[文档] def update(self, mapping_id: int, data: dict[str, Any]) -> CaliberMapping | None: """更新记录(不提交);id 不存在返回 None。""" obj = self.session.get(CaliberMapping, mapping_id) if obj is None: return None for key, value in data.items(): setattr(obj, key, value) return obj
[文档] def delete(self, mapping_id: int) -> bool: """删除记录(不提交);id 不存在返回 False。""" obj = self.session.get(CaliberMapping, mapping_id) if obj is None: return False self.session.delete(obj) return True
[文档] def seed_common_calibers(self) -> int: """幂等灌入常见口径映射示例,库非空则跳过(不破坏已有数据)。""" if self.session.query(CaliberMapping).first() is not None: return 0 samples = [ ("DN15", "15", False, "manual"), ("DN50", "50", False, "manual"), ("DN100", "100", False, "manual"), ('2"', "2", True, "manual"), ('1/2"', "1/2", True, "manual"), ] for raw_text, caliber_value, is_imperial, source in samples: self.session.add( CaliberMapping( raw_text=raw_text, caliber_value=caliber_value, is_imperial=is_imperial, source=source, usage_count=0, ) ) return len(samples)
[文档] def export_csv(self, path: str | Any) -> int: """把当前 caliber_mappings 表整表导出为 CSV 快照,返回行数。""" return self._csv_sync.export_csv(self.session, path)
[文档] def import_csv(self, path: str | Any) -> dict[str, int]: """把 CSV 快照 upsert 回 caliber_mappings 表,返回统计。""" return self._csv_sync.import_csv(self.session, path)
# 供控制器/其他模块从服务层间接引用模型类,避免 controller 直连 certflow.models。 __all__ = ["CaliberMapping", "CaliberMappingService"]