订单服务-提交订单业务立即购买业务

文章目录

  • 1、提交订单 业务
    • 2、在 OrderController 创建 submitOrder 方法
    • 3、 在 OrderServiceImpl 中实现 submitOrder 方法
    • 4、根据id查询sku详情(service-product")
    • 5、查询用户地址保存到订单项中(service-user)
    • 6、删除购物车选中的购物项接口(service-cart)
    • 7、验价验库存
    • 8、创建订单持久化到mysql
    • 9、清空选中的购物车
  • 2、立即购买,不经过购物车
    • 2.1、在 OrderController 创建 buy方法
    • 2.2、在 OrderServiceImpl 中实现 buy 方法
  • 3、订单幂等性校验分析

在这里插入图片描述

1、提交订单 业务

在这里插入图片描述

2、在 OrderController 创建 submitOrder 方法

@RestController
@Tag(name = "订单管理模块", description = "订单管理模块")
@RequestMapping("/api/order/orderInfo")
public class OrderController {
    @Operation(summary = "提交订单")
    @PostMapping("/auth/submitOrder")
    public Result submitOrder(@RequestBody OrderSubmitDto orderSubmitDto) {
        Long orderId = orderService.submitOrder(orderSubmitDto);
        return Result.ok(orderId);
    }
}

3、 在 OrderServiceImpl 中实现 submitOrder 方法

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {
    @Resource
    private CartClient cartClient;
    @Resource
    private ProductClient productClient;
    @Resource
    private UserClient userClient;
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
        //1、幂等性保证:用户订单确认页提交的相同的请求只处理一次
        //获取前端提交的token
        List<OrderItem> orderItemList = orderSubmitDto.getOrderItemList();
        if (CollectionUtils.isEmpty(orderItemList)) {
            //没有订单项
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        Long token = orderItemList.get(0).getOrderId();
        //获取redis缓存的token,如果存在删除

        Boolean delete = stringRedisTemplate.delete("spzx:order:" + token.toString());
        if (!delete) {
            //幂等性保证:用户订单确认页提交的相同的请求只处理一次
            throw new SpzxException(ResultCodeEnum.ORDER_SUBMIT_REPEAT,null);
        }

        //2、保证订单中商品价格正确、库存足够

        //远程调用:service-product 验证前端提交的每个orderItem的价格 和skuId对应的商品当前价格是否一样 和库存是否足够
        orderItemList.forEach(orderItem -> {
            Result<ProductSku> skuResult = productClient.getById(orderItem.getSkuId());
            if (skuResult.getCode() != 200) {
                throw new SpzxException(ResultCodeEnum.FAIL,null);
            }
            ProductSku sku = skuResult.getData();
            if (sku.getSalePrice().compareTo(orderItem.getSkuPrice()) != 0) {
                //价格不一致
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_PRICE_ERROR,null);
            }
            if (sku.getStockNum() < orderItem.getSkuNum()) {
                //库存不足
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_STOCK_ERROR,null);
            }
        });
        Result<UserAddress> userAddressResult = userClient.findUserAddressById(orderSubmitDto.getUserAddressId());
        if (userAddressResult.getCode() != 200) {
            //用户收件人地址信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        //3、创建订单持久化
        OrderInfo orderInfo = new OrderInfo();

        UserInfo userInfo = SpzxServiceAuthInterceptor.THREAD_LOCAL.get();
        orderInfo.setUserId(userInfo.getId());

        orderInfo.setNickName(userInfo.getNickName());

        //订单号:展示给用户的唯一订单编号
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(orderNo);

        orderInfo.setCouponId(0L);

        //计算订单总金额,并返回总金额
        BigDecimal totalAmount = orderItemList.stream().map(orderItem -> {
            return orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
        }).reduce(BigDecimal::add).get();
        orderInfo.setTotalAmount(totalAmount);

        orderInfo.setCouponAmount(new BigDecimal("0"));

        orderInfo.setOriginalTotalAmount(totalAmount);

        orderInfo.setFreightFee(orderSubmitDto.getFreightFee());
        //orderInfo.setPayType(0);
        orderInfo.setOrderStatus(0);
        //收件人信息
        UserAddress userAddress = userAddressResult.getData();
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getAddress());

        orderInfo.setPaymentTime(new Date());
        orderInfo.setDeliveryTime(new Date());
        orderInfo.setReceiveTime(new Date());
        orderInfo.setCancelTime(new Date());
        orderInfo.setCancelReason("");
        orderInfo.setRemark(orderSubmitDto.getRemark());

        this.save(orderInfo);
        Long orderId = orderInfo.getId();


        //4、保存订单的所有的订单项列表
        //订单项关联订单 id 保存
        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(orderId);
        });
        orderItemService.saveBatch(orderItemList);

        //5、删除购物车中已创建订单的购物项
        //远程调用:service-cart 删除当前用户选中的购物项
        cartClient.delCheckedCartInfos();

        //异步:购物车清空无需阻塞订单创建的业务    解耦合:订单创建业务不需要耦合购物车的操作
        //order服务是生产者发送消息
        //cart服务是消费者消费消息:根据userId获取他的购物车 再删除选中的购物项
        //发送订单创建成功的消息到kafka中:购物车需要userId 如果还有其他的业务需要使用订单创建成功的消息,也可以携带其他的数据
        //userId  幂等性校验使用订单id    order信息
        /*HashMap<String, Object> params = new HashMap<>();
        params.put("userId",userInfo.getId());
        params.put("orderId", orderId);
        params.put("totalAmount", orderInfo.getTotalAmount());
        params.put("type", "1");//type表示订单的类型  1、创建订单  2、取消订单   3、更新订单状态
        //将对象转为json字符串
        String json = JSON.toJSONString(params);
        kafkaTemplate.send(SpzxConst.SPZX_ORDER_TOPIC, json);*/

        /*Map<String,Object> params = new HashMap<String,Object>();
        params.put("userId" , userInfo.getId());
        params.put("orderId" , orderId);
        params.put("totalAmount" , orderInfo.getTotalAmount());
        params.put("type","1");//type表示订单操作的类型  1创建订单  2取消订单  3更新订单状态
        //将对象转为json字符串
        String paramsJson = JSON.toJSONString(params);
        kafkaTemplate.send(SpzxConst.SPZX_ORDER_TOPIC ,paramsJson );*/


        //6、返回订单id
        return orderId;
    }
}

4、根据id查询sku详情(service-product")

@RestController
@Tag(name = "商品模块")
@RequestMapping(value = "/api/product")
public class ProductController {
    @Operation(summary = "根据id查询sku商品详情")
    @GetMapping("getById/{skuId}")
    public Result getById(@PathVariable("skuId") Long skuId) {
        ProductSku productSku = productSkuService.getById(skuId);
        return Result.ok(productSku);
    }
}

@FeignClient(value = "service-product")
public interface ProductClient {
    //因为远程调用时data存的是 sku对象
    @GetMapping("/api/product/getById/{skuId}")
    public Result<ProductSku> getById(@PathVariable("skuId") Long skuId);

    @PutMapping("/api/product/updateSkuSaleAndStock/{skuId}/{skuNum}")
    public Result updateSkuSaleAndStock(@PathVariable Long skuId, @PathVariable Integer skuNum);
}

5、查询用户地址保存到订单项中(service-user)

@RestController
@Tag(name = "用户地址管理模块", description = "用户地址管理模块")
@RequestMapping("/api/user/userAddress")
public class UserAddressController {
    @Operation(summary = "获取地址信息")
    @GetMapping("/getUserAddress/{id}")
    public Result findUserAddressById(@PathVariable("id") Long id) {
        UserAddress userAddress = userAddressService.getById(id);
        return Result.ok(userAddress);
    }
}

@FeignClient(value = "service-user")
public interface UserClient {
    @Operation(summary = "获取地址信息")
    @GetMapping("/api/user/userAddress/getUserAddress/{id}")
    public Result<UserAddress> findUserAddressById(@PathVariable("id") Long id);
}

6、删除购物车选中的购物项接口(service-cart)

@RestController
@RequestMapping("/api/order/cart")
@Tag(name = "购物车模块")
public class CartController {

    @Resource
    private CartService cartService;
    @Operation(summary = "删除购物车选中购物项")
    @DeleteMapping("/auth/delCheckedCartInfos")
    public Result delCheckedCartInfos()
    {
        cartService.deleteCheckedCartInfos();
        return Result.ok();
    }
}

@FeignClient(value = "service-cart")
public interface CartClient {

    @GetMapping("/api/order/cart/auth/checkedCartInfos")
    public Result<List<CartInfo>> checkedCartInfos();

    @DeleteMapping("/api/order/cart/auth/delCheckedCartInfos")
    public Result<Void> delCheckedCartInfos();
}

7、验价验库存

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
        List<OrderItem> orderItemList = orderSubmitDto.getOrderItemList();
        if (CollectionUtils.isEmpty(orderItemList)) {
            //没有订单项
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
    	//2、保证订单中商品价格正确、库存足够
        //远程调用:service-product 验证前端提交的每个orderItem的价格 和skuId对应的商品当前价格是否一样 和库存是否足够
        orderItemList.forEach(orderItem -> {
            Result<ProductSku> skuResult = productClient.getById(orderItem.getSkuId());
            if (skuResult.getCode() != 200) {
                throw new SpzxException(ResultCodeEnum.FAIL,null);
            }
            ProductSku sku = skuResult.getData();
            if (sku.getSalePrice().compareTo(orderItem.getSkuPrice()) != 0) {
                //价格不一致
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_PRICE_ERROR,null);
            }
            if (sku.getStockNum() < orderItem.getSkuNum()) {
                //库存不足
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_STOCK_ERROR,null);
            }
        });
    }
}

8、创建订单持久化到mysql

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
   		 Result<UserAddress> userAddressResult = userClient.findUserAddressById(orderSubmitDto.getUserAddressId());
        if (userAddressResult.getCode() != 200) {
            //用户收件人地址信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        //3、创建订单持久化
        OrderInfo orderInfo = new OrderInfo();

        UserInfo userInfo = SpzxServiceAuthInterceptor.THREAD_LOCAL.get();
        orderInfo.setUserId(userInfo.getId());

        orderInfo.setNickName(userInfo.getNickName());

        //订单号:展示给用户的唯一订单编号
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(orderNo);

        orderInfo.setCouponId(0L);

        //计算订单总金额,并返回总金额
        BigDecimal totalAmount = orderItemList.stream().map(orderItem -> {
            return orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
        }).reduce(BigDecimal::add).get();
        orderInfo.setTotalAmount(totalAmount);

        orderInfo.setCouponAmount(new BigDecimal("0"));

        orderInfo.setOriginalTotalAmount(totalAmount);

        orderInfo.setFreightFee(orderSubmitDto.getFreightFee());
        //orderInfo.setPayType(0);
        orderInfo.setOrderStatus(0);
        //收件人信息
        UserAddress userAddress = userAddressResult.getData();
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getAddress());

        orderInfo.setPaymentTime(new Date());
        orderInfo.setDeliveryTime(new Date());
        orderInfo.setReceiveTime(new Date());
        orderInfo.setCancelTime(new Date());
        orderInfo.setCancelReason("");
        orderInfo.setRemark(orderSubmitDto.getRemark());

        this.save(orderInfo);
        Long orderId = orderInfo.getId();


        //4、保存订单的所有的订单项列表
        //订单项关联订单 id 保存
        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(orderId);
        });
        orderItemService.saveBatch(orderItemList);
}

9、清空选中的购物车

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
    	//5、删除购物车中已创建订单的购物项
        //远程调用:service-cart 删除当前用户选中的购物项
        cartClient.delCheckedCartInfos();
    }

}

2、立即购买,不经过购物车

在这里插入图片描述

2.1、在 OrderController 创建 buy方法

@RestController
@Tag(name = "订单管理模块", description = "订单管理模块")
@RequestMapping("/api/order/orderInfo")
public class OrderController {

    @Operation(summary = "立即购买:商品详情页立即购买按钮")
    @GetMapping("/auth/buy/{skuId}")
    public Result buy(@PathVariable("skuId") Long skuId) {
        TradeVo tradeVo = orderService.buy(skuId);
        return Result.ok(tradeVo);
    }
}

2.2、在 OrderServiceImpl 中实现 buy 方法

    @Override
    public TradeVo buy(Long skuId) {
        Result<ProductSku> skuResult = productClient.getById(skuId);
        if (skuResult.getCode() != 200) {
            //商品信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        ProductSku sku = skuResult.getData();
        Long token = IdUtil.getSnowflake(1,1).nextId();
        //将token存到redis:redis的大key问题
        stringRedisTemplate.opsForValue().set("spzx:order:"+token.toString(), "1",  30, TimeUnit.MINUTES);
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(token);
        orderItem.setSkuNum(1);
        orderItem.setSkuPrice(sku.getSalePrice());
        orderItem.setThumbImg(sku.getThumbImg());
        orderItem.setSkuId(skuId);
        orderItem.setSkuName(sku.getSkuName());

        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(orderItem.getSkuPrice());
        tradeVo.setOrderItemList(List.of(orderItem));
        return tradeVo;
    }

3、订单幂等性校验分析

幂等性如何保证:相同的请求多次提交如果处理的结果一致
解决思路:和防止表单重复提交一样

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/768621.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3开发过程中遇到的一些问题记录

问题&#xff1a; vue3在使用 defineProps、defineEmits、defineExpose 时不需要import&#xff0c;但是 eslint会报错error defineProps is not defined no-undef 解决方法&#xff1a; 安装 vue-eslint-parser 插件&#xff0c;在 .eslintrc.js 文件中添加配置 parser: vue-e…

论文学习_UVSCAN: Detecting Third-Party Component Usage Violations in IoT Firmware

论文名称发表时间发表期刊期刊等级研究单位 Understanding the Security Risks Introduced by Third-Party Components in IoT Firmware 2024年IEEE TDSCCCF A佐治亚理工学院 1. 引言 研究背景&#xff1a;物联网&#xff08;IoT&#xff09;已经无处不在&#xff0c;为我们…

DGMamba: Domain Generalization via Generalized State Space Model论文笔记

文章目录 DGMamba: Domain Generalization via Generalized State Space Model摘要动机DGMamba设计隐藏状态抑制(HSS)语义感知补丁细化(SPR)免先验扫描域上下文交换上下文patch识别 实验结果 DGMamba: Domain Generalization via Generalized State Space Model paper: https:/…

基于Cardinal的AWD攻防平台搭建与使用以及基于docker的题目环境部署

关于 CTF 靶场的搭建与完善勇师傅前面已经总结过了&#xff0c;参考&#xff1a; CTF靶场搭建及Web赛题制作与终端docker环境部署_ctfoj搭建-CSDN博客 基于H1ve一分钟搭好CTF靶场-CSDN博客 Nginx首页修改及使用Nginx实现端口转发_nginx 修改欢迎首页-CSDN博客 关于H1ve导…

Winform使用HttpClient调用WebApi的基本用法

Winform程序调用WebApi的方式有很多&#xff0c;本文学习并记录采用HttpClient调用基于GET、POST请求的WebApi的基本方式。WebApi使用之前编写的检索环境检测数据的接口&#xff0c;如下图所示。 调用基于GET请求的无参数WebApi 创建HttpClient实例后调用GetStringAsync函数获…

2.4 C#开发环境 xml格式保存参数----范例实现

2.4C#开发环境 xml格式保存参数----范例实现 1 程序参数保存目录层次说明 1 选择程序号| 相机号|窗口号 2 导入参数&#xff1a;就会从本地目录读取参数&#xff0c;并且显示图片和ROI 3 保存参数&#xff1a;把当前控件图片和ROI信息保存到指定程序号|相机号|窗口号中 2 参数…

剪映数字人口播原理终于搞清楚了

剪映版本升级了,新版本支持数字人定制,于是我赶紧申请了使用资格 目前的价格是49元单个价格/30天 支付49元之后剪映要求上传2.5至10分钟的视频 接着要阅读一段话并录制视频上传 第三步提交,提交完成之后大概两三个小时就会有一个特定数字人形象出现:

不只是咨询,更是转型加速器——精益生产咨询!

以前咱们说精益生产&#xff0c;总觉得是套现成的模板&#xff0c;每家企业都得照葫芦画瓢。但现在不一样了&#xff0c;精益生产咨询就像是个高级定制师&#xff0c;它深入了解你的企业现状、行业特点、市场趋势&#xff0c;然后给你量身打造一套专属的精益转型方案。这种既接…

java内存管理机制详解之运行时数据区

正文 C与java之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”&#xff0c;墙外的人想进去&#xff0c;墙里的人却想出来…… 与C、C程序员时刻要关注着内存的分配与释放&#xff0c;会不会又有哪里出现了内存泄露不同是&#xff0c;java程序员可以“高枕无忧”。因为…

Visual Studio 中的键盘快捷方式

1. Visual Studio 中的键盘快捷方式 1.1. 可打印快捷方式备忘单 1.2. Visual Studio 的常用键盘快捷方式 本部分中的所有快捷方式都将全局应用&#xff08;除非另有指定&#xff09;。 “全局”上下文表示该快捷方式适用于 Visual Studio 中的任何工具窗口。 生成&#xff1…

【C语言】指针经典例题

题1&#xff1a; #include <stdio.h>int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } //程序的结果是什么&#xff1f; 解答如下&#xff1a; 题2&#xff1a; #include <std…

Access数据操作

Access Access 作为 Office的组件之一&#xff0c;在很多 Excel难以施展其能力的场所&#xff0c;也能轻松应对。同为Office组件之一的Excel具有灵活的数据处理和分析能力&#xff0c;然而&#xff0c;其能力是有局限的&#xff0c; 比如当涉及两个数据表之间的“关联”操作时&…

【分布式数据仓库Hive】HivQL的使用

目录 一、Hive的基本操作 1. 使用Hive创建数据库test 2. 检索数据库&#xff08;模糊查看&#xff09;&#xff0c;检索形如’te*’的数据库 3. 查看数据库test详情 4. 删除数据库test 5. 创建一个学生数据库Stus&#xff0c;在其中创建一个内部表Student&#xff0c;表格…

快速下载!Windows 7旗舰版系统:集成所有补丁!

微软对Windows7系统停止支持后&#xff0c;Windows7设备不再收到安全补丁程序、修补程序。尽管如此&#xff0c;许多用户仍然认为Windows7是最好用、最经典的系统。有用户就特别喜欢Windows7旗舰版系统&#xff0c;那么接下来系统之家小编为大家带来的全补丁版本的Windows7系统…

互联网应用主流框架整合之SpringCloud微服务治理

微服务架构理念 关于微服务的概念、理念及设计相关内容,并没有特别严格的边界和定义,某种意义上说,适合的就是最好的,在之前的文章中有过详细的阐述,微服务[v1.0.0][Spring生态概述]、微服务[设计与运行]、微服务[v1.0.0][服务调用]、微服务[开发生命周期]、微服务[面临的…

LLM应用:传统NLP任务

LLM出来以后&#xff0c;知乎上就出现了“传统NLP已死”的言论&#xff0c;但是传统NLP真的就被扔进历史的垃圾桶了吗&#xff1f; 其实&#xff0c;尽管LLM具有出色的通用能力&#xff0c;但仍然无法有效应对低资源领域的自然语言处理任务&#xff0c;如小语种翻译。为了更好地…

springboot+vue+mybatis前台点菜系统+PPT+论文+讲解+售后

21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存储达到…

Linux静态库的制作

Linux操作系统支持的函数库分为&#xff1a; 静态库&#xff0c;libxxx.a&#xff0c;在编译时就将库编译进可执行程序中。 优点&#xff1a;程序的运行环境中不需要外部的函数库。 缺点&#xff1a;可执行程序大 动态库&#xff0c;又称共享库&#xff0c;libxxx.so&a…

【目标检测】DINO

一、引言 论文&#xff1a; DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection 作者&#xff1a; IDEA 代码&#xff1a; DINO 注意&#xff1a; 该算法是在Deformable DETR、DAB-DETR、DN-DETR基础上的改进&#xff0c;在学习该算法前&#…