SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 文章介绍了如何使用SpringBoot和Vue实现一个校园二手系统,采用前后端分离技术。系统具备完整的功能,包括客户端和管理员端的界面设计、个人信息管理、商品浏览和交易、订单处理、公告发布等。技术栈包括Vue框架、ElementUI、SpringBoot、Mybatis-plus和Redis。文章还提供了部分源代码,展示了前后端的请求接口和Redis验证码功能实现,以及系统重构和模块化设计的一些思考。

前言

全部源代码地址传送门

文章内容有点长,建议打开右侧目录导航栏查看。

   **这个系统基本上可以改造为其它类似的系统。后台管理基本上一致。前台进行一些页面样式的改造就可以变成一个新的系统。有时间,做几个变体系统。**

   闲的无聊,把大学时候做的一个系统进行了重构。将项目拆分成完全前后端分离的形式。客户端采用一套、商家端采用一套。后端只负责接口数据的处理(当然也可以只用一套)。对其中的一些页面进行了重新组织和美化。个人信息管理和后台商品信息添加、公告发布等模块做的比较好看一点。我直接仿照CSDN的个人信息管理界面进行的设计,包括头像的上修改。还有商品信息的展示,然后点击某一个商品进行商品详情的查看。公告的上传等等。使用到蛮多新的小技术。说实话,我还是不太满意,vue中的大部分代码有重复的,好想给他全部改成组件的形式。但是由于工作忙,现就这样了。

资料(源代码)

部分源代码在博客的最后给出

   把一个系统拆分成了几个练习使用。我还可以进一步将其改造为分布式微服务系统。其实不难。有时间在改造一下。有需要源码的可以加我联系方式,博客最下方有我的联系方式

在这里插入图片描述

技术栈

前端主要使用:Vue框架、组件使用ElementUI
后端主要使用:SpringBoot框架、Mybatis-plus、Redis等。

提示:主要说大的方面,小的技术点不在赘述

功能划分

1.1 客户端

客户端主要实现的功能划分
在这里插入图片描述

1.2 管理员

管理员方面主要负责的功能划分
在这里插入图片描述

顾客+游客(页面效果展示)

   游客可以免登录浏览商品信息,公告信息;但是不能查看个人信息,订单、购物车等信息。等用户注册登录系统后。这些信息可以看到

一、个人信息

1.1 信息展示

在这里插入图片描述

1.2 修改头像

在这里插入图片描述
在这里插入图片描述

1.3 修改信息

   直接在个人资料修改用户信息,点击保存就行了。还可以进行模拟充值  

在这里插入图片描述

1.4 充值

在这里插入图片描述

1.5 修改密码

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.6 修改手机号

   邮箱和手机号采用数据脱敏的形式,隐藏部分数据。这里修改手机号和邮箱类似,这里使用redis模拟发送验证码,同时保留三分钟的过期时间。在redis中很容易实现。还可以通过接入支付宝,购买他的那个验证码接口次数。可以实现真正的发送验证码到手机。我博客也有写过。这里不在赘述。

在这里插入图片描述

   这里的redis我是直接装在了虚拟机中,采用docker安装redis是真的很方便。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.7 修改邮箱

同修改手机号

二、 商品首页

1.1 首页

   首页采用轮播图的形式,这个图片是我随机找的。可以换成校园二手商城类似的。然后商品展示不同的分类对应不同的商品。

在这里插入图片描述

1.2 商品分类

   这里的分类数据我使用的同一个分类下的数据,这个只需要插入一些其它的测试数据就行了。大致思想就是,sql语句查询携带分类信息。查询的数据在对应的内容区域遍历出来。

在这里插入图片描述

1.3 商品详情页面

   这里的详情就是在首页通过点击某一个商品,然后跳转到新的页面。这个新页面根据跳转商品id查询数据库,然后展示出来。这里可以进行购买,或者进行加入购物车等操作。

在这里插入图片描述

二、 购物车

1.1 查看购物车商品

   购物车中的商品进行购买后,会自动删除在购物车中保留的商品信息。你也可以选择不删除,目前是只能进行单个商品的结算。有时间做一下一个订单进行多个商品的结算

在这里插入图片描述

1.2 购买

   购买的时候,会出现收获地址。和商品的一信息。

在这里插入图片描述

1.3 删除购物车商品

在这里插入图片描述

三、 订单

1.1 查看订单

在这里插入图片描述

1.2 查看订单状态

   在进行付款后,可以查看订单的信息状态。这里提示,订单状态使用int类型的数字替代,展示到前台的时候替换成对应的文字就可以

在这里插入图片描述

四、 公告

   公告这里点击左侧的公告列表,右侧的内容会替换对应公告内容。  

在这里插入图片描述
在这里插入图片描述

五、登录(顾客)

   说实话、这个登录页面有点丑。但是我不想换了。我还有其它不同的登录页面。懒得换了  

在这里插入图片描述

六、注册页面

在这里插入图片描述

管理员(页面效果展示)

提示:后台管理,基本上就是一些表格。增删改查

一、查看数据

   这个就是用到了echart,我也写过怎样使用。这个数据可以替换来自数据库中实际的数据  

在这里插入图片描述

二、管理用户信息

1.1 查看用户信息

在这里插入图片描述

1.2 修改用户信息

   一般来说是不允许修改的。这里为了练习使用。就修改吧。实在不想修改,就把操作中的修改按钮屏蔽掉。  

在这里插入图片描述

1.3 删除用户信息

在这里插入图片描述

1.4 添加用户信息

   按照常理,这里是不应该有的这个添加用户操作。为了练习弹窗的功能。就写出来了。你也可以屏蔽掉  

在这里插入图片描述

三、商品种类

   这里新添加相同的商品种类,会提示错误信息。不能重复添加相同商品种类。不在演示。

1.1 添加商品种类

在这里插入图片描述

1.2 修改商品信息

在这里插入图片描述

1.3 删除商品信息

二次提示删除操作

在这里插入图片描述

四、商品详情

1.1 查看商品详情

在这里插入图片描述

对应商品图片浏览

在这里插入图片描述

1.2 添加商品

   这里的商品图片上传,是和表格数据一块提交到后台保存的数据库的。大致流程,首先点击图片上传,选择图片。然后先发一个接口到后端。将图片复制到其它位置。返回前端保存该图片的位置。然后再次将表格内容和图片地址一块发送给后端。将数据放入数据库。要访问图片,只需要根据图片地址访问就可以。减少数据库存储的呢内容。  

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此时前台可以看到商品的商品
在这里插入图片描述

如果将商品上架情况改为未上架
在这里插入图片描述
前台不展示
在这里插入图片描述

1.3 修改商品

   点击修改商品信息。这里图片的修改,只需要重新上传一个图片就可以。相当于换了一个图片保存的地址。弊端,没有将之前的图片删除。可以优化,但是目前先不搞

在这里插入图片描述

修改后,预览修改后的图片,修改成功。

在这里插入图片描述

1.4 删除商品

其实也就是二次提示确认
在这里插入图片描述

五、订单信息

1.1 查看订单

   这里管理员可以看到商品的发货情况,然后根据订单情况,选择操作。  

在这里插入图片描述
在这里插入图片描述

1.2 发货

在这里插入图片描述

六、公告信息

1.1 查看公告

在这里插入图片描述

1.2 添加公告

   这里通过vue集成Quill可以实现编辑框的样式

在这里插入图片描述
添加公告
在这里插入图片描述

前台查看公告

   这个时间排序可以调整,查询数据的时候,按照注册的时间顺序就可以。  

在这里插入图片描述

1.3 修改公告

在这里插入图片描述
在这里插入图片描述

1.4 删除公告

也是进行二次确认删除

在这里插入图片描述

在这里插入图片描述

删除成功。
在这里插入图片描述

七、登录界面

在这里插入图片描述

部分源码

1、前端

1.1 项目结构

在这里插入图片描述

1.2 部分源码

一些接口数据请求


//用户调用的一些接口

import { Get, Post, Put, MyPut, Patch, Delete } from "@/api/request";

export default {

    //注销
    logout: (params) => {
        return Get('http://localhost:8282/user/logout', params)
    },
    //登录
    Login: (params) => {
        return Get('http://localhost:8282/user/userLogin', params)
    },
    //判断是否登录
    isLogin: (params) => {
        return Get('http://localhost:8282/user/isLogin', params)
    },
    //注册
    Register: (params) => {
        return Post('http://localhost:8282/user/register', params)
    },

    findAllGoodsInfo: (params) => {
        return Get('http://localhost:8282/goodsInfo/getInfo', params)
    },

    findGoodsDetail: (params) => {
        return Get('http://localhost:8282/goodsInfo/GoodsDetail', params)
    },

    /**
     * 购物车 addCartShopInfo
     */
    addCartShopInfo: (params) => {
        return Post('http://localhost:8282/cartshop/add', params)
    },

    queryCartShopInfo: (params) => {
        return Get('http://localhost:8282/cartshop/find', params)
    },

    deleteCartShopInfo: (params) => {
        return Delete('http://localhost:8282/cartshop/delete', params)
    },

    /**
     * 订单
     */

    addOrder: (params) => {
        return Post('http://localhost:8282/order/add', params)
    },
    queryOrder: (params) => {
        return Get('http://localhost:8282/order/findAll', params)
    },

    onPaymoney: (params) => {
        return Put('http://localhost:8282/order/paymoney', params)
    },

    onRefund: (params) => {
        return Put('http://localhost:8282/order/refund', params)
    },

    onCancelRefund: (params) => {
        return Put('http://localhost:8282/order/cancelRefund', params)
    },

    onReturnable: (params) => {
        return Put('http://localhost:8282/order/onReturnable', params)
    },

    onCancelReturnable: (params) => {
        return Put('http://localhost:8282/order/onCancelReturnable', params)
    },

    /**
     * 用户信息
     */
    updateFaceImage: (params) => {
        return Put('http://localhost:8282/user/updateFaceImage', params)
    },

    rechargeMoney: (params) => {
        return Put('http://localhost:8282/user/rechargeMoney', params)
    },
    findUserById: (params) => {
        return Get('http://localhost:8282/user/findUserById', params)
    },

    onChangePWD: (params) => {
        return Put('http://localhost:8282/user/changePwd', params)
    },

    UpdateUserInfo: (params) => {
        return Put('http://localhost:8282/user/updateUserInfo', params)
    },


    //获取验证码
    GetCode: (params) => {
        return Get('http://localhost:8282/user/getCode', params)
    },

    //验证验证码
    VerifyCode: (params) => {
        return Get('http://localhost:8282/user/verifyCode', params)
    },

    //修改手机号
    UpdatePhone: (params) => {
        return Put('http://localhost:8282/user/updatePhone', params)
    },

    /**
     * 公告
     */
    findAllAnnounceInfo: (params) => {
        return Get('http://localhost:8282/announce/findAllAnnounceInfo', params)
    },

}

//管理员
import { Get, Post, Put, MyPut, Patch, Delete } from "@/api/request";

export default {
    getListData: (params) => {
        return Get('../../static/data.json', params);
    },
    postListData: (params) => {
        return Post('../../static/data.json', params);
    },
    deleteListData: (params) => {
        return Delete('../../static/data.json', params);
    },
    Register: (params) => {
        return Post('http://localhost:8282/user/register', params)
    },
    adminRegister: (params) => {
        return Post('http://localhost:8282/admin/register', params)
    },
    Login: (params) => {
        return Post('http://localhost:8282/user/login', params)
    },

    findAllUser: (params) => {
        return Get('http://localhost:8282/user/findAllUser', params)
    },

    findAllAdmin: (params) => {
        return Get('http://localhost:8282/admin/findAllUser', params)
    },
    submitUpdateAdminForm: (params) => {
        return Put('http://localhost:8282/admin/updateUserInfo', params)
    },

    handleAdminDelete: (params) => {
        return Delete('http://localhost:8282/admin/deleteUser/', params)
    },

    submitUpdateUserForm: (params) => {
        return Put('http://localhost:8282/user/updateUserInfo', params)
    },

    handleDelete: (params) => {
        return Delete('http://localhost:8282/user/deleteUser/', params)
    },

    /**
     * 商品种类
     */
    addTypeInfo: (params) => {
        return Post('http://localhost:8282/typeInfo/addTypeInfo', params)
    },
    findAllTypeInfo: (params) => {
        return Get('http://localhost:8282/typeInfo/findAllTypeInfo', params)
    },
    handleDeleteTypeInfo: (params) => {
        return Delete('http://localhost:8282/typeInfo/deleteTypeInfo', params)
    },
    submitUpdateUserForm: (params) => {
        return Put('http://localhost:8282/typeInfo/updateTypeInfo', params)
    },

    /**
     * 商品详情
     */
    getTypeInfoOption: (params) => {
        return Get('http://localhost:8282/goodsInfo/getTypeInfo', params)
    },

    addGoodsInfo: (params) => {
        return Post('http://localhost:8282/goodsInfo/addGoodsInfo', params)
    },
    handleDeleteGoodsInfo: (params) => {
        return Delete('http://localhost:8282/goodsInfo/deleteGoodsInfo', params)
    },
    updateGoodsInfo: (params) => {
        return Put('http://localhost:8282/goodsInfo/updateGoodsInfo', params)
    },

    findAllGoodsInfo: (params) => {
        return Get('http://localhost:8282/goodsInfo/findAllGoodsInfo', params)
    },
    updateSaleStatus: (params) => {
        return Put('http://localhost:8282/goodsInfo/updateSaleStatus',params)
    },

    /**
     * 文件上传
     */
    uploadFile: (params) => {
        return Post('http://localhost:8282/goodsInfo/upload', params);
    },

    /**
     * 订单
     */
    findAllOrder: (params) => {
        return Get('http://localhost:8282/order/findAllOrderInfo', params)
    },

    onShipments: (params) => {
        return Put('http://localhost:8282/order/shipments', params)
    },

    /**
     * 公告
     */
    findAllAnnounceInfo: (params) => {
        return Get('http://localhost:8282/announce/findAllAnnounceInfo', params)
    },

    addAnnounceInfo: (params) => {
        return Post('http://localhost:8282/announce/addAnnounceInfo', params)
    },

    handleDeleteAnnounceInfo: (params) => {
        return Delete('http://localhost:8282/announce/deleteAnnounceInfo', params)
    },

    submitUpdateAnnounce: (params) => {
        return Put('http://localhost:8282/announce/updateAnnounceInfo', params)
    },

}

2、后端

1.1 项目结构

在这里插入图片描述

1.2 部分源码

这个是redis关于验证码的部分代码


/**
 * @author zyz
 * @version 1.0
 * @data 2022/12/5 14:18
 * @Description:
 */
@RestController
public class RedisController {

    @Autowired
    private RedisTemplate redisTemplate;

    //六位数验证码
    public static String getRandom() {
        Random random = new Random();
        String code = "";
        for (int i = 0; i < 6; i++) {
            int rand = random.nextInt(10);
            code += rand;
        }
        return code;

    }

    /**
     * 发送手机号、获取验证码
     *
     * @return
     */
    @RequestMapping(value = "/user/getCode", method = RequestMethod.GET)
    public Result getVerifyCode(@RequestParam Map<String, Object> maps) {
        String phone = (String) maps.get("phone");
        //手机发送次数key
        String countKey = "verifyCode" + phone + "count";
        //验证码key
        String codeKey = "verifyCode" + phone + "code";
        //每个手机1小时发送三次
        Integer count = (Integer) redisTemplate.opsForValue().get(countKey);

        if (count == null) {
            //第一次发送
            redisTemplate.opsForValue().set(countKey, 1, 60 * 60, TimeUnit.SECONDS);
            System.out.println("第几次发送:" + count);

        } else if (count <= 2) {
            Integer nums = ++count;
            redisTemplate.opsForValue().set(countKey, nums,  60 * 60, TimeUnit.SECONDS);
            System.out.println("第几次发送:" + count);

        } else {
            System.out.println("请一个小时后再次尝试");
            return Result.error().data("errMessage", "请一个小时后再次尝试");
        }
        //发送验证码到redis中
        String vcode = getRandom();
        System.out.println("生成的验证码是:" + vcode);
        redisTemplate.opsForValue().set(codeKey, vcode, 120, TimeUnit.SECONDS);
        return Result.ok().data("vcode", vcode);

    }

    /**
     * 验证验证码
     *
     * @return
     */
    @RequestMapping(value = "/user/verifyCode", method = RequestMethod.GET)
    public Result verifyCode(@RequestParam Map<String, Object> maps) {
        String phone = (String) maps.get("phone");
        String code = (String) maps.get("code");
        //验证码的key
        String codeKey = "verifyCode" + phone + "code";
        String redisCode = (String) redisTemplate.opsForValue().get(codeKey);

        //判断
        if (redisCode.equals(code)) {
            return Result.ok();
        } else {
            return Result.error();
        }

    }

    @GetMapping(value = "/getName")
    public String testRedis() {
        //设置值到redis
        redisTemplate.opsForValue().set("name", "lucy");
        //从redis获取值
        String name = (String) redisTemplate.opsForValue().get("name");
        return name;
    }
}

后语

做系统是学习提升最快的方式了

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
22 4
|
8天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
62 1
|
10天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
56 2
|
3天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
10 0
|
4天前
|
JavaScript Java 关系型数据库
自主版权的Java诊所管理系统源码,采用Vue 2、Spring Boot等技术栈,支持二次开发
这是一个自主版权的Java诊所管理系统源码,支持二次开发。采用Vue 2、Spring Boot等技术栈,涵盖患者管理、医生管理、门诊管理、药店管理、药品管理、收费管理、医保管理、报表统计及病历电子化等功能模块。
|
9天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
18 3
|
10天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第7天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建 Spring Boot 项目并配置 Spring Security。接着,实现后端 API 以提供菜单数据。在前端部分,使用 Ant Design Pro Vue 脚手架创建项目,并配置动态路由和菜单。最后,启动前后端服务,实现高效、美观且功能强大的应用框架。
13 2
|
11天前
|
机器学习/深度学习 移动开发 自然语言处理
基于人工智能技术的智能导诊系统源码,SpringBoot作为后端服务的框架,提供快速开发,自动配置和生产级特性
当身体不适却不知该挂哪个科室时,智能导诊系统应运而生。患者只需选择不适部位和症状,系统即可迅速推荐正确科室,避免排错队浪费时间。该系统基于SpringBoot、Redis、MyBatis Plus等技术架构,支持多渠道接入,具备自然语言理解和多输入方式,确保高效精准的导诊体验。无论是线上医疗平台还是大型医院,智能导诊系统均能有效优化就诊流程。
|
1月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决