校园二手物品交易平台(Python+Flask)设计与实现

校园二手物品交易平台(Python+Flask)设计与实现

校园二手物品交易平台(Python+Flask)设计与实现

一、系统文档

1. 需求分析

1.1 功能需求
角色 核心功能 具体描述
学生用户 账号管理 注册(需校园邮箱验证)、登录、个人信息维护、密码修改
学生用户 商品管理 发布二手商品(含分类、描述、价格、图片)、编辑商品、下架商品
学生用户 交易功能 浏览/搜索商品、加入收藏、私信沟通、下单购买、交易评价
学生用户 订单管理 查看我的订单(待付款/待发货/已完成)、取消订单、确认收货
管理员 用户管理 查看用户列表、禁用违规账号、处理用户举报
管理员 商品管理 审核商品(违规商品下架)、管理商品分类、清理过期商品
管理员 数据统计 交易成交量、活跃用户数、热门商品分类等数据可视化统计
1.2 非功能需求
  • 性能:页面响应时间≤3秒,支持1000用户同时在线
  • 安全性:用户密码加密存储,防止XSS和CSRF攻击,敏感操作需二次验证
  • 可用性:支持PC端和移动端访问,界面简洁易用
  • 可靠性:数据定期备份,保证交易记录不丢失

2. 系统设计

2.1 技术栈
  • 后端:Python 3.9 + Flask 2.0(轻量级Web框架)
  • 前端:HTML5 + CSS3(Tailwind CSS) + JavaScript(Vue.js)
  • 数据库:SQLite(开发环境)/ PostgreSQL(生产环境)
  • 其他工具:Redis(缓存热门商品)、JWT(身份认证)、Pillow(图片处理)
2.2 数据库设计
  • User(用户表):id(主键)、username(用户名)、email(校园邮箱)、password(加密密码)、avatar(头像)、is_active(是否激活)、role(角色)、created_at(注册时间)
  • Category(商品分类表):id(主键)、name(分类名称)、description(分类描述)、parent_id(父分类,用于多级分类)
  • Product(商品表):id(主键)、title(标题)、description(描述)、price(价格)、images(图片路径)、status(状态)、category_id(分类)、user_id(发布者)、created_at(发布时间)
  • Order(订单表):id(主键)、product_id(商品)、buyer_id(买家)、seller_id(卖家)、status(状态)、price(交易价格)、created_at(创建时间)
  • Message(消息表):id(主键)、sender_id(发送者)、receiver_id(接收者)、content(内容)、is_read(是否已读)、created_at(发送时间)
  • Favorite(收藏表):id(主键)、user_id(用户)、product_id(商品)、created_at(收藏时间)
  • Review(评价表):id(主键)、order_id(订单)、user_id(评价者)、rating(评分)、content(内容)、created_at(评价时间)
2.3 系统架构
校园二手物品交易平台
├── 表现层(Web界面)
│   ├── 用户前台(商品浏览、交易)
│   └── 管理后台(用户/商品管理)
├── 应用层(Flask核心)
│   ├── 蓝图模块(用户/商品/订单等)
│   ├── 视图函数(处理HTTP请求)
│   ├── 表单验证(数据合法性校验)
│   └── 权限控制(JWT认证)
├── 业务逻辑层
│   ├── 用户服务(注册/登录/信息管理)
│   ├── 商品服务(发布/编辑/搜索)
│   ├── 订单服务(创建/支付/完成)
│   └── 消息服务(私信/通知)
└── 数据层
    ├── 数据库(存储业务数据)
    └── 缓存(Redis缓存热门数据)

3. 系统测试与部署

3.1 测试用例(核心功能)
测试模块 测试步骤 预期结果
商品发布 1. 用户登录
2. 点击"发布商品"
3. 填写信息并上传图片
商品发布成功,状态为"待审核"
商品购买 1. 浏览商品详情
2. 点击"立即购买"
3. 确认订单信息
订单创建成功,商品状态变为"已售出"
私信沟通 1. 进入商品详情页
2. 点击"联系卖家"
3. 发送咨询消息
卖家收到消息提醒,消息内容正确
3.2 部署步骤
  1. 安装依赖:pip install flask flask-sqlalchemy flask-jwt-extended redis pillow
  2. 配置环境变量:export FLASK_APP=app.py FLASK_ENV=production
  3. 初始化数据库:flask db init && flask db migrate && flask db upgrade
  4. 启动Redis服务:redis-server --daemonize yes
  5. 启动应用:gunicorn -w 4 -b 0.0.0.0:8000 app:app
  6. 访问地址:http://localhost:8000

二、核心代码实现

1. 项目结构

campus_trading/
├── app.py               # 应用入口
├── config.py            # 配置文件
├── models/              # 数据模型
│   ├── __init__.py
│   ├── user.py
│   ├── product.py
│   └── order.py
├── routes/              # 路由蓝图
│   ├── __init__.py
│   ├── auth.py          # 认证相关
│   ├── products.py      # 商品相关
│   └── orders.py        # 订单相关
├── services/            # 业务逻辑
│   ├── __init__.py
│   ├── product_service.py
│   └── order_service.py
├── static/              # 静态文件
│   ├── images/
│   ├── css/
│   └── js/
└── templates/           # 模板文件
    ├── index.html
    ├── product_detail.html
    └── user/

2. 数据模型(models/product.py)

from datetime import datetime
from extensions import db

class Product(db.Model):
    """商品模型"""
    __tablename__ = 'products'
    
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text, nullable=False)
    price = db.Column(db.Float, nullable=False)
    images = db.Column(db.JSON)  # 存储图片路径列表
    status = db.Column(db.String(20), default='pending')  # pending/rejected/active/sold
    views = db.Column(db.Integer, default=0)  # 浏览量
    
    # 外键关联
    category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    
    # 关系
    category = db.relationship('Category', backref='products')
    user = db.relationship('User', backref='products')
    orders = db.relationship('Order', backref='product', lazy='dynamic')
    favorites = db.relationship('Favorite', backref='product', lazy='dynamic')
    
    created_at = db.Column(db.DateTime, default=datetime.ut***ow)
    updated_at = db.Column(db.DateTime, default=datetime.ut***ow, onupdate=datetime.ut***ow)
    
    def __repr__(self):
        return f'<Product {self.title}>'
    
    def to_dict(self):
        """转换为字典,用于API返回"""
        return {
            'id': self.id,
            'title': self.title,
            'description': self.description,
            'price': self.price,
            'images': self.images,
            'status': self.status,
            'views': self.views,
            'category': self.category.name if self.category else None,
            'user': {
                'id': self.user.id,
                'username': self.user.username,
                'avatar': self.user.avatar
            },
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S')
        }

3. 商品相关路由(routes/products.py)

from flask import Blueprint, request, jsonify, render_template, redirect, url_for
from flask_jwt_extended import jwt_required, get_jwt_identity
from models.product import Product
from models.category import Category
from models.favorite import Favorite
from services.product_service import create_product, get_product_detail, search_products
from extensions import db
from forms import ProductForm
import os
from utils import save_images

products_bp = Blueprint('products', __name__, url_prefix='/products')

@products_bp.route('/')
def product_list():
    """商品列表页,支持分页和筛选"""
    page = request.args.get('page', 1, type=int)
    per_page = 12
    category_id = request.args.get('category', type=int)
    keyword = request.args.get('keyword', '')
    min_price = request.args.get('min_price', type=float)
    max_price = request.args.get('max_price', type=float)
    
    # 搜索和筛选商品
    pagination = search_products(
        page=page,
        per_page=per_page,
        category_id=category_id,
        keyword=keyword,
        min_price=min_price,
        max_price=max_price
    )
    
    categories = Category.query.all()
    
    return render_template('products/list.html',
                           products=pagination.items,
                           pagination=pagination,
                           categories=categories,
                           keyword=keyword,
                           category_id=category_id,
                           min_price=min_price,
                           max_price=max_price)

@products_bp.route('/<int:product_id>')
def product_detail(product_id):
    """商品详情页"""
    product = get_product_detail(product_id)
    if not product:
        return render_template('404.html'), 404
    
    # 增加浏览量
    product.views += 1
    db.session.***mit()
    
    # 相关推荐(同分类商品)
    related_products = Product.query.filter(
        Product.category_id == product.category_id,
        Product.id != product_id,
        Product.status == 'active'
    ).limit(4).all()
    
    return render_template('products/detail.html',
                           product=product,
                           related_products=related_products)

@products_bp.route('/create', methods=['GET', 'POST'])
@jwt_required()
def create_product_page():
    """发布商品页面"""
    form = ProductForm()
    form.category_id.choices = [(c.id, c.name) for c in Category.query.all()]
    
    if form.validate_on_submit():
        user_id = get_jwt_identity()
        
        # 处理上传的图片
        images = request.files.getlist('images')
        image_paths = save_images(images)
        
        # 创建商品
        product = create_product(
            title=form.title.data,
            description=form.description.data,
            price=form.price.data,
            category_id=form.category_id.data,
            user_id=user_id,
            images=image_paths
        )
        
        return redirect(url_for('products.product_detail', product_id=product.id))
    
    return render_template('products/create.html', form=form)

@products_bp.route('/<int:product_id>/favorite', methods=['POST'])
@jwt_required()
def toggle_favorite(product_id):
    """收藏/取消收藏商品"""
    user_id = get_jwt_identity()
    product = Product.query.get_or_404(product_id)
    
    favorite = Favorite.query.filter_by(user_id=user_id, product_id=product_id).first()
    
    if favorite:
        # 取消收藏
        db.session.delete(favorite)
        db.session.***mit()
        return jsonify({'status': 'su***ess', 'action': 'unfavorite'})
    else:
        # 收藏商品
        favorite = Favorite(user_id=user_id, product_id=product_id)
        db.session.add(favorite)
        db.session.***mit()
        return jsonify({'status': 'su***ess', 'action': 'favorite'})

4. 订单服务(services/order_service.py)

from models.order import Order
from models.product import Product
from models.user import User
from models.message import Message
from extensions import db
from datetime import datetime
from sqlalchemy.exc import SQLAlchemyError

def create_order(product_id, buyer_id):
    """创建订单"""
    try:
        # 获取商品和卖家信息
        product = Product.query.get(product_id)
        if not product:
            return {'status': 'error', 'message': '商品不存在'}
            
        # 检查商品状态
        if product.status != 'active':
            return {'status': 'error', 'message': '商品无法购买'}
            
        # 检查买家是否为商品发布者
        if product.user_id == buyer_id:
            return {'status': 'error', 'message': '不能购买自己发布的商品'}
            
        # 创建订单
        order = Order(
            product_id=product_id,
            buyer_id=buyer_id,
            seller_id=product.user_id,
            price=product.price,
            status='pending'  # 待付款
        )
        
        # 更新商品状态为已售出
        product.status = 'sold'
        
        # 创建通知消息给卖家
        message = Message(
            sender_id=buyer_id,
            receiver_id=product.user_id,
            content=f'有用户购买了你的商品《{product.title}》,请及时处理订单'
        )
        
        db.session.add(order)
        db.session.add(message)
        db.session.***mit()
        
        return {'status': 'su***ess', 'order_id': order.id}
        
    except SQLAlchemyError as e:
        db.session.rollback()
        return {'status': 'error', 'message': str(e)}

def update_order_status(order_id, user_id, new_status):
    """更新订单状态"""
    order = Order.query.get(order_id)
    if not order:
        return {'status': 'error', 'message': '订单不存在'}
        
    # 验证权限(只能是买家或卖家操作)
    if order.buyer_id != user_id and order.seller_id != user_id:
        return {'status': 'error', 'message': '没有操作权限'}
        
    # 检查状态流转是否合法
    valid_transitions = {
        'pending': ['paid', 'cancelled'],
        'paid': ['shipped', 'refunded'],
        'shipped': ['***pleted', 'returned'],
        '***pleted': [],
        'cancelled': [],
        'refunded': [],
        'returned': []
    }
    
    if new_status not in valid_transitions[order.status]:
        return {'status': 'error', 'message': '不合法的状态转换'}
        
    # 更新订单状态
    order.status = new_status
    order.updated_at = datetime.ut***ow()
    
    # 如果订单完成,创建评价提醒消息
    if new_status == '***pleted':
        message = Message(
            sender_id=order.seller_id,
            receiver_id=order.buyer_id,
            content=f'订单《{order.product.title}》已完成,欢迎对商品进行评价'
        )
        db.session.add(message)
        
    db.session.***mit()
    return {'status': 'su***ess', 'order': order.to_dict()}

5. 前端页面(商品详情页 templates/products/detail.html)

{% extends "base.html" %}
{% block title %}{{ product.title }} - 校园二手交易平台{% endblock %}

{% block content %}
<div class="container mx-auto px-4 py-8">
    <div class="flex flex-col md:flex-row gap-8">
        <!-- 商品图片 -->
        <div class="md:w-1/2">
            <div class="bg-white rounded-lg shadow-md overflow-hidden">
                <img src="{{ product.images[0] if product.images else '/static/images/default-product.jpg' }}" 
                     alt="{{ product.title }}" 
                     class="w-full h-80 object-cover">
                {% if product.images|length > 1 %}
                <div class="flex gap-2 p-4 overflow-x-auto">
                    {% for img in product.images[1:] %}
                    <img src="{{ img }}" alt="商品图片" class="w-20 h-20 object-cover rounded cursor-pointer">
                    {% endfor %}
                </div>
                {% endif %}
            </div>
        </div>
        
        <!-- 商品信息 -->
        <div class="md:w-1/2">
            <h1 class="text-2xl font-bold text-gray-800">{{ product.title }}</h1>
            <div class="flex items-center mt-2 text-yellow-500">
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
                <i class="fas fa-star-half-alt"></i>
                <span class="ml-2 text-gray-600">(0 评价)</span>
            </div>
            
            <div class="mt-4 mb-6">
                <span class="text-3xl font-bold text-red-600">¥{{ product.price }}</span>
                <span class="ml-2 text-gray-500">发布于 {{ product.created_at }}</span>
            </div>
            
            <div class="bg-gray-50 p-4 rounded-lg mb-6">
                <h3 class="font-semibold mb-2">商品描述</h3>
                <p class="text-gray-700 whitespace-pre-line">{{ product.description }}</p>
            </div>
            
            <div class="flex flex-wrap gap-4 mb-6">
                <div class="flex items-center">
                    <span class="text-gray-500 mr-2">分类:</span>
                    <span>{{ product.category }}</span>
                </div>
                <div class="flex items-center">
                    <span class="text-gray-500 mr-2">浏览:</span>
                    <span>{{ product.views }}</span>
                </div>
            </div>
            
            <!-- 操作按钮 -->
            <div class="flex flex-wrap gap-4">
                {% if current_user.is_authenticated %}
                    {% if product.user_id != current_user.id and product.status == 'active' %}
                    <button id="buyBtn" class="px-6 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition">
                        立即购买
                    </button>
                    <button id="favoriteBtn" class="px-6 py-2 border border-gray-300 rounded-lg hover:bg-gray-100 transition">
                        <i class="far fa-heart mr-1"></i> 收藏
                    </button>
                    {% endif %}
                    {% if product.user_id == current_user.id %}
                    <a href="{{ url_for('products.edit_product', product_id=product.id) }}" 
                       class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
                        编辑商品
                    </a>
                    {% endif %}
                {% else %}
                <a href="{{ url_for('auth.login', next=request.path) }}" 
                   class="px-6 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition">
                    登录后购买
                </a>
                {% endif %}
                <button id="contactBtn" class="px-6 py-2 border border-gray-300 rounded-lg hover:bg-gray-100 transition">
                    <i class="far fa-***ment mr-1"></i> 联系卖家
                </button>
            </div>
            
            <!-- 卖家信息 -->
            <div class="mt-8 flex items-center">
                <img src="{{ product.user.avatar or '/static/images/default-avatar.png' }}" 
                     alt="{{ product.user.username }}" 
                     class="w-12 h-12 rounded-full object-cover">
                <div class="ml-4">
                    <div class="font-medium">{{ product.user.username }}</div>
                    <div class="text-sm text-gray-500">已认证学生</div>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 商品详情 -->
    <div class="mt-12 bg-white p-6 rounded-lg shadow-md">
        <h2 class="text-xl font-bold mb-4">商品详情</h2>
        <div class="prose max-w-none">
            <!-- 这里会显示富文本编辑的商品详情 -->
            <p>商品详细描述将在这里显示,包括商品的新旧程度、使用情况等信息。</p>
        </div>
    </div>
    
    <!-- 相关推荐 -->
    {% if related_products %}
    <div class="mt-12">
        <h2 class="text-xl font-bold mb-4">相关推荐</h2>
        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
            {% for p in related_products %}
            <a href="{{ url_for('products.product_detail', product_id=p.id) }}" class="block">
                <div class="bg-white rounded-lg shadow hover:shadow-md transition">
                    <img src="{{ p.images[0] if p.images else '/static/images/default-product.jpg' }}" 
                         alt="{{ p.title }}" 
                         class="w-full h-48 object-cover rounded-t-lg">
                    <div class="p-4">
                        <h3 class="font-medium line-clamp-1">{{ p.title }}</h3>
                        <p class="text-red-600 font-bold mt-2">¥{{ p.price }}</p>
                    </div>
                </div>
            </a>
            {% endfor %}
        </div>
    </div>
    {% endif %}
</div>

<!-- 购买确认模态框 -->
<div id="buyModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
    <div class="bg-white rounded-lg w-full max-w-md p-6">
        <h3 class="text-xl font-bold mb-4">确认购买</h3>
        <p class="mb-4">你确定要购买 <span class="font-medium">{{ product.title }}</span> 吗?</p>
        <p class="text-red-600 font-bold mb-6">价格:¥{{ product.price }}</p>
        <div class="flex gap-4">
            <button id="cancelBuy" class="flex-1 py-2 border border-gray-300 rounded-lg">取消</button>
            <button id="confirmBuy" class="flex-1 py-2 bg-red-600 text-white rounded-lg">确认购买</button>
        </div>
    </div>
</div>
{% endblock %}

{% block scripts %}
<script>
// 购买功能
document.getElementById('buyBtn').addEventListener('click', function() {
    document.getElementById('buyModal').classList.remove('hidden');
});

document.getElementById('cancelBuy').addEventListener('click', function() {
    document.getElementById('buyModal').classList.add('hidden');
});

document.getElementById('confirmBuy').addEventListener('click', function() {
    // 发送创建订单请求
    fetch('{{ url_for('orders.create_order', product_id=product.id) }}', {
        method: 'POST',
        headers: {
            'X-CSRFToken': '{{ csrf_token() }}',
            'Content-Type': 'application/json'
        }
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 'su***ess') {
            window.location.href = '{{ url_for('orders.order_detail', order_id=0) }}'.replace('0', data.order_id);
        } else {
            alert(data.message);
        }
    });
});

// 收藏功能
document.getElementById('favoriteBtn').addEventListener('click', function() {
    fetch('{{ url_for('products.toggle_favorite', product_id=product.id) }}', {
        method: 'POST',
        headers: {
            'X-CSRFToken': '{{ csrf_token() }}',
            'Content-Type': 'application/json'
        }
    })
    .then(response => response.json())
    .then(data => {
        const btn = document.getElementById('favoriteBtn');
        if (data.action === 'favorite') {
            btn.innerHTML = '<i class="fas fa-heart mr-1 text-red-500"></i> 已收藏';
        } else {
            btn.innerHTML = '<i class="far fa-heart mr-1"></i> 收藏';
        }
    });
});

// 联系卖家
document.getElementById('contactBtn').addEventListener('click', function() {
    window.location.href = '{{ url_for('messages.chat', user_id=product.user_id) }}';
});
</script>
{% endblock %}

该校园二手物品交易平台设计充分考虑了高校学生的实际需求,采用Python+Flask技术栈实现了完整的二手交易功能。系统具有以下特点:

  1. 技术适用性:使用轻量级Flask框架,开发效率高,适合毕业设计的时间周期
  2. 功能完整性:包含商品发布、浏览、购买、订单管理等核心交易功能
  3. 可扩展性:模块化设计便于后续添加新功能,如在线支付、物流跟踪等
  4. 安全性:实现了用户认证、权限控制等安全机制

文档部分详细阐述了系统的需求分析、设计方案和测试部署流程,代码部分实现了核心功能模块,可作为毕业论文设计的完整素材。

转载请说明出处内容投诉
CSS教程网 » 校园二手物品交易平台(Python+Flask)设计与实现

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买