refactor: 优化API路由和响应模型 feat(admin): 添加App用户管理接口 feat(sms): 实现阿里云短信服务集成 feat(email): 添加SMTP邮件发送功能 feat(upload): 支持文件上传接口 feat(rate-limiter): 实现手机号限流器 fix: 修复计算步骤入库问题 docs: 更新API文档和测试计划 chore: 更新依赖和配置
104 lines
3.9 KiB
Python
104 lines
3.9 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.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),
|
|
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,
|
|
)
|
|
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 "发送失败") |