每天40分玩转Django:Django实战 - 在线打印服务系统

Django实战 - 在线打印服务系统

一、系统功能概览表

模块 主要功能 技术要点
文件上传 PDF/Word文件上传、文件验证 文件处理、MIME类型验证
异步处理 文件转换、打印队列 Celery、Redis
通知邮件 打印状态通知、订单确认 SMTP、邮件模板

二、系统架构设计

2.1 模型设计

python 复制代码
# models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import FileExtensionValidator

class PrintJob(models.Model):
    STATUS_CHOICES = (
        ('pending', '等待处理'),
        ('processing', '处理中'),
        ('completed', '已完成'),
        ('failed', '失败'),
    )
    
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    document = models.FileField(
        upload_to='documents/%Y/%m/%d/',
        validators=[FileExtensionValidator(allowed_extensions=['pdf', 'doc', 'docx'])]
    )
    copies = models.IntegerField(default=1)
    double_sided = models.BooleanField(default=True)
    color = models.BooleanField(default=False)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    notes = models.TextField(blank=True)
    page_count = models.IntegerField(null=True, blank=True)
    estimated_cost = models.DecimalField(max_digits=10, decimal_places=2, null=True)
    
    def __str__(self):
        return f"Print Job #{self.id} - {self.user.username}"

class PrintNotification(models.Model):
    print_job = models.ForeignKey(PrintJob, on_delete=models.CASCADE)
    sent_at = models.DateTimeField(auto_now_add=True)
    message = models.TextField()
    is_read = models.BooleanField(default=False)

2.2 文件上传处理

python 复制代码
# forms.py
from django import forms
from .models import PrintJob

class PrintJobForm(forms.ModelForm):
    class Meta:
        model = PrintJob
        fields = ['document', 'copies', 'double_sided', 'color', 'notes']
        
    def clean_document(self):
        document = self.cleaned_data.get('document')
        if document:
            if document.size > 20 * 1024 * 1024:  # 20MB限制
                raise forms.ValidationError('文件大小不能超过20MB')
        return document

# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import PrintJobForm
from .tasks import process_print_job

@login_required
def submit_print_job(request):
    if request.method == 'POST':
        form = PrintJobForm(request.POST, request.FILES)
        if form.is_valid():
            print_job = form.save(commit=False)
            print_job.user = request.user
            print_job.save()
            
            # 启动异步任务
            process_print_job.delay(print_job.id)
            
            messages.success(request, '打印任务已提交,请等待处理')
            return redirect('print_job_status', job_id=print_job.id)
    else:
        form = PrintJobForm()
    
    return render(request, 'print/submit.html', {'form': form})

2.3 Celery异步任务配置

python 复制代码
# celery.py
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

app = Celery('print_service')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

# tasks.py
from celery import shared_task
from django.core.mail import send_mail
from django.template.loader import render_to_string
from .models import PrintJob

@shared_task
def process_print_job(job_id):
    try:
        print_job = PrintJob.objects.get(id=job_id)
        print_job.status = 'processing'
        print_job.save()
        
        # 处理文件并计算页数
        page_count = calculate_page_count(print_job.document)
        print_job.page_count = page_count
        
        # 计算估计成本
        cost = calculate_cost(page_count, print_job.color, print_job.double_sided)
        print_job.estimated_cost = cost
        
        # 模拟打印过程
        import time
        time.sleep(5)  # 模拟打印时间
        
        print_job.status = 'completed'
        print_job.save()
        
        # 发送完成通知
        send_completion_email.delay(job_id)
        
    except Exception as e:
        print_job.status = 'failed'
        print_job.notes = str(e)
        print_job.save()
        
@shared_task
def send_completion_email(job_id):
    print_job = PrintJob.objects.get(id=job_id)
    context = {
        'job': print_job,
        'user': print_job.user,
    }
    
    html_message = render_to_string('print/email/completion.html', context)
    
    send_mail(
        subject='您的打印任务已完成',
        message=f'打印任务 #{print_job.id} 已完成,请及时取件。',
        html_message=html_message,
        from_email='printservice@example.com',
        recipient_list=[print_job.user.email],
        fail_silently=False,
    )

2.4 邮件模板设计

html 复制代码
<!-- templates/print/email/completion.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        .container { padding: 20px; }
        .header { color: #333; }
        .details { margin: 20px 0; }
        .footer { color: #666; font-size: 12px; }
    </style>
</head>
<body>
    <div class="container">
        <h2 class="header">打印任务完成通知</h2>
        <div class="details">
            <p>尊敬的 {{ user.username }}:</p>
            <p>您的打印任务已经完成,详情如下:</p>
            <ul>
                <li>任务编号:#{{ job.id }}</li>
                <li>页数:{{ job.page_count }}</li>
                <li>费用:¥{{ job.estimated_cost }}</li>
                <li>完成时间:{{ job.updated_at|date:"Y-m-d H:i" }}</li>
            </ul>
            <p>请尽快到打印点取件,谢谢!</p>
        </div>
        <div class="footer">
            <p>此邮件为系统自动发送,请勿回复。</p>
        </div>
    </div>
</body>
</html>

三、流程图设计

四、实用工具函数

python 复制代码
# utils.py
import os
import PyPDF2
from docx import Document

def calculate_page_count(document):
    """计算文档页数"""
    file_extension = os.path.splitext(document.name)[1].lower()
    
    if file_extension == '.pdf':
        with document.open('rb') as pdf_file:
            pdf_reader = PyPDF2.PdfReader(pdf_file)
            return len(pdf_reader.pages)
            
    elif file_extension in ['.doc', '.docx']:
        doc = Document(document)
        return len(doc.paragraphs) // 40  # 估算页数
    
    return 0

def calculate_cost(page_count, is_color, is_double_sided):
    """计算打印费用"""
    base_price = 0.5  # 单面黑白价格
    
    if is_color:
        base_price *= 3  # 彩印价格是黑白的3倍
    
    if is_double_sided:
        # 双面打印优惠10%
        total_pages = (page_count + 1) // 2
        return round(total_pages * base_price * 0.9, 2)
    
    return round(page_count * base_price, 2)

五、单元测试

python 复制代码
# tests.py
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from .models import PrintJob
from .tasks import process_print_job

class PrintServiceTests(TestCase):
    def setUp(self):
        self.client = Client()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 创建测试PDF文件
        self.test_file = SimpleUploadedFile(
            "test.pdf",
            b"file_content",
            content_type="application/pdf"
        )
        
    def test_print_job_creation(self):
        """测试打印任务创建"""
        self.client.login(username='testuser', password='testpass123')
        
        response = self.client.post('/print/submit/', {
            'document': self.test_file,
            'copies': 1,
            'double_sided': True,
            'color': False,
        })
        
        self.assertEqual(response.status_code, 302)
        self.assertEqual(PrintJob.objects.count(), 1)
        
    def test_email_notification(self):
        """测试邮件通知"""
        print_job = PrintJob.objects.create(
            user=self.user,
            document=self.test_file,
            status='processing'
        )
        
        process_print_job(print_job.id)
        
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].to[0], self.user.email)

六、部署配置

python 复制代码
# settings.py
# Celery配置
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Shanghai'

# 邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@example.com'
EMAIL_HOST_PASSWORD = 'your-email-password'

# 文件上传配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
FILE_UPLOAD_MAX_MEMORY_SIZE = 20 * 1024 * 1024  # 20MB
FILE_UPLOAD_PERMISSIONS = 0o644

七、安全考虑

  1. 文件上传安全:

    • 限制文件大小和类型
    • 使用安全的文件存储位置
    • 实施病毒扫描
  2. 用户认证:

    • 要求用户登录
    • 实施访问控制
    • 防止未授权访问
  3. 异步任务安全:

    • 任务队列加密
    • 错误处理和重试机制
    • 监控和日志记录

八、性能优化建议

  1. 文件处理优化:

    • 使用分块上传
    • 实现断点续传
    • 文件压缩处理
  2. 任务队列优化:

    • 合理设置并发数
    • 实现任务优先级
    • 添加任务超时机制
  3. 邮件发送优化:

    • 使用邮件队列
    • 批量处理通知
    • 模板预编译

九、后续扩展建议

  1. 功能扩展:

    • 添加打印预览
    • 支持更多文件格式
    • 实现打印参数配置
  2. 用户体验提升:

    • 添加进度条显示
    • 实时状态更新
    • 打印历史记录
  3. 管理功能:

    • 打印机管理
    • 耗材管理
    • 统计报表

本节课程介绍了如何使用Django框架构建一个在线打印服务系统,包括文件上传、异步处理和邮件通知等核心功能。建议学习者在理解基础代码的同时,注意系统的安全性和性能优化,并根据实际需求进行功能扩展。


相关推荐
HsuHeinrich41 分钟前
流程图(四)利用python绘制漏斗图
python·数据可视化
tjjingpan1 小时前
HCIA-Access V2.5_8_1_EPON原理_PON基本概念
网络
hc_bmxxf1 小时前
【无标题】
网络
BinaryBardC2 小时前
Bash语言的数据类型
开发语言·后端·golang
码农丁丁2 小时前
[python3]Excel解析库-xlwt
python·excel·xlwt
reasonsummer2 小时前
【办公类-47-02】20250103 课题资料快速打印(单个docx转PDF,多个pdf合并一个PDF 打印)
python·pdf
Pandaconda2 小时前
【Golang 面试题】每日 3 题(二十一)
开发语言·笔记·后端·面试·职场和发展·golang·go
想要入门的程序猿3 小时前
Qt菜单栏、工具栏、状态栏(右键)
开发语言·数据库·qt
_院长大人_3 小时前
使用 Spring Boot 实现钉钉消息发送消息
spring boot·后端·钉钉
键盘上的蚂蚁-3 小时前
Python 语言结合 Flask 框架来实现一个基础的代购商品管理
jvm·数据库·oracle