该项目开始是要求我们使用JavaWeb(java+jsp+servlet+MySQL+jdbc+css+js+jQuery)实现,但我学过一丢丢的框架,就改用了SpringBoot+Vue实现。
注意!!!!!代码中的serverIp我设置的我服务器的IP,所以不在服务器上面应该是localhost!!!!!!!!
在线商城项目演示视频:https://www.bilibili.com/video/BV1kY41117DH/

目录
1. 产品介绍
2. 产品面向的用户群体
3. 产品的范围
4. 产品中的角色
5. 产品的功能需求
5.1 功能性需求分类
5.2功能层次结构图
6. 产品的非功能性需求
6.1 用户界面需求
6.2 软硬件环境需求
6.3 产品质量需求
数据库设计:
前台页面:
在线商城首页:
编辑
首页Home.vue源代码:
商品详情页:
编辑 源代码Detail.Vue
我的购物车:
我的订单:
返回数据:
个人信息:
源代码Person.vue
联系商家:
后台页面:
核心代码:
1.UserController
2.TokenUtils
3.CartController
4.EchartsController
5.FileController
6.MybatisPlusConfig
7.Constants
1. 产品介绍
对于网上商城,其最大好处是要能给用户带来最大的便捷,这种便捷不仅体现在网络之外的物流、商品的折扣等,更要体现在进行网络操作时的易用性,能够模拟用户的购物行为,营造一种尽量真实、贴切的用户购物过程。所以,在设计网络商城时,最重要的就是完成“用户功能”。其次,对众多商品、订单、用户信息的网络管理,对于网站经营者的经营效率的意义,也是不言而喻的,这些则可以称为“管理功能”。
2. 产品面向的用户群体
本系统主要面向系统管理员和普通用户。
(1)系统管理员:订单管理、用户管理、商品管理等。
(2)普通用户:主要使用的业务模块包括系统登录、注册、购买商品、查询订单。
3. 产品的范围
本项目主要分为系统设置模块、用户管理模块、商品管理模块、购买商品管理模块、订单管理模块。
4. 产品中的角色
| 
 角色名称  | 
 职责描述  | 
| 
 普通用户  | 
 注册,登录,添加购物车,商品付款  | 
| 
 管理员  | 
 登录,注册,商品信息管理,用户信息管理  | 
5. 产品的功能需求
5.1 功能性需求分类
| 
 功能类别  | 
 功能名称  | 
 描述  | 
| 
 用户管理  | 
 个人信息管理  | 
 管理用户的个人信息  | 
| 
 用户管理  | 
 管理已存在用户  | 
|
| 
 商品管理  | 
 增加商品  | 
 对商城的商品进行添加  | 
| 
 删除商品  | 
 删除商城的在架商品  | 
|
| 
 订单管理  | 
 订单详情管理  | 
 对订单进行处理,对已付款的订单进行发货,对发货的商品进行收货  | 
| 
 订单支付  | 
 支付当前未付款的订单  | 
|
| 
 查看商品  | 
 查看该笔订单的商品信息  | 
|
| 
 购物车管理  | 
 添加购物车  | 
 添加商品到购物车  | 
| 
 删除购物车  | 
 删除当前购物车内的商品  | 
|
| 
 商品结算  | 
 结算购物车的商品  | 
5.2功能层次结构图

6. 产品的非功能性需求
6.1 用户界面需求
| 
 需求名称  | 
 详细要求  | 
| 
 整体风格  | 
 以蓝色为主色调  | 
| 
 兼容性  | 
 能在主流浏览器(火狐、谷歌、IE8+、360浏览器)上运行  | 
6.2 软硬件环境需求
| 
 需求名称  | 
 详细要求  | 
| 
 开发语言  | 
 Java或.NET  | 
| 
 运行环境  | 
 Jdk1.6+或.NET Framework 3.5以上  | 
| 
 数据库  | 
 Mysql5.0或者SqlServer 2005以上  | 
| 
 操作系统  | 
 Windows Server2008  | 
6.3 产品质量需求
| 
 主要质量属性  | 
 详细要求  | 
| 
 正确性  | 
 无数据计算错误,无流程错误  | 
| 
 健壮性  | 
 程序出错后,系统能正常捕获异常,不会导致程序终止运行  | 
| 
 可靠性  | 
 系统支持7*24无间断运行,不会因系统功能的复杂运算而导致系统崩溃  | 
| 
 性能、效率  | 
 数据请求在0.2S内返回  | 
| 
 易用性  | 
 功能使用,操作简单,避免繁琐的逻辑设定  | 
| 
 清晰性  | 
 功能结果及名称清晰,避免用户误解  | 
| 
 安全性  | 
 用户必须成功登陆后,根据权限才可使用对应的功能  | 
| 
 可扩展性  | 
 提供良好的系统接口,支持后续功能的开发扩展  | 
| 
 兼容性  | 
 兼容主流浏览器(火狐、谷歌、IE8+、360浏览器)  | 
| 
 可移植性  | 
 能较好部署到其他版本的Windows操作系统上  | 
数据库设计:
1.1 数据库系统:
SQL Server 2008 / My SQL,服务器为MySQL8.0版本

1.2 设计工具:
Enterprise Architect
1.3连接工具:
Navicat、服务器为1核(vCPU) 2 GiB



前台页面:
在线商城首页:

 
首页Home.vue源代码:
<template>
  <div class="base">
    <div style="background-color: #545c64; width: 1510px">
      <el-menu background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"
               class="el-menu-demo"
               mode="horizontal">
        <el-menu-item index="/home" @click="$router.push('/home')">在线商城首页</el-menu-item>
        <el-menu-item index="1" >
          <a href="https://www.jd.com" target="_blank" style="text-decoration: none">商城官网</a></el-menu-item>
        <el-menu-item index="/front/cart" @click="$router.push('/front/cart')">我的购物车<i class="el-icon-shopping-cart-1"/></el-menu-item>
        <el-menu-item index="/front/orders" @click="$router.push('/front/orders')">我的订单<i class="el-icon-truck"/></el-menu-item>
<!--        <el-menu-item index="/user" @click="$router.push('/user')" v-if="user.role == 1">后台管理</el-menu-item>-->
        <!--        <el-submenu index="7" v-if="user.role == 1">-->
        <!--          <template slot="title">后台管理</template>-->
        <!--          <el-menu-item index="/user" @click="$router.push('/user')">用户管理</el-menu-item>-->
        <!--          <el-menu-item index="/goods" @click="$router.push('/goods')">商品管理</el-menu-item>-->
        <!--          <el-menu-item index="/cart" @click="$router.push('/cart')">购物车管理</el-menu-item>-->
        <!--          <el-menu-item index="/orders" @click="$router.push('/orders')">订单管理</el-menu-item>-->
        <!--        </el-submenu>-->
        <div style="float: right; display: flex">
          <el-menu-item index="/login" @click="$router.push('/login')">登录/注册</el-menu-item>
<!--          <el-menu-item index="/register" @click="$router.push('/login')">退出商城</el-menu-item>-->
<!--          <el-menu-item index="/front/person" @click="$router.push('/front/person')">{{ user.nickname }}</el-menu-item>-->
          <el-submenu index="7">
            <template slot="title">{{ user.nickname }}</template>
            <el-menu-item index="/front/person" @click="$router.push('/front/person')">个人信息</el-menu-item>
            <el-menu-item index="/login" @click="$router.push('/login')">退出</el-menu-item>
          </el-submenu>
        </div>
      </el-menu>
    </div>
    <el-card style="height: 80px; background-color: white">
      <el-input style="width: 600px; margin-left: 400px" placeholder="请输入你要查询的商品" clearable v-model="name" size="big"></el-input>
<!--      <el-input style="width: 200px; margin-left: 10px" placeholder="请输入用户名" clearable suffix-icon="el-icon-user" v-model="username" ></el-input>-->
      <el-button type="primary" style="margin-left: 5px" @click="load" size="big"><i class="el-icon-search" />搜索</el-button>
    </el-card>
    <div style="width: 1500px; height: 410px; display: flex">
      <div style="width: 300px; text-align: right; padding-right: 10px; background-color: whitesmoke; margin: 10px 0 0">
        <ul style="margin: 10px 0; font-weight: inherit; font-size: 18px; color: #545c64">
          <li style="margin-top: 10px; cursor: pointer">手机</li>
          <li style="margin-top: 20px; cursor: pointer">电脑</li>
          <li style="margin-top: 20px; cursor: pointer">家装</li>
          <li style="margin-top: 20px; cursor: pointer">医药</li>
          <li style="margin-top: 20px; cursor: pointer">女装</li>
          <li style="margin-top: 20px; cursor: pointer">男装</li>
          <li style="margin-top: 20px; cursor: pointer">美妆</li>
          <li style="margin-top: 20px; cursor: pointer">食品</li>
        </ul>
      </div>
      <!--    轮播图-->
      <div style="flex: 1; margin: 10px 0 0 ">
        <span class="demonstration"></span>
        <el-carousel trigger="click" height="400px">
          <el-carousel-item v-for="item in list" :key="item">
            <img :src="item.img" >
          </el-carousel-item>
        </el-carousel>
      </div>
    </div>
    <div style="width: 100px; height: 50px; text-align: center; margin:25px 0 0 710px">
      <a style="font-size: 28px; font-weight: bold; padding-top: 100px">手机</a>
    </div>
    <div style="margin: 10px 60px; background-color: whitesmoke; height: 640px; width: 1400px; ">
      <el-row :gutter="12" >
        <el-col :span="4"  v-for="item in tableData" :key="item.id">
            <div class="phone">
              <img :src="item.url" alt="" @click="$router.push('/detail?id=' + item.id)" style="width: 140px; height: 140px; margin-top: 5px; margin-left: 2px; cursor: pointer">
              <div class="refer" @click="$router.push('/detail?id=' + item.id)"><b>{{item.refer}}</b></div>
              <div class="goodsName" @click="$router.push('/detail?id=' + item.id)">{{item.name}}</div>
              <div class="price" @click="$router.push('/detail?id=' + item.id)">¥{{item.price}}</div>
            </div>
        </el-col>
      </el-row>
    </div>
    <div style="padding: 10px 0 0 50px">
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[ 5, 10, 15]"
          :page-size="pageSize"
          layout="total, prev, pager, next"
          :total="total">
      </el-pagination>
    </div>
<!--    <div style="width: 100%; height: 50px; margin-top: 50px">-->
<!--      <a style="font-size: 28px; font-weight: bold; margin-left: 730px">电脑</a>-->
<!--    </div>-->
<!--    <div style="margin: 10px 60px; background-color: whitesmoke; height: 640px; width: 1400px; ">-->
<!--      <el-row :gutter="10" >-->
<!--        <el-col :span="4"  v-for="item in tableData" :key="item.id">-->
<!--          <div class="phone">-->
<!--            <img :src="item.url" alt="" @click="$router.push('/detail?id=' + item.id)" style="width: 170px; height: 170px; margin-top: 5px; margin-left: 3px; cursor: pointer">-->
<!--            <div class="refer" @click="$router.push('/detail?id=' + item.id)"><b>{{item.refer}}</b></div>-->
<!--            <div class="goodsName" @click="$router.push('/detail?id=' + item.id)">{{item.name}}</div>-->
<!--            <div class="price" @click="$router.push('/detail?id=' + item.id)">¥{{item.price}}</div>-->
<!--          </div>-->
<!--        </el-col>-->
<!--      </el-row>-->
<!--    </div>-->
<!--    <div style="width: 100%; height: 50px; margin-top: 50px">-->
<!--      <a style="font-size: 28px; font-weight: bold; margin-left: 705px">电脑外设</a>-->
<!--    </div>-->
<!--    <div style="margin: 10px 60px; background-color: whitesmoke; height: 640px; width: 1400px; ">-->
<!--      <el-row :gutter="10" >-->
<!--        <el-col :span="4"  v-for="item in tableData" :key="item.id">-->
<!--          <div class="phone">-->
<!--            <img :src="item.url" alt="" @click="$router.push('/detail?id=' + item.id)" style="width: 170px; height: 170px; margin-top: 5px; margin-left: 3px; cursor: pointer">-->
<!--            <div class="refer" @click="$router.push('/detail?id=' + item.id)"><b>{{item.refer}}</b></div>-->
<!--            <div class="goodsName" @click="$router.push('/detail?id=' + item.id)">{{item.name}}</div>-->
<!--            <div class="price" @click="$router.push('/detail?id=' + item.id)">¥{{item.price}}</div>-->
<!--          </div>-->
<!--        </el-col>-->
<!--      </el-row>-->
<!--    </div>-->
<!--    <div style="width: 100%; height: 50px; margin-top: 50px">-->
<!--      <a style="font-size: 28px; font-weight: bold; margin-left: 705px">电脑配件</a>-->
<!--    </div>-->
<!--    <div style="margin: 10px 60px; background-color: whitesmoke; height: 640px; width: 1400px; ">-->
<!--      <el-row :gutter="10" >-->
<!--        <el-col :span="4"  v-for="item in tableData" :key="item.id">-->
<!--          <div class="phone">-->
<!--            <img :src="item.url" alt="" @click="$router.push('/detail?id=' + item.id)" style="width: 170px; height: 170px; margin-top: 5px; margin-left: 3px; cursor: pointer">-->
<!--            <div class="refer" @click="$router.push('/detail?id=' + item.id)"><b>{{item.refer}}</b></div>-->
<!--            <div class="goodsName" @click="$router.push('/detail?id=' + item.id)">{{item.name}}</div>-->
<!--            <div class="price" @click="$router.push('/detail?id=' + item.id)">¥{{item.price}}</div>-->
<!--          </div>-->
<!--        </el-col>-->
<!--      </el-row>-->
<!--    </div>-->
    <div style="width: 100%; height: 250px; margin-top: 80px">
      <div style="height: 250px;width: 1510px; background-color: #545c64">
          <h3 style="margin-left: 705px; font-size: 28px; color: white">合作伙伴</h3>
          <img src="src/assets/images/footer/facebook.png" alt="" style="width: 30px; height: 30px; margin-left: 665px">
          <img src="src/assets/images/footer/推特.png" alt="" style="width: 30px; height: 30px; margin-left: 10px">
          <img src="src/assets/images/footer/telegram.png" alt="" style="width: 30px; height: 30px; margin-left: 10px">
          <img src="src/assets/images/footer/xbox.png" alt="" style="width: 30px; height: 30px; margin-left: 10px">
          <img src="src/assets/images/footer/Youtube.png" alt="" style="width: 30px; height: 30px; margin-left: 10px">
        <div style="margin-top: 15px">
          <a style="margin-left: 280px; font-size: 16px; color: white">商城 | 游戏 | 政企服务 | 集团隐私政策 | 公司儿童信息保护规则 | 商城隐私政策 | 商城用户协议 | 问题反馈 | Select Location</a>
        </div>
        <div style="margin-top: 10px">
          <a style="margin-left: 315px;font-size: 16px; color: white"> 互联网ICP备案:沪ICP备13002172号-3 沪-非经营性-2016-0143 营业性演出许可证 沪市文演(经)00-2253 | </a>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "Home",
  components: {},
  data() {
    return {
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: 10,
      name: "",
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {},
      list:[
        {img:require('../../assets/images/Carousel/img.png')},
        {img:require('../../assets/images/Carousel/img_1.png')},
        {img:require('../../assets/images/Carousel/img_2.png')},
        {img:require('../../assets/images/Carousel/img_3.png')},
        {img:require('../../assets/images/Carousel/img_4.png')},
      ]
    }
  },
  created() {
    this.load()
  },
  methods: {
    load: function () {
      this.request.get("/goods/page", {
        params: {
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          name: this.name,
        }
      }).then(res => {
        this.tableData = res.data.records
        this.total = res.data.total
      })
    },
    home() {
      this.$router.push("/")
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },
  }
}
</script>
<style scoped>
li:hover {
  color: orangered;
}
.goodsName {
  font-size: 14px;
  text-align: center;
  cursor: pointer
}
.price {
  font-size: 16px;
  font-weight: bold;
  color: orangered;
  cursor: pointer;
}
.refer {
  padding: 2px;
  cursor: pointer;
  /*margin-top: 5px;*/
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.base {
  margin: 0px;
  padding: 0px;
}
img {
  width: 100%;
  height: 100%;
}
.phone {
  background-color: white;
  padding-bottom: 10px;
  font-size: 16px;
  text-align: center;
  width: 160px;
  height: 150px;
  transition: all 0.8s;
  margin: 60px 16px;
}
.box~.phone{
  margin-left: 30px;
}
.box0 img{
  width: 170px;
  height: 170px;
}
.phone:hover{
  transform: scale(1.08);
}
</style>
商品详情页:
 源代码Detail.Vue
<template>
  <div style="padding: 10px 0">
    <el-card style="width: 1100px; margin-left: 200px">
      <div style="display: flex">
        <div style="width: 350px">
          <el-image :src="goods.url" :preview-src-list="[goods.url]" style="width: 80%; position: center; margin-top: 30px"></el-image>
        </div>
        <div style="flex: 1; padding-left: 50px">
          <div class="goodsName">{{ goods.name }}</div>
          <div class="refer">{{ goods.refer }}</div>
          <div class="price">¥{{ goods.price }}</div>
          <div class="refer" style="margin-top: 10px;">库存{{ goods.num }}台</div>
          <div class="goodsName">
            <el-input-number size="big" v-model="form.number" :min="1" :max="100" label="数量"></el-input-number>
          </div>
          <div class="goodsName" >
<!--            <template v-slot="scope">-->
              <el-button class="addCar" size="big" @click="buy(scope.row.id)"><i class="el-icon-bank-card"/> 直接购买</el-button>
              <el-button class="addCar" size="big" v-on:click="addCart"><i class="el-icon-shopping-cart-1" /> 加入购物车</el-button>
<!--            </template>-->
          </div>
          <a size="big" @click="chat" style="float: left; color: #DF3033; cursor: pointer; padding-left: 10px">联系商家</a>
        </div>
      </div>
    </el-card>
  </div>
</template>
<script>
import {serverIp} from "../../../public/config";
const baseUrl = `http://${serverIp}:9090`
export default {
  name: "Detail",
  data() {
    return {
      id: this.$route.query.id,
      goods: {},
      form: {number: 1},
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
    }
  },
  created() {
    this.load()
  },
  methods: {
    load() {
      this.request.get("/goods/" + this.id).then(res => {
        this.goods = res.data
      })
    },
    chat() {
      this.$router.push("/im")
    },
    addCart() {
      if (!this.user.username) {
        this.$message.warning("请登录后操作")
        return
      }
      this.form.goodsId = this.goods.id  //商品ID
      this.request.post("/cart", this.form).then(res => {
        if (res.code === '200') {
          this.$message.success("加入购物车成功")
        } else {
          this.$message.error(res.msg)
        }
      })
    },
    buy(goodsId) {
      fetch(baseUrl + '/api/buy?goodsId=' + goodsId, {
        headers: {
          'Content-Type': 'application/json;charset=utf-8'
        },
        method: 'POST'
      }).then(res => res.json()).then(res => {
        if (res) {
          this.$message.success("下单成功")
          this.load()
        } else {
          this.$message.error("下单失败")
        }
      })
    }
  }
}
</script>
<style scoped>
.addCar {
  padding: 10px;
  /*margin-left: 10px;*/
  width: 140px;
  height: 45px;
  background-color: #DF3033;
  color: white;
  font-weight: bold;
  font-size: 18px
}
.goodsName {
  padding: 10px;
  font-size: 22px;
  font-weight: bold;
}
.refer {
  padding: 10px;
  font-size: 16px;
  font-weight: bold
}
.price {
  height: 60px;
  padding: 15px 10px 10px 20px;
  background-color: #FCE5E5;
  color: orangered;
  font-weight: bold;
  margin-left: 10px;
  margin-top: 10px;
  font-size: 24px;
}
</style>
我的购物车:
可以勾选商品进行计算价格,然后进行商品结算 去订单页面付款,也可以选择删除购物车里面的商品。


我的订单:
可以查看自己的订单的商品,设置5s时间自动关闭。支付使用支付宝沙箱支付,对于已经支付的订单无法二次支付,即使支付也会报错,对于已支付的订单也不能进行取消。




返回数据:
支付宝进行回调信息存储到数据库,后台可以接收信息。

进行支付之后,后台可以对订单进行发货,用户在我的订单里面也可以看见订单状态是否发货,从而进行收货操作。
个人信息:

源代码Person.vue
<template>
  <el-card style="width: 500px; margin-left: 50px">
    <el-form label-width="80px" size="small">
      <el-form-item label="用户名">
        <el-input v-model="form.username" disabled autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="昵称">
        <el-input v-model="form.nickname" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="性别">
        <el-input v-model="form.sex" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="邮箱">
        <el-input v-model="form.email" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="电话">
        <el-input v-model="form.phone" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="地址">
        <el-input type="textarea" v-model="form.address" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="save">保 存</el-button>
        <el-button type="primary" @click="sign" disabled><i class="el-icon-location" />定位</el-button>
        <el-button type="success" @click="return1" style="float: right">返回主页</el-button>
      </el-form-item>
    </el-form>
  </el-card>
</template>
<script>
export default {
  name: "Person",
  data() {
    return {
      form: {},
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
    }
  },
  mounted() {
    // 获取地理位置
    var geolocation = new BMapGL.Geolocation();
    geolocation.getCurrentPosition(function(r){
      if(this.getStatus() == BMAP_STATUS_SUCCESS){
        const province = r.address.province
        const city = r.address.city
        localStorage.setItem("address", province + city)
      }
    });
  },
  created() {
    this.request.get("/user/username/" + this.user.username).then(res => {
      if (res.code === '200') {
        this.form = res.data
      }
    })
  },
  methods: {
    sign() {
      const address = localStorage.getItem("address")
      const username = this.user.username
      this.request.post("/user", { user: username, address: address }).then(res => {
        if (res.code === '200') {
          this.$message.success("获取成功")
        } else {
          this.$message.error(res.msg)
        }
      })
    },
    save() {
      this.request.post("/user", this.form).then(res => {
        if (res.data) {
          this.$message.success("保存成功")
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    return1() {
      this.$router.push("/")
    }
  }
}
</script>
<style>
</style>
联系商家:
此功能可以参考我的另外一篇文章:(2条消息) SpringBoot实现简易聊天室_慕言要努力的博客-CSDN博客

后台页面:





核心代码:
1.UserController
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.Constants;
import com.example.demo.common.Result;
import com.example.demo.controller.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.service.IUserService;
import com.example.demo.utils.TokenUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    private IUserService userService;
    @PostMapping("/login")
    public Result login(@RequestBody UserDTO userDTO) {
        String username = userDTO.getUsername();
        String password = userDTO.getPassword();
        if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {
            return Result.error(Constants.CODE_400,"参数错误");
        }
        UserDTO dto = userService.login(userDTO);
        return Result.success(dto);
    }
    @PostMapping("/register")
    public Result register(@RequestBody UserDTO userDTO) {
        String username = userDTO.getUsername();
        String password = userDTO.getPassword();
        if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {
            return Result.error(Constants.CODE_400,"参数错误");
        }
        return Result.success(userService.register(userDTO));
    }
    //新增或者更新
    @PostMapping
    public Result save(@RequestBody User user) {
        return Result.success(userService.saveOrUpdate(user));
    }
    //删除
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
        return Result.success(userService.removeById(id));
    }
    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {//批量删除
        return Result.success(userService.removeByIds(ids));
    }
    //    //查询所有数据
//    @GetMapping
//    public Result findAll() {
//        return Result.success(userService.list());
//    }
//
    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
        return Result.success(userService.getById(id));
    }
    @GetMapping("/username/{username}")
    public Result findOne(@PathVariable String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        return Result.success(userService.getOne(queryWrapper));
    }
    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        if (!"".equals(username)) {
            queryWrapper.like("username", username);
        }
        return Result.success(userService.page(new Page<>(pageNum, pageSize), queryWrapper));
    }
}
2.TokenUtils
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.demo.entity.User;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Component
public class TokenUtils {
    private static IUserService staticUserService;
    @Resource
    private IUserService userService;
    @PostConstruct
    public void setUserService() {
        staticUserService = userService;
    }
    /**
     * 生成token
     *
     * @return
     */
    public static String genToken(String userId, String sign) {
        return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期
                .sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
    }
    /**
     * 获取当前登录的用户信息
     *
     * @return user对象
     */
    public static User getCurrentUser() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = request.getHeader("token");
            if (StrUtil.isNotBlank(token)) {
                String userId = JWT.decode(token).getAudience().get(0);
                return staticUserService.getById(Integer.valueOf(userId));
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }
}
3.CartController
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.Result;
import com.example.demo.entity.Cart;
import com.example.demo.entity.User;
import com.example.demo.mapper.CartMapper;
import com.example.demo.service.ICartService;
import com.example.demo.utils.TokenUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@CrossOrigin
@RestController
@RequestMapping("/cart")
public class CartController {
    @Resource
    private ICartService cartService;
    @Resource
    private CartMapper cartMapper;
    //新增或者更新
    @PostMapping
    public Result save(@RequestBody Cart cart) {
        Integer userId = TokenUtils.getCurrentUser().getId();
        //相同商品进行处理
        Integer goodsId = cart.getGoodsId();
        QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("goods_id", goodsId);
        queryWrapper.eq("user_id", userId);
        Cart db = cartService.getOne(queryWrapper);
        if (db != null) {
//            db.setNumber(db.getNumber() + cart.getNumber());
            db.setNumber(db.getNumber() + cart.getNumber());
            cartService.updateById(db);
            return Result.success();
        }
        //新增或更新
        if (cart.getId() == null) {
            cart.setUserId(userId);
        }
        cartService.saveOrUpdate(cart);
        return Result.success();
    }
    @PostMapping("/number/{id}/{number}")
    public Result updateNum(@PathVariable Integer id, @PathVariable Integer number) {
        cartMapper.updateNum(number, id);
        return Result.success();
    }
    //删除
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
        return Result.success(cartService.removeById(id));
    }
    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {//批量删除
        return Result.success(cartService.removeByIds(ids));
    }
    //查询所有数据
    @GetMapping
    public Result findAll() {
        return Result.success(cartService.list());
    }
    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
        return Result.success(cartService.getById(id));
    }
//    @GetMapping("/id/{id}")
//    public Result findOne(@PathVariable String userId) {
//        QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("userId", userId);
//        return Result.success(cartService.getOne(queryWrapper));
//    }
    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name) {
        User currentUser = TokenUtils.getCurrentUser();
        Integer userId = currentUser.getId();
        String role = currentUser.getRole();
        return Result.success(cartMapper.page(new Page<>(pageNum, pageSize), userId, role, name));
    }
}
4.EchartsController
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Quarter;
import com.example.demo.common.Result;
import com.example.demo.entity.User;
import com.example.demo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/echarts")
public class EchartsController {
    @Autowired
    private IUserService userService;
    @GetMapping("/example")
    public Result get() {
        Map<String, Object> map = new HashMap<>();
        map.put("x", CollUtil.newArrayList("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"));
        map.put("y", CollUtil.newArrayList(150, 230, 224, 218, 135, 147, 260));
        return Result.success(map);
    }
    @GetMapping("/members")
    public Result members() {
        List<User> list = userService.list();
        int q1 = 0;  //第一季度
        int q2 = 0;  //第二季度
        int q3 = 0;  //第三季度
        int q4 = 0;  //第四季度
        for (User user : list) {
            Date createTime = user.getCreateTime();
            Quarter quarter = DateUtil.quarterEnum(createTime);
            switch (quarter) {
                case Q1: q1 += 1; break;
                case Q2: q2 += 1; break;
                case Q3: q3 += 1; break;
                case Q4: q4 += 1; break;
                default: break;
            }
        }
        return Result.success(CollUtil.newArrayList(q1, q2, q3, q4));
    }
}
5.FileController
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.entity.Files;
import com.example.demo.entity.Goods;
import com.example.demo.mapper.FileMapper;
import com.example.demo.mapper.GoodsMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
@RestController
@RequestMapping("/file")
public class FileController {
    @Value("${files.upload.path}")
    private String fileUploadPath;
    @Value("${server.ip}")
    private String serverIp;
    @Resource
    private FileMapper fileMapper;
    @Resource
    private GoodsMapper goodsMapper;
    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String type = FileUtil.extName(originalFilename);
        long size = file.getSize();
        //定义一个文件的唯一标识码
//        String uuid = IdUtil.fastSimpleUUID();
//        String fileUUID = uuid + StrUtil.DOT + type;
//        File uploadFile = new File(fileUploadPath + fileUUID);
        //先存储到磁盘
        File parentFile = new File(fileUploadPath);
//        File parentFile = uploadFile.getParentFile();
        //判断目录是否存在,不存在就新建
        if (!parentFile.exists()) {
             parentFile.mkdirs();
        }
        String uuid = IdUtil.fastSimpleUUID();
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);
        file.transferTo(uploadFile);
        String url = "http://" + serverIp + ":9090/file/" + fileUUID;
        //存储到数据库
        Files saveFile = new Files();
        Goods goods = new Goods();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size/1024);
        saveFile.setUrl(url);
        goods.setUrl(url);
        fileMapper.insert(saveFile);
        goodsMapper.insert(goods);
        return url;
//        String md5 = SecureUtil.md5(file.getInputStream());
//        Files files = getFileByMd5(md5);
//
//        String url;
//        if (files != null) {
//            url = files.getUrl();
//        } else {
//            file.transferTo(uploadFile);
//            url = "http://localhost:9090/file/" + fileUUID;
//        }
//        //存储到数据库
//        Files saveFile = new Files();
//        saveFile.setName(originalFilename);
//        saveFile.setType(type);
//        saveFile.setSize(size/1024);
//        saveFile.setUrl(url);
//        saveFile.setMd5(md5);
//        fileMapper.insert(saveFile);
//        return url;
    }
    @GetMapping("/{fileUUID}")
    public void download(@PathVariable String fileUUID, HttpServletResponse response) throws IOException {
        // 根据文件的唯一标识码获取文件
        File uploadFile = new File(fileUploadPath + fileUUID);
        // 设置输出流的格式
        ServletOutputStream os = response.getOutputStream();
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUID, "UTF-8"));
        response.setContentType("application/octet-stream");
        // 读取文件的字节流
        os.write(FileUtil.readBytes(uploadFile));
        os.flush();
        os.close();
    }
    /**
     * 通过文件的md5查询文件
     * @param md5
     * @return
     */
    private Files getFileByMd5(String md5) {
        // 查询文件的md5是否存在
        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("md5", md5);
        List<Files> filesList = fileMapper.selectList(queryWrapper);
        return filesList.size() == 0 ? null : filesList.get(0);
    }
}
6.MybatisPlusConfig
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.example.demo.mapper")
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
7.Constants
public interface Constants {
    String CODE_200 = "200"; //成功
    String CODE_400 = "400"; //参数不足
    String CODE_401 = "401"; //权限不足
    String CODE_500 = "500"; //系统错误
    String CODE_600 = "600"; //其他业务异常
}
完结撒花,对于在线商城这个项目呢,肯定还会有很多的优化,这个项目我也有2个版本,就是服务器上面的和不是服务器上面的,但是基本上都差不多。基本功能呢也都能实现, 只是跟商业的比起来肯定差点意思啦。各位小伙伴觉得笔者写的还不错,就多多点赞,需要源码的也可以私信我哦,看见信息第一时间回复大家。记得点赞关注!!!!
