电脑商城项目05商品详情和购物车

显示商品详情

1.显示商品详情-持久层

1.1规划需要执行的SQL语句

根据商品id显示商品详情的SQL语句

1
SELECT * FROM t_product WHERE id=?

1.2设计接口和抽象方法

在ProductMapper接口中添加抽象方法

1
2
3
4
5
6
/**
* 根据商品id查询商品详情
* @param id 商品id
* @return 匹配的商品详情,如果没有匹配的数据则返回null
*/
Product findById(Integer id);

1.3编写映射

在ProductMapper.xml文件中配置findById(Integer id)方法的映射

1
2
3
<select id="findById" resultMap="ProductEntityMap">
select * from t_product where id=#{id}
</select>

2.显示商品详情-业务层

2.1规划异常

如果商品数据不存在,应该抛出ProductNotFoundException,所以创建ProductNotFoundException异常类并使其继承ServiceException

1
2
3
4
/** 商品数据不存在的异常 */
public class ProductNotFoundException extends ServiceException {
/**重写ServiceException的所有构造方法*/
}

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

1.在业务层IProductService接口中添加findById(Integer id)抽象方法

1
2
3
4
5
6
/**
* 根据商品id查询商品详情
* @param id 商品id
* @return 匹配的商品详情,如果没有匹配的数据则返回null
*/
Product findById(Integer id);

2.在ProductServiceImpl类中,实现接口中的findById(Integer id)抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public Product findById(Integer id) {
Product product = productMapper.findById(id);
// 判断查询结果是否为null
if (product == null) {
throw new ProductNotFoundException("尝试访问的商品数据不存在");
}
// 将查询结果中的部分属性设置为null
product.setPriority(null);
product.setCreatedUser(null);
product.setCreatedTime(null);
product.setModifiedUser(null);
product.setModifiedTime(null);

return product;
}

3.显示商品详情-控制层

3.1处理异常

在BaseController类中的handleException()方法中添加处理ProductNotFoundException的异常

1
2
3
4
else if (e instanceof ProductNotFoundException) {
result.setState(4006);
result.setMessage("访问的商品数据不存在的异常");
}

3.2 设计请求

  • /products/{id}/details
  • Integer id
  • GET
  • JsonResult

3.3处理请求

在ProductController类中添加处理请求的getById()方法

1
2
3
4
5
@GetMapping("{id}/details")
public JsonResult<Product> getById(@PathVariable("id") Integer id) {
Product data = productService.findById(id);
return new JsonResult<Product>(OK, data);
}

4.显示商品详情-前端页面

1.首页将商品id发送给详情页后,详情页需要从url中裁取获得该id,实现方法在jquery-getUrlParam.js中(目前怎么实现裁取可以先不学),所以需要在product.html页面中导入该js文件,这里我在body标签内部的最后引入该js文件

1
<script type="text/javascript" src="../js/jquery-getUrlParam.js"></script>

2.在product.html页面中body标签内部的最后添加获取当前商品详情的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script type="text/javascript">
//调用jquery-getUrlParam.js文件的getUrlParam方法获取商品id
var id = $.getUrlParam("id");
console.log("id=" + id);
$(document).ready(function() {
$.ajax({
url: "/products/" + id + "/details",
type: "GET",
dataType: "JSON",
success: function(json) {
if (json.state == 200) {
console.log("title=" + json.data.title);
//html()方法:
// 假设有个标签<div id="a"></div>
//那么$("#a").html(<p></p>)就是给该div标签加p标签
//$("#a").html("我爱中国")就是给该div标签填充"我爱中国"内容
$("#product-title").html(json.data.title);
$("#product-sell-point").html(json.data.sellPoint);
$("#product-price").html(json.data.price);

for (var i = 1; i <= 5; i++) {
$("#product-image-" + i + "-big").attr("src", ".." + json.data.image + i + "_big.png");
$("#product-image-" + i).attr("src", ".." + json.data.image + i + ".jpg");
}
} else if (json.state == 4006) { // 商品数据不存在的异常
location.href = "index.html";
} else {
alert("获取商品信息失败!" + json.message);
}
}
});
});
</script>

加入购物车

1.创建数据表

1.使用use命令先选中store数据库

1
2
USE store;
1

2.在store数据库中创建t_cart用户数据表

1
2
3
4
5
6
7
8
9
10
11
12
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;

2.创建购物车的实体类

在entity包下创建购物车的Cart实体类并使其继承BaseEntity

1
2
3
4
5
6
7
8
9
10
11
12
13
/**购物车数据的实体类*/
public class Cart extends BaseEntity {
private Integer cid;
private Integer uid;
private Integer pid;
private Long price;
private Integer num;
/**
* get,set
* equals和hashCode
* toString
*/
}

3.加入购物车-持久层

3.1规划需要执行的SQL语句

1.向购物车表中插入商品数据的SQL语句

1
insert into t_cart (除了cid以外的所有字段) values (匹配的值列表);

2.如果当前商品已经在购物车存在,则直接更新商品即可

1
update t_cart set num=? where cid=?

3.在插入或者更新具体执行哪个语句,取决于数据库中是否有当前的这个购物车商品的数据,需要查询语句才能确定

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

3.2设计接口和抽象方法

在mapper包下创建CartMapper接口,并添加抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface CartMapper {

/**
* 插入购物车数据
* @param cart 购物车数据
* @return 受影响的行数
*/
Integer insert(Cart cart);

/** 修改购物车数据中商品的数量*/
Integer updateNumByCid(
@Param("cid") Integer cid,
@Param("num") Integer num,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);

/**
* 根据用户id和商品id查询购物车中的数据
* @param uid 用户id
* @param pid 商品id
* @return 匹配的购物车数据,如果该用户的购物车中并没有该商品,则返回null
*/
Cart findByUidAndPid(
@Param("uid") Integer uid,
@Param("pid") Integer pid);
}

3.3编写映射

在resources.mapper文件夹下创建CartMapper.xml文件,并在文件中配置以上三个方法的映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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 column="cid" property="cid"/>
<result column="created_user" property="createdUser"/>
<result column="created_time" property="createdTime"/>
<result column="modified_user" property="modifiedUser"/>
<result column="modified_time" property="modifiedTime"/>
</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>

<!-- 根据用户id和商品id查询购物车中的数据-->
<select id="findByUidAndPid" resultMap="CartEntityMap">
select * from t_cart where uid=#{uid} AND pid=#{pid}
</select>
</mapper>

3.4单元测试

创建CartMapperTests测试类进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartMapperTests {
@Autowired
private CartMapper cartMapper;

@Test
public void insert() {
Cart cart = new Cart();
cart.setUid(11);
cart.setPid(10000001);
cart.setNum(3);
cart.setPrice(4L);//长整型
cartMapper.insert(cart);
}

@Test
public void updateNumByCid() {
cartMapper.updateNumByCid(1, 4, "张三", new Date());
}

@Test
public void findByUidAndPid() {
Cart cart = cartMapper.findByUidAndPid(11, 10000001);
System.out.println(cart);
}
}

4.加入购物车-业务层

4.1规划异常

在插入数据时,可能抛出InsertException异常;在修改数据时,可能抛出UpdateException异常.这两个异常已开发

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

1.在com.cy.store.service包下创建ICartService接口,并添加抽象方法

该抽象方法都需要哪些参数呢,还是依据持久层,看持久层三条sql语句的实现需要什么参数:

findByUidAndPid:查询购物车数据,参数是uid,pid

insert:插入购物车数据,参数是cart对象(属性有cid,uid,pid,price,num)

updateNumByCid:修改购物车中商品数量,参数是cid,num,modifiedUser,modifiedTime

price可以通过业务层中调用ProductMapper接口的findById获取,modifiedTime在业务层实现类的内部创建,所以需要的参数是uid,pid,num,username

经过这次分析结合以前给业务层方法声明参数,可以发现即使持久层的方法参数是实体类对象,业务层的方法参数也大多不是实体类对象,因为实体类的部分属性是可以在业务层进行拼接然后封装到实体类对象中,再传给持久层(比如这里的price),这样的话就降低了前端传递数据的压力,如果该对象的所有方法都必须由前端传递过来,那么业务层方法参数可以是实体类对象(如注册用户时业务层的方法参数就是User对象)

1
2
3
4
5
6
7
8
9
10
public interface ICartService {
/**
* 将商品添加到购物车
* @param uid 当前登录用户的id
* @param pid 商品的id
* @param amount 增加的数量
* @param username 当前登录的用户名
*/
void addToCart(Integer uid, Integer pid, Integer amount, String username);
}

2.创建CartServiceImpl类,并实现ICartService接口.在类中声明CartMapper持久层对象和IProductService处理商品数据的业务对象,并实现业务层的抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@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) {

//根据参数pid和uid查询购物车中该商品是否已经存在
Cart result = cartMapper.findByUidAndPid(uid, pid);

Integer cid = result.getCid();
Date date = new Date();
if (result == null) {
Cart cart = new Cart();

//封装数据:uid,pid,amount
cart.setUid(uid);
cart.setPid(pid);
cart.setNum(amount);//注意前端传来amount时并没有和数据库商品数量进行求和

//查询商品数据,得到商品价格并封装
Product product = productMapper.findById(pid);
cart.setPrice(product.getPrice());

//封装数据:4个日志
cart.setCreatedUser(username);
cart.setCreatedTime(date);
cart.setModifiedUser(username);
cart.setModifiedTime(date);

Integer rows = cartMapper.insert(cart);
if (rows != 1) {
throw new InsertException("插入数据时出现未知异常");
}
} else {
//从查询结果中取出原数量,与参数amount相加,得到新的数量
Integer num = result.getNum() + amount;//加入购物车时只会有+不可能有-

Integer rows = cartMapper.updateNumByCid(
result.getCid(),
num,
username,
date);
if (rows != 1) {
throw new InsertException("更新数据时产生未知异常");
}
}
}
}

4.3单元测试

创建测试类CartServiceTests并编写测试方法。

1
2
3
4
5
6
7
8
9
10
11
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartServiceTests {
@Autowired
private ICartService cartService;

@Test
public void addToCart() {
cartService.addToCart(11, 10000002, 5, "Tom");
}
}

5.加入购物车-控制层

5.1处理异常

InsertException异常和UpdateException异常都已经设置到BaseController类中了,这里无需重复开发

5.2设计请求

  • /carts/add_to_cart
  • post
  • Integer pid, Integer amount, HttpSession session
  • JsonResult

5.3处理请求

在controller包下创建CartController类并继承BaseController类,在类中添加处理请求的addToCart()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("carts")
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<Void>(OK);
}
}

启动服务,登录账号后在地址栏输入http://localhost:8080/carts/add_to_cart?pid=10000002&amount=5进行测试

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

在product.html页面中的body标签内的script标签里为“加入购物车”按钮添加点击事件

回顾一下在ajax函数中data参数的数据设置的方式

  • data:$(“选择的form表单”).serialize()。当需要提交的参数过多并且在同一个表单中时使用

  • data:new FormData($(“选择的form表单”)[0])。只适用提交文件

  • data:“username=TOM”。手动拼接,适合参数值固定并且参数值列表有限.等同于

    1
    2
    var user = "控件某属性值或控件文本内容或自己声明的值"
    data: "username="+user
  • 使用JSON格式提交数据

    1
    2
    3
    4
    data: {
    "username": "Tom",
    "age": 18
    }
  • 使用RestFul风格不属于前端给后端传参数

这里表单里面有很多无用参数,所以不使用表单提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$("#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("增加失败!" + json.message);
}
},
error: function(xhr) {
alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status);
location.href = "login.html";
}
});
});

点击”加入购物车”按钮后页面跳转的实现:product.html导入的product.js文件里面实现了点击后跳转

显示购物车列表

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

1.1规划需要执行的SQL语句

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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设计接口和抽象方法

1.

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** 购物车数据的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接口中添加抽象方法

1
2
3
4
5
6
/**
* 查询某用户的购物车数据
* @param uid 用户id
* @return 该用户的购物车数据的列表
*/
List<CartVO> findVOByUid(Integer uid);

1.3编写映射

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 查询某用户的购物车数据: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()方法的测试

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

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

2.1 规划异常

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

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

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

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

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

1
2
3
4
@Override
public List<CartVO> getVOByUid(Integer uid) {
return cartMapper.findVOByUid(uid);
}

2.3单元测试

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

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

3.1 处理异常

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

3.2 设计请求

  • /carts/
  • GET
  • HttpSession session
  • JsonResult<List>

3.3 处理请求

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

1
2
3
4
5
@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文件注释掉(这个就是文件的功能:点击”+“,”-“,“删除”,”全选”等按钮时执行相应的操作)

1
<!-- <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标签中编写展示购物车列表的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<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.首先进行查询需要操作的购物车数据信息

1
SELECT * FROM t_cart WHERE cid=?

1.2设计接口和抽象方法

在CartMapper接口中添加抽象方法

1
Cart findByCid(Integer cid);

1.3编写映射

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

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

1.4单元测试

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

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

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

2.1规划异常

  • 在更新时产生UpdateException未知异常,此异常类无需再次创建
  • 可能该购物车列表数据归属不是登录的用户,抛AccessDeniedException异常,此异常类无需再次创建
  • 要查询的数据不存在.抛出CartNotFoundException异常,创建该异常类并使其继承ServiceException
1
2
3
4
/** 购物车数据不存在的异常 */
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类型.这样的话就把数据库中更新后的数量层层传给前端,前端接收后填充到控件中就可以了
1
2
3
4
5
6
7
8
/**
* 增加用户的购物车中某商品的数量
* @param cid
* @param uid
* @param username
* @return 增加成功后新的数量
*/
Integer addNum(Integer cid,Integer uid, String username);

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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异常类的统一管理

1
2
3
4
else if (e instanceof CartNotFoundException) {
result.setState(4007);
result.setMessage("购物车表不存在该商品的异常");
}

3.2设计请求

  • /carts/{cid}/num/add
  • post
  • @PathVariable(“cid”) Integer cid, HttpSession session
  • JsonResult

3.3处理请求

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

1
2
3
4
5
6
7
8
@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()事件,如果已经添加无需重复添加

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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);
}
});
}

电脑商城项目05商品详情和购物车
https://yztldxdz.top/2022/07/28/电脑商城项目05商品详情和购物车/
发布于
2022年7月28日
许可协议