111 lines
3.8 KiB
Python
111 lines
3.8 KiB
Python
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.dependencies import get_db, get_current_user
|
|
from app.models.invoice import Invoice, InvoiceOrder
|
|
from app.models.user import User
|
|
from app.schemas.invoice import InvoiceCreateRequest, InvoiceOut, InvoiceOrderOut
|
|
from app.services.invoice import (
|
|
create_invoice,
|
|
get_user_invoices,
|
|
get_invoice_by_id,
|
|
get_invoice_with_orders,
|
|
)
|
|
|
|
logger = logging.getLogger("videogen")
|
|
|
|
router = APIRouter(prefix="/invoices", tags=["invoices"])
|
|
|
|
|
|
@router.post("", response_model=InvoiceOut)
|
|
async def create_invoice_endpoint(
|
|
req: InvoiceCreateRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""创建发票申请。"""
|
|
invoice = await create_invoice(db, current_user.id, req)
|
|
await db.commit()
|
|
|
|
# 重新查询以获取关联订单
|
|
detail = await get_invoice_with_orders(db, invoice.id)
|
|
return _invoice_to_out(detail["invoice"], detail["orders"])
|
|
|
|
|
|
@router.get("")
|
|
async def list_invoices(
|
|
page: int = Query(1, ge=1),
|
|
page_size: int = Query(20, ge=1, le=100),
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""获取当前用户的发票列表(分页)。"""
|
|
invoices, total = await get_user_invoices(db, current_user.id, page, page_size)
|
|
|
|
# 加载每个发票的关联订单
|
|
items = []
|
|
for inv in invoices:
|
|
result = await db.execute(
|
|
select(InvoiceOrder).where(InvoiceOrder.invoice_id == inv.id)
|
|
)
|
|
orders = result.scalars().all()
|
|
items.append(_invoice_to_out(inv, list(orders)))
|
|
|
|
return {"items": items, "total": total, "page": page, "page_size": page_size}
|
|
|
|
|
|
@router.get("/{invoice_id}")
|
|
async def get_invoice(
|
|
invoice_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""获取发票详情(含关联订单)。"""
|
|
detail = await get_invoice_with_orders(db, invoice_id)
|
|
if not detail:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="发票不存在")
|
|
|
|
invoice = detail["invoice"]
|
|
if invoice.user_id != current_user.id:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权查看该发票")
|
|
|
|
return _invoice_to_out(invoice, detail["orders"])
|
|
|
|
|
|
def _invoice_to_out(invoice: Invoice, orders: list[InvoiceOrder]) -> dict:
|
|
"""将 Invoice ORM 对象转换为响应 dict。"""
|
|
return {
|
|
"id": invoice.id,
|
|
"userId": invoice.user_id,
|
|
"invoiceNo": invoice.invoice_no,
|
|
"headerType": invoice.header_type,
|
|
"headerName": invoice.header_name,
|
|
"headerTaxNo": invoice.header_tax_no,
|
|
"headerRegisterAddress": invoice.header_register_address,
|
|
"headerRegisterPhone": invoice.header_register_phone,
|
|
"headerBankName": invoice.header_bank_name,
|
|
"headerBankAccount": invoice.header_bank_account,
|
|
"email": invoice.email,
|
|
"totalAmount": round(float(invoice.total_amount), 2),
|
|
"totalCredits": round(float(invoice.total_credits), 2),
|
|
"status": invoice.status,
|
|
"failureReason": invoice.failure_reason,
|
|
"issuedAt": invoice.issued_at.isoformat() if invoice.issued_at else None,
|
|
"createdAt": invoice.created_at.isoformat() if invoice.created_at else None,
|
|
"updatedAt": invoice.updated_at.isoformat() if invoice.updated_at else None,
|
|
"orders": [
|
|
{
|
|
"id": o.id,
|
|
"invoiceId": o.invoice_id,
|
|
"orderId": o.order_id,
|
|
"orderNo": o.order_no,
|
|
"amount": round(float(o.amount), 2),
|
|
"credits": round(float(o.credits), 2),
|
|
}
|
|
for o in orders
|
|
],
|
|
}
|