温馨提示本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片1. 项目背景与需求分析在高校校园中大量闲置物品教材、电子产品、生活用品等因缺乏便捷的流转渠道而被浪费。本系统旨在搭建一个面向在校学生的二手闲置交易平台帮助用户发布、浏览、搜索和交易闲置物品实现资源的循环利用。系统核心角色包括普通用户注册登录、发布闲置、浏览搜索、收藏、发起交易、评价。管理员用户管理、商品审核、分类管理、举报处理、数据统计。核心业务流程如下flowchart TD A[用户注册登录] -- B[发布闲置商品] B -- C[管理员审核] C --|通过| D[商品上架展示] C --|驳回| E[用户修改重新提交] D -- F[买家浏览/搜索/收藏] F -- G[买家发起交易/留言] G -- H[双方线下或平台内完成交易] H -- I[互相评价]2. 技术选型与架构设计本系统采用前后端分离架构后端基于 Python Django REST Framework 提供 API前端使用 Vue 3 Element Plus 构建单页应用数据库选用 MySQL缓存与登录态使用 Redis。层次技术栈说明前端Vue 3、Vue Router、Pinia、Axios、Element PlusSPA 单页应用组件化开发后端Python 3.10、Django 4、Django REST Framework提供 RESTful API 与后台管理数据库MySQL 8、Redis业务数据存储、缓存与 Token 管理部署Nginx、Gunicorn、Docker可选前后端分离部署系统整体架构图如下flowchart LR subgraph 前端 A[Vue3 SPA] -- B[Nginx 静态资源] end subgraph 后端 C[Gunicorn] -- D[Django DRF API] D -- E[(MySQL)] D -- F[(Redis)] end B --|/api 反向代理| C D --|JWT 认证| F3. 数据库设计根据业务需求核心数据表包括用户表、商品表、商品图片表、收藏表、交易订单表、留言表、评价表和举报表。下面给出主要表结构。3.1 用户表CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(128) NOT NULL, email VARCHAR(100), avatar VARCHAR(255), school VARCHAR(100), phone VARCHAR(20), is_active TINYINT(1) DEFAULT 1, is_staff TINYINT(1) DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );3.2 商品表CREATE TABLE goods ( id INT AUTO_INCREMENT PRIMARY KEY, seller_id INT NOT NULL, category_id INT NOT NULL, title VARCHAR(100) NOT NULL, description TEXT, price DECIMAL(10, 2) NOT NULL, original_price DECIMAL(10, 2), status TINYINT DEFAULT 0 COMMENT 0待审核 1在售 2已售 3下架, views INT DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (seller_id) REFERENCES user(id), FOREIGN KEY (category_id) REFERENCES category(id) );3.3 交易订单表CREATE TABLE trade_order ( id INT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL UNIQUE, goods_id INT NOT NULL, buyer_id INT NOT NULL, seller_id INT NOT NULL, price DECIMAL(10, 2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0待付款 1待发货 2待收货 3已完成 4已取消, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, paid_at DATETIME, finished_at DATETIME, FOREIGN KEY (goods_id) REFERENCES goods(id), FOREIGN KEY (buyer_id) REFERENCES user(id), FOREIGN KEY (seller_id) REFERENCES user(id) );4. 后端核心实现后端基于 Django REST Framework 实现下面给出关键模块的代码实现。4.1 项目初始化与依赖创建虚拟环境并安装依赖python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install django djangorestframework django-cors-headers djangorestframework-simplejwt pillow mysqlclient redis4.2 用户认证JWT# settings.py 中配置 REST_FRAMEWORK { DEFAULT_AUTHENTICATION_CLASSES: ( rest_framework_simplejwt.authentication.JWTAuthentication, ), DEFAULT_PERMISSION_CLASSES: ( rest_framework.permissions.IsAuthenticated, ), } serializers.py from rest_framework import serializers from django.contrib.auth import get_user_model User get_user_model() class UserRegisterSerializer(serializers.ModelSerializer): password serializers.CharField(write_onlyTrue, min_length6) class Meta: model User fields (id, username, password, email, school) def create(self, validated_data): user User.objects.create_user( usernamevalidated_data[username], passwordvalidated_data[password], emailvalidated_data.get(email, ), schoolvalidated_data.get(school, ) ) return user4.3 商品发布与审核# models.py from django.db import models from django.contrib.auth import get_user_model User get_user_model() class Category(models.Model): name models.CharField(max_length50, uniqueTrue) def __str__(self): return self.name class Goods(models.Model): STATUS_CHOICES ( (0, 待审核), (1, 在售), (2, 已售), (3, 下架), ) seller models.ForeignKey(User, on_deletemodels.CASCADE, related_namegoods) category models.ForeignKey(Category, on_deletemodels.SET_NULL, nullTrue) title models.CharField(max_length100) description models.TextField() price models.DecimalField(max_digits10, decimal_places2) original_price models.DecimalField(max_digits10, decimal_places2, nullTrue, blankTrue) status models.SmallIntegerField(choicesSTATUS_CHOICES, default0) views models.IntegerField(default0) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) def __str__(self): return self.title class GoodsImage(models.Model): goods models.ForeignKey(Goods, on_deletemodels.CASCADE, related_nameimages) image models.ImageField(upload_togoods/) sort models.IntegerField(default0)# views.py from rest_framework import viewsets, permissions, status from rest_framework.response import Response from .models import Goods, Category from .serializers import GoodsSerializer, GoodsCreateSerializer class GoodsViewSet(viewsets.ModelViewSet): queryset Goods.objects.all() permission_classes [permissions.IsAuthenticatedOrReadOnly] def get_serializer_class(self): if self.action create: return GoodsCreateSerializer return GoodsSerializer def perform_create(self, serializer): serializer.save(sellerself.request.user) def list(self, request, *args, **kwargs): # 仅返回审核通过的商品 queryset Goods.objects.filter(status1) category request.query_params.get(category) keyword request.query_params.get(keyword) if category: queryset queryset.filter(category_idcategory) if keyword: queryset queryset.filter(title__icontainskeyword) serializer self.get_serializer(queryset, manyTrue) return Response(serializer.data)4.4 交易订单接口# views.py from rest_framework import generics, permissions from .models import TradeOrder, Goods from .serializers import OrderSerializer, OrderCreateSerializer import uuid class OrderCreateView(generics.CreateAPIView): serializer_class OrderCreateSerializer permission_classes [permissions.IsAuthenticated] def perform_create(self, serializer): goods serializer.validated_data[goods] if goods.status ! 1: raise serializers.ValidationError(该商品不可购买) goods.status 2 goods.save() serializer.save( buyerself.request.user, sellergoods.seller, order_nouuid.uuid4().hex[:16], pricegoods.price )5. 前端核心实现前端使用 Vue 3 组合式 API 开发下面给出关键页面与组件实现。5.1 项目初始化npm create vuelatest campus-market cd campus-market npm install element-plus axios pinia vue-router5.2 API 封装// src/api/request.js import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器携带 Token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) // 响应拦截器统一错误处理 request.interceptors.response.use( response response.data, error { if (error.response?.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.response?.data?.detail || 请求失败) return Promise.reject(error) } ) export default request5.3 商品发布页面template el-form refformRef :modelform :rulesrules label-width80px el-form-item label标题 proptitle el-input v-modelform.title placeholder请输入商品标题 / /el-form-item el-form-item label分类 propcategory el-select v-modelform.category placeholder请选择分类 el-option v-foritem in categories :keyitem.id :labelitem.name :valueitem.id / /el-select /el-form-item el-form-item label价格 propprice el-input-number v-modelform.price :min0 :precision2 / /el-form-item el-form-item label描述 propdescription el-input v-modelform.description typetextarea :rows4 / /el-form-item el-form-item label图片 el-upload action/api/upload/ list-typepicture-card :on-successhandleUploadSuccess el-iconPlus //el-icon /el-upload /el-form-item el-form-item el-button typeprimary clickhandleSubmit发布商品/el-button /el-form-item /el-form /template script setup import { ref, reactive, onMounted } from vue import { ElMessage } from element-plus import request from /api/request const formRef ref() const categories ref([]) const form reactive({ title: , category: null, price: 0, description: , images: [] }) const rules { title: [{ required: true, message: 请输入标题, trigger: blur }], category: [{ required: true, message: 请选择分类, trigger: change }], price: [{ required: true, message: 请输入价格, trigger: blur }] } onMounted(async () { const res await request.get(/categories/) categories.value res }) const handleUploadSuccess (response) { form.images.push(response.url) } const handleSubmit async () { await formRef.value.validate() await request.post(/goods/, form) ElMessage.success(发布成功等待审核) } /script5.4 商品列表与搜索template div el-input v-modelkeyword placeholder搜索闲置好物 clearable keyup.enterfetchGoods template #append el-button clickfetchGoods搜索/el-button /template /el-input el-row :gutter16 el-col v-foritem in goodsList :keyitem.id :span6 el-card :body-style{ padding: 0px } clickgoDetail(item.id) img :srcitem.cover classgoods-img / div classgoods-info h3{{ item.title }}/h3 p classprice¥{{ item.price }}/p /div /el-card /el-col /el-row /div /template script setup import { ref, onMounted } from vue import { useRouter } from vue-router import request from /api/request const router useRouter() const keyword ref() const goodsList ref([]) const fetchGoods async () { const res await request.get(/goods/, { params: { keyword: keyword.value } }) goodsList.value res } const goDetail (id) { router.push(/goods/${id}) } onMounted(fetchGoods) /script6. 系统部署前后端分离项目推荐使用 Nginx 反向代理部署。后端使用 Gunicorn 运行 Django 应用前端构建后由 Nginx 托管静态文件。6.1 后端部署pip install gunicorn gunicorn campus_market.wsgi:application --bind 0.0.0.0:8000 --workers 36.2 Nginx 配置
阅读完成 · 觉得有帮助?