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.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(data: SendEmailRequest, file: Optional[UploadFile] = File(None)): if not data.email or "@" not in data.email: raise HTTPException(status_code=422, detail="邮箱格式不正确") if not data.body: raise HTTPException(status_code=422, detail="文案内容不能为空") file_bytes = None file_name = None if file is not None: file_bytes = await file.read() file_name = file.filename elif data.file_url: try: async with httpx.AsyncClient(timeout=10) as client: r = await client.get(data.file_url) r.raise_for_status() file_bytes = r.content file_name = data.file_url.split("/")[-1] except Exception as e: raise HTTPException(status_code=400, detail=f"附件下载失败: {e}") logger.info("transactions.email_send_start email={} subject={}", data.email, data.subject or "") result = email_client.send(data.email, data.subject, data.body, file_bytes, file_name, getattr(file, "content_type", None)) body_summary = data.body[:500] status = result.get("status") error = result.get("error") log = await EmailSendLog.create( email=data.email, subject=data.subject, body_summary=body_summary, file_name=file_name, file_url=data.file_url, status=status, error=error, ) if status == "OK": logger.info("transactions.email_send_ok email={}", data.email) else: logger.error("transactions.email_send_fail email={} err={}", data.email, error) 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")