60. 商品-显示详情-持久层
(a) 规划所需要执行的SQL语句
select * from t_product where id=?
(b) 接口与抽象方法
Product findById(Integer id);
(c) 配置映射
映射:
<!-- 根据商品id查询商品详情 -->
<!-- Product findById(Integer id) -->
<select id="findById"
resultMap="ProductEntityMap">
SELECT
*
FROM
t_product
WHERE
id=#{id}
</select>
测试:
@Test
public void findById() {
Integer id = 10000001;
Product result = mapper.findById(id);
System.err.println(result);
}
61. 商品-显示详情-业务层
(a) 规划可能出现的异常
创建ProductNotFoundException
。
(b) 业务接口及抽象方法
Product getById(Integer id);
(c) 实现抽象方法
先将持久层的方法复制到实现类,实现为私有方法:
/**
* 根据商品id查询商品详情
* @param id 商品id
* @return 匹配的商品详情,如果没有匹配的数据,则返回null
*/
private Product findById(Integer id) {
return productMapper.findById(id);
}
然后,重写接口中的抽象方法:
@Override
public Product getById(Integer id) {
Product product = findById(id);
if (product == null) {
throw new ProductNotFoundException(
"获取商口数据失败!尝试访问的数据不存在!");
}
product.setCategoryId(null);
product.setPriority(null);
product.setCreatedUser(null);
product.setCreatedTime(null);
product.setModifiedUser(null);
product.setModifiedTime(null);
return product;
}
测试:
@Test
public void getById() {
try {
Integer id = 10088001;
Product result = service.getById(id);
System.err.println(result);
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
62. 商品-显示详情-控制器层
(a) 处理新创建的异常
处理ProductNotFoundException
。
(b) 设计所需要处理的请求
// http://localhost:8080/products/10000002/details
请求路径:/products/{id}/details
请求参数:@PathVariable("id") Integer id
请求方式:GET
响应结果:JsonResult<Product>
(c) 处理请求
// http://localhost:8080/products/10000002/details
@GetMapping("{id}/details")
public JsonResult<Product> getById(
@PathVariable("id") Integer id) {
Product data = productService.getById(id);
return new JsonResult<>(OK, data);
}
63. 商品-显示详情-前端页面
常用的jquery函数:
-
empty()
:将某个标签的子级内容清空,例如<h1>xxx</h1>
,如果选中该<h1>
标签后调用该方法,得到的效果将是<h1></h1>
; -
html()
:将某标签的子级设置为参数值,无视该标签原本子级内容,例如调用对某<h1>
对象调用html("HAHA")
,得到的效果将是<h1>HAHA</h1>
; -
append()
:向某标签的子级追加参数值内容,例如存在<h1>HAHA</h1>
,如果对该标签对象调用append("!!!")
,得到的效果将是<h1>HAHA!!!</h1>
; -
val()
:获取/设置某个表单控件的值,调用该函数时,如果没有参数,则表示获取值,如果使用了参数,则表示设置值; -
attr()
:设置某个标签的某个属性的值,该函数有2个参数,第1个参数表示要设置哪个属性,第2个参数表示该属性的值,例如对<img>
标签调用attr("src", "http://www.tedu.cn/logo.png");
就可以设置该图片标签显示的图片路径; -
prop()
:设置某个标签的某个属性的值,其参数列表的意义与attr()
相同,可以简单的理解为:如果某个属性的值是true
/false
,或该属性名与属性值相同,例如readonly="readonly"
,则需要调用prop()
函数,如果是其它属性,则调用attr()
函数;其根本是:attr()
函数用于调整HTML属性,而prop()
函数用于调用对象在JS中的属性。
64. 购物车-创建数据表
CREATE TABLE t_cart (
cid INT AUTO_INCREMENT COMMENT '购物车数据id',
uid INT NOT NULL COMMENT '用户id',
pid INT NOT NULL COMMENT '商品id',
num INT NOT NULL COMMENT '商品数量',
price BIGINT(20) NOT NULL COMMENT '将商品添加到购物车时的价格',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '最后修改人',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY (cid)
) DEFAULT CHARSET=utf8mb4;
65. 购物车-创建实体类
/**
* 购物车数据的实体类
*/
public class Cart extends BaseEntity {
private static final long serialVersionUID = -9051846958681813039L;
private Integer cid;
private Integer uid;
private Integer pid;
private Integer num;
private Long price;
}
66. 购物车-加入购物车-持久层
(a) 规划所需要执行的SQL语句
将商品添加到购物车,向购物车数据表中添加新的数据,需要执行的SQL语句大致是:
insert into t_cart (除了cid以外的字段的列表) values (匹配的值列表);
如果是同一个用户将同一个商品重复添加到购物车中,并不会增加新的数据,而只是在原有的数据基础之上修改数量即可!需要执行的SQL语句大致是:
update t_cart set num=?, modified_user=?, modified_time=? where cid=?
既然“加入购物车”时,可能插入数据,也可能是修改数据,其判断标准应该是“该用户有没有将该商品添加到购物车”,“用户”与“商品”都是判断标准!需要执行的SQL语句大致是:
select * from t_cart where uid=? and pid=?
(b) 接口与抽象方法
创建CartMapper
接口,并声明抽象方法:
/**
* 插入购物车数据
* @param cart 购物车数据
* @return 受影响的行数
*/
Integer insert(Cart cart);
/**
* 修改购物车数据中商品的数量
* @param cid 购物车数据id
* @param num 新的数量
* @param modifiedUser 修改执行人
* @param modifiedTime 修改时间
* @return 受影响的行数
*/
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);
(c) 配置映射
映射:
<mapper namespace="cn.tedu.store.mapper.CartMapper">
<resultMap id="CartEntityMap"
type="cn.tedu.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>
<!-- 插入购物车数据 -->
<!-- Integer insert(Cart cart) -->
<insert id="insert"
useGeneratedKeys="true"
keyProperty="cid">
INSERT INTO t_cart (
uid, pid,
num, price,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{uid}, #{pid},
#{num}, #{price},
#{createdUser}, #{createdTime},
#{modifiedUser}, #{modifiedTime}
)
</insert>
<!-- 修改购物车数据中商品的数量 -->
<!-- Integer updateNumByCid(
@Param("cid") Integer cid,
@Param("num") Integer num,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime) -->
<update id="updateNumByCid">
UPDATE
t_cart
SET
num=#{num},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
WHERE
cid=#{cid}
</update>
<!-- 根据用户id与商品id查询购物车数据 -->
<!-- Cart findByUidAndPid(
@Param("uid") Integer uid,
@Param("pid") Integer pid) -->
<select id="findByUidAndPid"
resultMap="CartEntityMap">
SELECT
*
FROM
t_cart
WHERE
uid=#{uid} AND pid=#{pid}
</select>
</mapper>
测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartMapperTests {
@Autowired
private CartMapper mapper;
@Test
public void insert() {
Cart cart = new Cart();
cart.setUid(1);
cart.setPid(2);
cart.setNum(3);
cart.setPrice(4L);
Integer rows = mapper.insert(cart);
System.err.println("rows=" + rows);
}
@Test
public void updateNumByCid() {
Integer cid = 1;
Integer num = 10;
String modifiedUser = "默认管理员";
Date modifiedTime = new Date();
Integer rows = mapper.updateNumByCid(cid, num, modifiedUser, modifiedTime);
System.err.println("rows=" + rows);
}
@Test
public void findByAid() {
Integer uid = 1;
Integer pid = 2;
Cart result = mapper.findByUidAndPid(uid, pid);
System.err.println(result);
}
}
67. 购物车-加入购物车-业务层
(a) 规划可能出现的异常
无
(b) 业务接口及抽象方法
创建ICartService
接口,并添加抽象方法:
/**
* 将商品添加到购物车
* @param uid 当前登录的用户的id
* @param username 当前登录的用户名
* @param pid 商品id
* @param amount 将商品添加到购物车中的数量
*/
void addToCart(Integer uid, String username, Integer pid, Integer amount);
(c) 实现抽象方法
创建CartServiceImpl
类,实现以上接口,在类的声明之前添加@Service
注解,在类中声明@Autowired private CartMapper cartMapper;
持久层对象,并声明@Autowired private IProductService productService;
处理商品数据的业务对象!将持久层中的3个方法复制到当前实现类中,声明为私有方法,并实现:
/**
* 处理购物车数据的业务层实现类
*/
@Service
public class CartServiceImpl implements ICartService {
@Autowired
private CartMapper cartMapper;
@Autowired
private IProductService productService;
@Override
public void addToCart(Integer uid, String username, Integer pid, Integer amount) {
// TODO Auto-generated method stub
}
/**
* 插入购物车数据
* @param cart 购物车数据
* @return 受影响的行数
*/
private void insert(Cart cart) {
Integer rows = cartMapper.insert(cart);
if (rows != 1) {
throw new InsertException(
"创建购物车数据失败!插入购物车数据时出现未知错误,请联系系统管理员!");
}
}
/**
* 修改购物车数据中商品的数量
* @param cid 购物车数据id
* @param num 新的数量
* @param modifiedUser 修改执行人
* @param modifiedTime 修改时间
* @return 受影响的行数
*/
private void updateNumByCid(Integer cid, Integer num,
String modifiedUser, Date modifiedTime) {
Integer rows = cartMapper.updateNumByCid(cid, num, modifiedUser, modifiedTime);
if (rows != 1) {
throw new InsertException(
"更新商品数量失败!更新购物车数据时出现未知错误,请联系系统管理员!");
}
}
/**
* 根据用户id与商品id查询购物车数据
* @param uid 用户id
* @param pid 商品id
* @return 匹配的购物车数据,如果没有匹配的数据,则返回null
*/
private Cart findByUidAndPid(Integer uid, Integer pid) {
return cartMapper.findByUidAndPid(uid, pid);
}
}
然后,重写抽象方法:
public void addToCart(Integer uid, String username, Integer pid, Integer amount) {
// 创建当前时间对象
// 根据uid和pid查询购物车数据
// 判断查询结果是否为null
// 是:表示该用户尚未将该商品添加到购物车,则需要插入购物车数据
// -- 调用productService.getById()方法查询商品数据
// -- 创建Cart对象
// -- 补全数据:uid, pid, num, price(从商品数据中获取)
// -- 补全数据:4个日志
// -- 调用insert(cart)执行插入数据
// 否:表示该用户已经将该商品添加到购物车,则需要修改购物车数据中商品的数量
// -- 从查询结果中获取cid
// -- 从查询结果中获取num,与参数amount相加,得到新的数量
// -- 调用updateNumByCid(cid, num, modifiedUser, modifiedTime)方法执行修改数量
}
实现代码:
@Override
public void addToCart(Integer uid, String username, Integer pid, Integer amount) {
// 创建当前时间对象
Date now = new Date();
// 根据uid和pid查询购物车数据
Cart result = findByUidAndPid(uid, pid);
// 判断查询结果是否为null
if (result == null) {
// 是:表示该用户尚未将该商品添加到购物车,则需要插入购物车数据
// 调用productService.getById()方法查询商品数据
Product product = productService.getById(pid);
// 创建Cart对象
Cart cart = new Cart();
// 补全数据:uid, pid, num, price(从商品数据中获取)
cart.setUid(uid);
cart.setPid(pid);
cart.setNum(amount);
cart.setPrice(product.getPrice());
// 补全数据:4个日志
cart.setCreatedUser(username);
cart.setCreatedTime(now);
cart.setModifiedUser(username);
cart.setModifiedTime(now);
// 调用insert(cart)执行插入数据
insert(cart);
} else {
// 否:表示该用户已经将该商品添加到购物车,则需要修改购物车数据中商品的数量
// 从查询结果中获取cid
Integer cid = result.getCid();
// 从查询结果中获取num,与参数amount相加,得到新的数量
Integer num = result.getNum() + amount;
// 调用updateNumByCid(cid, num, modifiedUser, modifiedTime)方法执行修改数量
updateNumByCid(cid, num, username, now);
}
}
测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartServiceTests {
@Autowired
private ICartService service;
@Test
public void addToCart() {
try {
Integer uid = 1;
Integer pid = 10000002;
Integer amount = 30;
String username = "土豪";
service.addToCart(uid, username, pid, amount);
System.err.println("OK.");
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
}
68. 购物车-加入购物车-控制器层
(a) 处理新创建的异常
无
(b) 设计所需要处理的请求
请求路径:/carts/add_to_cart
请求参数:Integer pid, Integer num, HttpSession session
请求方式:POST
响应结果:JsonResult<Void>
(c) 处理请求
/**
* 处理购物车相关请求的控制器类
*/
@RestController
@RequestMapping("carts")
public class CartController extends BaseController {
@Autowired
private ICartService cartService;
// http://localhost:8080/carts/add_to_cart?pid=10000005&amount=3
@RequestMapping("add_to_cart")
public JsonResult<Void> addToCart(
Integer pid, Integer amount, HttpSession session) {
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
cartService.addToCart(uid, username, pid, amount);
return new JsonResult<>(OK);
}
}
69. 购物车-加入购物车-前端页面
使用$.ajax()
处理异常请求时,关于data
属性的值,可以是:
-
$("#表单id").serialize()
:适用于表单控件较多的应用场景; -
"参数1=值1&参数2=值2&参数3=值3"
:适用于需要提交的参数较少,且字符串的拼接较简单的应用场景; -
{ "参数1":值1, "参数2":值2, "参数3":值3 }
:适用于需要提交的参数较少,但是字符串的拼接较麻烦的应用场景; -
new FormData($("#表单id")[0])
:仅适用于文件上传,且需要与processData
和contentType
属性一起使用。
70. 【作业】购物车-显示列表-持久层
(a) 规划所需要执行的SQL语句
显示购物车列表时,只会显示当前登录的用户的购物车数据,需要执行的SQL语句大致是:
select
cid, uid, pid, t_cart.num, t_cart.price,
title, t_product.price AS realPrice, image
from
t_cart
left join
t_product
on
t_cart.pid=t_product.id
where
uid=?
order by
t_cart.created_time desc
(b) 接口与抽象方法
在cn.tedu.store
包中创建子级的vo
包,并在这个包中创建CartVO
类:
public class CartVO implements Serializable {
private Integer cid;
private Integer uid;
private Integer pid;
private Integer num;
private String title;
private String image;
private Long price;
private Long realPrice;
// SET/GET/基于cid的hashCode和equals/toString
}
在CartMapper
中添加:
List<CartVO> findVOByUid(Integer uid);
(c) 配置映射
映射:
<select id="xx" resultType="cn.tedu.store.vo.CartVO">
</select>
测试:
...
71. 【作业】购物车-显示列表-业务层
(a) 规划可能出现的异常
(b) 业务接口及抽象方法
(c) 实现抽象方法
72. 【作业】购物车-显示列表-控制器层
(a) 处理新创建的异常
(b) 设计所需要处理的请求
请求路径:
请求参数:
请求方式:
响应结果:JsonResult<?>
(c) 处理请求