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

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

一、系统功能概览表

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

二、系统架构设计

2.1 模型设计

# 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 文件上传处理

# 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异步任务配置

# 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 邮件模板设计

<!-- 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>

三、流程图设计

在这里插入图片描述

四、实用工具函数

# 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)

五、单元测试

# 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)

六、部署配置

# 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/948680.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WPS计算机二级•数据查找分析

听说这里是目录哦 通配符&#x1f30c;问号&#xff08;?&#xff09;星号&#xff08;*&#xff09;波形符&#xff08;~&#xff09; 排序&#x1f320;数字按大小排序以当前选定区域排序以扩展选定区域排序 文字按首字母排序 快速筛选分类数据☄️文字筛选数字筛选颜色筛选…

基于海思soc的智能产品开发(camera sensor的两种接口)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于嵌入式开发设备来说&#xff0c;除了图像显示&#xff0c;图像输入也是很重要的一部分。说到图像输入&#xff0c;就不得不提到camera。目前ca…

网安入门之MySQL后端基础

数据库 (Database) 数据库是指长期存储在计算机中的&#xff0c;有组织、可共享的数据集合。它通过表、列、行等结构来组织数据&#xff0c;目的是使数据可以高效存储、检索和管理。数据库通常包括多个表&#xff0c;每个表存储与特定主题或对象相关的数据 数据库管理系统 (D…

概率基本概念 --- 离散型随机变量实例

条件概率&独立事件 随机变量 - 离散型随机变量 - 非离散型随机变量 连续型随机变量奇异性型随机变量 概率表示 概率分布函数概率密度函数概率质量函数全概率公式贝叶斯公式 概率计算 数学期望方差协方差 计算实例 假设有两个离散型随机变量X和Y&#xff0c;它们代…

w139华强北商城二手手机管理系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

LLM大语言模型中RAG切片阶段改进策略

切片方法的特点和示例&#xff1a; Token 切片 适合对 Token 数量有严格要求的场景&#xff0c;比如使用上下文长度较小的模型时。 示例文本&#xff1a; “LlamaIndex是一个强大的RAG框架。它提供了多种文档处理方式。用可以根据需选择合适的方法。” 使用Token切片(chunk…

Earth靶场

打开靶机后使用 arp-scan -l 查询靶机 ip 我们使用 nmap 进行 dns 解析 把这两条解析添加到hosts文件中去&#xff0c;这样我们才可以访问页面 这样网站就可以正常打开 扫描ip时候我们发现443是打开的&#xff0c;扫描第二个dns解析的443端口能扫描出来一个 txt 文件 dirsear…

Kafka 消费者专题

目录 消费者消费者组消费方式消费规则独立消费主题代码示例&#xff08;极简&#xff09;代码示例&#xff08;独立消费分区&#xff09; offset自动提交代码示例&#xff08;自动提交&#xff09;手动提交代码示例&#xff08;同步&#xff09;代码示例&#xff08;异步&#…

重庆大学软件工程复试怎么准备?

重大软件复试相对来说不算刁钻&#xff0c;关键是对自己的竞赛和项目足够了解&#xff0c;能应对老师的提问。专业课范围广&#xff0c;英文文献看个人水平&#xff0c;难度不算大&#xff0c;整体只要表现得得体从容&#xff0c;以及充分的准备&#xff0c;老师不会为难你。 …

Hadoop•FinalShell连接VMware免密登录

听说这是目录哦 FinalShell连接VMware&#x1f324;️解决重连失效FinalShell的使用 免密登录⛈️能量站&#x1f61a; FinalShell连接VMware&#x1f324;️ 保持虚拟机的开机状态&#xff0c;打开FinalShell&#xff0c;如果虚拟机关机或者挂起&#xff0c;连接就会断开。 …

List-顺序表--2

目录 1、ArrayList 2、ArrayList构造方法 3、ArrayList常见方法 4、ArrayList的遍历 5、ArrayList的扩容机制 6、ArrayList的具体使用 6.1、杨辉三角 6.2、简单的洗牌算法 1、ArrayList 在集合框架中&#xff0c;ArrayList 是一个普通的类&#xff0c;实现了 List 接口…

通过串口通信控制led灯的亮灭

初始化led灯的gpio接口控制灯的亮灭 初始化uart1串口 将gpio9和gpio10设置为复用模式进行串口通信 通过串口的输入输出函数实现串口通信控制led灯的亮灭

git知识点汇总

git init 初始化一个git仓库&#xff0c;后面可以加仓库名&#xff0c;在当前目录下创建指定名称的目录并在该目录下创建仓库&#xff0c;若不加则直接在当前目录下创建仓库。git仓库的三个区域&#xff1a;工作区&#xff08;当前目录&#xff09;、暂存区&#xff08;.git/in…

电子电气架构 --- 中央HPC架构

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…

让 Agent 具备语音交互能力:技术突破与应用前景(16/30)

让 Agent 具备语音交互能力&#xff1a;技术突破与应用前景 一、引言 在当今数字化时代&#xff0c;人机交互方式正经历着深刻的变革。从早期的命令行界面到图形用户界面&#xff0c;再到如今日益普及的语音交互&#xff0c;人们对于与机器沟通的便捷性和自然性有了更高的追求…

L27.【LeetCode笔记】2 的幂(五种解法)

目录 1.题目 2.自解 方法1:调用log函数 代码 提交结果 方法2:循环 提交结果 3.优解 方法3:位运算n & (n-1) 0 代码 提交结果 方法4:位运算lowbit 代码 提交结果 4.投机取巧的方法 代码 提交结果 1.题目 https://leetcode.cn/problems/power-of-two/?env…

第0章 机器人及自动驾驶SLAM定位方法全解析及入门进阶学习建议

嗨&#xff0c;各位同学大家好&#xff01;笔者自985硕士毕业后&#xff0c;在机器人算法领域已经深耕 7 年多啦。这段时间里&#xff0c;我积累了不少宝贵经验。本专栏《机器人工程师带你从零入门SLAM》将结合下面的SLAM知识体系思维导图及多年的工作实战总结&#xff0c;将逐…

密码学原理技术-第十一章-Hash Functions

文章目录 总结Why we need hash functionsDigital Signature with a Hash FunctionBasic Protocol for Digital Signatures with a Hash FunctionPrincipal input–output behavior of hash functions Security propertiesThe three security requirements of hash functionsWh…

Docker 远程访问完整配置教程以及核心参数理解

Docker 远程访问完整配置教程 以下是配置 Docker 支持远程访问的完整教程&#xff0c;包括参数说明、配置修改、云服务器安全组设置、主机防火墙配置&#xff0c;以及验证远程访问的详细步骤。 1. 理解 -H fd:// 参数的作用&#xff08;理解了以后容易理解后面的操作&#xff…

单元测试3.0+ @RunWith(JMockit.class)+mock+injectable+Expectations

Jmockit使用笔记_基本功能使用Tested_Injectable_Mocked_Expectations_jmockit.class-CSDN博客 静态变量直接赋值就好&#xff0c;没必要mock了 测试框架Jmockit集合junit使用 RunWith(JMockit.class) 写在测试案例类上的注解 Tested 在测试案例中,写在我们要测试的类上…