guzhi/app/api/v1/transactions/transactions.py

164 lines
6.0 KiB
Python

from fastapi import APIRouter, Query, UploadFile, File, HTTPException
from typing import Optional
from app.schemas.base import Success, SuccessExtra, PageResponse, BasicResponse
from app.schemas.invoice import PaymentReceiptOut
from app.controllers.invoice import invoice_controller
from app.models.invoice import PaymentReceipt
from fastapi import Body
from app.schemas.transactions import SendEmailRequest, SendEmailResponse
from app.services.email_client import email_client
from app.models.invoice import EmailSendLog
from app.settings.config import settings
from app.log.log import logger
import httpx
transactions_router = APIRouter(tags=["交易管理"])
@transactions_router.get("/receipts", summary="对公转账记录列表", response_model=PageResponse[PaymentReceiptOut])
async def list_receipts(
phone: Optional[str] = Query(None),
wechat: Optional[str] = Query(None),
company_name: Optional[str] = Query(None),
tax_number: Optional[str] = Query(None),
status: Optional[str] = Query(None),
ticket_type: Optional[str] = Query(None),
invoice_type: Optional[str] = Query(None),
created_at: Optional[list[int]] = Query(None),
submitted_start: Optional[str] = Query(None),
submitted_end: Optional[str] = Query(None),
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100),
):
"""
对公转账记录列表(含提交时间、凭证与关联企业信息)
"""
result = await invoice_controller.list_receipts(
page=page,
page_size=page_size,
phone=phone,
wechat=wechat,
company_name=company_name,
tax_number=tax_number,
status=status,
ticket_type=ticket_type,
invoice_type=invoice_type,
created_at=created_at,
submitted_start=submitted_start,
submitted_end=submitted_end,
)
return SuccessExtra(
data=result["items"],
total=result["total"],
page=result["page"],
page_size=result["page_size"],
msg="获取成功",
)
@transactions_router.get("/receipts/{id}", summary="对公转账记录详情", response_model=BasicResponse[PaymentReceiptOut])
async def get_receipt_detail(id: int):
"""
对公转账记录详情
"""
data = await invoice_controller.get_receipt_by_id(id)
return Success(data=data or {}, msg="获取成功" if data else "未找到")
@transactions_router.post("/send-email", summary="发送邮件", response_model=BasicResponse[SendEmailResponse])
async def send_email(payload: SendEmailRequest = Body(...)):
attachments = []
urls = []
try:
domain = payload.email.split("@")[-1]
import dns.resolver
try:
dns.resolver.resolve(domain, "MX")
except Exception:
dns.resolver.resolve(domain, "A")
except Exception:
raise HTTPException(status_code=400, detail="收件方地址域名不可用或未正确解析")
if payload.file_urls:
urls.extend([u.strip().strip('`') for u in payload.file_urls if isinstance(u, str)])
if urls:
try:
async with httpx.AsyncClient(timeout=10) as client:
for u in urls:
r = await client.get(u)
r.raise_for_status()
attachments.append((r.content, u.split("/")[-1]))
except Exception as e:
raise HTTPException(status_code=400, detail=f"附件下载失败: {e}")
logger.info("transactions.email_send_start email={} subject={}", payload.email, payload.subject or "")
try:
result = email_client.send_many(payload.email, payload.subject, payload.body, attachments)
except RuntimeError as e:
result = {"status": "FAIL", "error": str(e)}
except Exception as e:
result = {"status": "FAIL", "error": str(e)}
body_summary = payload.body
status = result.get("status")
error = result.get("error")
first_name = attachments[0][1] if attachments else None
first_url = urls[0] if urls else None
log = await EmailSendLog.create(
email=payload.email,
subject=payload.subject,
body_summary=body_summary,
file_name=first_name,
file_url=first_url,
status=status,
error=error,
)
if status == "OK":
logger.info("transactions.email_send_ok email={}", payload.email)
else:
logger.error("transactions.email_send_fail email={} err={}", payload.email, error)
if status == "OK" and payload.receipt_id:
try:
r = await PaymentReceipt.filter(id=payload.receipt_id).first()
if r:
r.extra = (r.extra or {}) | payload.model_dump()
await r.save()
try:
inv = await r.invoice
if inv:
inv.status = "invoiced"
await inv.save()
logger.info("transactions.invoice_mark_invoiced receipt_id={} invoice_id={}", payload.receipt_id, inv.id)
except Exception as e2:
logger.warning("transactions.invoice_mark_invoiced_fail receipt_id={} err={}", payload.receipt_id, str(e2))
except Exception as e:
logger.error("transactions.email_extra_save_fail id={} err={}", payload.receipt_id, str(e))
return Success(data={"status": status, "log_id": log.id, "error": error}, msg="发送成功" if status == "OK" else "发送失败")
@transactions_router.get("/smtp-config", summary="SMTP配置状态", response_model=BasicResponse[dict])
async def smtp_config_status():
configured = all([
settings.SMTP_HOST,
settings.SMTP_PORT,
settings.SMTP_FROM,
settings.SMTP_USERNAME,
settings.SMTP_PASSWORD,
])
data = {
"host": bool(settings.SMTP_HOST),
"port": bool(settings.SMTP_PORT),
"from": bool(settings.SMTP_FROM),
"username": bool(settings.SMTP_USERNAME),
"password": bool(settings.SMTP_PASSWORD),
"tls": settings.SMTP_TLS,
"configured": configured,
}
return Success(data=data, msg="OK")