每天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框架构建一个在线打印服务系统,包括文件上传、异步处理和邮件通知等核心功能。建议学习者在理解基础代码的同时,注意系统的安全性和性能优化,并根据实际需求进行功能扩展。


相关推荐
A-刘晨阳17 小时前
AI原生时序数据库选型指南:从数据存储到智能决策的范式跃迁
数据库·时序数据库·ai-native
whinc18 小时前
Rust技术周刊 2026年第17周
后端·rust
whinc18 小时前
Rust技术周刊 2026年第18周
后端·rust
辉视广播对讲18 小时前
医院IPTV,让医疗服务更有温度
网络·人工智能
whinc18 小时前
Rust技术周刊 2026年第16周
后端·rust
HalvmånEver18 小时前
MySQL的增删改查命令合集合集
数据库·sql·oracle
Tim风声(网络工程师)18 小时前
光功率计中的红光(光衰测试设备)的使用
运维·网络
jieyucx18 小时前
Go语言深度解剖:Map扩容机制全解析(增量扩容+等量扩容+渐进式迁移)
开发语言·后端·golang·map·扩容策略
不剪发的Tony老师18 小时前
dblab:一款基于终端的交互式数据库客户端
数据库·sql
YJlio18 小时前
7.4.5 Windows 11 企业网络连接与网络重置实战:远程访问、本地策略与故障恢复
前端·chrome·windows·python·edge·机器人·django