import io import urllib.parse from urllib.parse import urlparse from django.http import JsonResponse from django.utils import timezone from django.conf import settings from django.http import FileResponse, HttpResponseBadRequest from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt from playwright.sync_api import sync_playwright from django.core.mail import EmailMultiAlternatives from pdf2image import convert_from_bytes import base64 from io import BytesIO charts_map = { "UserActivity": { "title": "用户活动", "path": "/ui/#/reports/users/user-activity" } } def export_chart_to_pdf(chart_name, sessionid, request=None): chart_info = charts_map.get(chart_name) if not chart_info: return None, None if request: url = request.build_absolute_uri(urllib.parse.unquote(chart_info['path'])) else: url = urllib.parse.unquote(chart_info['path']) if settings.DEBUG_DEV: url = url.replace(":8080", ":9528") print("Url: ", url) with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context(viewport={"width": 1000, "height": 800}) # 设置 sessionid cookie parsed_url = urlparse(url) context.add_cookies([ { 'name': settings.SESSION_COOKIE_NAME, 'value': sessionid, 'domain': parsed_url.hostname, 'path': '/', 'httpOnly': True, 'secure': False, # 如有 https 可改 True } ]) page = context.new_page() try: page.goto(url, wait_until='networkidle') pdf_bytes = page.pdf(format="A4", landscape=True, margin={"top": "35px", "bottom": "30px", "left": "20px", "right": "20px"}) except Exception as e: print(f'Playwright error: {e}') pdf_bytes = None finally: browser.close() return pdf_bytes, chart_info['title'] @method_decorator(csrf_exempt, name='dispatch') class ExportPdfView(View): def get(self, request): chart_name = request.GET.get('chart') return self._handle_export(request, chart_name) def post(self, request): chart_name = request.POST.get('chart') return self._handle_export(request, chart_name) def _handle_export(self, request, chart_name): if not chart_name: return HttpResponseBadRequest('Missing chart parameter') sessionid = request.COOKIES.get(settings.SESSION_COOKIE_NAME) if not sessionid: return HttpResponseBadRequest('No sessionid found in cookies') pdf_bytes, title = export_chart_to_pdf(chart_name, sessionid, request=request) if not pdf_bytes: return HttpResponseBadRequest('Failed to generate PDF') filename = f"{title}-{timezone.now().strftime('%Y%m%d%H%M%S')}.pdf" response = FileResponse(io.BytesIO(pdf_bytes), as_attachment=True, filename=filename, content_type='application/pdf') return response class SendMailView(View): def get(self, request): chart_name = request.GET.get('chart') email = "ibuler@qq.com" if not chart_name or not email: return HttpResponseBadRequest('Missing chart or email parameter') sessionid = request.COOKIES.get(settings.SESSION_COOKIE_NAME) if not sessionid: return HttpResponseBadRequest('No sessionid found in cookies') # 1. 生成 PDF pdf_bytes, title = export_chart_to_pdf(chart_name, sessionid, request=request) if not pdf_bytes: return HttpResponseBadRequest('Failed to generate PDF') # 2. PDF 转图片 images = convert_from_bytes(pdf_bytes, dpi=200) # 3. 图片转 base64 img_tags = [] for img in images: buffer = BytesIO() img.save(buffer, format="PNG") encoded = base64.b64encode(buffer.getvalue()).decode("utf-8") img_tags.append(f'') html_content = "
".join(img_tags) # 4. 发送邮件 subject = f"{title} 报表" from_email = settings.EMAIL_HOST_USER to = [email] msg = EmailMultiAlternatives(subject, '', from_email, to) msg.attach_alternative(html_content, "text/html") filename = f"{title}-{timezone.now().strftime('%Y%m%d%H%M%S')}.pdf" msg.attach(filename, pdf_bytes, "application/pdf") msg.send() return JsonResponse({"message": "邮件发送成功"})