项目目录
32.主页-热销排行-持久层
“热销排行”将显示所有分类中销量最高的4条数据,由于目前没有销量数据,可以将规则调整为“优先级最高的4条数据”。
所以,在查询时,执行的SQL语句应该是:
SELECT
id, title, price, image
FROM
t_goods
WHERE
status=1 AND num>0
ORDER BY
priority DESC
LIMIT 0, 4
则在持久层接口中添加抽象方法:
List<Goods> findByPriority(Integer count);
33.主页-热销排行-业务层
将持久层的抽象方法复制到业务层接口中,并修改方法名称,在业务层实现类中,通过私有方法调用持久层,然后,接口中的公有方法通过调用私有方法实现功能。
34.主页-热销排行-控制器层
在控制器类中添加处理请求的方法
@GetMapping("/hot")
public ResponseResult<List<Goods>> getHotGoods() {
List<Goods> list
= goodsService.getByPriority(4);
return new ResponseResult<List<Goods>>(SUCCESS, list);
}
35.购物车-加入购物车-持久层
1. 数据表
CREATE TABLE t_cart (
id INT AUTO_INCREMENT,
uid INT NOT NULL COMMENT '用户id',
gid BIGINT NOT NULL COMMENT '商品id',
price BIGINT(20) NOT NULL COMMENT '商品价格',
count INT NOT NULL COMMENT '商品数量',
created_user VARCHAR(50),
created_time DATETIME,
modified_user VARCHAR(50),
modified_time DATETIME,
PRIMARY KEY (id)
);
2. 实体类
3. 设计SQL语句
加入购物车的操作有2种情况:此前没有该商品;购物车中已有该商品。前者,应该执行INSERT操作,后者,应该执行UPDATE操作。
至于执行哪种操作,应该先判断“购物车中是否已经存在该商品的信息”:
SELECT
id, count
FROM
t_cart
WHERE
gid=? AND uid=?
新加入此前未加入的商品时:
INSERT INTO t_cart (
除了id以外的所有字段
) VALUES (
值列表
)
如果加入购物车的商品是在购物车中已存在的,应该只更新数量:
UPDATE
t_cart
SET
'count'=?
WHERE
id=?
4. 接口与抽象方法
Cart findByUidAndGid(
@Param("uid") Integer uid,
@Param("goodsId") Long goodsId);
Integer addnew(Cart cart);
Integer updateCount(
@Param("id") Integer id,
@Param("count") Integer count);
5. XML映射
<!-- 新增购物车数据 -->
<!-- Integer addnew(Cart cart) -->
<insert id="addnew"
useGeneratedKeys="true"
keyProperty="id">
INSERT INTO t_cart (
uid, gid,
price, count,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{uid}, #{gid},
#{price}, #{count},
#{createdUser}, #{createdTime},
#{modifiedUser}, #{modifiedTime}
)
</insert>
<!-- 更新购物车中商品的数量 -->
<!-- Integer updateCount(
@Param("id") Integer id,
@Param("count") Integer count) -->
<update id="updateCount">
UPDATE
t_cart
SET
count=#{count}
WHERE
id=#{id}
</update>
<!-- 根据用户id和商品id查询购物车数据 -->
<!-- Cart findByUidAndGid(
@Param("uid") Integer uid,
@Param("goodsId") Long goodsId) -->
<select id="findByUidAndGid"
resultType="cn.tedu.store.entity.Cart">
SELECT
id, count
FROM
t_cart
WHERE
uid=#{uid} AND gid=#{goodsId}
</select>
分析业务
void addToCart(Cart cart)
throws InsertException, UpdateException;
public void addToCart(Cart cart) {
// 1. 查询:findByUidAndGid(uid, gid)
// 2. 新增:addnew(cart)
// 3. 更新:updateCount(id, count)
// -------------------------------
// 根据参数cart中的uid和gid查询数据
// 判断查询结果是否为null
// 是:该用户尚未在购物车中添加该商品,则执行新增
// 否:该用户已经在购物车中添加该商品,则取出此前查询到的数据中的id和count
// -- 根据上一步取出的count与参数cart中的count(此次用户提交的count),相加得到新的count
// -- 执行更新
}
36.购物车-加入购物车-控制器层
1. 处理新异常
无
2. 设计请求
请求路径:/cart/add
请求参数:HttpSession, Cart
请求类型:POST
响应数据:ResponseResult<Void>
是否拦截:拦截,在拦截器黑名单中添加 /cart/**
3. 处理请求
先创建cn.tedu.store.controller.CartController
类,继承自BaseController
,添加@RestController
和@RequestMapping("/cart")
注解,添加@Autowired private ICartService cartService;
属性。
然后,添加处理请求的方法:
@GetMapping("/add")
public ResponseResult<Void> addToCart(HttpSession session, Cart cart) {
// 从session中获取username
// 从session中获取uid
// 将uid封装到cart中
// 执行业务方法
// 返回
}
完成后,通过http://localhost:8080/cart/add?gid=998&price=1024&count=6
进行测试,测试无误,则将请求修改为@PostMapping
37.购物车-显示列表-持久层
显示列表时,单查t_cart
表无法查询到足够显示的数据,需要联合t_goods
表一起查询:
SELECT
t_cart.id,
uid, gid,
t_cart.price AS oldPrice, count,
t_goods.price AS newPrice,
title, image
FROM
t_cart
INNER JOIN
t_goods
ON
t_cart.gid = t_goods.id
WHERE
uid=?
ORDER BY
id DESC
以上查询结果是没有匹配的实体类的!则需要创建与以上查询结果匹配的VO类cn.tedu.store.vo.CartVO
:
public class CartVO {
private Integer id;
private Integer uid;
private Long gid;
private String title;
private String image;
private Integer count;
private Long oldPrice;
private Long newPrice;
// SET/GET,Serializable
}
所以,在接口中的抽象方法应该是:
List<CartVO> findByUid(Integer uid);
配置映射时:
resultType="cn.tedu.store.vo.CartVO"
38.购物车-显示列表-业务层
在业务层接口中添加新的抽象方法:
List<CartVO> getByUid(Integer uid);
在实现类中,先添加与持久层方法一致的私有方法,然后实现接口中定义的公有方法。
39.购物车-显示列表-控制器层
设计请求
请求路径:/cart/list
请求类型:GET / POST
请求参数:HttpSession
响应数据:ResponseResult<List<CartVO>>
是否拦截:是,但无需修改配置
处理请求
@RequestMapping("/list")
public ResponseResult<List<CartVO>> getByUid(HttpSession session) {
// 获取uid
// 查询
// 返回
}
40.购物车-增加数量-持久层
增加数量可以通过现有的updateCount(id, count)
方法来实现,主要过程应该是:根据id找出数据,将原有的数量取出,增加1,然后更新数据表。
则需要新增:
SELECT uid, count FROM t_cart WHERE id=?
对应的方法可以是:
Cart findById(Integer id);
41.购物车-增加数量-业务层
抽象方法:
void addCount(Integer id, Integer uid)
throws CartNotFoundException,
AccessDeniedException,
UpdateException;
实现:
public void addCount(Integer id, Integer uid) {
// 根据id查询数据
// 判断数据是否为null
// 是:抛出异常:CartNotFoundException
// 判断数据归属是否不匹配
// 是:抛出异常:AccessDeniedException
// 获取原来的数量
// 将数量+1
// 更新购物车数据中的数量:updateCount(id, count)
}
42.购物车-增加数量-控制器
@GetMapping("/add_count")
public ResponseResult<Void> addCount(
@RequestParam("id") Integer id,
HttpSession session) {
}
43.确认订单-显示确认页-持久层
在显示“确认订单”页面时,主要显示“所有收货地址”和“前序页面所勾选的购物车数据”,由于“所有收货地址”对应的功能已经完成,无须再次开发,在最后处理界面时直接显示即可。
需要“根据一系列id获取购物车中的数据”的功能,则抽象方法:
List<CartVO> findByIds(Integer[] ids);
映射的SQL语句为:
SELECT
t_cart.id,
uid, gid,
t_cart.price AS oldPrice, count,
t_goods.price AS newPrice,
title, image
FROM
t_cart
INNER JOIN
t_goods
ON
t_cart.gid = t_goods.id
WHERE
t_cart.id IN
<foreach collection="array"
item="id" seperator=","
open="(" close=")">
#{id}
</foreach>
ORDER BY t_cart.id DESC
44.确认订单-显示确认页-业务层
参考其它查询功能完成业务层开发。
45.确认订单-显示确认页-控制器层
@GetMapping("/get_by_ids")
public ResponseResult<List<CartVO>>
getByIds(Integer[] ids) {
// 参考/cart/list
}
46.订单-创建订单-持久层
创建订单数据表
DROP TABLE IF EXISTS t_order;
CREATE TABLE t_order (
id INT AUTO_INCREMENT COMMENT 'id',
uid INT COMMENT '数据归属用户',
recv_name VARCHAR(20) COMMENT '收货人姓名',
recv_phone VARCHAR(20) COMMENT '收货人电话',
recv_district VARCHAR(30) COMMENT '收货的省市区',
recv_address VARCHAR(50) COMMENT '收货详细地址',
pay BIGINT COMMENT '支付金额',
order_time DATETIME COMMENT '下单时间',
status INT COMMENT '订单状态:0-未支付,1-已支付,2-已取消',
created_user VARCHAR(20) COMMENT '创建者',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '最后修改者',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY(id)
) DEFAULT CHARSET=UTF8;
DROP TABLE IF EXISTS t_order_item;
CREATE TABLE t_order_item (
id INT AUTO_INCREMENT COMMENT 'id',
oid INT COMMENT '归属订单的id',
goods_id BIGINT COMMENT '商品id',
goods_image VARCHAR(500) COMMENT '商品图片',
goods_title VARCHAR(100) COMMENT '商品标题',
goods_count INT COMMENT '商品数量',
goods_price BIGINT COMMENT '商品价格',
created_user VARCHAR(20) COMMENT '创建者',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '最后修改者',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY(id)
) DEFAULT CHARSET=UTF8;
创建实体类
创建cn.tedu.store.entity.Order
和cn.tedu.store.entity.OrderItem
这2个实体类。
接口
创建cn.tedu.store.mapper.OrderMapper
接口,并声明2个抽象方法:
Integer insertOrder(Order order);
Integer insertOrderItem(OrderItem orderItem);
映射文件
复制得到OrderMapper.xml
并配置:
<!-- 插入订单数据 -->
<!-- Integer insertOrder(Order order) -->
<insert id="insertOrder"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_order (
uid,
recv_name, recv_phone,
recv_district, recv_address,
pay, order_time, status,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{uid},
#{recvName}, #{recvPhone},
#{recvDistrict}, #{recvAddress},
#{pay}, #{orderTime}, #{status},
#{createdUser}, #{createdTime},
#{modifiedUser}, #{modifiedTime}
)
</insert>
<!-- 插入订单商品数据 -->
<!-- Integer insertOrderItem(OrderItem orderItem) -->
<insert id="insertOrderItem"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_order_item (
oid,
goods_id,
goods_image, goods_title,
goods_price, goods_count,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{oid},
#{goodsId},
#{goodsImage}, #{goodsTitle},
#{goodsPrice}, #{goodsCount},
#{createdUser}, #{createdTime},
#{modifiedUser}, #{modifiedTime}
)
</insert>
47.订单-创建订单-业务层
在IAddressService
中声明并完成:
Address getById(Integer id);
创建业务层接口cn.tedu.store.service.IOrderService
并添加抽象方法:
Order createOrder(
Integer uid, String username,
Integer addressId,
Integer[] cartIds);
创建业务层实现类cn.tedu.store.service.impl.OrderServiceImp
,添加注解、声明持久层对象、添加未实现的方法、添加私有方法,然后:
@Autowired
private IAddressService addressService;
@Autowired
private ICartService cartService;
@Transactional
public Order createOrder(Integer uid, String username, Integer addressId, Integer[] cartIds) {
// 创建Date对象
// 声明pay变量
// List<CartVO> cartService.getByIds(ids)
// 遍历集合,过程中,计算总价pay
// 创建List<OrderItem> orderItems
// -- 创建OrderItem
// -- item属性:goods_5,OK
// -- item属性:4个日志,OK
// 创建Order对象
// order属性:uid,OK
// order属性:pay,OK
// 通过addressService.getById()得到收货地址数据
// order属性:recv_4,OK
// order属性:order_time,OK
// order属性:status,OK,值为0
// order属性:4个日志,OK
// 插入订单数据并获取oid:insertOrder(order)
// 遍历orderItems
// item属性:oid
// 插入订单商品数据
// }
}
控制器方法:
@RequestMapping("/create")
public ResponseResult<Order> createOrder(
HttpSession session,
Integer addressId, Integer[] cartIds) {
}
48.订单-查询订单详情-持久层
分析SQL语句:
select
t_order.id,
uid,
recv_name, recv_phone,
recv_district, recv_address,
pay, status,
goods_id,
goods_image, goods_title,
goods_price, goods_count
from
t_order
inner
join t_order_item
on
t_order.id=t_order_item.oid
where
t_order.id=?
以上查询结果没有匹配的实体类,且以上查询是关联查询,通常需要有匹配的VO类,则创建cn.tedu.store.vo.OrderVO
类:
public class OrderVO {
private Integer id;
private Integer uid;
private String recvName;
private String recvPhone;
private String recvDistrict;
private String recvAddress;
private Long pay;
private Integer status;
private List<OrderItem> items;
// SET / GET / Serializable
}
则接口中的抽象方法:
OrderVO findById(Integer id);
然后,在配置映射时,还需要先配置<resultMap>
:
<!-- 查询订单详情得到OrderVO的映射配置 -->
<resultMap id="Order_VO_Map"
type="cn.tedu.store.vo.OrderVO">
<id column="id" property="id" />
<result column="uid" property="uid" />
<result column="recv_name" property="recvName" />
<result column="recv_phone" property="recvPhone" />
<result column="recv_district" property="recvDistrict" />
<result column="recv_address" property="recvAddress" />
<result column="pay" property="pay" />
<result column="status" property="status" />
<collection property="items"
ofType="cn.tedu.store.entity.OrderItem">
<result column="goods_id" property="goodsId" />
<result column="goods_image" property="goodsImage" />
<result column="goods_title" property="goodsTitle" />
<result column="goods_price" property="goodsPrice" />
<result column="goods_count" property="goodsCount" />
</collection>
</resultMap>
<!-- 根据id查询订单详情 -->
<!-- OrderVO findById(Integer id) -->
<select id="findById" resultMap="Order_VO_Map">
SELECT
t_order.id,
uid,
recv_name, recv_phone,
recv_district, recv_address,
pay, status,
goods_id,
goods_image, goods_title,
goods_price, goods_count
FROM
t_order
INNER JOIN
t_order_item
ON
t_order.id=t_order_item.oid
WHERE
t_order.id=#{id}
</select>
完成后,执行单元测试。
OrderVO
[
id=4,
uid=1,
recvName=小范同学,
recvPhone=,
recvDistrict=山东省, 莱芜市, 钢城区,
recvAddress=,
pay=97230,
status=0,
items=[
OrderItem [id=null, oid=null, goodsId=10000022, goodsImage=/images/portal/13LenovoIdeaPad310_black/, goodsTitle=联想(Lenovo)IdeaPad310经典版黑色, goodsCount=10, goodsPrice=5119],
OrderItem [id=null, oid=null, goodsId=10000017, goodsImage=/images/portal/12DELLXPS13-silvery/, goodsTitle=戴尔(DELL)XPS13-9360-R1609 13.3高配版银色, goodsCount=10, goodsPrice=4604]]]