From 9f71f3a073f319afe7bbf33143e05ff8653bf3f0 Mon Sep 17 00:00:00 2001 From: lhx Date: Sat, 8 Nov 2025 17:10:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=87=BA=E6=95=B0=E6=8D=AE=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/export_excel.py | 128 +++++++++++++++++++++-------------- app/utils/time_utils.py | 39 +++++++++++ 2 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 app/utils/time_utils.py diff --git a/app/services/export_excel.py b/app/services/export_excel.py index 1675359..7da5b53 100644 --- a/app/services/export_excel.py +++ b/app/services/export_excel.py @@ -13,6 +13,7 @@ from ..services.account import AccountService from ..core.exceptions import DataNotFoundException, AccountNotFoundException import pandas as pd import logging +from ..utils.time_utils import TimeUtils from datetime import datetime logger = logging.getLogger(__name__) @@ -25,14 +26,6 @@ class ExportExcelService: self.settlement_service = SettlementDataService() self.level_service = LevelDataService() - def get_field_comments(self, model_class) -> Dict[str, str]: - """获取模型字段的注释信息""" - comments = {} - for column in model_class.__table__.columns: - if column.comment: - comments[column.name] = column.comment - return comments - def merge_settlement_with_related_data(self, db: Session, settlement_data: SettlementData, @@ -43,48 +36,62 @@ class ExportExcelService: 合并沉降数据与关联数据,去除重复和id字段 """ result = {} + # 导出数据列格式 + desired_column_config = [ + {"display_name": "观测点名称", "model_class": Checkpoint, "field_name_in_model": "aname"}, + {"display_name": "断面里程", "model_class": SectionData, "field_name_in_model": "mileage"}, + {"display_name": "工点名称", "model_class": SectionData, "field_name_in_model": "work_site"}, + {"display_name": "水准线路编码", "model_class": LevelData, "field_name_in_model": "linecode"}, + {"display_name": "修正量(mm)", "model_class": SettlementData, "field_name_in_model": "CVALUE"}, + {"display_name": "成果值(m)", "model_class": SettlementData, "field_name_in_model": "MAVALUE"}, + {"display_name": "埋设日期", "model_class": Checkpoint, "field_name_in_model": "burial_date"}, + {"display_name": "观测时间", "model_class": SettlementData, "field_name_in_model": "MTIME_W"}, + {"display_name": "观测阶段", "model_class": SettlementData, "field_name_in_model": "workinfoname"}, + {"display_name": "累计天数", "model_class": SettlementData, "field_name_in_model": "day"}, + {"display_name": "两次观测时间间隔", "model_class": SettlementData, "field_name_in_model": "day_jg"}, + {"display_name": "本次沉降(mm)", "model_class": SettlementData, "field_name_in_model": "mavalue_bc"}, + {"display_name": "累计沉降(mm)", "model_class": SettlementData, "field_name_in_model": "mavalue_lj"}, + {"display_name": "上传时间", "model_class": SettlementData, "field_name_in_model": "createdate"}, + {"display_name": "司镜人员", "model_class": SettlementData, "field_name_in_model": "sjName"}, + {"display_name": "基础类型", "model_class": SectionData, "field_name_in_model": "basic_types"}, + {"display_name": "桥墩台高度", "model_class": SectionData, "field_name_in_model": "height"}, + {"display_name": "断面状态", "model_class": SectionData, "field_name_in_model": "status"}, + {"display_name": "桥梁墩(台)编号", "model_class": SectionData, "field_name_in_model": "number"}, + {"display_name": "过渡段", "model_class": SectionData, "field_name_in_model": "transition_paragraph"}, + {"display_name": "设计填土高度", "model_class": SectionData, "field_name_in_model": "design_fill_height"}, + {"display_name": "压实层厚度", "model_class": SectionData, "field_name_in_model": "compression_layer_thickness"}, + {"display_name": "处理深度", "model_class": SectionData, "field_name_in_model": "treatment_depth"}, + {"display_name": "地基处理方法", "model_class": SectionData, "field_name_in_model": "foundation_treatment_method"}, + {"display_name": "围岩级别", "model_class": SectionData, "field_name_in_model": "rock_mass_classification"}, + {"display_name": "工作基点名称序列", "model_class": LevelData, "field_name_in_model": "benchmarkids"}, + {"display_name": "工作基点高程序列(m)", "model_class": LevelData, "field_name_in_model": "wsphigh"}, + {"display_name": "水准_上传时间", "model_class": LevelData, "field_name_in_model": "createDate"}, + {"display_name": "备注", "model_class": SettlementData, "field_name_in_model": "upd_remark"} + ] - # 沉降数据字段映射(用注释名作为键) - settlement_comments = self.get_field_comments(SettlementData) - settlement_dict = settlement_data.to_dict() - for field_name, value in settlement_dict.items(): - # 跳过id字段 - if field_name == 'id': - continue - # 使用注释名作为键,如果没有注释则使用字段名 - key = settlement_comments.get(field_name, field_name) - result[key] = value + result = {item["display_name"]: None for item in desired_column_config} - # 断面数据字段映射(添加前缀) - section_comments = self.get_field_comments(SectionData) - section_dict = section_data.to_dict() - for field_name, value in section_dict.items(): - # 跳过id和account_id字段 - if field_name in ['id', 'account_id']: - continue - key = section_comments.get(field_name, field_name) - result[f"断面_{key}"] = value - - # 观测点数据字段映射(添加前缀) - checkpoint_comments = self.get_field_comments(Checkpoint) - checkpoint_dict = checkpoint_data.to_dict() - for field_name, value in checkpoint_dict.items(): - # 跳过id和section_id字段(section_id可能重复) - if field_name in ['id', 'section_id']: - continue - key = checkpoint_comments.get(field_name, field_name) - result[f"观测点_{key}"] = value - - # 水准数据字段映射(添加前缀) + data_map_by_class = { + SettlementData: settlement_data.to_dict(), + SectionData: section_data.to_dict(), + Checkpoint: checkpoint_data.to_dict(), + } + # 对于可选的 level_data,只有当它存在时才添加到映射中 if level_data is not None: - level_comments = self.get_field_comments(LevelData) - level_dict = level_data.to_dict() - for field_name, value in level_dict.items(): - # 跳过id和NYID字段(NYID可能重复) - if field_name in ['id', 'NYID']: - continue - key = level_comments.get(field_name, field_name) - result[f"水准_{key}"] = value + data_map_by_class[LevelData] = level_data.to_dict() + + for config_item in desired_column_config: + display_name = config_item["display_name"] + model_class = config_item["model_class"] + field_name_in_model = config_item["field_name_in_model"] + + # 检查这个模型类的数据是否存在于映射中 + if model_class in data_map_by_class: + source_dict = data_map_by_class[model_class] + # 检查模型数据中是否包含这个字段 + if field_name_in_model in source_dict: + # 将从源数据中取出的值赋给结果字典中对应的 display_name 键 + result[display_name] = source_dict[field_name_in_model] return result @@ -136,7 +143,7 @@ class ExportExcelService: if not all_settlements: logger.warning("未找到任何沉降数据") - logger.info(f"观测点id集合{point_ids}") + # logger.info(f"观测点id集合{point_ids}") raise DataNotFoundException("未找到任何沉降数据") logger.info(f"批量查询到 {len(all_settlements)} 条沉降数据") @@ -162,6 +169,7 @@ class ExportExcelService: # 建立NYID->水准数据映射 nyid_level_map = {} for level_data in all_level_data: + level_data.createDate = TimeUtils.datetime_to_date_string(level_data.createDate) if level_data.NYID not in nyid_level_map: nyid_level_map[level_data.NYID] = level_data @@ -170,11 +178,33 @@ class ExportExcelService: for section in sections: checkpoints = section_checkpoint_map.get(section.section_id, []) for checkpoint in checkpoints: + checkpoint.burial_date = TimeUtils.string_to_date_string(checkpoint.burial_date) settlements = point_settlement_map.get(checkpoint.point_id, []) for settlement in settlements: + settlement.MTIME_W = TimeUtils.datetime_to_date_string(settlement.MTIME_W) + settlement.createdate = TimeUtils.datetime_to_date_string(settlement.createdate) + import decimal + d = decimal.Decimal(settlement.CVALUE) + bc = decimal.Decimal(settlement.mavalue_bc) + lj = decimal.Decimal(settlement.mavalue_lj) + d = d * 1000 + bc = bc * 1000 + lj = lj * 1000 + if d == d.to_integral_value(): + settlement.CVALUE = str(int(d)) + else: + settlement.CVALUE = str(d) + if bc == bc.to_integral_value(): + settlement.mavalue_bc = str(int(bc)) + else: + settlement.mavalue_bc = str(bc) + if lj == lj.to_integral_value(): + settlement.mavalue_lj = str(int(lj)) + else: + settlement.mavalue_lj = str(lj) + # 从映射中获取水准数据 level_data = nyid_level_map.get(settlement.NYID) - # 合并数据 merged_record = self.merge_settlement_with_related_data( db, settlement, section, checkpoint, level_data diff --git a/app/utils/time_utils.py b/app/utils/time_utils.py new file mode 100644 index 0000000..81e60c8 --- /dev/null +++ b/app/utils/time_utils.py @@ -0,0 +1,39 @@ +from datetime import datetime +from typing import Union + +class TimeUtils: + """时间处理工具类""" + + @staticmethod + def string_to_date_string(time_string: str, fmt: str = "%Y-%m-%d %H:%M:%S.%f") -> str: + """ + 将字符串格式的时间(如 '2025-11-04 08:39:48')转换为日期字符串 '2025-11-04'。 + + Args: + time_string: 输入的时间字符串。 + fmt: 输入时间字符串的格式。 + + Returns: + 格式为 'YYYY-MM-DD' 的日期字符串。 + """ + try: + dt_object = datetime.strptime(time_string, fmt) + return dt_object.strftime("%Y-%m-%d") + except (ValueError, TypeError): + # 如果转换失败 + return time_string + + @staticmethod + def datetime_to_date_string(dt: datetime) -> str: + """ + 将datetime对象转换为日期字符串 '2025-11-04'。 + + Args: + dt: 输入的datetime对象。 + + Returns: + 格式为 'YYYY-MM-DD' 的日期字符串。 + """ + if not isinstance(dt, datetime): + return dt + return dt.strftime("%Y-%m-%d")