diff --git a/app/api/daily.py b/app/api/daily.py new file mode 100644 index 0000000..3e7d7da --- /dev/null +++ b/app/api/daily.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from typing import Dict, Any +from ..core.database import get_db +from ..core.response_code import ResponseCode +from ..schemas.daily import LinecodeToDailyRequest +from ..services.daily import DailyDataService +import logging + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/daily", tags=["日常数据管理"]) + +# 实例化服务类 +daily_service = DailyDataService() + + +@router.post("/create_from_linecode", response_model=Dict[str, Any]) +def create_daily_data_from_linecode( + request: LinecodeToDailyRequest, + db: Session = Depends(get_db) +): + """ + 综合业务接口:通过水准线路编码生成 daily 数据 + + 业务逻辑: + 1. 在水准数据表(level_data)中查找符合 linecode 的记录,且 NYID 最大 + 2. 通过 NYID 查询沉降数据表(settlement_data) + 3. 通过沉降数据的 point_id 查询观测点表(checkpoint),得到 section_id + 4. 通过 section_id 查询断面表(section_data),得到 account_id + 5. 整合这些数据,形成 daily 对象,插入到数据库表 + + 返回:成功插入的记录数 + """ + try: + logger.info(f"开始处理 linecode={request.linecode} 的 daily 数据生成请求") + + # 调用服务层方法 + created_records = daily_service.create_daily_from_linecode( + db=db, + linecode=request.linecode, + account_id=request.account_id + ) + + if not created_records: + return { + "code": ResponseCode.SUCCESS, + "message": "没有生成任何 daily 记录", + "total": 0, + "data": [] + } + + logger.info(f"成功生成 {len(created_records)} 条 daily 记录") + + return { + "code": ResponseCode.SUCCESS, + "message": f"成功生成 {len(created_records)} 条 daily 记录", + "total": len(created_records), + "data": [ + { + "id": record.id, + "user_id": record.user_id, + "account_id": record.account_id, + "point_id": record.point_id, + "NYID": record.NYID, + "linecode": record.linecode, + "section_id": record.section_id, + "remaining": record.remaining, + "is_all": record.is_all + } + for record in created_records + ] + } + + except ValueError as e: + logger.warning(str(e)) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(e) + ) + except HTTPException: + raise + except Exception as e: + logger.error(f"生成 daily 数据失败:{str(e)}", exc_info=True) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"生成 daily 数据失败:{str(e)}" + ) diff --git a/app/main.py b/app/main.py index 5d5e094..0ec27cd 100644 --- a/app/main.py +++ b/app/main.py @@ -11,6 +11,7 @@ from .api.database import router as database_router from .api.task import router as task_router from .api.comprehensive_data import router as comprehensive_data_router from .api.export_excel import router as export_excel_router +from .api.daily import router as daily_router from .utils.scheduler import task_scheduler # 初始化日志系统 @@ -70,6 +71,7 @@ app.include_router(database_router, prefix="/api") app.include_router(task_router, prefix="/api") app.include_router(comprehensive_data_router, prefix="/api") app.include_router(export_excel_router, prefix="/api") +app.include_router(daily_router, prefix="/api") # app.include_router(test_router, prefix="/api") # 根路径 diff --git a/app/models/daily.py b/app/models/daily.py index ad9a470..d7f65dc 100644 --- a/app/models/daily.py +++ b/app/models/daily.py @@ -5,12 +5,11 @@ class DailyData(Base): __tablename__ = "daily" id = Column(Integer, primary_key=True, index=True, autoincrement=True) - user_id = Column(Integer, nullable=False, comment="用户id") + user_id = Column(Integer, default=1, nullable=False, comment="用户id") account_id = Column(Integer, nullable=False, comment="账户id") point_id = Column(String(100), comment="测点id") NYID = Column(String(100), nullable=False, comment="期数id") linecode = Column(String(255), nullable=False, comment="水准线路编码") section_id = Column(String(255), nullable=False, comment="所属断面id") remaining = Column(Integer, nullable=False, comment="剩余天数") - user_id = Column(Integer, default=1, nullable=False, comment="用户id") - is_all = Column(Integer, default=1, nullable=False, comment="是否全量数据") + is_all = Column(Integer, default=0, nullable=False, comment="是否全量数据") diff --git a/app/schemas/daily.py b/app/schemas/daily.py new file mode 100644 index 0000000..eeb20ff --- /dev/null +++ b/app/schemas/daily.py @@ -0,0 +1,22 @@ +from pydantic import BaseModel, Field +from typing import Optional + +class LinecodeToDailyRequest(BaseModel): + """通过水准线路编码生成 daily 数据的请求模型""" + linecode: str = Field(..., description="水准线路编码") + account_id: Optional[int] = Field(None, description="账户ID(可选,如果沉降数据中有)") + +class DailyDataResponse(BaseModel): + """Daily 数据响应模型""" + id: int + user_id: int + account_id: int + point_id: Optional[str] + NYID: str + linecode: str + section_id: str + remaining: int + is_all: int + + class Config: + from_attributes = True diff --git a/app/services/daily.py b/app/services/daily.py index 20444bc..7822f46 100644 --- a/app/services/daily.py +++ b/app/services/daily.py @@ -233,4 +233,115 @@ class DailyDataService(BaseService[DailyData]): except Exception as e: logger.error(f"获取account_id={account_id}的日常数据失败:{str(e)}", exc_info=True) - raise e \ No newline at end of file + raise e + + def create_daily_from_linecode( + self, + db: Session, + linecode: str, + account_id: Optional[int] = None + ) -> List[DailyData]: + """ + 通过水准线路编码生成 daily 数据 + + 业务逻辑: + 1. 在水准数据表(level_data)中查找符合 linecode 的记录,且 NYID 最大 + 2. 通过 NYID 查询沉降数据表(settlement_data) + 3. 通过沉降数据的 point_id 查询观测点表(checkpoint),得到 section_id + 4. 通过 section_id 查询断面表(section_data),得到 account_id + 5. 整合这些数据,形成 daily 对象,插入到数据库表 + + Args: + db: 数据库会话 + linecode: 水准线路编码 + account_id: 可选的账户ID筛选条件 + + Returns: + 创建的 DailyData 记录列表 + """ + try: + logger.info(f"开始处理 linecode={linecode} 的 daily 数据生成请求") + + from ..models.level_data import LevelData + from ..models.settlement_data import SettlementData + from ..models.checkpoint import Checkpoint + from ..models.section_data import SectionData + + # 1. 在水准数据表中查找符合 linecode 的记录,且 NYID 最大 + level_data_list = db.query(LevelData)\ + .filter(LevelData.linecode == linecode)\ + .all() + + if not level_data_list: + raise ValueError(f"未找到 linecode={linecode} 对应的水准数据") + + # 找到 NYID 最大的记录(将 String 转换为数字进行比较) + max_nyid = max(level_data_list, key=lambda x: int(x.NYID) if x.NYID.isdigit() else 0) + target_nyid = max_nyid.NYID + logger.info(f"找到最大 NYID: {target_nyid}") + + # 2. 通过 NYID 查询沉降数据 + settlement_data_list = db.query(SettlementData)\ + .filter(SettlementData.NYID == target_nyid)\ + .all() + + if not settlement_data_list: + raise ValueError(f"未找到 NYID={target_nyid} 对应的沉降数据") + + # 3. 遍历沉降数据,构建 daily 记录 + daily_records = [] + for settlement in settlement_data_list: + # 3.1 通过沉降数据的 point_id 查询观测点表,得到 section_id + checkpoint = db.query(Checkpoint)\ + .filter(Checkpoint.point_id == settlement.point_id)\ + .first() + if not checkpoint: + logger.warning(f"未找到 point_id={settlement.point_id} 对应的观测点,跳过该记录") + continue + + # 3.2 通过 section_id 查询断面表,得到 account_id + section = db.query(SectionData)\ + .filter(SectionData.section_id == checkpoint.section_id)\ + .first() + if not section: + logger.warning(f"未找到 section_id={checkpoint.section_id} 对应的断面,跳过该记录") + continue + + # 3.3 从断面数据中获取 account_id,作为 user_id + user_id = int(section.account_id) if section.account_id else None + if not user_id: + logger.warning(f"断面 section_id={checkpoint.section_id} 没有 account_id,跳过该记录") + continue + + # 如果提供了 account_id 筛选条件,则只处理匹配的记录 + if account_id is not None and user_id != account_id: + continue + + # 3.4 构建 daily 记录 + daily_record = DailyData( + user_id=user_id, + account_id=user_id, + point_id=settlement.point_id, + NYID=settlement.NYID, + linecode=linecode, + section_id=checkpoint.section_id, + remaining=0, + is_all=0 + ) + daily_records.append(daily_record) + + if not daily_records: + logger.warning(f"没有生成任何 daily 记录") + return [] + + # 4. 批量插入 daily 记录 + created_records = self.batch_create_by_account_nyid(db, daily_records) + + logger.info(f"成功生成 {len(created_records)} 条 daily 记录") + return created_records + + except ValueError: + raise + except Exception as e: + logger.error(f"生成 daily 数据失败:{str(e)}", exc_info=True) + raise \ No newline at end of file