【基于SprintBoot+Mybatis+Mysql】电脑商城项目之加入购物车和显示购物车列表

news/2025/2/25 15:48:58

🧸安清h:个人主页 

   🎥个人专栏:【Spring篇】【计算机网络】【Mybatis篇】

🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。


目录

🚀1.加入购物车-数据创建

🚀2.加入购物车-实体类

🚀3.加入购物车-持久层

✨3.1规划需要执行的SQL语句

✨3.2设计接口和抽象方法

✨3.3 SQL映射

🚀4.加入购物车-业务层

✨4.1规划异常

✨4.2接口和抽象方法的设计

✨4.3实现接口 

🚀5.加入购物车-控制层

✨5.2设计请求

✨5.3处理请求

🚀6.加入购物车-前端页面

🎯1.显示购物车列表- 持久层

✨1.1规划SQL语句

✨1.2构建VO类

✨1.3设计接口和抽象方法

✨1.4配置SQL映射

🎯2.显示购物车列表- 业务层

🎯3.显示购物车列表- 控制层

✨3.1设计请求

✨3.2处理请求 

🎯4.显示购物车列表- 前端页面

🎃1. 增加购物车商品数量-持久层

✨1.1规划需要执行的SQL语句

✨1.2设计接口和抽象方法

✨1.3配置SQL映射 

🎃 2.增加购物车商品数量-业务层

✨2.1规划异常

✨2.2设计接口和抽象方法

✨ 2.3实现方法

🎃 3.增加购物车商品数量-控制层

✨3.1处理异常

✨3.2设计请求

✨3.3处理请求 

🎃 4.增加购物车商品数量-前端页面


🚀1.加入购物车-数据创建

CREATE TABLE t_cart (
	cid INT AUTO_INCREMENT COMMENT '购物车数据id',
	uid INT NOT NULL COMMENT '用户id',
	pid INT NOT NULL COMMENT '商品id',
	price BIGINT COMMENT '加入时商品单价',
	num INT COMMENT '商品数量',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (cid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

num:当用户重复添加商品时,只修改num就可以了,无需再重复添加商品。 

🚀2.加入购物车-实体类

public class Cart extends BaseEntity{
    private Integer cid;
    private Integer uid;
    private Integer pid;
    private Long price;
    private Integer num;
。。。。。。
}

🚀3.加入购物车-持久层

✨3.1规划需要执行的SQL语句

1.向购物车表中插入数据。

insert into t_cart values(值列表)

2.当当前的商品已经在购物车中存在,则直接更新num的数量即可。

update t_cart set num=? where cid=?

 3.在插入或更新具体执行那个语句,取决于数据库中是否有当前的这个购物车商品的数据,得去查询才能查询。对当前的用户的pid进行查询,加上uid=?,而不是对当前的整张表进行查询。

select * from t_cart where pid=? and uid=?

✨3.2设计接口和抽象方法

创建一个CartMapper接口持久层的文件。

/**
     * 插入购物车数据
     * @param cart 购物车数据
     * @return 受影响的行数
     * 插入时最好放在一个对象中传递,所以用Cart参数列表
     * 插入后在业务层可能调用,需要有返回值判断能否插入成功
     */
    Integer insert(Cart cart);

    /**
     * 更新购物车某件商品的数量
     * @param cid 购物数据id
     * @param num 更新的数量
     * @param modifiedUser 修改人
     * @param modifiedTime 修改时间
     * @return 受影响的行数
     * 更新时涉及到修改人和修改时间,除了cid外,还需要知道数量num
     */
    Integer updateNumByCid(Integer cid, Integer num, String modifiedUser, Date modifiedTime);

    /**
     * 根据用户的id和商品的id来查询购物车中的数据
     * @param uid 用户id
     * @param pid 商品id
     */
    Cart findByUidAndPid(Integer uid,Integer pid);

✨3.3 SQL映射

1.创建一个CartMapper.xml映射文件,添加以上三个抽象方法的SQL映射。

<?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.cy.store.mapper.CartMapper">
    <resultMap id="CartEntityMap" type="com.cy.store.entity.Cart">
        <id property="cid" column="cid"/>
        <result column="created_user" property="createdUser"></result>
        <result column="created_time" property="createdTime"></result>
        <result column="modified_user" property="modifiedUser"></result>
        <result column="modified_time" property="modifiedTime"></result>
    </resultMap>

    <insert id="insert" useGeneratedKeys="true" keyProperty="cid">
        insert into t_cart (uid, pid, price, num, created_user, created_time, modified_user, modified_time)
        values (#{uid}, #{pid}, #{price}, #{num}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
    </insert>

    <update id="updateNumByCid">
        update t_cart set num=#{num},modified_user=#{modifiedUser},modified_time=#{modifiedTime}
        where cid=#{cid}
    </update>
    
    <select id="findByUidAndPid" resultMap="CartEntityMap">
        select * from t_cart where pid=#{pid} and uid=#{uid}
    </select>
</mapper>

2.进行测试 

@SpringBootTest
public class CartMapperTests {
    @Autowired
    private CartMapper cartMapper;

    @Test
    public void insert(){
        Cart cart = new Cart();
        cart.setNum(3);
        cart.setPid(10000002);
        cart.setUid(6);
        cartMapper.insert(cart);
    }

    @Test
    public void updateNumByCid(){
        cartMapper.updateNumByCid(1,6,"小明",new Date());
    }

    @Test
    public void findByUidAndPid(){
        Cart cart = cartMapper.findByUidAndPid(6,10000002);
        System.err.println(cart);
    }
}

🚀4.加入购物车-业务层

✨4.1规划异常

1.插入数据时可能产生异常:InsertException。

2.更新数据时可能产生异常:UpdateException。

✨4.2接口和抽象方法的设计

分析:

1.首先要进行商品的查询,这里面通过findByUidAndPid方法必须要传递过来的是uid和pid的字段。

2.假设拿到后,就要对原有的数据进行更新,需要传递的字段有:cid,num,username。

    /**
     * 将商品添加到购物车中
     * @param uid 用户id
     * @param pid 商品id
     * @param amount 新增数量
     * @param username 用户名(修改者)
     */
    void addToCart(Integer uid,Integer pid,Integer amount,String username);

✨4.3实现接口 

1.创建一个CartServiceImpl的实现类。

@Service
public class CartServiceImpl implements ICartService {
//购物车的业务层依赖于购物车的持久层和商品的持久层
    @Autowired
    private CartMapper cartMapper;
    @Autowired
    //购物车中的一些定义的字段实际上是从商品表中分离出来的
    private ProductMapper productMapper;

    @Override
    public void addToCart(Integer uid, Integer pid, Integer amount, String username) {
        //查询当前要添加的购物车是否在表中已存在
        Cart result = cartMapper.findByUidAndPid(uid,pid);
        Date date = new Date();
        if(result == null){  //表示这个商品从来没有被添加到购物车中,则进行新增操作
            //创建一个Cart对象
            Cart cart = new Cart();
            //补全数据:首先补全参数传递过来的数据
            cart.setPid(pid);
            cart.setUid(uid);
            //这里的amount暂且理解为在前端加好,把总数传递过来
            cart.setNum(amount);
            //补全价格:来自于商品中的数据
            Product product = productMapper.findById(pid);
            cart.setPrice(product.getPrice());
            //补全四日志
            cart.setCreatedUser(username);
            cart.setCreatedTime(date);
            cart.setModifiedUser(username);
            cart.setModifiedTime(date);
            //执行数据的插入操作
            Integer rows = cartMapper.insert(cart);
            
            if(rows != 1){
                throw new InsertException("插入数据时产生未知的异常");
            }
        }else{  //表示当前商品已经存在于购物车中,则更新这条数据的num值
            Integer num = result.getNum()+amount;
            Integer cid = result.getCid();
            Integer rows = cartMapper.updateNumByCid(cid,num,username,date);
            if(rows != 1){
                throw new UpdateException("更新时产生未知的异常");
            }
        }
    }
}

2. 再创建对应的测试类CartServiceTests。

@SpringBootTest
public class CartServiceTests {
    @Autowired
    private ICartService cartService;

    @Test
    public void addToCart(){
        cartService.addToCart(6,10000013,2,"北伐不成功不改名");
    }
}

🚀5.加入购物车-控制层

1.没有需要处理的异常。

✨5.2设计请求

请求路径:/carts/add_to_cart

请求方式:POST

请求数据:Integer pid,Integer amount,HttpSession session

响应结果:JsonResult<Void>

✨5.3处理请求

1.创建一个CartController类,具体代码如下:

@RequestMapping("carts")
@RestController
public class CartController extends BaseController{
    @Autowired
    private ICartService cartService;

    @RequestMapping("add_to_cart")
    public JsonResult<Void> addToCart(Integer pid, Integer amount, HttpSession session){
        cartService.addToCart(getuidFromSession(session),pid,amount,getUsernameFromSession(session));
        return new JsonResult<>(OK);
    }
}

 2.登录后访问:http://localhost:8080/carts/add_to_cart?pid=10000003&amount=1

🚀6.加入购物车-前端页面

在product.html页面给【加入购物车】按钮添加点击事件,并发送ajax请求。

$("#btn-add-to-cart").click(function (){
				$.ajax({
					url:"/carts/add_to_cart",
					type:"POST",
					data:{
						"pid":id,
						"amount":$("#num").val()
					},
					dataType:"JSON",
					success:function (json){
						if(json.state==200){
							alert("加入购物车成功");
						}else{
							alert("加入购物车失败");
						}
					},
					error:function (xhr){
						alert("加入购物车时产生未知的异常"+xhr.message);
					}
				});
			});

在ajax函数中data参数的数据设置的方式:

  • data:$("form表单选择").serialize()。适合要么就是可以全部选择的,或者手动输入的串的类型。当参数过多并且在同一个字符串中。
  • data:new FormData($("form表单选择")[0])。只适用提交文件,其他形式提交不了。
  • data:"username=Tom"。适合参数值固定并且参数值列表有限,可以进行手动拼接。
    let user = "tom";
    data:"username="+user
  • 适用JSON格式提交数据:
    data:{
        "username":"tom",
        "age":18,
        "sex":0
    }

🎯1.显示购物车列表- 持久层

✨1.1规划SQL语句

分析上图可知,红线框住的图片和商品标题来自于product表,而蓝线框住的应该属于 cart表,单价和数量应该是从购物车中传过来的,再根据两者计算总金额。由于数据来自于两张表中的部分字段,所以要用到关联查询。

#多表查询如果字段不重复则不需要显式声明字段属于哪张表
select cid,
       uid,
       pid,
       t_cart.price,
       t_cart.num,
       t_product.image,
       t_product.title,
       t_product.price as real
from t_cart left join t_product on t_cart.pid=t_product.id
where uid=#{uid}
order by t_cart.createdTime DESC;

✨1.2构建VO类

 VO:Value Object,值对象。当进行select查询时,查询的结果属于多张表中的内容,此时发现结果集不能直接使用某个POJO实体类来接收,POJO实体类不能包含多表查询出来的结果。解决方案:重新构建一个新的对象,这个对象用于存储所查询出来的结果集对应的映射,所以把这样的对象称之为值对象。

在com.cy.store下新建一个包VO,在VO包里创建CartVO类。 

//购物车数据的VO类(Value Object)值对象
public class CartVO implements Serializable {
    private Integer cid;
    private Integer uid;
    private Integer pid;
    private Long price;
    private Integer num;
    private String title;
    private String image;
    private Long realPrice;
......
}

✨1.3设计接口和抽象方法

在CartMapper中编写如下代码:

List<CartVO> findVOByUid(Integer uid);

✨1.4配置SQL映射

    <select id="findVOByUid" resultType="com.cy.store.Vo.CartVO">
        select cid,
               uid,
               pid,
               t_cart.price,
               t_cart.num,
               t_product.image,
               t_product.title,
               t_product.price as realPrice
        from t_cart left join t_product on t_cart.pid=t_product.id
        where uid=#{uid}
        order by t_cart.createdTime DESC
    </select>

单元测试

    @Test
    public void findVOByUid(){
        System.out.println(cartMapper.findVOByUid(6));
    }

🎯2.显示购物车列表- 业务层

1.先编写业务层的接口方法。

List<CartVO> getVOByUid(Integer uid);

2.在实现类中实现方法。

@Override
    public List<CartVO> getVOByUid(Integer uid) {
        List<CartVO> list = cartMapper.findVOByUid(uid);
        return list;
    }

🎯3.显示购物车列表- 控制层

✨3.1设计请求

请求路径:/carts/(只要发一个carts就可以把列表返回,不需要carts下的什么)

请求方式:GET

请求数据:HttpSession session

响应结果:JsonResult<List<CartVO>>

✨3.2处理请求 

1.实现请求处理方法的代码编写。

    @RequestMapping({"/",""})
    public JsonResult<List<CartVO>> getVOByUid(HttpSession session){
        List<CartVO> data = cartService.getVOByUid(getuidFromSession(session));
        return new JsonResult<>(OK,data);
    } 

2.先登录再进行功能测试,访问http://localhost:8080/carts。

🎯4.显示购物车列表- 前端页面

要把cart.html页面通过向Controller层中的getVOByUid方法发送请求就可以返回所有的数据。

1.先注释掉以下代码:

<script src="../js/cart.js" type="text/javascript" charset="utf-8"></script>

2.用户一打开页面就自动发送请求,请求数据。读取form表单,对它的结构做一个了解,因为要把数据显示在form表单中。

  • action="orderConfirm.html"
  • tbody标签的id="cart-list"属性,自动加载的内容需要体现在tbody中。
  • type="button":结算按钮的submit改成button,后续需要传数据。

3.ready()函数来完成自动的ajax请求的提交和处理。

<script type="text/javascript">
				$(document).ready(function () {
				showCartList();
			})
			;

				//展示购物车列表数据
				function showCartList() {
				$("#cart-list").empty();
				$.ajax({
				url: "/carts",
				type: "GET",
				dataType: "JSON",
				success: function(json) {
				let list = json.data;
				for (var i = 0; i < list.length; i++) {
				//用户所拿到的是一个list集合,在这个list集合中封装的是cartVO对象,在这里先拿到这个list集合
				let tr = '<tr>\n' +
				'<td>\n' + 
				//在这里给复选框一个cid值,在往后点击结算的时候会把这个cid值传递给下个页面
				//把这个数据提交给另一个页面是以参数的形式提交,所以这个表单一定要有内部属性
				'<input name="cids" value="#{cid}" type="checkbox" class="ckitem" />\n' +
				'</td>\n' +
				'<td><img src="..#{image}collect.png" class="img-responsive" /></td>\n' +
				'<td>#{title}#{msg}</td>\n' +
				'<td>¥<span id="goodsPrice#{cid}">#{singlePrice}</span></td>\n' +
				'<td>\n' +
				'<input type="button" value="-" class="num-btn" οnclick="reduceNum(1)" />\n' +
				'<input id="goodsCount#{cid}" type="text" size="2" readonly="readonly" class="num-text" value="#{num}">\n' +
				'<input class="num-btn" type="button" value="+" οnclick="addNum(#{cid})" />\n' +
				'</td>\n' +
				'<td><span id="goodsCast#{cid}">#{totalPrice}</span></td>\n' +
				'<td>\n' +
				'<input type="button" οnclick="delCartItem(this)" class="cart-del btn btn-default btn-xs" value="删除" />\n' +
				'</td>\n' +
				'</tr>';
				tr = tr.replaceAll(/#{cid}/g, list[i].cid);
				tr = tr.replaceAll(/#{image}/g, list[i].image);
				tr = tr.replaceAll(/#{title}/g, list[i].title);
				tr = tr.replaceAll(/#{singlePrice}/g, list[i].realPrice);
				tr = tr.replaceAll(/#{num}/g, list[i].num);
				tr = tr.replaceAll(/#{totalPrice}/g, list[i].realPrice * list[i].num);
				if (list[i].realPrice < list[i].price) {
				tr = tr.replace(/#{msg}/g, "比加入时降价" + (list[i].price - list[i].realPrice) + "元");
			} else {
				tr = tr.replace(/#{msg}/g, "");
			}
				$("#cart-list").append(tr);
			}
			},
				error: function (xhr) {
				alert("加载购物车列表数据时产生未知的异常"+xhr.status);
			}
			});
			}

		</script>

🎃1. 增加购物车商品数量-持久层

✨1.1规划需要执行的SQL语句

1.执行更新t_cart表记录的num的值,无需重复开发。

update t_cart set num=#{num},modified_time={modifiedTime},modified_user=#{modifiedUser} where cid=#{cid}

2.根据cid查询购物车的这条记录是否存在。

select * from t_cart where cid=?

✨1.2设计接口和抽象方法

Cart findByCid(Integer cid);

✨1.3配置SQL映射 

    <select id="findByCid" resultMap="CartEntityMap">
        select * from t_cart where cid=#{cid}
    </select>

编写单元测试。

    @Test
    public void findByCid(){
        System.out.println(cartMapper.findByCid(2));
    }

🎃 2.增加购物车商品数量-业务层

✨2.1规划异常

1.在更新时会产生更新异常。

2.查询到的数据是否有访问权限。

3.查询的数据不存在,抛出:CartNotFoundException异常。

public class CartNotFoundException extends ServiceException{
    public CartNotFoundException() {
        super();
    }

    public CartNotFoundException(String message) {
        super(message);
    }

    public CartNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public CartNotFoundException(Throwable cause) {
        super(cause);
    }

    protected CartNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

✨2.2设计接口和抽象方法

    /**
     * 更新用户的购物车数据
     * @param cid 
     * @param uid
     * @param username 
     * @return 增加成功后新的数量
     */
    Integer addNum(Integer cid,Integer uid,String username);

✨ 2.3实现方法

    @Override
    public Integer addNum(Integer cid, Integer uid, String username) {
        Cart result = cartMapper.findByCid(cid);
        if(result == null){
            throw new CartNotFoundException("数据不存在");
        }
        if(!result.getUid().equals(uid)){
            throw new AccessDeniedException("数据非法访问");
        }
        Integer num = result.getNum()+1;
        Integer rows = cartMapper.updateNumByCid(cid,num,username,new Date());
        if(rows != 1){
            throw new UpdateException("更新时产生异常");
        }

        //返回新的购物车总量
        return num;
    }

🎃 3.增加购物车商品数量-控制层

✨3.1处理异常

else if(e instanceof CartNotFoundException) {
            result.setState(4007);
            result.setMessage("购物车数据不存在的异常");
        }

✨3.2设计请求

请求路径:/carts/{cid}/num/add

请求方式:POST

请求数据:Integer cid,HttpSession session

响应结果:JsonResult<Integer>

✨3.3处理请求 

    @RequestMapping("{cid}/num/add")
    public JsonResult<Integer> addNum(Integer cid,HttpSession session){
        Integer data = cartService.addNum(cid,getuidFromSession(session),getUsernameFromSession(session));
        return new JsonResult<>(OK,data);
    }

先登录在访问url地址对应的地址。 

🎃 4.增加购物车商品数量-前端页面

1.前面已经在onclick中改过里面的内容,所以无需重复修改了,但是需要重新编写addNum()以确保点击后能够增加数量。

<input class="num-btn" type="button" value="+" οnclick="addNum(#{cid})" />
			function addNum(cid){
				$.ajax({
					url: "/carts/"+cid+"/num/add",
					type: "POST",
					dataType: "JSON",
					success: function (json) {
						if (json.state == 200) {
							//先拿到数量展示的id,
							$("#goodsCount"+cid).val(json.data)
							//由于price的值不是放在val控件上,也不是放在某一个属性上,通过html来拿,
							//html就是拿到它标签内部的这个东西,如果内部是个串拿到的就是个串
							//获取某个标签内部的内容:文本、标签
							//因为刚好这个内容即:singlePrice作为一个子内容放在了开始和结束的中间
							let price = $("#goodsPrice"+cid).html();
							let totalPrice = price * json.data;

							$("#goodsCast"+cid).html(totalPrice);
						} else {
							alert("增加购物车商品数量失败")
						}
					},
					error: function (xhr) {
						alert("增加购物车商品数量时产生未知的异常!"+xhr.message);
					}
				});
				}

http://www.niftyadmin.cn/n/5865668.html

相关文章

git 克隆及拉取github项目到本地微信开发者工具,微信开发者工具通过git commit、git push上传代码到github仓库

git 克隆及拉取github项目到本地微信开发者工具&#xff0c;微信开发者工具通过git commit、git push上传代码到github仓库 git 克隆及拉取github项目到本地 先在自己的用户文件夹新建一个项目文件夹&#xff0c;取名为项目名 例如这样 C:\Users\HP\yzj-再打开一个终端页面&…

数据库的MVCC如何理解?

数据库的MVCC如何理解&#xff1f; MVCC&#xff08;多版本并发控制&#xff0c;Multi-Version Concurrency Control&#xff09;是数据库系统中的一种并发控制机制&#xff0c;用于允许多个事务在不互相干扰的情况下并行执行&#xff0c;同时保持数据的一致性和隔离性。 MVC…

基于python+django的宠物商店-宠物管理系统源码+运行步骤

该系统是基于pythondjango开发的宠物商店-宠物管理系统。是给师妹开发的课程作业。现将源码开放给大家。大家学习过程中&#xff0c;如遇问题可以在github咨询作者。加油 演示地址 前台地址&#xff1a; http://pet.gitapp.cn 后台地址&#xff1a; http://pet.gitapp.cn/adm…

mac升级系统后聚焦Spotlight Search功能无法使用,进入安全模式可解

mac升级系统后&#xff0c;聚焦功能无法使用&#xff0c;表现为&#xff1a; 1&#xff09;快捷键无法唤起聚焦框 2&#xff09;点击右上角 聚焦图标&#xff08;放大镜&#xff09;&#xff0c;没有任何反应 解决方案&#xff1a; 1&#xff09;聚焦重建索引&#xff0c;无…

ArcGIS Pro中生成带计曲线等高线的全面指南

一、引言 在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;等高线作为表达地形起伏的重要视觉元素&#xff0c;被广泛应用于地图制作、空间分析以及地形可视化等方面。ArcGIS Pro&#xff0c;作为Esri公司推出的新一代GIS平台&#xff0c;提供了强大的空间分析和地…

嵌入式产品级-超小尺寸游戏机(从0到1 硬件-软件-外壳)

Ultra-small size gaming console。 超小尺寸游戏机-Pico This embedded product is mainly based on miniaturization, followed by his game functions are also very complete, for all kinds of games can be played, and there will be relevant illustrations in the fo…

jupyterhub on k8s 配置用户名密码 简单版

如果只是小组内使用 不想共用密码 也不想搞复杂认证方案 那么就直接通过map(用户名,密码md5值)来制定密码 config.yaml部分内容 hub:config:JupyterHub:shutdown_on_logout: true # 用户logout 自动stop jupyter pod,家目录下所有文件会被保存到pvc 即启动后之前家目录下…

图神经网络实战(24)——基于LightGCN构建推荐系统

图神经网络实战&#xff08;24&#xff09;——基于LightGCN构建推荐系统 0. 前言1. Book-Crossing 数据集介绍2. Book-Crossing 数据集预处理3. 构建 LightGCN 架构3.1 LightGCN 架构2. 实现 LightGCN3.3 损失函数3.4 模型训练与测试3.5 生成推荐 小结系列链接 0. 前言 推荐系…