Files
shanxia2.0/backend/app/api/pl/vehicle_fuel.py
T
2026-10-15 08:50:25 +08:00

293 lines
11 KiB
Python

"""车辆加油登记 API"""
from datetime import date, datetime
from typing import Any, List, Optional
from fastapi import APIRouter, Body, Depends, HTTPException, Query, Request, status
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.db_auth import DbUserContext, get_db_current_user
from app.core.tenant_context import get_tenant_no
from app.schemas.pl.cancel_reason import CancelReasonBody
from app.schemas.pl.vehicle_fuel import (
VehicleFuelPlateMonthSummaryResponse,
VehicleFuelRegisterCreate,
VehicleFuelRegisterListResponse,
VehicleFuelRegisterRead,
VehicleFuelRegisterUpdate,
)
from app.services import pl_vehicle_fuel_service as svc
from app.utils.excel_export import (
EXPORT_MAX_ROWS,
ExportColumn,
ExportValidationError,
export_rows_to_xlsx_response,
filter_export_columns,
)
router = APIRouter(prefix="/vehicle-fuel-register", tags=["运输计划-加油登记"])
FUEL_CATEGORY_LABELS = {
"bailian": "百链",
"gas_station": "加油站",
"shanxia_factory": "山峡",
}
FUEL_STATUS_LABELS = {"normal": "正常", "cancelled": "已作废"}
def _merge_query(request: Request, **known) -> dict:
out = {k: v for k, v in known.items() if v is not None and v != ""}
for k, v in request.query_params.multi_items():
if k in ("page", "pageSize"):
continue
if k not in out and v not in (None, ""):
out[k] = v
return out
def _param_get(params: dict, *keys: str) -> Any:
for key in keys:
if key in params and params[key] not in (None, ""):
return params[key]
return None
def _as_bool(value: Any) -> bool:
if isinstance(value, bool):
return value
if value is None:
return False
return str(value).strip().lower() in {"1", "true", "yes", "y"}
def _body_to_params(body: BaseModel) -> dict:
data = body.model_dump(by_alias=True, exclude_none=True)
for key in ("ids", "columns"):
data.pop(key, None)
extra = getattr(body, "model_extra", None) or {}
for key, value in extra.items():
if key not in data and value not in (None, ""):
data[key] = value
return data
def _list_kwargs(params: dict) -> dict:
return dict(
q=_param_get(params, "q"),
register_no=_param_get(params, "registerNo", "register_no"),
license_plate=_param_get(params, "licensePlate", "license_plate"),
fuel_category=_param_get(params, "fuelCategory", "fuel_category"),
fuel_category_in=_param_get(params, "fuelCategoryIn", "fuel_category_in"),
status=_param_get(params, "status"),
status_in=_param_get(params, "statusIn", "status_in"),
register_date_from=_param_get(params, "registerDateFrom", "register_date_from"),
register_date_to=_param_get(params, "registerDateTo", "register_date_to"),
registrant_id=_param_get(params, "registrantId", "registrant_id"),
registrant_name=_param_get(params, "registrantName", "registrant_name"),
odometer_km_op=_param_get(params, "odometerKmOp", "odometer_km_op"),
odometer_km=_param_get(params, "odometerKm", "odometer_km"),
odometer_km_min=_param_get(params, "odometerKmMin", "odometer_km_min"),
odometer_km_max=_param_get(params, "odometerKmMax", "odometer_km_max"),
fuel_liters_op=_param_get(params, "fuelLitersOp", "fuel_liters_op"),
fuel_liters=_param_get(params, "fuelLiters", "fuel_liters"),
fuel_liters_min=_param_get(params, "fuelLitersMin", "fuel_liters_min"),
fuel_liters_max=_param_get(params, "fuelLitersMax", "fuel_liters_max"),
fuel_tons_op=_param_get(params, "fuelTonsOp", "fuel_tons_op"),
fuel_tons=_param_get(params, "fuelTons", "fuel_tons"),
fuel_tons_min=_param_get(params, "fuelTonsMin", "fuel_tons_min"),
fuel_tons_max=_param_get(params, "fuelTonsMax", "fuel_tons_max"),
create_time_from=_param_get(params, "createTimeFrom", "create_time_from"),
create_time_to=_param_get(params, "createTimeTo", "create_time_to"),
mine_only=_as_bool(_param_get(params, "mineOnly", "mine_only")),
)
@router.get("/", response_model=VehicleFuelRegisterListResponse)
def list_registers(
request: Request,
q: Optional[str] = Query(default=None),
fuel_category: Optional[str] = Query(default=None, alias="fuelCategory"),
status: Optional[str] = Query(default=None),
register_date_from: Optional[date] = Query(default=None, alias="registerDateFrom"),
register_date_to: Optional[date] = Query(default=None, alias="registerDateTo"),
registrant_id: Optional[str] = Query(default=None, alias="registrantId"),
mine_only: Optional[bool] = Query(default=None, alias="mineOnly"),
page: int = Query(default=1, ge=1),
page_size: int = Query(default=20, ge=1, le=500, alias="pageSize"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelRegisterListResponse:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
params = _merge_query(
request,
q=q,
fuelCategory=fuel_category,
status=status,
registerDateFrom=register_date_from,
registerDateTo=register_date_to,
registrantId=registrant_id,
mineOnly=mine_only,
)
items, total = svc.list_registers(
db,
tenant_no,
user,
**_list_kwargs(params),
page=page,
page_size=page_size,
)
return VehicleFuelRegisterListResponse(
items=items, total=total, page=page, pageSize=page_size
)
FUEL_EXPORT_COLUMNS = [
ExportColumn(title="序号", field="seqNo"),
ExportColumn(title="登记单号", field="registerNo"),
ExportColumn(title="加油类别", field="fuelCategory"),
ExportColumn(title="日期", field="registerDate"),
ExportColumn(title="登记人", field="registrantName"),
ExportColumn(title="车牌号", field="licensePlate"),
ExportColumn(title="期初公里数", field="odometerKm"),
ExportColumn(title="加油升数", field="fuelLiters"),
ExportColumn(title="加油/吨", field="fuelTons"),
ExportColumn(title="状态", field="status"),
ExportColumn(title="备注", field="remark"),
ExportColumn(title="创建时间", field="createTime"),
]
class FuelRegisterExportBody(BaseModel):
q: Optional[str] = None
fuel_category: Optional[str] = Field(default=None, alias="fuelCategory")
status: Optional[str] = None
register_date_from: Optional[date] = Field(default=None, alias="registerDateFrom")
register_date_to: Optional[date] = Field(default=None, alias="registerDateTo")
registrant_id: Optional[str] = Field(default=None, alias="registrantId")
mine_only: Optional[bool] = Field(default=None, alias="mineOnly")
ids: Optional[List[str]] = None
columns: Optional[List[str]] = None
model_config = {"populate_by_name": True, "extra": "allow"}
@router.post("/export")
def export_registers(
body: FuelRegisterExportBody,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
):
try:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
params = _body_to_params(body)
items, _ = svc.list_registers(
db,
tenant_no,
user,
**_list_kwargs(params),
ids=body.ids,
page=1,
page_size=EXPORT_MAX_ROWS,
max_page_size=EXPORT_MAX_ROWS,
)
rows = []
for item in items:
data = item.model_dump(by_alias=True)
data["fuelCategory"] = FUEL_CATEGORY_LABELS.get(
data.get("fuelCategory") or "", data.get("fuelCategory")
)
data["status"] = FUEL_STATUS_LABELS.get(
data.get("status") or "", data.get("status")
)
for k, v in list(data.items()):
if hasattr(v, "isoformat"):
data[k] = str(v)[:19]
rows.append(data)
cols = filter_export_columns(FUEL_EXPORT_COLUMNS, body.columns)
date_str = datetime.now().strftime("%Y%m%d")
return export_rows_to_xlsx_response(
db,
tenant_no=tenant_no,
sheet_title="加油登记",
columns=cols,
rows=rows,
filename_stem=f"加油登记_{date_str}",
)
except ExportValidationError as e:
raise HTTPException(status_code=400, detail=str(e)) from e
@router.get("/plate-month-summary", response_model=VehicleFuelPlateMonthSummaryResponse)
def plate_month_summary(
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelPlateMonthSummaryResponse:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
return svc.list_plate_month_summary(db, tenant_no, user)
@router.get("/{id}", response_model=VehicleFuelRegisterRead)
def get_register(
id: str,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelRegisterRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
return svc.get_register(db, tenant_no, user, id)
@router.post("/", response_model=VehicleFuelRegisterRead, status_code=status.HTTP_201_CREATED)
def create_register(
payload: VehicleFuelRegisterCreate,
mine_only: bool = Query(default=False, alias="mineOnly"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelRegisterRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
return svc.create_register(db, tenant_no, user, payload, force_self=mine_only)
@router.put("/{id}", response_model=VehicleFuelRegisterRead)
def update_register(
id: str,
payload: VehicleFuelRegisterUpdate,
mine_only: bool = Query(default=False, alias="mineOnly"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelRegisterRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
return svc.update_register(
db, tenant_no, user, id, payload, force_self=mine_only
)
@router.post("/{id}/cancel", response_model=VehicleFuelRegisterRead)
def cancel_register(
id: str,
body: Optional[CancelReasonBody] = Body(default=None),
mine_only: bool = Query(default=False, alias="mineOnly"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelRegisterRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
return svc.cancel_register(
db,
tenant_no,
user,
id,
force_self=mine_only,
cancel_reason=body.cancel_reason if body else None,
)
@router.post("/{id}/restore", response_model=VehicleFuelRegisterRead)
def restore_register(
id: str,
mine_only: bool = Query(default=False, alias="mineOnly"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> VehicleFuelRegisterRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
return svc.restore_register(db, tenant_no, user, id, force_self=mine_only)