fix:优化项目内容

This commit is contained in:
Daniel
2026-03-18 17:01:10 +08:00
parent da63282a10
commit 27dc89e251
64 changed files with 3421 additions and 4982 deletions

View File

@@ -1,6 +1,9 @@
from typing import List
from datetime import date
import os
from pathlib import Path
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
from fastapi import APIRouter, Body, Depends, File, HTTPException, Query, UploadFile
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
@@ -9,6 +12,8 @@ from backend.app import models
from backend.app.schemas import (
FinanceRecordRead,
FinanceRecordUpdate,
FinanceBatchDeleteRequest,
FinanceSyncRequest,
FinanceSyncResponse,
FinanceSyncResult,
FinanceUploadResponse,
@@ -21,9 +26,14 @@ router = APIRouter(prefix="/finance", tags=["finance"])
@router.post("/sync", response_model=FinanceSyncResponse)
async def sync_finance():
async def sync_finance(payload: FinanceSyncRequest = Body(default=FinanceSyncRequest())):
try:
items_raw = await sync_finance_emails()
items_raw = await sync_finance_emails(
mode=payload.mode,
start_date=payload.start_date,
end_date=payload.end_date,
doc_types=payload.doc_types,
)
except RuntimeError as exc:
# 邮箱配置/连接等问题属于可预期的业务错误,用 400 让前端直接展示原因,而不是泛化为 500。
raise HTTPException(status_code=400, detail=str(exc)) from exc
@@ -108,6 +118,60 @@ async def update_finance_record(
return record
@router.delete("/records/{record_id}")
async def delete_finance_record(
record_id: int,
db: Session = Depends(get_db),
):
"""删除单条财务记录及对应文件(若存在)。"""
record = db.query(models.FinanceRecord).get(record_id)
if not record:
raise HTTPException(404, "记录不存在")
file_path = Path(record.file_path)
if not file_path.is_absolute():
file_path = Path(".") / file_path
if file_path.exists():
try:
file_path.unlink()
except OSError:
pass
db.delete(record)
db.commit()
return {"status": "deleted", "id": record_id}
@router.post("/records/batch-delete")
async def batch_delete_finance_records(
payload: FinanceBatchDeleteRequest,
db: Session = Depends(get_db),
):
"""批量删除财务记录及对应文件。"""
if not payload.ids:
return {"status": "ok", "deleted": 0}
records = (
db.query(models.FinanceRecord)
.filter(models.FinanceRecord.id.in_(payload.ids))
.all()
)
for record in records:
file_path = Path(record.file_path)
if not file_path.is_absolute():
file_path = Path(".") / file_path
if file_path.exists():
try:
file_path.unlink()
except OSError:
pass
db.delete(record)
db.commit()
return {"status": "deleted", "deleted": len(records)}
@router.get("/download/{month}")
async def download_finance_month(month: str):
"""
@@ -124,3 +188,53 @@ async def download_finance_month(month: str):
filename=f"finance_{month}.zip",
)
@router.get("/download-range")
async def download_finance_range(
start_date: date = Query(..., description="起始日期 YYYY-MM-DD"),
end_date: date = Query(..., description="结束日期 YYYY-MM-DD含当日"),
only_invoices: bool = Query(True, description="是否仅包含发票类型"),
db: Session = Depends(get_db),
):
"""
按时间范围打包下载发票(默认仅发票,可扩展)。
"""
if end_date < start_date:
raise HTTPException(status_code=400, detail="结束日期不能早于开始日期")
q = db.query(models.FinanceRecord).filter(
models.FinanceRecord.billing_date.isnot(None),
models.FinanceRecord.billing_date >= start_date,
models.FinanceRecord.billing_date <= end_date,
)
if only_invoices:
q = q.filter(models.FinanceRecord.type == "invoices")
records = q.all()
if not records:
raise HTTPException(status_code=404, detail="该时间段内没有可导出的记录")
base_dir = Path("data/finance")
base_dir.mkdir(parents=True, exist_ok=True)
zip_name = f"invoices_{start_date.isoformat()}_{end_date.isoformat()}.zip"
zip_path = base_dir / zip_name
import zipfile
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for r in records:
file_path = Path(r.file_path)
if not file_path.is_absolute():
file_path = Path(".") / file_path
if not file_path.exists():
continue
# 保持月份/类型的相对结构
rel = file_path.relative_to(Path("data")) if "data" in file_path.parts else file_path.name
zf.write(file_path, arcname=rel)
return FileResponse(
path=str(zip_path),
media_type="application/zip",
filename=zip_name,
)