JSD-2204-(业务逻辑开发)-续开发购物车功能-新增订单功能-Leaf-Day10

1.续开发购物车功能

1.1续新增sku到购物车

1.1.1开发业务逻辑层

上次课完成了持久层的代码

下面开发业务逻辑层

创建OmsCartServiceImpl类实现IOmsCartService接口

实现其中方法,先实现新增购物车的方法即可

在编写业务逻辑层具体代码前,先在该类中编写一个从SpringSecurity上下文中获取用户信息的方法

@Service
@Slf4j
public class OmsCartServiceImpl implements IOmsCartService {

    @Autowired
    private OmsCartMapper omsCartMapper;

    @Override
    public void addCart(CartAddDTO cartDTO) {
        // 获得当前登录用户id
        Long userId=getUserId();
        // 先检查要添加到购物车中的sku是否已经在当前用户的购物车中了
        OmsCart omsCart=omsCartMapper.selectExistsCart(userId,cartDTO.getSkuId());
        // 判断omsCart是否为null
        if(omsCart==null){
            // 如果oms为null,表示当前用户这个sku没有新增到购物车中,执行新增操作
            // 执行新增,新增方法的参数是omsCart,所以要实例化一个omsCart对象
            OmsCart newOmsCart=new OmsCart();
            // 将CartAddDTO对象的同名属性赋值到newOmsCart中
            BeanUtils.copyProperties(cartDTO,newOmsCart);
            // CartAddDTO对象是没有userId的,需要单独赋值
            newOmsCart.setUserId(userId);
            // 执行新增
            omsCartMapper.saveCart(newOmsCart);
        }else{
            // omsCart不是null,表示当前用户选择的sku已经在购物车中
            // 那么我们需要做的就是修改购物车中sku的数量
            // 因为我们编写的修改数量的方法是直接赋值给数据库,
            // 所以要赋的值需要在java代码中计算好
            omsCart.setQuantity(omsCart.getQuantity()+cartDTO.getQuantity());
            // 调用修改购物车数量的方法
            omsCartMapper.updateQuantityById(omsCart);
        }
    }

    @Override
    public JsonPage<CartStandardVO> listCarts(Integer page, Integer pageSize) {
        return null;
    }

    @Override
    public void removeCart(Long[] ids) {
    }

    @Override
    public void removeAllCarts() {
    }

    @Override
    public void removeUserCarts(OmsCart omsCart) {
    }

    @Override
    public void updateQuantity(CartUpdateDTO cartUpdateDTO) {
    }

    // 业务逻辑层中获得用户信息的方法
    // 目标是从SpringSecurity上下文中获取由JWT解析而来的对象
    public CsmallAuthenticationInfo getUserInfo(){
        // 声明SpringSecurity上下文对象
        UsernamePasswordAuthenticationToken authenticationToken=
                (UsernamePasswordAuthenticationToken)
                        SecurityContextHolder.getContext().getAuthentication();
        // 为了保险起见,判断一下这个对象是否为空
        if(authenticationToken==null){
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"没有登录");
        }
        // 从上下文中获取登录用户的信息
        // 这个信息是由JWT解析获得的
        CsmallAuthenticationInfo csmallAuthenticationInfo=
                (CsmallAuthenticationInfo) authenticationToken.getCredentials();
        // 返回登录信息
        return csmallAuthenticationInfo;
    }
    // 业务逻辑层大多数方法都是只需要用户的ID,所以专门编写一个方法返回id
    public Long getUserId(){
        return getUserInfo().getId();
    }

}

1.1.2开发控制层

创建OmsCartController

@RestController
@RequestMapping("/oms/cart")
@Api(tags = "购物车管理模块")
public class OmsCartController {

    @Autowired
    private IOmsCartService omsCartService;

    @PostMapping("/add")
    @ApiOperation("新增购物车信息")
    // 判断过滤器中对JWT解析出来,并保存在SpringSecurity上下文中的用户
    // 是否具备指定的权限
    // 如果具备,表示当前用户已经登录
    // sso服务器用户登录时,代码中已经写好会保存一个ROLE_user的权限
    @PreAuthorize("hasAuthority('ROLE_user')")
    // @Validated注解是激活SpringValidation框架的验证功能
    // 如果cartAddDTO参数有不符合的值,会抛出BindException异常
    // 这个异常会在统一异常处理类中处理
    public JsonResult addCart(@Validated CartAddDTO cartAddDTO){
        omsCartService.addCart(cartAddDTO);
        return JsonResult.ok("新增sku到购物车完成!");
    }

}

先注意sso模块application-test.yml的地址和端口号和密码(密码有两个)

也要注意order模块application-test.yml的地址和端口号和密码

都保证正确的前提下

先启动Nacos/Seata

启动 passport/order

sso:10002

order:10005

先访问10002前台用户登录获得JWT 用户名jackson密码123456

先登录看到JWT 然后复制JWT

转到10005 order模块 文档管理->全局参数设置->添加参数

参数名:Authorization

参数值:Bearer [粘贴JWT]

然后刷新当前10005的界面

然后进行发送请求即可成功!

如果测试结果中包含一个错误

错误信息里有"xml/bind"的错误信息

order/sso模块需要添加下面依赖即可解决

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>

1.2开发查询购物车功能

1.2.1开发持久层

OmsCartMapper添加方法如下

// 根据用户id查询购物车中sku信息
List<CartStandardVO> selectCartByUserId(Long userId);

OmsCartMapper.xml添加对应内容

<!--  根据用户id查询购物车中sku信息 -->
    <!--  
    resultType指定一个类做返回值,编写这个类的全类名
     自动映射列名和属性名
     user_id   -> userId
     sku_id    -> skuId
     price     -> price
     -->
    <select id="selectCartByUserId" 
            resultType="cn.tedu.mall.pojo.order.vo.CartStandardVO">
        select
            <include refid="SimpleQueryFields" />
        from
            oms_cart
        where
            user_id=#{id}
    </select>

1.2.2开发业务逻辑层

OmsCartServiceImpl业务实现

返回值支持分页结果,按分页条件查询

// 根据用户id分页查询当前用户购物车sku商品列表
@Override
public JsonPage<CartStandardVO> listCarts(Integer page, Integer pageSize) {
    // 从SpringSecurity上下文中获得用户id
    Long userId=getUserId();
    // PageHelper框架设置分页条件
    PageHelper.startPage(page,pageSize);
    // 执行查询,会自动在查询sql语句末尾,添加limit关键字
    List<CartStandardVO> list=omsCartMapper.selectCartByUserId(userId);
    // 将分页结果返回,实例化PageInfo对象,转换为JsonPage
    return JsonPage.restPage(new PageInfo<>(list));
}

1.2.3开发控制层

下面开发控制层,调用方法进行测试

OmsCartController添加方法如下

// 根据用户Id分页查询购物车sku列表
@GetMapping("/list")
@ApiOperation("根据用户Id分页查询购物车sku列表")
@ApiImplicitParams({
    @ApiImplicitParam(value = "页码",name="page",dataType = "int"),
    @ApiImplicitParam(value = "每页条数",name="pageSize",dataType = "int")
})
@PreAuthorize("hasAuthority('ROLE_user')")
public JsonResult<JsonPage<CartStandardVO>> listCartByPage(
    // 在控制器参数位置添加@RequestParam注解
    // 可以设置当前参数为空时的默认值,WebConsts.DEFAULT_PAGE是事先定义好的常量
    @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模块

再次测试http://localhost:10005/doc.html

1.3删除\清空购物车

1.3.1删除购物车的持久层

我们删除购物车的功能支持同时删除一个或多个购物车中的商品

基本思路就是将要删除的购物车商品的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>

1.3.2删除购物车的业务逻辑层

OmsCartServiceImpl添加方法

@Override
public void removeCart(Long[] ids) {
    // 调用mapper删除的方法,完成按ids数组删除购物车的功能
    int rows=omsCartMapper.deleteCartsByIds(ids);
    if(rows==0){
        throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
                "您要删除的商品不存在");
    }
}

1.3.3删除购物车的控制层

OmsCartController

@PostMapping("/delete")
@ApiOperation("根据id数组删除购物车中的sku信息")
@ApiImplicitParam(value = "要删除的id数组",name="ids",
                    required = true,dataType = "array")
// 当@PreAuthorize注解后括号中判断参数为hasRole时
// 相当于在做针对角色(role)的判断,这个写法的效果是对判断内容前(左侧)自动添加"ROLE_"
// 既@PreAuthorize("hasRole('user')") 写法的最终效果就等价于
// @PreAuthorize("hasAuthority('ROLE_user')")
@PreAuthorize("hasRole('user')")
public JsonResult removeCartsByIds(Long[] ids){
    omsCartService.removeCart(ids);
    return JsonResult.ok("运行了删除功能!");
}

其它程序没有变化的前提下

重启Order模块,测试删除功能

**课上作业: **

开发清空当前登录用户购物车的功能

<delete id="deleteCartsByUserId">
    delete from
        oms_cart
    where
        user_id=#{userId}
</delete>
@Override
public void removeAllCarts() {

}

1.3.4清空购物车的功能

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("购物车已清空");
}

测试略!

1.4修改购物车商品数量

开发修改购物车数量的业务逻辑层

因为之前开发新增购物车功能时,我们已经完成了修改购物车数量的持久层,所以不需要再编写了,直接从业务层开始

@Override
public void updateQuantity(CartUpdateDTO cartUpdateDTO) {
    // 当前方法参数是CartUpdateDTO
    // 这个类型中只有id和quantity两个属性
    // 要想实现修改数量,需要将这个类型转换为OmsCart
    // 实例化OmsCart
    OmsCart omsCart=new OmsCart();
    // 同名属性赋值
    BeanUtils.copyProperties(cartUpdateDTO,omsCart);
    // 调用mapper方法进行修改
    omsCartMapper.updateQuantityById(omsCart);
}

控制层OmsCartController

// 修改购物车数量
@PostMapping("/update/quantity")
@ApiOperation("修改购物车数量")
@PreAuthorize("hasRole('user')")
public JsonResult updateQuantity(@Validated CartUpdateDTO cartUpdateDTO){
    omsCartService.updateQuantity(cartUpdateDTO);
    return JsonResult.ok("修改完成!");
}

重启order测试清空和修改购物车数量的功能

1.5新增订单

1.5.1新增订单业务逻辑分析

用户选中购物车中的商品后,点击添加订单

我们要收集订单信息(sku商品信息,价格信息,优惠和运费信息等)然后才能执行生成订单操作

具体步骤如下

1.首先将用户选中的sku库存减少相应的数量

2.用户购物车要删除对应的商品

3.对应oms_order表执行新增,也就是创建一个订单

4.在新增订单成功后,我们还要将订单中的每种商品和订单关系添加在oms_order_item表中

 

除了理解业务之外我们还要确定要使用的技术

除了之前一直使用的Nacos\Dubbo之外,创建订单的业务在减少库存时,是Dubbo调用的pms中的sku表,这就涉及了分布式事务seata,删除购物车,新增订单和新增订单项是order模块的功能

减少库存的功能是product模块写好的

1.6开发删除选中的购物车商品的功能

本次删除我们使用用户id和skuId来指定要删除的购物车商品

之前没有写过,在OmsCartMapper编写

// 根据用户id和skuId删除商品
int deleteCartByUserIdAndSkuId(OmsCart omsCart);

对应的mapper.xml

<!--  根据用户id和skuId删除商品 -->
<delete id="deleteCartByUserIdAndSkuId">
    delete from
        oms_cart
    where 
        user_id=#{userId}
    and 
        sku_id=#{skuId}
</delete>

当前删除购物车商品的功能是为生成订单准备的

所以只需要开发出业务逻辑层即可不需要控制层的代码

// 生成订单时,从购物车中删除订单中选择的商品
@Override
public void removeUserCarts(OmsCart omsCart) {
    // OmsCart是包含userId和skuId的,直接调用即可
    omsCartMapper.deleteCartByUserIdAndSkuId(omsCart);
}

1.7开始编写新增订单功能

1.7.1编写新增order_item的持久层

order_item表中保存每张订单包含什么商品的信息

我们新增这个表,要包含订单号,商品id和相关信息

mapper下创建OmsOrderItemMapper

@Repository
public interface OmsOrderItemMapper {
    
    // 新增订单项(order_item)的方法
    // 一个订单可以包含多个订单项,为了减少连接数据库新增订单项的次数
    // 我们新增订单项方法的参数可以是List
    int insertOrderItems(List<OmsOrderItem> omsOrderItems);
    
}

OmsOrderItemMapper.xml文件添加内容

<!-- 新增订单项(order_item)的方法 -->
<insert id="insertOrderItems">
    insert into oms_order_item(
        id,
        order_id,
        sku_id,
        title,
        bar_code,
        data,
        main_picture,
        price,
        quantity
    ) values
    <foreach collection="list" item="ooi" separator="," >
        (
         #{ooi.id},
         #{ooi.orderId},
         #{ooi.skuId},
         #{ooi.title},
         #{ooi.barCode},
         #{ooi.data},
         #{ooi.mainPicture},
         #{ooi.price},
         #{ooi.quantity}
        )
    </foreach>
</insert>

1.7.2编写新增order的持久层

mapper包下再创建OmsOrderMapper

添加新增Order的方法

@Repository
public interface OmsOrderMapper {

    // 新增订单的mapper方法
    int insertOrder(OmsOrder order);

}

OmsOrderMapper.xml中添加方法

<!--  新增订单的mapper   -->
<insert id="insertOrder">
    insert into oms_order(
        id,
        sn,
        user_id,
        contact_name,
        mobile_phone,
        telephone,
        province_code,
        province_name,
        city_code,
        city_name,
        district_code,
        district_name,
        street_code,
        street_name,
        detailed_address,
        tag,
        payment_type,
        state,
        reward_point,
        amount_of_original_price,
        amount_of_freight,
        amount_of_discount,
        amount_of_actual_pay,
        gmt_pay,
        gmt_order,
        gmt_create,
        gmt_modified
    ) values (
         #{id},
         #{sn},
         #{userId},
         #{contactName},
         #{mobilePhone},
         #{telephone},
         #{provinceCode},
         #{provinceName},
         #{cityCode},
         #{cityName},
         #{districtCode},
         #{districtName},
         #{streetCode},
         #{streetName},
         #{detailedAddress},
         #{tag},
         #{paymentType},
         #{state},
         #{rewardPoint},
         #{amountOfOriginalPrice},
         #{amountOfFreight},
         #{amountOfDiscount},
         #{amountOfActualPay},
         #{gmtPay},
         #{gmtOrder},
         #{gmtCreate},
         #{gmtModified}
     )
</insert>

2.Leaf

2.1什么是Leaf

leaf是叶子的意思

我们使用的Leaf是美团公司开源的一个分布式序列号(id)生成系统

我们可以在Github网站上下载项目直接使用

2.2为什么需要Leaf

上面的图片中

是一个实际开发中常见的读写分离的数据库部署格式

专门进行数据更新(写)的有两个数据库节点

它们同时新增数据可能产生相同的id

一旦生成相同的id,数据同步就会有问题

会产生id冲突,甚至引发异常

我们为了在这种多数据库节点的环境下能够产生唯一id

可以使用Leaf来生成

2.3Leaf的工作原理

Leaf底层支持通过"雪花算法"生成不同id

我们使用的是单纯的序列

要想使用,需要事先设置好leaf的起始值和缓存id数

举例,从1000开始缓存500

也就是从id1000~1499这些值,都会保存在Leaf的内存中,当有服务需要时,直接取出下一个值

取出过的值不会再次生成

leaf要想设置起始值和缓存数

需要给leaf创建一个指定格式的数据库表

运行过程中会从数据库表获取信息

我们当前的信息保存在leafdb.leaf_alloc表中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值