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

news2024/11/18 11:26:33

文章目录

  • 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.coloradmin.cn/o/1890932.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从.mat文件中导入数据到simulink进行FFT分析

1. 在matlab中准备数据 .mat 文件中包含时间向量和需要分析的数据 load(fcssiabc061302.mat);提取时间和需要分析的数据 time fcssiabc061302.X.Data; % 时间向量 signal fcssiabc061302.Y(1).Data; % A相电流数据 将数据转换为“structure with time”格式…

Redis(十八) 分布式锁

文章目录 前言什么是分布式锁分布式锁的基本实现引入过期时间引入校验 id引入 lua 脚本引入 watch dog&#xff08;看门狗&#xff09;引入 Redlock 算法 前言 在使用 redis 作为中间件的时候&#xff0c;如果使用单机部署的话&#xff0c;如果这个机器故障的话&#xff0c;那…

优先级队列(堆)学的好,头发掉的少(Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

取证与数据恢复:冷系统分析,实时系统分析与镜像分析之间的过渡办法

天津鸿萌科贸发展有限公司是 ElcomSoft 系列取证软件的授权代理商。 ElcomSoft 系列取证软件 ElcomSoft 系列取证软件支持从计算机和移动设备进行数据提取、解锁文档、解密压缩文件、破解加密容器、查看和分析证据。 计算机和手机取证的完整集合硬件加速解密最多支持10,000计…

arduino IDE 处于read only editor模式

当我们浏览一些arduino的例子的时候&#xff0c;有时候想修改这些例子。但是这些例子即使另存到自己的文件目录下&#xff0c;仍然不能修改&#xff0c;提示处于read only 模式。 网上有一些什么说法&#xff0c;说要设置什么之类的&#xff0c;当我们点开之后&#xff0c;好像…

13-4 GPT-5:博士级AI,人工智能的新时代

图片来源&#xff1a;AI Disruptive 人工智能世界正在迅速发展&#xff0c;新的创新和突破层出不穷。在本文中&#xff0c;我们将深入探讨最新的进展&#xff0c;从即将推出的 GPT-5 模型到 Apple 和 Meta 之间可能的合作。 GPT-5&#xff1a;博士级别的人工智能 虽然尚未正…

Windows Server 2008近源应急OS-1

前景需要&#xff1a;小王从某安全大厂被优化掉后&#xff0c;来到了某私立小学当起了计算机老师。某一天上课的时候&#xff0c;发现鼠标在自己动弹&#xff0c;又发现除了某台电脑&#xff0c;其他电脑连不上网络。感觉肯定有学生捣乱&#xff0c;于是开启了应急。 我们需要…

微信小程序 typescript 开发日历界面

1.界面代码 <view class"o-calendar"><view class"o-calendar-container" ><view class"o-calendar-titlebar"><view class"o-left_arrow" bind:tap"prevMonth">《</view>{{year}}年{{month…

py黑帽子学习笔记_burp

配置burp kali虚机默认装好了社区版burp和java&#xff0c;其他os需要手动装 burp是用java&#xff0c;还得下载一个jython包&#xff0c;供burp用 配apt国内源&#xff0c;然后apt install jython --download-only&#xff0c;会只下载包而不安装&#xff0c;下载的目录搜一…

基于最大相邻夹角的边缘点提取(matlab)

1、背景介绍 边缘点是指点云数据中代表物体或场景几何形状突变的那些点。在三维点云中&#xff0c;边缘点通常标志着不同表面或物体的分界&#xff0c;或者是物体表面上的不规则性&#xff0c;如裂缝、棱角、突起等。点云边缘检测的作用非常重要&#xff0c;最常见是进行特征点…

Transformation(转换)开发-switch/case组件

一、switch/case组件-条件判断 体育老师要做一件非常重要的事情&#xff1a;判断学生是男孩还是女孩、或者是蜘蛛&#xff0c;然后让他们各自到指定的队伍中 体育老师做的事情&#xff0c;我们同样也会在Kettle中会经常用来。在Kettle中&#xff0c;switch/case组件可以来做类似…

私有云统一多云管理平台主要服务内容

私有云统一多云管理平台&#xff0c;作为企业IT架构现代化的关键组成部分&#xff0c;旨在为企业提供高效、灵活、安全的云计算资源管理解决方案。这类平台通过整合和优化不同云环境(包括私有云、公有云、混合云)的管理&#xff0c;帮助企业打破云孤岛&#xff0c;实现资源的统…

守护创新之魂:源代码防泄漏的终极策略

在信息化快速发展的今天&#xff0c;企业的核心机密数据&#xff0c;尤其是源代码&#xff0c;成为了企业竞争力的关键所在。然而&#xff0c;源代码的泄露风险也随之增加&#xff0c;给企业的安全和发展带来了巨大威胁。在这样的背景下&#xff0c;SDC沙盒作为一种创新的源代码…

【JAVA入门】Day12 - 权限修饰符

【JAVA入门】Day12 - 权限修饰符 文章目录 【JAVA入门】Day12 - 权限修饰符一、private二、空着不写三、protected四、public五、权限修饰符的使用规则 权限修饰符是用来控制一个成员能够被访问的范围的。 权限修饰符可以修饰成员变量、方法、构造方法、内部类。 publ…

解析Kotlin中的内联函数,inline、noinline、crossinline【笔记摘要】

用编译时常量的概念&#xff0c;引出本文要讲内联函数inline&#xff1a; 1.编译时常量 Java的编译时常量 Compile-time Constant 它有四个要求&#xff1a;1.这个变量需要是 final 的  2.类型只能是字符串或者基本类型  3.这个变量需要在声明的时候就赋值  4.等号右边…

华为路由器静态路由配置(eNSP模拟实验)

实验目标 如图下所示&#xff0c;让PC1ping通PC2 具体操作 配置PC设备ip 先配置PC1的ip、掩码、网关。PC2也做这样的配置 配置路由器ip 配置G0/0/0的ip信息 #进入系统 <Huawei>system-view #进入GigabitEthernet0/0/0接口 [Huawei]int G0/0/0 #设置接口的ip和掩码 […

springboot 自定义的全局捕获异常失效

背景&#xff1a;springbootspringcloud 分布式微服务。 问题&#xff1a;公共模块在使用RestControllerAdvice全局捕获异常时&#xff0c;捕获不到子服务抛出的相应异常 首先看一下全局异常组件有么有被扫描到 如何查看&#xff0c;很简单只需要写一段类加载打印代码&#x…

Ansys Zemax|场曲跟畸变图的前世今生

实现 OpticStudio通过在X和Y方向&#xff08;弧矢和子午方向&#xff09;的傍轴光线追踪确定近轴图像平面的Z坐标&#xff0c;并测量该近轴焦平面与系统图像平面的Z坐标之间的距离。 切向数据是沿Z轴从图像平面到近轴图像平面在切向&#xff08;YZ&#xff09;平面测量的距离…

解决pip安装时的“SyntaxError: invalid syntax”错误

项目场景&#xff1a; 项目中有新的成员加入时&#xff0c;第一步就是安装开发环境&#xff0c;然而往往同样的机器、同样的配置&#xff0c;我们却总能遇到各种各样不同的问题。 今天分享一个简单的操作问题。 问题描述 项目用到pandas&#xff0c;安装pandas时遇到Syntax…

使用 App Store Connect API 生成和读取分析报告

文章目录 前言安装 API Swift SDK配置 API Swift SDK生成分析报告获取所有可用的报告获取报告的分段下载分段的数据总结 前言 Apple 最近推出了50多个新的分析报告&#xff0c;其中包含数百个新的数据点和指标&#xff0c;以帮助开发者了解他们的应用程序的表现情况。 这些报…