fix:优化数据
This commit is contained in:
@@ -1,8 +1,20 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from typing import List
|
||||
|
||||
from backend.app.schemas import FinanceSyncResponse, FinanceSyncResult
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.db import get_db
|
||||
from backend.app import models
|
||||
from backend.app.schemas import (
|
||||
FinanceRecordRead,
|
||||
FinanceRecordUpdate,
|
||||
FinanceSyncResponse,
|
||||
FinanceSyncResult,
|
||||
FinanceUploadResponse,
|
||||
)
|
||||
from backend.app.services.email_service import create_monthly_zip, sync_finance_emails
|
||||
from backend.app.services.invoice_upload import process_invoice_upload
|
||||
|
||||
|
||||
router = APIRouter(prefix="/finance", tags=["finance"])
|
||||
@@ -13,10 +25,87 @@ async def sync_finance():
|
||||
try:
|
||||
items_raw = await sync_finance_emails()
|
||||
except RuntimeError as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
# 邮箱配置/连接等问题属于可预期的业务错误,用 400 让前端直接展示原因,而不是泛化为 500。
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
items = [FinanceSyncResult(**item) for item in items_raw]
|
||||
return FinanceSyncResponse(items=items)
|
||||
details = [FinanceSyncResult(**item) for item in items_raw]
|
||||
return FinanceSyncResponse(
|
||||
status="success",
|
||||
new_files=len(details),
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/months", response_model=List[str])
|
||||
async def list_finance_months(db: Session = Depends(get_db)):
|
||||
"""List distinct months that have finance records (YYYY-MM), newest first."""
|
||||
from sqlalchemy import distinct
|
||||
|
||||
rows = (
|
||||
db.query(distinct(models.FinanceRecord.month))
|
||||
.order_by(models.FinanceRecord.month.desc())
|
||||
.all()
|
||||
)
|
||||
return [r[0] for r in rows]
|
||||
|
||||
|
||||
@router.get("/records", response_model=List[FinanceRecordRead])
|
||||
async def list_finance_records(
|
||||
month: str = Query(..., description="YYYY-MM"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""List finance records for a given month."""
|
||||
records = (
|
||||
db.query(models.FinanceRecord)
|
||||
.filter(models.FinanceRecord.month == month)
|
||||
.order_by(models.FinanceRecord.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
@router.post("/upload", response_model=FinanceUploadResponse, status_code=201)
|
||||
async def upload_invoice(
|
||||
file: UploadFile = File(...),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Upload an invoice (PDF or image). Saves to data/finance/{YYYY-MM}/manual/, runs AI OCR for amount/date."""
|
||||
suf = (file.filename or "").lower().split(".")[-1] if "." in (file.filename or "") else ""
|
||||
allowed = {"pdf", "jpg", "jpeg", "png", "webp"}
|
||||
if suf not in allowed:
|
||||
raise HTTPException(400, "仅支持 PDF、JPG、PNG、WEBP 格式")
|
||||
file_name, file_path, month_str, amount, billing_date = await process_invoice_upload(file)
|
||||
record = models.FinanceRecord(
|
||||
month=month_str,
|
||||
type="manual",
|
||||
file_name=file_name,
|
||||
file_path=file_path,
|
||||
amount=amount,
|
||||
billing_date=billing_date,
|
||||
)
|
||||
db.add(record)
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
return record
|
||||
|
||||
|
||||
@router.patch("/records/{record_id}", response_model=FinanceRecordRead)
|
||||
async def update_finance_record(
|
||||
record_id: int,
|
||||
payload: FinanceRecordUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update amount and/or billing_date of a finance record (e.g. after manual review)."""
|
||||
record = db.query(models.FinanceRecord).get(record_id)
|
||||
if not record:
|
||||
raise HTTPException(404, "记录不存在")
|
||||
if payload.amount is not None:
|
||||
record.amount = payload.amount
|
||||
if payload.billing_date is not None:
|
||||
record.billing_date = payload.billing_date
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
return record
|
||||
|
||||
|
||||
@router.get("/download/{month}")
|
||||
|
||||
Reference in New Issue
Block a user