Update project 11 FRONT MVP phase 2 complete

This commit is contained in:
2026-03-06 16:22:41 +00:00
parent d19660b50c
commit 08c5a8387f
6 changed files with 76 additions and 29 deletions

View File

@@ -0,0 +1,53 @@
import io
import qrcode
from reportlab.lib.pagesizes import landscape, A5
from reportlab.pdfgen import canvas
from reportlab.lib import colors
from reportlab.lib.utils import ImageReader
def generate_qr_ticket(ticket_id: int, user_id: int, seat_id: int = 0) -> bytes:
buffer = io.BytesIO()
width, height = landscape(A5)
c = canvas.Canvas(buffer, pagesize=(width, height))
# Темный фон (под дизайн фронтенда)
c.setFillColor(colors.HexColor("#121212"))
c.rect(0, 0, width, height, fill=1, stroke=0)
# Белая карточка билета
margin = 20
c.setFillColor(colors.white)
c.roundRect(margin, margin, width - 2*margin, height - 2*margin, 10, fill=1, stroke=0)
# Текст
c.setFillColor(colors.black)
c.setFont("Helvetica-Bold", 28)
c.drawString(margin + 30, height - margin - 50, "EVENT TICKET")
c.setFont("Helvetica", 14)
c.drawString(margin + 30, height - margin - 100, f"Ticket ID: {ticket_id}")
c.drawString(margin + 30, height - margin - 130, f"User ID: {user_id}")
if seat_id:
c.drawString(margin + 30, height - margin - 160, f"Seat ID: {seat_id}")
c.setFont("Helvetica-Oblique", 10)
c.setFillColor(colors.gray)
c.drawString(margin + 30, margin + 30, "Scan this QR code at the entrance.")
# QR-код
qr = qrcode.QRCode(box_size=5, border=1)
qr.add_data(f"TICKET:{ticket_id}|USER:{user_id}|SEAT:{seat_id}")
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# Буферизация картинки для ReportLab
img_buffer = io.BytesIO()
img.save(img_buffer, format="PNG")
img_buffer.seek(0)
c.drawImage(ImageReader(img_buffer), width - margin - 160, margin + 30, width=130, height=130)
c.showPage()
c.save()
buffer.seek(0)
return buffer.read()

View File

@@ -12,3 +12,4 @@ aio-pika
reportlab
aioboto3
pydantic[email]>=2.5.0
qrcode[pil]==7.4.2

View File

@@ -1,22 +1,20 @@
"""
Standalone worker: слушает очередь RabbitMQ 'ticket_events',
генерирует PDF-билет через reportlab, загружает в MinIO,
генерирует PDF-билет через core.pdf_generator, загружает в MinIO,
сохраняет pdf_url в PostgreSQL.
"""
import asyncio
import io
import json
import logging
import os
from typing import Any
import aio_pika
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from core.minio import ensure_bucket_exists, upload_pdf
from core.pdf_generator import generate_qr_ticket
from database.models import Ticket
logging.basicConfig(
@@ -33,27 +31,6 @@ DATABASE_URL: str = os.getenv(
QUEUE_NAME: str = "ticket_events"
def _build_pdf(ticket_id: int, seat_id: int, user_id: int) -> bytes:
"""Генерирует PDF-билет в памяти и возвращает байты."""
buffer = io.BytesIO()
pdf = canvas.Canvas(buffer, pagesize=A4)
width, height = A4
pdf.setFont("Helvetica-Bold", 24)
pdf.drawCentredString(width / 2, height - 80, "TICKET")
pdf.setFont("Helvetica", 14)
pdf.drawCentredString(width / 2, height - 130, f"Ticket ID: {ticket_id}")
pdf.drawCentredString(width / 2, height - 160, f"Seat ID: {seat_id}")
pdf.drawCentredString(width / 2, height - 190, f"User ID: {user_id}")
pdf.setFont("Helvetica-Oblique", 10)
pdf.drawCentredString(width / 2, 40, "Thank you for your purchase!")
pdf.save()
return buffer.getvalue()
async def _handle_ticket_paid(
payload: dict[str, Any],
db_session: AsyncSession,
@@ -75,10 +52,10 @@ async def _handle_ticket_paid(
return
log.info("Generating PDF for ticket %s", ticket_id)
pdf_bytes = _build_pdf(
pdf_bytes = generate_qr_ticket(
ticket_id=ticket.id,
seat_id=ticket.seat_id,
user_id=ticket.user_id or 0,
seat_id=ticket.seat_id,
)
object_name = f"tickets/ticket_{ticket_id}.pdf"