Backend: - Add auth.py for JWT token verification - Update main.py to protect all routes with auth middleware - Remove dashboard router (frontend handles aggregation) - Add Docker support with Dockerfile and docker-compose.yml Frontend: - Add OIDC authentication using oidc-client-ts with PKCE flow - Create router.js with auth guards for automatic login/logout - Add api.js for unified Axios instance with auth headers - Add composables: useAuth.js, useVehicleData.js for caching - Add views/Main.vue as main application page - Simplify App.vue to router-view container - Add deploy-web.sh deployment script Documentation: - Update AGENTS.md with new architecture and auth flow
183 lines
6.5 KiB
Python
183 lines
6.5 KiB
Python
from fastapi import APIRouter, HTTPException
|
|
from typing import Optional
|
|
from decimal import Decimal
|
|
|
|
from database import get_session
|
|
from models import FuelRecord, Vehicle
|
|
from schemas import (
|
|
FuelRecordCreate,
|
|
FuelRecordUpdate,
|
|
FuelRecordDelete,
|
|
FuelRecordResponse,
|
|
)
|
|
|
|
|
|
router = APIRouter(prefix="/fuel-records", tags=["fuel_records"])
|
|
|
|
|
|
@router.get("/list", response_model=list[FuelRecordResponse])
|
|
def get_fuel_records(vehicle_id: Optional[int] = None):
|
|
"""获取加油记录列表(排除已删除),返回原始数据不计算油耗"""
|
|
session = get_session()
|
|
try:
|
|
query = session.query(FuelRecord).filter(FuelRecord.is_deleted == False)
|
|
if vehicle_id:
|
|
query = query.filter(FuelRecord.vehicle_id == vehicle_id)
|
|
|
|
records = query.order_by(
|
|
FuelRecord.date.desc(), FuelRecord.mileage.desc()
|
|
).all()
|
|
|
|
# 直接返回原始数据,不计算油耗
|
|
return [
|
|
FuelRecordResponse(
|
|
id=r.id,
|
|
vehicle_id=r.vehicle_id,
|
|
date=r.date,
|
|
mileage=r.mileage,
|
|
fuel_amount=float(r.fuel_amount),
|
|
fuel_price=float(r.fuel_price) if r.fuel_price else None,
|
|
display_cost=float(r.display_cost) if r.display_cost else None,
|
|
actual_cost=float(r.actual_cost),
|
|
is_full_tank=r.is_full_tank,
|
|
notes=r.notes,
|
|
fuel_consumption=None, # 油耗由前端计算
|
|
is_deleted=r.is_deleted,
|
|
created_at=r.created_at,
|
|
updated_at=r.updated_at,
|
|
)
|
|
for r in records
|
|
]
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
@router.post("/create", response_model=FuelRecordResponse)
|
|
def create_fuel_record(record: FuelRecordCreate):
|
|
"""添加加油记录"""
|
|
session = get_session()
|
|
try:
|
|
# 验证车辆存在
|
|
vehicle = session.query(Vehicle).filter(Vehicle.id == record.vehicle_id).first()
|
|
if not vehicle:
|
|
raise HTTPException(status_code=404, detail="Vehicle not found")
|
|
|
|
# 自动计算价格
|
|
fuel_price = record.fuel_price
|
|
display_cost = record.display_cost
|
|
actual_cost = record.actual_cost
|
|
|
|
# 如果没有单价,根据总价计算
|
|
if fuel_price is None and actual_cost is not None and record.fuel_amount > 0:
|
|
fuel_price = actual_cost / record.fuel_amount
|
|
|
|
db_record = FuelRecord(
|
|
vehicle_id=record.vehicle_id,
|
|
date=record.date,
|
|
mileage=record.mileage,
|
|
fuel_amount=Decimal(str(record.fuel_amount)),
|
|
fuel_price=Decimal(str(fuel_price)) if fuel_price else None,
|
|
display_cost=Decimal(str(display_cost)) if display_cost else None,
|
|
actual_cost=Decimal(str(actual_cost)),
|
|
is_full_tank=record.is_full_tank,
|
|
notes=record.notes or "",
|
|
)
|
|
session.add(db_record)
|
|
session.commit()
|
|
session.refresh(db_record)
|
|
|
|
# 返回原始数据,不计算油耗
|
|
return FuelRecordResponse(
|
|
id=db_record.id,
|
|
vehicle_id=db_record.vehicle_id,
|
|
date=db_record.date,
|
|
mileage=db_record.mileage,
|
|
fuel_amount=float(db_record.fuel_amount),
|
|
fuel_price=float(db_record.fuel_price) if db_record.fuel_price else None,
|
|
display_cost=float(db_record.display_cost)
|
|
if db_record.display_cost
|
|
else None,
|
|
actual_cost=float(db_record.actual_cost),
|
|
is_full_tank=db_record.is_full_tank,
|
|
notes=db_record.notes,
|
|
fuel_consumption=None, # 油耗由前端计算
|
|
is_deleted=db_record.is_deleted,
|
|
created_at=db_record.created_at,
|
|
updated_at=db_record.updated_at,
|
|
)
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
@router.post("/update", response_model=FuelRecordResponse)
|
|
def update_fuel_record(record_update: FuelRecordUpdate):
|
|
"""更新加油记录"""
|
|
session = get_session()
|
|
try:
|
|
record = (
|
|
session.query(FuelRecord).filter(FuelRecord.id == record_update.id).first()
|
|
)
|
|
if not record:
|
|
raise HTTPException(status_code=404, detail="Fuel record not found")
|
|
|
|
# 更新字段
|
|
if record_update.date is not None:
|
|
record.date = record_update.date
|
|
if record_update.mileage is not None:
|
|
record.mileage = record_update.mileage
|
|
if record_update.fuel_amount is not None:
|
|
record.fuel_amount = Decimal(str(record_update.fuel_amount))
|
|
if record_update.fuel_price is not None:
|
|
record.fuel_price = Decimal(str(record_update.fuel_price))
|
|
if record_update.display_cost is not None:
|
|
record.display_cost = Decimal(str(record_update.display_cost))
|
|
if record_update.actual_cost is not None:
|
|
record.actual_cost = Decimal(str(record_update.actual_cost))
|
|
if record_update.is_full_tank is not None:
|
|
record.is_full_tank = record_update.is_full_tank
|
|
if record_update.notes is not None:
|
|
record.notes = record_update.notes
|
|
|
|
session.commit()
|
|
session.refresh(record)
|
|
|
|
# 返回原始数据,不计算油耗
|
|
return FuelRecordResponse(
|
|
id=record.id,
|
|
vehicle_id=record.vehicle_id,
|
|
date=record.date,
|
|
mileage=record.mileage,
|
|
fuel_amount=float(record.fuel_amount),
|
|
fuel_price=float(record.fuel_price) if record.fuel_price else None,
|
|
display_cost=float(record.display_cost) if record.display_cost else None,
|
|
actual_cost=float(record.actual_cost),
|
|
is_full_tank=record.is_full_tank,
|
|
notes=record.notes,
|
|
fuel_consumption=None, # 油耗由前端计算
|
|
is_deleted=record.is_deleted,
|
|
created_at=record.created_at,
|
|
updated_at=record.updated_at,
|
|
)
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
@router.post("/delete")
|
|
def delete_fuel_record(record: FuelRecordDelete):
|
|
"""软删除加油记录"""
|
|
session = get_session()
|
|
try:
|
|
db_record = (
|
|
session.query(FuelRecord)
|
|
.filter(FuelRecord.id == record.id, FuelRecord.is_deleted == False)
|
|
.first()
|
|
)
|
|
if not db_record:
|
|
raise HTTPException(status_code=404, detail="Fuel record not found")
|
|
|
|
db_record.is_deleted = True
|
|
session.commit()
|
|
return {"message": "Fuel record deleted successfully"}
|
|
finally:
|
|
session.close()
|