10天手敲一个SpringBoot网上商城项目(八)——显示购物车列表功能及增加商品数量功能的实现

静态资源及sql文件分享
链接:https://pan.baidu.com/s/1X-yjmQcPD3PqS21x0HplNA?pwd=23gr
提取码:23gr

显示购物车列表

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

1.1规划需要执行的SQL语句

这里需要将商品表和购物车表进行连表查询

显示某用户的购物车列表数据的SQL语句大致是

select
	cid, #日后勾选购物车商品模块需要用到cid来确定勾选的是购物车表的哪一条数据
	
	uid, #感觉没必要,因为uid可以从session中拿的呀,难道是为
	#了后面提交购物车订单时判断提交的商品的uid和登录的uid是否一致?
	
	pid, #日购提交订单模块需要用到pid来确定购买的是商品表的哪件商
	#品,然后对商品表的该商品的库存,销售热度等信息进行修改
	
	t_cart.price, #两个表都有该字段,需要指定获取的是哪个数据表的
	
	t_cart.num, #两个表都有该字段且含义不同,需要指定获取的是哪个数据表的
	
	title,
	
	t_product.price as realPrice, #为了在购物车列表页展示两个价格的差值
	
	image
	
from t_cart
left join t_product on t_cart.pid = t_product.id #把t_cart作为主表(老师说现在处理的是购物车表的数据所以让其为主表,我不明白)
where
	uid = #{uid}
order by
	t_cart.created_time desc #进行排序使最新加入购物车的在最上面
1.2设计接口和抽象方法

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

在store包下创建一个vo包,在该包下面创建CartVO类,不需要继承BaseController类,那相应的就需要单独实现Serializable接口

/** 购物车数据的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 Long realPrice;
    private String image;
/**
 * get,set
 * equals和hashCode
 * toString
 */
}

2.在CartMapper接口中添加抽象方法

/**
 * 查询某用户的购物车数据
 * @param uid 用户id
 * @return 该用户的购物车数据的列表
 */
List<CartVO> findVOByUid(Integer uid);
1.3编写映射

1.在CartMapper.xml文件中添加findVOByUid()方法的映射

<!-- 查询某用户的购物车数据:List<CartVO> findVOByUid(Integer uid) -->
<select id="findVOByUid" resultType="com.cy.store.vo.CartVO">
    select
        cid,
        uid,
        pid,
        t_cart.price,
        t_cart.num,
        title,
        t_product.price as realPrice,
        image
    from t_cart
    left join t_product on t_cart.pid = t_product.id
    where
    	uid = #{uid}
    order by
    	t_cart.created_time desc
</select>
1.4单元测试

在CartMapperTests测试类中添加findVOByUid()方法的测试

@Test
public void findVOByUid() {
    List<CartVO> list = cartMapper.findVOByUid(11);
    System.out.println(list);
}

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

2.1 规划异常

查询不到就返回空,不需要规划异常

2.2设计接口和抽象方法及实现

1.在ICartService接口中添加findVOByUid()抽象方法

/**
 * 查询某用户的购物车数据
 * @param uid 用户id
 * @return 该用户的购物车数据的列表
 */
List<CartVO> getVOByUid(Integer uid);

2.在CartServiceImpl类中重写业务接口中的抽象方法

@Override
public List<CartVO> getVOByUid(Integer uid) {
    return cartMapper.findVOByUid(uid);
}
2.3单元测试

该业务层只是调用了持久层的方法并返回,可以不再测试

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

3.1 处理异常

业务层没有抛出异常,所以这里不需要处理异常

3.2 设计请求
  • /carts/
  • GET
  • HttpSession session
  • JsonResult<List<CartVO>>
3.3 处理请求

在CartController类中编写处理请求的代码。

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

启动服务,登录后在地址栏输入http://localhost:8080/carts进行测试

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

1.将cart.html页面的head头标签内引入的cart.js文件注释掉(这个就是文件的功能:点击"+“,”-",“删除”,"全选"等按钮时执行相应的操作)

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

多说一下,form标签的action="orderConfirm.html"属性(规定表单数据提交到哪里)和结算按钮的类型"type=submit"是必不可少的,这样点击"结算"时才能将数据传给"确认订单页"并在"确认订单页"展示选中的商品数据

当然也可以把这两个删掉,然后给结算按钮添加"type=button"然后给该按钮绑定一个点击事件实现页面跳转和数据传递,但是这样太麻烦了

2.在cart.html页面body标签内的script标签中编写展示购物车列表的代码

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

    //展示购物车列表数据
    function showCartList() {
        $("#cart-list").empty();
        $.ajax({
            url: "/carts",
            type: "GET",
            dataType: "JSON",
            success: function(json) {
                var list = json.data;
                for (var i = 0; i < list.length; i++) {
                    var tr = '<tr>\n' +
                        '<td>\n' +
                        '<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>

这tr变量是怎么声明的呢:

先敲下var=‘’;然后在上面的html里面找到tbody下的任意一个tr标签复制在单引号里面,然后删掉制表符.最后对该字符串稍加改动:

1.第18行name=“cids” value="#{cid}"是为"点击结算按钮跳转到确认订单页面"模块做准备。这两个属性都是自己添加的,在tbody复制的tr标签里面没有,这两个属性是为了跳转到"确认订单页"时能够携带该参数(比如传递cids=1)

2.第26οnclick="addNum(#{cid})“是为"在购物车列表增加商品数量"模块做准备。是为了点击”+"后能调用addNum函数并传入对应的cid

3.第22行id="goodsPrice#{cid}"和第25行id="goodsCount#{cid}"和第28行id="goodsCast#{cid}"都是为"在购物车列表增加商品数量"模块做准备。在后端更新完商品数量相应的前端页面也要更新:

  • 根据id="goodsCount#{cid}"获取数量相关的控件后更新其value属性的值(value属性用.val()赋值)
  • 根据id="goodsPrice#{cid}"获取价格相关的控件后拿到其单价
  • 将单价和数量相乘后,根据id="goodsCast#{cid}"获取总价相关的控件并更新其文本值(文本用.html()更新)

4.上面这三条都是和本模块无关的,其余的修改都是和本模块相关的,在tbody复制的tr标签里面都有,比葫芦画瓢就可以了

点击"结算"按钮页面跳转的实现:在cart.html页面点击"结算"后会跳转到"确认订单页"并将表单中的数据作为参数传递给"确认订单页"

增加商品数量

购物车详情页点击"+“”-"修改商品数量时必须和数据库进行交互,因为这是即使展示给用户的,不能说用户看到的数量是5,结果数据库的购物车表中的数量是4吧?

但是在商品详情页点击"+“”-“修改商品数量时可以不用和数据库进行交互而是等到用户点击"加入购物车"后再进行交互,因为在用户点击"加入购物车"之前并不需要将商品数量更新到购物车表,可以去看看这个项目的商品详情页,那里点击”+“”-“修改商品数量时就是js实现的,并没有和数据库交互.(如果加一个模块:商品详情页点击”+“”-"时要知道库存够不够用户选择的这个数量,此时就需要和数据库交互了)

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

1.1规划需要执行的SQL语句

1.更新该商品的数量.此SQL语句无需重复开发

2.首先进行查询需要操作的购物车数据信息

SELECT * FROM t_cart WHERE cid=?
1.2设计接口和抽象方法

在CartMapper接口中添加抽象方法

Cart findByCid(Integer cid);
1.3编写映射

在CartMapper文件中添加findByCid(Integer cid)方法的映射

<select id="findByCid" resultMap="CartEntityMap">
    select * from t_cart where cid=#{cid}
</select>
1.4单元测试

在CartMapperTests测试类中添加findByCid()测试方法

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

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

2.1规划异常
  • 在更新时产生UpdateException未知异常,此异常类无需再次创建
  • 可能该购物车列表数据归属不是登录的用户,抛AccessDeniedException异常,此异常类无需再次创建
  • 要查询的数据不存在.抛出CartNotFoundException异常,创建该异常类并使其继承ServiceException
/** 购物车数据不存在的异常 */
public class CartNotFoundException extends ServiceException {
    /**重写ServiceException的所有构造方法*/
}
2.2设计接口和抽象方法及实现

在业务层ICartService接口中添加addNum()抽象方法

1.先判断需要哪些参数,该抽象方法的实现依赖于CartMapper接口的两个方法:

updateNumByCid方法.参数是cid,num,String modifiedUser,Date modifiedTime

findByCid方法.参数是cid

在业务层中从购物车表查询到该商品的数量,然后再和前端传过来的增加的数量进行求和得到num

所以该方法的参数是cid,uid,username

2.判断一下该方法的返回值:

  • 该方法返回值void.这样的话就需要在前端页面加location.href使该页面自己跳转到自己,实现刷新页面(不建议,每次都加载整个页面,数据量太大了)
  • 返回值是Integer类型.这样的话就把数据库中更新后的数量层层传给前端,前端接收后填充到控件中就可以了
/**
* 增加用户的购物车中某商品的数量
* @param cid
* @param uid
* @param username
* @return 增加成功后新的数量
*/
Integer addNum(Integer cid,Integer uid, String username);

3.在CartServiceImpl类中实现接口中的抽象方法

@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;
}
2.3单元测试

就接收个参数,然后业务层将其加一后返回,不需要再测了

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

3.1处理异常

在BaseController类中添加CartNotFoundException异常类的统一管理

else if (e instanceof CartNotFoundException) {
    result.setState(4007);
    result.setMessage("购物车表不存在该商品的异常");
}
3.2设计请求
  • /carts/{cid}/num/add
  • post
  • @PathVariable(“cid”) Integer cid, HttpSession session
  • JsonResult<Integer>
3.3处理请求

在CartController类中添加处理请求的addNum()方法

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

启动服务,登录后在地址栏输入http://localhost:8080/carts/1/num/add进行验证

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

1.首先确定在showCartList()函数中动态拼接的增加购物车按钮是绑定了addNum()事件,如果已经添加无需重复添加

<input class="num-btn" type="button" value="+" onclick="addNum(#{cid})" />

2.在script标签中定义addNum()函数并编写增加购物车数量的逻辑代码

function addNum(cid) {
    $.ajax({
        url: "/carts/"+cid+"/num/add",
        type: "POST",
        dataType: "JSON",
        success: function (json) {
            if (json.state == 200) {
                $("#goodsCount"+cid).val(json.data);//字符串+整数cid后结果为字符串

                //更新该商品总价
                /*
                            html()方法:
                            不传参:是获取某个标签内部的内容(文本或标签)
                            传参:将参数放到标签里面替换掉该标签原有内容
                            * */
                var price = $("#goodsPrice"+cid).html();
                var totalPrice = price * json.data;

                //将商品总价更新到控件中
                $("#goodsCast"+cid).html(totalPrice);
            } else {
                alert("增加购物车商品数量失败"+json.message);
            }
        },
        error: function (xhr) {
            alert("增加购物车商品数量时产生未知的异常!"+xhr.message);
        }
    });
}
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

认真生活的灰太狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值