校园二手闲置交易系统技术讲解
1. 项目介绍
校园二手闲置交易系统是一个面向高校学生的在线交易平台,旨在帮助学生处理闲置物品,实现资源的高效流转。系统提供了完整的商品发布、浏览、购买、评价等功能,同时集成了用户管理、订单管理、举报处理等系统功能,为校园二手交易提供了安全、便捷的线上环境。
该系统采用前后端分离架构,后端基于Spring Boot框架开发,前端使用Vue.js框架构建,实现了响应式设计,能够适配不同设备的访问需求。系统不仅满足了基本的交易功能,还提供了数据统计分析、用户信用评价等增值服务,为校园二手交易市场提供了全方位的支持。
2. 技术架构
2.1 技术栈概述
本系统采用前后端分离的架构设计,主要技术栈如下:
后端技术栈:
- 基础框架:Spring Boot 2.5.15
- 权限控制:Spring Security
- 持久层:MyBatis
- 数据库:MySQL 5.7
- 缓存:Redis
- 任务调度:Quartz
- API文档:Swagger 3.0
- 数据导出:POI
- JSON处理:Fastjson 2.0.53
- JWT认证:JJWT 0.9.1
- 代码生成:Velocity 2.3
前端技术栈:
- 基础框架:Vue.js 2.6.12
- UI组件库:Element UI 2.15.14
- 路由管理:Vue Router 3.4.9
- 状态管理:Vuex 3.6.0
- HTTP客户端:Axios 0.28.1
- 图表库:ECharts 5.4.0
- 富文本编辑器:Quill 2.0.2
- 树形选择器:vue-treeselect 0.4.0
- CSS预处理器:SASS
2.2 系统架构图
2.3 项目结构
后端项目采用模块化设计,主要包含以下模块:
- admin:系统启动和配置模块
- framework:核心框架模块
- system:系统功能模块
- quartz:定时任务模块
- generator:代码生成模块
- common:通用工具模块
前端项目结构:
- src/api:接口请求
- src/assets:静态资源
- src/components:公共组件
- src/layout:布局组件
- src/router:路由配置
- src/store:状态管理
- src/utils:工具函数
- src/views:页面视图
3. 功能点
3.1 用户功能
- 用户注册与登录
- 个人信息管理
- 密码修改与找回
- 用户认证
3.2 商品功能
- 商品分类浏览
- 商品搜索与筛选
- 商品详情查看
- 商品发布与管理
- 商品状态更新
3.3 交易功能
- 商品购买
- 订单生成与管理
- 交易状态追踪
- 交易评价
- 电子钱包支付
3.4 社区功能
- 用户评价系统
- 举报机制
- 消息通知
3.5 管理功能
- 用户管理
- 商品审核
- 订单管理
- 举报处理
- 系统配置
- 数据统计与分析
4. 数据库设计
系统使用MySQL数据库,主要包含以下表结构:
4.1 核心业务表
商品信息表(sh_products)
4.2 系统管理表
系统还包含多个用于系统管理的表,如用户表(sys_user)、角色表(sys_role)、菜单表(sys_menu)、部门表(sys_dept)、字典表(sys_dict_data)等,用于支持系统的权限管理、配置管理等功能。
5. 核心代码展示
5.1 前端核心代码
商品详情页面(部分代码)
<template>
<div class="product-detail-container" :class="{'full-screen': isFullScreen}">
<!-- 使用头部组件 -->
<mall-header v-if="isFullScreen" :isFullScreen="isFullScreen" />
<!-- 主内容区域 -->
<div class="detail-content" :class="{'has-header': isFullScreen}">
<!-- 返回导航区 -->
<div class="nav-actions">
<el-button icon="el-icon-arrow-left" class="back-btn" @click="goBack">返回商城</el-button>
<el-button icon="el-icon-s-home" class="home-btn" @click="goBackToMain">返回主页</el-button>
</div>
<!-- 商品详情卡片 -->
<div class="product-detail-card" v-if="product.productId">
<div class="product-detail">
<!-- 商品图片区域 -->
<div class="product-gallery">
<div class="main-image">
<img :src="product.imageUrl || 'https://via.placeholder.com/400'" alt="商品图片">
<div class="product-status" :class="getStatusClass(product.status)">{{ getStatusText(product.status) }}</div>
</div>
</div>
<!-- 商品信息区域 -->
<div class="product-info">
<h1 class="product-title">{{ product.name }}</h1>
<!-- 卖家信息 -->
<div class="seller-info" @click="goToSellerPage">
<div class="seller-avatar">
<img :src="sellerInfo.avatar || 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'" alt="卖家头像">
</div>
<div class="seller-detail">
<div class="seller-name">{{ sellerInfo.nickName || '未知用户' }}</div>
</div>
<div class="seller-action">
<el-button type="text" icon="el-icon-s-comment">查看评价</el-button>
</div>
</div>
<div class="product-meta">
<span class="category"><i class="el-icon-collection-tag"></i> 分类:{{ categoryName }}</span>
<span class="status"><i class="el-icon-shopping-cart-1"></i> 状态:{{ getStatusText(product.status) }}</span>
<span class="view-count"><i class="el-icon-view"></i> 浏览次数:{{ product.viewCount }}</span>
</div>
<div class="product-price">¥{{ product.price }}</div>
<div class="product-description">
<h3><i class="el-icon-document"></i> 商品描述</h3>
<div class="description-content">{{ product.description || '暂无描述' }}</div>
</div>
<div class="product-actions">
<el-button type="primary" size="large" :disabled="product.status !== 'available' || currentUserId === product.userId" @click="handlePurchase" class="buy-btn" :loading="purchaseLoading">
<i class="el-icon-shopping-cart-2"></i>
<span v-if="product.status === 'available'">
{{ currentUserId === product.userId ? '不能购买自己的商品' : '立即购买' }}
</span>
<span v-else-if="product.status === 'sold'">已售出</span>
<span v-else-if="product.status === 'removed'">已下架</span>
<span v-else>未知状态</span>
</el-button>
<el-button type="danger" size="large" @click="handleReport" class="report-btn">
<i class="el-icon-warning-outline"></i> 举报
</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
商城首页(部分代码)
// 商品搜索与筛选
methods: {
// 获取商品列表
getProductList() {
this.loading = true;
listAllProducts(this.queryParams).then(response => {
this.productList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 处理分类筛选
handleCategoryChange(categoryId) {
this.queryParams.categoryId = categoryId;
this.getProductList();
},
// 处理排序方式改变
handleSortChange(sort) {
this.queryParams.orderByColumn = sort.prop;
this.queryParams.isAsc = sort.order === 'ascending' ? 'asc' : 'desc';
this.getProductList();
}
}
5.2 后端核心代码
商品控制器
package com.ruoyi.system.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.ShProducts;
import com.ruoyi.system.service.IShProductsService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
* 商品信息Controller
*
* @author mp
* @date 2025-05-23
*/
@RestController
@RequestMapping("/system/products")
public class ShProductsController extends BaseController
{
@Autowired
private IShProductsService shProductsService;
/**
* 查询商品信息列表
*/
@PreAuthorize("@ss.hasPermi('system:products:list')")
@GetMapping("/list")
public TableDataInfo list(ShProducts shProducts)
{
startPage();
List<ShProducts> list = shProductsService.selectShProductsList(shProducts);
return getDataTable(list);
}
/**
* 获取商品信息详细信息
*/
@PreAuthorize("@ss.hasPermi('system:products:query')")
@GetMapping(value = "/{productId}")
public AjaxResult getInfo(@PathVariable("productId") Long productId)
{
return success(shProductsService.selectShProductsByProductId(productId));
}
/**
* 新增商品信息
*/
@PreAuthorize("@ss.hasPermi('system:products:add')")
@Log(title = "商品信息", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ShProducts shProducts)
{
return toAjax(shProductsService.insertShProducts(shProducts));
}
/**
* 查询所有商品信息列表
*/
@GetMapping("/listAll")
public TableDataInfo listAll(ShProducts shProducts)
{
startPage();
List<ShProducts> list = shProductsService.selectAllShProductsList(shProducts);
return getDataTable(list);
}
}
订单服务实现(示例)
/**
* 订单服务实现类
*/
@Service
public class ShOrdersServiceImpl implements IShOrdersService {
@Autowired
private ShOrdersMapper ordersMapper;
@Autowired
private ShProductsMapper productsMapper;
@Autowired
private ShWalletsMapper walletsMapper;
/**
* 创建订单并处理商品状态和钱包余额
*/
@Override
@Transactional
public int createOrder(ShOrders order) {
// 1. 检查商品状态
ShProducts product = productsMapper.selectShProductsByProductId(order.getProductId());
if (product == null || !"available".equals(product.getStatus())) {
throw new ServiceException("商品不可购买");
}
// 2. 检查买家钱包余额
ShWallets buyerWallet = walletsMapper.selectShWalletsByUserId(order.getBuyerId());
if (buyerWallet == null || buyerWallet.getBalance().compareTo(order.getPrice()) < 0) {
throw new ServiceException("余额不足");
}
// 3. 更新商品状态为已售出
ShProducts updateProduct = new ShProducts();
updateProduct.setProductId(product.getProductId());
updateProduct.setStatus("sold");
productsMapper.updateShProducts(updateProduct);
// 4. 扣除买家余额
ShWallets updateBuyerWallet = new ShWallets();
updateBuyerWallet.setWalletId(buyerWallet.getWalletId());
updateBuyerWallet.setBalance(buyerWallet.getBalance().subtract(order.getPrice()));
walletsMapper.updateShWallets(updateBuyerWallet);
// 5. 增加卖家余额
ShWallets sellerWallet = walletsMapper.selectShWalletsByUserId(order.getSellerId());
if (sellerWallet != null) {
ShWallets updateSellerWallet = new ShWallets();
updateSellerWallet.setWalletId(sellerWallet.getWalletId());
updateSellerWallet.setBalance(sellerWallet.getBalance().add(order.getPrice()));
walletsMapper.updateShWallets(updateSellerWallet);
}
// 6. 创建订单记录
order.setStatus("completed");
order.setCreateTime(DateUtils.getNowDate());
return ordersMapper.insertShOrders(order);
}
}
6. 技术亮点与实现细节


6.1 前后端分离架构
本系统采用前后端分离架构,前端使用Vue.js构建单页面应用(SPA),后端提供RESTful API接口。这种架构带来以下优势:
1.
开发效率提升:前后端可以并行开发,互不阻塞
2.
用户体验优化:前端可以实现更流畅的交互体验
3.
接口复用:同一套后端API可以服务多个客户端(Web、移动端等)
4.
技术栈解耦:前后端技术栈可以独立演进


6.2 权限控制系统
系统采用基于RBAC(Role-Based Access Control)的权限控制模型,结合Spring Security实现:
1.
多级权限控制:系统管理员、普通管理员、普通用户等多级权限
2.
细粒度权限设计:可以精确控制到按钮级别的操作权限
3.
动态权限分配:权限可以动态分配,无需修改代码
4.
注解式权限控制:使用@PreAuthorize注解进行方法级权限控制


6.3 交易安全保障
为保障交易安全,系统实现了多重安全机制:
1.
电子钱包系统:避免直接涉及银行卡等敏感信息
2.
交易状态追踪:完整记录交易全过程,便于追溯
3.
举报与投诉机制:用户可举报不良行为,管理员及时处理
4.
用户信用评价:建立用户信用体系,提高交易安全性


6.4 响应式设计
前端采用响应式设计,适配不同设备:
1.
弹性布局:使用flex布局实现界面弹性适配
2.
媒体查询:针对不同屏幕尺寸调整样式
3.
组件自适应:核心组件能够根据容器大小自动调整


6.5 性能优化
系统在性能方面进行了多项优化:
1.
数据库索引优化:为常用查询字段建立合适的索引
2.
查询缓存:使用Redis缓存热点数据,减轻数据库压力
3.
延迟加载:对非核心内容采用延迟加载策略
4.
分页查询:大数据量查询采用分页机制,避免一次性加载过多数据


7. 总结与展望
校园二手闲置交易系统基于Spring Boot和Vue.js技术栈,实现了完整的校园二手物品交易功能。系统设计合理,功能完善,性能优良,为校园二手交易提供了安全、便捷的线上平台。
未来系统可以在以下方面进行扩展和优化:
1.
移动端应用开发:开发配套的移动端应用,提供更便捷的访问方式
2.
智能推荐系统:基于用户行为和偏好,实现个性化商品推荐
3.
社交功能增强:增加更多社交互动功能,提高用户粘性
4.
支付方式多元化:接入更多支付渠道,提供更灵活的支付选择
5.
大数据分析:对交易数据进行深度分析,为运营决策提供支持
通过不断迭代和优化,校园二手闲置交易系统将为高校学生提供更加优质的二手交易服务,促进校园资源的高效流转和可持续利用。