一、新增sku到购物车
sku的官方解释:
SKU全称为Stock Keeping Unit(库存量单位),即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。 SKU这是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。 现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。
前言
我们开发完成了显示商品详情的功能后,可以通过选中具体规格之后要确定购买的sku信息,再点击"添加到购物车"按钮,就应该将选中的sku信息保存在购物车中
业务逻辑分析
1. 判断用户是否登录,只有登陆后才能将商品新增到购物车
2. 验证购物车信息的完整性(SpringValidation)
3. 业务逻辑层要判断新增的sku是否在当前用户的购物车列表中已经存在
如果不存在是新增sku流程
如果已经存在,是修改数量的流程
开发持久层
按上面业务逻辑,持久层要开发多个方法
1. 判断当前登录用户购物车中是否包含指定sku商品的方法
2. 新增sku到购物车中的方法
3. 修改购物车中指定sku数量的方法
在mapper包中创建OmsCartMapper接口,编写代码如下
@Repository
public interface OmsCartMapper{
// 判断当前用户购物车中是否已经包含指定的商品
OmsCart selectExistsCart(@Param("userId") Long userId,
@Param("skuId") Long skuId);
// 新增sku信息到购物车
int saveCart(OmsCart omsCart);
// 修改购物车中sku商品的数量
int updateQuantityById(OmsCart omsCart);
}
对应的OmsCartMapper.xml文件
<?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="cn.tedu.mall.order.mapper.OmsCartMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="cn.tedu.mall.pojo.order.model.OmsCart">
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="sku_id" property="skuId" />
<result column="title" property="title" />
<result column="main_picture" property="mainPicture" />
<result column="price" property="price" />
<result column="quantity" property="quantity" />
<result column="gmt_create" property="gmtCreate" />
<result column="gmt_modified" property="gmtModified" />
<result column="bar_Code" property="barCode" />
<result column="data" property="data" />
</resultMap>
<!-- 定义查询omsCart表时使用的列名sql片段 -->
<sql id="SimpleQueryFields">
<if test="true">
id,
user_id,
sku_id,
title,
main_picture,
price,
quantity,
gmt_create,
gmt_modified
</if>
</sql>
<!-- 判断当前用户购物车中是否已经包含指定的商品 -->
<select id="selectExistsCart" resultMap="BaseResultMap">
select
<include refid="SimpleQueryFields" />
from
oms_cart
where
user_id=#{userId}
and
sku_id=#{skuId}
</select>
<!-- 新增sku信息到购物车 -->
<insert id="saveCart" useGeneratedKeys="true" keyProperty="id">
insert into oms_cart(
user_id,
sku_id,
title,
main_picture,
price,
quantity
) values (
#{userId},
#{skuId},
#{title},
#{mainPicture},
#{price},
#{quantity}
)
</insert>
<!-- 修改购物车中sku商品的数量 -->
<update id="updateQuantityById">
update
oms_cart
set
quantity=#{quantity}
where
id=#{id}
</update>
</mapper>
开发业务逻辑层
创建OmsCartServiceImpl类实现IOmsCartService接口
实现其中方法,先实现新增购物车的方法即可
在编写业务逻辑层具体代码之前,现在该类中编写一个从SpringSecurity上下文中获取用户信息的方法
@Service
@Slf4j
public class OmsCartServiceImpl implements IOmsCartService{
@Autowired
private OmsCartMapper omsCartMapper;
@Override
public void addCart(CartAddDTO cartDTO){
// 要查询购物车中是否有指定商品之前,必须确定用户的Id
Long userId = getUserId();
// 根据用户Id和商品skuId,查询商品信息
OmsCart omsCart = omsCartMapper.selectExistsCart(userId,cartDTO.getSkuId());
// 判断该商品是否存在
if(omsCart == null){
// 如果omsCart为null,表示当前用户没有将这个商品新增到购物车
// 所以执行新增操作,新增操作需要一个OmsCart对象
OmsCart newCart = new OmsCart();
// 将参数CartAddDTO对象中的同名属性赋值给newCart
BeanUtils.copyProperties(cartDTO,newCart);
// CartAddDTO中没有userId属性,需要单独赋值
newCart.setUserId(userId);
// 执行新增操作
omsCartMapper.saveCart(newCart);
}else{
// 如果omsCart不是null,表示当前用户已经将这个商品新增到购物车中了
// 我们需要做的就是将这次新增的数量和原有的数量相加,保存到数据库中
// 我们写的mapper方法是直接修改商品数量的值
// 所以要在java代码层面完成本次业务数量的相加操作
omsCart.setQuantity(omsCart.getQuantity() + cartDTO.getQuantity());
// 确定了数量之后,直接调用修改购物车数量方法即可
omsCartMapper.updateQuantityById(omsCart);
}
}
@Override
public JsonPage<CartStandardVO> listCart(Integer page,Integer pageSize){
return null;
}
@Override
public void removeCart(Long[] ids){
}
@Override
public void removeAllCarts(){
}
@Override
public void removeUserCarts(OmsCart omsCCart){
}
@Override
public void updateQuantity(CartUpdateDTO cartUpdateDTO){
}
// 业务逻辑层中有获得当前登录用户信息的需求
// 我们的项目会在控制器方法运行前运行的过滤器中,解析前端传入的JWT
// 将解析获得的用户信息保存在SpringSecurity上下文中
// 这里可以编写方法从SpringSecurity上下文中获得用户信息
public CsmallAuthenticationInfo getUserInfo(){
// 编写SpringSecurity上下文中获得用户信息的代码
UsernamePasswordAuthenticationToken authenticationToken =
(UsernamePasswordAuthenticationToken)
SecurityContextHolder.getContext().getAuthentication();
// 为了逻辑严谨性,判断一下SpringSecurity上下文中的信息是不是null
if(authenticationToken == null){
throw new CoolSharkServiceException(
ResponseCode.UNAUTHORIZED,"您没有登录!");
}
// 确定authenticationToken不为null
// 就可以从中获得用户信息了
CsmallAuthenticationInfo csmallAuthenticationInfo =
(CsmallAuthenticationInfo)authenticationToken.getCredentials();
// 返回
return csmallAuthenticationInfo;
}
// 业务逻辑层中的方法实际上都只需要用户的id即可
// 我们可以再编写一个方法,从用户对象中获得id
public Long getUserId(){
return getUserInfo().getId();
}
}
开发控制层
创建OmsCartController
@RestController
@RequestMapping("/oms/cart")
@Api(tags = "购物车管理模块")
public class OmsCartController{
@Autowired
private IOmsCartSerivce omsCartService;
@PostMapping("/add")
@Apioperation("新增购物车信息")
// 在程序运行控制方法前,已经运行了过滤器中解析JWT的代码,
// 解析正确的话,用户信息已经保存在了SpringSecurity上下文中
// 酷鲨商城前台用户登录时,我们编写的代码会向用户信息中固定设置一个ROLE_user的角色
// 下面的注解,主要目的是判断用户是否登录,权限统一都是ROLE_user
// 如果用户没有登录,是无法运行控制方法的!401错误
@PreAuthorize("hasAuthority('ROLE_user')")
// @Validated注解是激活SpringValidation框架用的
// 参数CartAddDTO中,各个属性设置了验证规则,如果有参数值不符合规则
// 会抛出BindException异常,之后会运行统一异常处理类中专门的方法,控制器方法终止
public JsonResult addCart(@Validated CartAddDTO cartAddDTO){
omsCartService.addCart(cartAddDTO);
return JsonResult.ok("新增sku到购物车完成!");
}
}
测试
先启动Nacos/Seata
启动passport/order
sso: 10002
order: 10005
先访问10002前台用户登录获得JWT 用户名jackson密码123456
先登录看到JWT然后复制JWT
转到10005 order模块 文档管理->全局参数设置->添加参数
参数名:Authorization
参数值:Bearer [粘贴JWT]
然后刷新当前10005的界面
然后进行发送请求即可成功
二、开发查询购物车功能
开发持久层
OmsCartMapper添加方法如下
// 根据用户id查询购物车中sku信息
List<CartStandardVO> selectCartsByUserId(Long userId);
OmsCartMapper.xml添加对应内容
<!-- 根据用户id查询购物车中sku信息 -->
<!--
我们使用resultType声明返回类型时
Mybatis内置按驼峰命名法自动生成列名对属性名的映射
列名 属性名
id id
user_id userId
main_picture mainPicture
title title
-->
<select id="selectCartsByUserId"
resultType="cn.tedu.mall.pojo.order.vo.CartStandardVO">
select
<include refid="SimpleQueryFields" />
from
oms_cart
where
user_id=#{id}
</select>
开发业务逻辑层
OmsCartServiceImpl业务实现
返回值支持分页结果,按分页条件查询
@Override
public JsonPage<CartStandardVO> listCarts(Integer page,Integer pageSize){
// 从SpringSecurity上下文中获得用户id
Long userId = getUserId();
// 要想执行分页查询,先设置分页条件
PageHelper.startPage(page,pageSize);
// 设置完分页条件执行的查询,会自动在sql语句后添加limit关键字
List<CartStandardVO> list = omsCartMapper.selectCartsByUserId(userId);
// list是分页数据,实例化PageInfo对象将分页数据传入,转成JsonPage返回
return JsonPage.restPage(new pageInfo<>(list));
}
开发控制层
下面开发控制层,调用方法进行测试
OmsCartController添加方法如下
@GetMapping("/list")
@ApiOperation("根据用户Id分页查询购物车sku列表")
@ApiImplicitParams({
@ApiImplicitParam(value = "页码", name = "page", example = "1"),
@ApiImplicitParam(value = "每页条数", name = "pageSize", example = "10")
})
@PreAuthorize("hasAuthority('ROLE_user')")
public JsonResult<JsonPage<CartStandardVO>> listCartsByPage(
// 控制器方法中的参数可以使用@RequestParam注解来赋默认值
// WebConsts是我们自己编写的常量类,DEFAULT_PAGE:1 DEFAULT_PAGE_SIZE:20
@RequestParam(required = false,defaultValue = WebConsts.DEFAULT_PAGE)
Integer page,
@RequestParam(required = false,defaultValue = WebConsts.DEFAULT_PAGE_SIZE)
Integer pageSize){
JsonPage<CartStandardVO> jsonPage=
omsCartService.listCarts(page, pageSize);
return JsonResult.ok(jsonPage);
}
在上面测试了新增购物车环境的基础上
重启order模块再次测试
三、删除/清空购物车
持久层
我们删除购物车的功能支持同时删除一个或多个购物车中的商品
基本思路就是将要删除的购物车商品的id数组传入到Mapper中进行删除
在OmsCartMapper中添加方法
// 根据用户选中的一个或多个id,删除购物车中的商品(批量删除操作)
int deleteCartsByIds(Long[] ids);
OmsCartMapper.xml新增代码
<!-- 根据用户选中的一个或多个id,删除购物车中的商品(批量删除操作) -->
<!-- collection属性,编写的值是数组时,可以写array或ids -->
<delete id="deleteCartsByIds" >
delete from
oms_cart
where
id in
<foreach collection="array" item="id" separator=","
open="(" close=")">
#{id}
</foreach>
</delete>
业务逻辑层
OmsCartServiceImpl添加方法
// 支持批量删除购物车中sku商品的方法
@Override
public void removeCart(Long[] ids) {
// 调用mapper中批量删除的方法即可
int rows=omsCartMapper.deleteCartsByIds(ids);
if(rows==0){
throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
"您要删除的商品已经删除了!");
}
}
控制层
OmsCartController
@PostMapping("/delete")
@ApiOperation("根据id数组删除购物车中的sku信息")
@ApiImplicitParam(value = "包含要删除id的数组",name = "ids",
required = true, dataType = "array")
// 当@PreAuthorize注解后面要判断的权限内容以ROLE_开头时
// 表示我们判断的内容是SpringSecurity框架约定的角色
// 我们可以在@PreAuthorize注解()里使用hasRole来简化对角色的判断
// hasRole('user')这样的判断会检查当前登录用户是否有ROLE_user这个角色
// 也就是会自动在user前加ROLE_来判断
// @PreAuthorize("hasAuthority('ROLE_user')")
@PreAuthorize("hasRole('user')")
public JsonResult removeCartsByIds(Long[] ids){
omsCartService.removeCart(ids);
return JsonResult.ok("删除完成!");
}
重启Order模块,测试删除功能
四、清空当前登录用户购物车的功能
OmsCartMapper
// 清空指定用户购物车中所有sku商品
int deleteCartsByUserId(Long userId);
OmsCartMapper.xml
<!-- 清空指定用户购物车中所有sku商品 -->
<delete id="deleteCartsByUserId">
delete from
oms_cart
where
user_id=#{userId}
</delete>
OmsCartServiceImpl
@Override
public void removeAllCarts() {
Long userId=getUserId();
int rows=omsCartMapper.deleteCartsByUserId(userId);
if(rows==0){
throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
"您的购物车已经是空的了!");
}
}
OmsCartController
@PostMapping("/delete/all")
@ApiOperation("清空当前登录用户的购物车")
@PreAuthorize("hasRole('user')")
public JsonResult removeCartsByUserId(){
omsCartService.removeAllCarts();
return JsonResult.ok("购物车已清空");
}
测试
五、修改购物车的商品数量
开发修改购物车数量的业务逻辑层
因为之前开发新增购物车功能时,我们已经完成了修改购物车数量的持久层,所以不需要再编写了,直接从业务层开始
OmsCartServiceImpl
// 修改购物车中商品数量的业务逻辑层方法
@Override
public void updateQuantity(CartUpdateDTO cartUpdateDTO) {
// 因为执行修改mapper方法要求的参数是OmsCart
// 所以要先实例化OmsCart类型对象
OmsCart omsCart=new OmsCart();
// 然后将参数cartUpdateDTO的同名属性赋值到omsCart中
BeanUtils.copyProperties(cartUpdateDTO,omsCart);
// omsCart被赋必要值之后,直接调用mapper方法即可
omsCartMapper.updateQuantityById(omsCart);
}
控制层OmsCartController
@PostMapping("/update/quantity")
@ApiOperation("修改购物车中sku数量")
@PreAuthorize("hasRole('user')")
public JsonResult updateQuantity(@Validated CartUpdateDTO cartUpdateDTO){
omsCartService.updateQuantity(cartUpdateDTO);
return JsonResult.ok("修改完成!");
}
重启order测试清空和修改购物车数量的功能