1.开发工具:eclipse、Spring Boot+Thymeleaf + MyBatis、MySQL8.0
2.项目功能:
游客浏览商品,注册用户浏览商品,管理员后台增删改查商品
商品类型(goodstype)的增删查
商品(goodstable)的增删改查
后台顾客信息的删除,注册界面顾客的添加
查看或加入购物车,查看或加入收藏夹,查看或加入用户个人订单,查看用户个人信息。
3.其他功能
后台上传前台商品照片到mysql,前台验证码登录,异常界面处理,商品关键字搜索,前台轮播图展示,后台设置前台的推荐商品栏,后台设置前台的新添加商品栏,后台使用分页查询,前台模拟订单支付。
4.数据库表
在MySQL8中创建数据库shop1
创建8张与系统相关的数据表: ausertable 、busertable、carttable、focustable、goodstable、goodstype、orderdetail和orderbasetable.
一、eclipse中新建spring boot项目。
二、建立如下项目目录的文件。
1.1.1系统设计
电面务平台分为两个子系统,一是后台管理子系统, 二是前台电子商务子系统,下面分别说明这两个子系统的功能需求与模块划分。
1.1.1系统功能需求
1、后台管理子系统
后台管理子系统要求管理员登录成功后,才能对商品进行管理,包括添加商品、查询商品、修改商品以及删除商品。除商品管理外,管理员还需要对商品类型、注册用户以及用户的订单等进行管理。
2、前台电子商务子系统
1)非注册用户
非注册用户或未登录用户具有的功能如下:浏览首页、查看商品详情以及搜索商品。
2)用户
成功登录的用户除具有未登录用户具有的功能外,还具有购买商品、查看购物车、收藏商品、查看订单、查看收藏以及查看用户个人信息的功能。
三、编写HTML代码
addGood.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>商品类型添加页面</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
<body>
<div th:include="admin/header"></div>
<br><br><br>
<div class="container">
<div class="bg-primary" style="width:70%; height: 60px;padding-top: 0.5px;">
<h3 align="center">添加商品</h3>
</div><br>
<form th:action="@{/goods/addGoods?act=add}"
name="myform" method="post"
th:object="${goods}"
class="form-horizontal"
enctype="multipart/form-data" >
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">商品名称</label>
<div class="col-sm-4 col-md-4">
<input type="text" class="form-control"
placeholder="请输入商品名"
th:field="*{gname}"/>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">商品原价</label>
<div class="col-sm-4 col-md-4">
<input type="number" class="form-control"
placeholder="请输入商品原价"
th:field="*{goprice}"/>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">商品折扣价</label>
<div class="col-sm-4 col-md-4">
<input type="number" class="form-control"
placeholder="请输入商品折扣价"
th:field="*{grprice}"/>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">商品库存</label>
<div class="col-sm-4 col-md-4">
<input type="number" class="form-control"
placeholder="请输入商品库存"
th:field="*{gstore}"/>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">商品图片</label>
<div class="col-sm-4 col-md-4">
<input type="file" placeholder="请选择商品图片" class="form-control" name="fileName"/>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">是否推荐</label>
<div class="col-sm-4 col-md-4 radio">
<label>
<input type="radio" th:field="*{isRecommend}" value="1">是
</label>
<label>
<input type="radio" th:field="*{isRecommend}" value="0">否
</label>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">是否广告</label>
<div class="col-sm-4 col-md-4 radio">
<label>
<input type="radio" th:field="*{isAdvertisement}" value="1">是
</label>
<label>
<input type="radio" th:field="*{isAdvertisement}"s value="0">否
</label>
</div>
</div>
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">商品类型</label>
<div class="col-sm-4 col-md-4">
<select class="form-control" th:field="*{goodstype_id}">
<option th:each="gty:${goodsType}" th:value="${gty.id}" th:text="${gty.typename}">
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit"class="btn btn-success" >添加</button>
<button type="reset" class="btn btn-primary" >重置</button>
</div>
</div>
</form>
</div>
</body>
</html>
addType.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>商品类型添加页面</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
<body>
<div th:include="admin/header"></div>
<br><br><br>
<div class="container">
<div class="bg-primary" style="width:70%; height: 60px;padding-top: 0.5px;">
<h3 align="center">添加类型</h3>
</div><br>
<form th:action="@{/type/addType}" name="myform" method="post" th:object="${goodsType}" class="form-horizontal" role="form" >
<div class="form-group has-success">
<label class="col-sm-2 col-md-2 control-label">类型名称</label>
<div class="col-sm-4 col-md-4">
<input type="text" class="form-control"
placeholder="请输入类型名"
th:field="*{typename}"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit"class="btn btn-success" >添加</button>
<button type="reset" class="btn btn-primary" >重置</button>
</div>
</div>
</form>
</div>
</body>
</html>
allOrder.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="@{/}">
<meta charset="UTF-8">
<title>主页</title>
<link rel="stylesheet" href="css/bootstrap.min.css" />
</head>
<body>
<!-- 加载header.html -->
<div th:include="admin/header"></div>
<br><br><br>
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">订单列表</h3>
</div>
<div class="panel-body">
<div class="table table-responsive">
<table class="table table-bordered table-hover">
<tbody class="text-center">
<tr>
<th>订单ID</th>
<th>用户邮箱</th>
<th>订单金额</th>
<th>订单状态</th>
<th>下单日期</th>
</tr>
<tr th:each="ao:${allOrders}">
<td th:text="${ao.id}"></td>
<td th:text="${ao.bemail}"></td>
<td th:text="${ao.amount}"></td>
<td th:text="(${ao.status} == 1)?'已支付':'未支付'"></td>
<td th:text="${ao.orderdate}"></td>
</tr>
<tr>
<td colspan="5" align="right">
<ul class="pagination">
<li><a>第<span th:text="${currentPage}" ></span>页</a></li>
<li><a>共<span th:text="${totalPage}" ></span>页</a></li>
<li>
<span th:if="${currentPage} != 1" >
<a th:href="@{selectOrder?currentPage=${currentPage - 1}}">上一页</a>
</span>
<span th:if="${currentPage} != ${totalPage}" >
<a th:href="@{selectOrder?currentPage=${currentPage - 1}}">下一页</a>
</span>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
allUser.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="@{/}">
<meta charset="UTF-8">
<title>主页</title>
<link rel="stylesheet" href="css/bootstrap.min.css" />
<script src="js/jquery.min.js"></script>
<script type="text/javascript" th:inline="javascript">
function deleteUsers(tid){
$.ajax(
{
//请求路径,要注意的是url和th:inline="javascript"
url : [[@{/deleteUser}]],
//请求类型
type : "post",
//data表示发送的数据
data : {
id : tid
},
//成功响应的结果
success : function(obj){//obj响应数据
if(obj == "no"){
alert("该用户有关联不允许删除!");
}else{
if(window.confirm("真的删除该用户吗?")){
//获取路径
var pathName=window.document.location.pathname;
//截取,得到项目名称
var projectName=pathName.substring(0,pathName.substr(1).indexOf('/')+1);
window.location.href = projectName + obj;
}
}
},
error : function() {
alert("处理异常!");
}
}
);
}
</script>
</head>
<body>
<!-- 加载header.html -->
<div th:include="admin/header"></div>
<br><br><br>
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">商品列表</h3>
</div>
<div class="panel-body">
<div class="table table-responsive">
<table class="table table-bordered table-hover">
<tbody class="text-center">
<tr>
<th>用户ID</th>
<th>用户邮箱</th>
<th>删除</th>
</tr>
<tr th:each="u:${allUsers}">
<td th:text="${u.id}"></td>
<td th:text="${u.bemail}"></td>
<td>
<a th:href="'javascript:deleteUsers('+ ${u.id} +')'" >删除</a>
</td>
</tr>
<tr>
<td colspan="3" align="right">
<ul class="pagination">
<li><a>第<span th:text="${currentPage}" ></span>页</a></li>
<li><a>共<span th:text="${totalPage}" ></span>页</a></li>
<li>
<span th:if="${currentPage} != 1" >
<a th:href="@{selectUser?currentPage=${currentPage - 1}}">上一页</a>
</span>
<span th:if="${currentPage} != ${totalPage}" >
<a th:href="@{selectUser?currentPage=${currentPage - 1}}">下一页</a>
</span>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
四、spring boot层级
SpringBoot分为四层: controller层、 service层 、dao层、entity层
1.entity层:和model层- -样, 存放的是实体类,属性
值与数据库值保持一致, 实现setter和getter方法
AUser.java
package com.ch.ebusiness.entity;
public class AUser {
private String aname;
private String apwd;
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname = aname;
}
public String getApwd() {
return apwd;
}
public void setApwd(String apwd) {
this.apwd = apwd;
}
}
Buser.java
package com.ch.ebusiness.entity;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import org.hibernate.validator.constraints.Length;
public class BUser {
private Integer id;
@NotBlank(message="邮箱必须输入!")
@Email(message="邮箱格式不正确!")
private String bemail;
@NotBlank(message="密码必须输入!")
@Length(min=6, max=20, message="密码长度在6到20之间!")
private String bpwd;
private String rebpwd;
private String code;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBemail() {
return bemail;
}
public void setBemail(String bemail) {
this.bemail = bemail;
}
public String getBpwd() {
return bpwd;
}
public void setBpwd(String bpwd) {
this.bpwd = bpwd;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getRebpwd() {
return rebpwd;
}
public void setRebpwd(String rebpwd) {
this.rebpwd = rebpwd;
}
}
Goods.java
package com.ch.ebusiness.entity;
import org.springframework.web.multipart.MultipartFile;
public class Goods {
private int id;
private String gname;
private double goprice;
private double grprice;
private int gstore;
private String gpicture;
private MultipartFile fileName;
private int goodstype_id;
private String typename;
private int buyNumber;//加入购物车使用
private int isAdvertisement;
private int isRecommend;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGname() {
return gname;
}
public void setGname(String gname) {
this.gname = gname;
}
public double getGoprice() {
return goprice;
}
public void setGoprice(double goprice) {
this.goprice = goprice;
}
public double getGrprice() {
return grprice;
}
public void setGrprice(double grprice) {
this.grprice = grprice;
}
public int getGstore() {
return gstore;
}
public void setGstore(int gstore) {
this.gstore = gstore;
}
public String getGpicture() {
return gpicture;
}
public void setGpicture(String gpicture) {
this.gpicture = gpicture;
}
public int getGoodstype_id() {
return goodstype_id;
}
public void setGoodstype_id(int goodstype_id) {
this.goodstype_id = goodstype_id;
}
public String getTypename() {
return typename;
}
public void setTypename(String typename) {
this.typename = typename;
}
public int getBuyNumber() {
return buyNumber;
}
public void setBuyNumber(int buyNumber) {
this.buyNumber = buyNumber;
}
public int getIsAdvertisement() {
return isAdvertisement;
}
public void setIsAdvertisement(int isAdvertisement) {
this.isAdvertisement = isAdvertisement;
}
public int getIsRecommend() {
return isRecommend;
}
public void setIsRecommend(int isRecommend) {
this.isRecommend = isRecommend;
}
public MultipartFile getFileName() {
return fileName;
}
public void setFileName(MultipartFile fileName) {
this.fileName = fileName;
}
}
2.dao层:即mapper层,对数据库进行持久化操作,他的方法是针对数据库操作额,基本. 上用的就是增删改查,就是一个接口,只有方法名,具体实现在mapper.xmI中。
GoodsRepository.java
package com.ch.ebusiness.repository.admin;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.ch.ebusiness.entity.Goods;
import com.ch.ebusiness.entity.GoodsType;
@Mapper
public interface GoodsRepository {
int addGoods(Goods goods);
int updateGoods(Goods goods);
Goods selectAGoods(Integer id);
int selectAllGoods();
List<GoodsType> selectAllGoodsType();
List<Goods> selectAllGoodsByPage(@Param("startIndex") int startIndex, @Param("perPageSize") int perPageSize);
int deleteAGoods(Integer id);
List<Map<String, Object>> selectFocusGoods(Integer id);
List<Map<String, Object>> selectCartGoods(Integer id);
List<Map<String, Object>> selectOrderGoods(Integer id);
}
TypeRepository.java
package com.ch.ebusiness.repository.admin;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.ch.ebusiness.entity.Goods;
import com.ch.ebusiness.entity.GoodsType;
@Mapper
public interface TypeRepository {
int selectAll();
List<GoodsType> selectAllTypeByPage(@Param("startIndex") int startIndex, @Param("perPageSize") int perPageSize);
int deleteType(int id);
List<Goods> selectGoods(int goodstype_id);
int addType(GoodsType goodsType);
}
AdminMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ch.ebusiness.repository.admin.AdminRepository">
<select id="login" parameterType="AUser" resultType="AUser">
select * from ausertable where aname = #{aname} and apwd = #{apwd}
</select>
</mapper>
IndexMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ch.ebusiness.repository.before.IndexRepository">
<!-- 查询广告商品 -->
<select id="selectAdvertisementGoods" resultType="Goods">
select
gt.*, gy.typename
from
goodstable gt,goodstype gy
where
gt.goodstype_id = gy.id
and gt.isAdvertisement = 1
order by gt.id desc limit 5
</select>
<!-- 查询商品详情 -->
<select id="selectAGoods" resultType="Goods">
select
gt.*, gy.typename
from
goodstable gt,goodstype gy
where
gt.goodstype_id = gy.id
and gt.id = #{id}
</select>
<!-- 查询商品类型 -->
<select id="selectGoodsType" resultType="GoodsType">
select * from goodstype
</select>
<!-- 查询推荐商品 -->
<select id="selectRecommendGoods" resultType="Goods" parameterType="integer">
select
gt.*, gy.typename
from
goodstable gt,goodstype gy
where
gt.goodstype_id = gy.id
and gt.isRecommend = 1
<if test="tid != 0">
and gy.id = #{tid}
</if>
order by gt.id desc limit 6
</select>
<!-- 查询最新商品 -->
<select id="selectLastedGoods" resultType="Goods" parameterType="integer">
select
gt.*, gy.typename
from
goodstable gt,goodstype gy
where
gt.goodstype_id = gy.id
<if test="tid != 0">
and gy.id = #{tid}
</if>
order by gt.id desc limit 6
</select>
<!-- 首页搜索 -->
<select id="search" resultType="Goods" parameterType="String">
select gt.*, gy.typename from GOODSTABLE gt,GOODSTYPE gy where gt.goodstype_id = gy.id
and gt.gname like concat('%',#{mykey},'%')
</select>
</mapper>
TypeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ch.ebusiness.repository.admin.TypeRepository">
<select id="selectAll" resultType="integer">
select count(*) from goodstype
</select>
<!-- 分页查询 -->
<select id="selectAllTypeByPage" resultType="GoodsType">
select * from goodstype limit #{startIndex}, #{perPageSize}
</select>
<!-- 删除类型 -->
<delete id="deleteType" parameterType="integer">
delete from goodstype where id=#{id}
</delete>
<!-- 查询该类型下是否有商品 -->
<select id="selectGoods" parameterType="integer" resultType="Goods">
select * from goodstable where goodstype_id = #{goodstype_id}
</select>
<!-- 添加类型 -->
<insert id="addType" parameterType="GoodsType">
insert into goodstype (id, typename) values(null, #{typename})
</insert>
</mapper>
3.service层:业务层,存放业务逻辑处理,不直接对数据库进行操作,有接口和接口实现类,提供controller层调用方法。
AdminService.java
package com.ch.ebusiness.service.admin;
import javax.servlet.http.HttpSession;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import com.ch.ebusiness.entity.AUser;
@Service
//@Transactional
public interface AdminService {
public String login(AUser aUser, HttpSession session, Model model);
}
GoodsService.java
package com.ch.ebusiness.service.admin;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import com.ch.ebusiness.entity.Goods;
@Service
//@Transactional
public interface GoodsService {
public String selectAllGoodsByPage(Model model, int currentPage, String act);
public String addGoods(Goods goods, HttpServletRequest request, String act) throws IllegalStateException, IOException ;
public String toAddGoods(Goods goods, Model model);
public String detail(Model model, Integer id, String act);
public String delete(Integer id);
}
TypeService.java
package com.ch.ebusiness.service.admin;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import com.ch.ebusiness.entity.GoodsType;
@Service
//@Transactional
public interface TypeService {
public String selectAllTypeByPage(Model model, int currentPage);
public String delete(int id);
public String addType(GoodsType goodsType);
}
4.controller层:控制层,导入service层,调用你service方法,controller通过 接收前端传来的参数进
行业务操作,在返回一个指定的路径或数据表。
AdminBaseController.java
package com.ch.ebusiness.controller.admin;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import com.ch.ebusiness.NoLoginException;
@Controller
public class AdminBaseController {
/**
* 登录权限控制,处理方法执行前执行该方法
*/
@ModelAttribute
public void isLogin(HttpSession session) throws NoLoginException {
if(session.getAttribute("auser") == null){
throw new NoLoginException("没有登录");
}
}
}
AdminController.java
package com.ch.ebusiness.controller.admin;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ch.ebusiness.entity.AUser;
import com.ch.ebusiness.service.admin.AdminService;
@Controller
@RequestMapping("/admin")
public class AdminController {
@Autowired
private AdminService adminService;
@RequestMapping("/toLogin")
public String toLogin(@ModelAttribute("aUser") AUser aUser) {
return "admin/login";
}
@RequestMapping("/login")
public String login(@ModelAttribute("aUser") AUser aUser, HttpSession session, Model model) {
return adminService.login(aUser, session, model);
}
}
GoodsController.java
package com.ch.ebusiness.controller.admin;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ch.ebusiness.entity.Goods;
import com.ch.ebusiness.service.admin.GoodsService;
@Controller
@RequestMapping("/goods")
public class GoodsController extends AdminBaseController{
@Autowired
private GoodsService goodsService;
@RequestMapping("/selectAllGoodsByPage")
public String selectAllGoodsByPage(Model model, int currentPage, String act) {
return goodsService.selectAllGoodsByPage(model, currentPage, act);
}
@RequestMapping("/toAddGoods")
public String toAddGoods(@ModelAttribute("goods") Goods goods, Model model) {
goods.setIsAdvertisement(0);
goods.setIsRecommend(1);
return goodsService.toAddGoods(goods, model);
}
@RequestMapping("/addGoods")
public String addGoods(@ModelAttribute("goods") Goods goods, HttpServletRequest request, String act) throws IllegalStateException, IOException {
return goodsService.addGoods(goods, request, act);
}
@RequestMapping("/detail")
public String detail(Model model, Integer id, String act) {
return goodsService.detail(model, id, act);
}
@RequestMapping("/delete")
@ResponseBody
public String delete(Integer id) {
return goodsService.delete(id);
}
}
五、SpringBoot各 层详解
constant:常量包,存放一些常量数据,如定义服务器响应状态码。
controller:控制器,存放各种控制器,来提供数据或
者返回界面
entity:实体类包,存放各种与数据库对应的实体类
mapper:存放返回数据json的格式样式
service:返回数据给控制调用
六、运行截图
管理员登录界面
管理员后台管理商品界面
商城前台界面
电商平台系统录屏