商城项目商城订单结算幂等性设计与运费接口重写-----商城项目

商城订单结算购物幂等性设计
根据Session状态判断登录,准备订单确认页信息
(获取购物车商品,获取会员收货地址列表,获取优惠卷,计算价格)
创建防虫令牌,渲染订单页(在渲染页面前创建令牌,防止占用资源)
防虫令牌采用Redis来设计
渲染订单确认页,然后结束流程

商城订单结算购物幂等性设计
根据Session状态判断登录,准备订单确认页信息
(获取购物车商品,获取会员收货地址列表,获取优惠卷,计算价格)
创建防虫令牌,渲染订单页(在渲染页面前创建令牌,防止占用资源)
防虫令牌采用Redis来设计
渲染订单确认页,然后结束流程

package com.alatus.mall.order.vo;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class OrderSubmitVo {
//    收货地址的ID
    private Long addrId;
//    支付方式
    private Integer payType;
//    TODO 送货清单
//    TODO 无需提交购物车的商品,我们还会再去获取一次的
//    TODO 优惠,发票
//    令牌
    private String orderToken;
//    应付价格验证价格
    private BigDecimal payPrice;
//    用户信息在session里面
//    订单备注
    private String note;
}
package com.alatus.mall.order.vo;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class OrderSubmitVo {
//    收货地址的ID
    private Long addrId;
//    支付方式
    private Integer payType;
//    TODO 送货清单
//    TODO 无需提交购物车的商品,我们还会再去获取一次的
//    TODO 优惠,发票
//    令牌
    private String orderToken;
//    应付价格验证价格
    private BigDecimal payPrice;
//    用户信息在session里面
//    订单备注
    private String note;
}
package com.alatus.mall.order.service;

import com.alatus.mall.order.vo.OrderConfirmVo;
import com.baomidou.mybatisplus.extension.service.IService;
import com.alatus.common.utils.PageUtils;
import com.alatus.mall.order.entity.OrderEntity;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * 订单
 *
 * @author alatus
 * @date 2024-03-12 13:50:51
 */
public interface OrderService extends IService<OrderEntity> {

    PageUtils queryPage(Map<String, Object> params);

    OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException;
    BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException;
}
package com.alatus.mall.order.service;

import com.alatus.mall.order.vo.OrderConfirmVo;
import com.baomidou.mybatisplus.extension.service.IService;
import com.alatus.common.utils.PageUtils;
import com.alatus.mall.order.entity.OrderEntity;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * 订单
 *
 * @author alatus
 * @date 2024-03-12 13:50:51
 */
public interface OrderService extends IService<OrderEntity> {

    PageUtils queryPage(Map<String, Object> params);

    OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException;
    BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException;
}
package com.alatus.mall.order.web;

import com.alatus.mall.order.service.OrderService;
import com.alatus.mall.order.vo.OrderConfirmVo;
import com.alatus.mall.order.vo.OrderSubmitVo;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.math.BigDecimal;
import java.util.concurrent.ExecutionException;

@Controller
public class OrderWebController {
    @Autowired
    private OrderService orderService;
    @GetMapping("/toTrade")
    public String toTrade(Model model) throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = orderService.confirmOrder();
        model.addAttribute("orderConfirmData",confirmVo);
        return "confirm";
    }
    @GetMapping("/changeAddr/{addrId}")
    @ResponseBody
    public String changeAddr(@PathVariable("addrId") Long addrId) throws ExecutionException, InterruptedException {
        BigDecimal bigDecimal = orderService.changeAddr(addrId);
        return JSON.toJSONString(bigDecimal);
    }
    @PostMapping("/submitOrder")
    public String submitOrder(OrderSubmitVo orderSubmitVo){
//        去创建订单,验证令牌,验证价格,锁定库存
//        下单成功去支付选择页面,下单失败去重新确认订单信息
        return null;
    }
}

package com.alatus.mall.order.web;

import com.alatus.mall.order.service.OrderService;
import com.alatus.mall.order.vo.OrderConfirmVo;
import com.alatus.mall.order.vo.OrderSubmitVo;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.math.BigDecimal;
import java.util.concurrent.ExecutionException;

@Controller
public class OrderWebController {
    @Autowired
    private OrderService orderService;
    @GetMapping("/toTrade")
    public String toTrade(Model model) throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = orderService.confirmOrder();
        model.addAttribute("orderConfirmData",confirmVo);
        return "confirm";
    }
    @GetMapping("/changeAddr/{addrId}")
    @ResponseBody
    public String changeAddr(@PathVariable("addrId") Long addrId) throws ExecutionException, InterruptedException {
        BigDecimal bigDecimal = orderService.changeAddr(addrId);
        return JSON.toJSONString(bigDecimal);
    }
    @PostMapping("/submitOrder")
    public String submitOrder(OrderSubmitVo orderSubmitVo){
//        去创建订单,验证令牌,验证价格,锁定库存
//        下单成功去支付选择页面,下单失败去重新确认订单信息
        return null;
    }
}
package com.alatus.mall.order.service.impl;

import com.alatus.common.to.MemberAddressTo;
import com.alatus.common.utils.R;
import com.alatus.common.vo.MemberRespVo;
import com.alatus.common.vo.SkuHasStockVo;
import com.alatus.mall.order.constant.OrderConstant;
import com.alatus.mall.order.feign.CartFeignService;
import com.alatus.mall.order.feign.MemberFeignService;
import com.alatus.mall.order.feign.WareFeignService;
import com.alatus.mall.order.interceptor.LoginUserInterceptor;
import com.alatus.mall.order.vo.OrderConfirmVo;
import com.alatus.mall.order.vo.OrderItemVo;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.alatus.common.utils.PageUtils;
import com.alatus.common.utils.Query;
import com.alatus.mall.order.dao.OrderDao;
import com.alatus.mall.order.entity.OrderEntity;
import com.alatus.mall.order.service.OrderService;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private CartFeignService cartFeignService;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private WareFeignService wareFeignService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );
        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
//        获取原请求信息,避免异步导致丢失
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
//        远程查询购物车所有选中的购物项
        CompletableFuture<Void> itemVoList = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            confirmVo.setItemVoList(cartFeignService.getCurrentCartItem());
        }, threadPoolExecutor).thenRunAsync(()->{
//        获取原请求信息,避免异步导致丢失
            RequestContextHolder.setRequestAttributes(requestAttributes);
//            查询库存
            List<OrderItemVo> itemList = confirmVo.getItemVoList();
            List<Long> ids = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R skuHasStock = wareFeignService.getSkuHasStock(ids);
            if(skuHasStock.getCode()==0){
                List<SkuHasStockVo> data = skuHasStock.getData(new TypeReference<List<SkuHasStockVo>>(){});
                if(data!=null&& !data.isEmpty()){
                    Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                    confirmVo.setStocks(collect);
                }
            }
        },threadPoolExecutor);
//        远程查询所有的用户地址列表
        CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            confirmVo.setAddress(memberFeignService.getAddress(memberRespVo.getId()));
        }, threadPoolExecutor).thenRunAsync(()->{
//            获取运费信息
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressTo> address = confirmVo.getAddress();
            for (MemberAddressTo memberAddressTo : address) {
                if(memberAddressTo.getDefaultStatus()==1){
                    R fare = wareFeignService.getFare(memberAddressTo.getId());
                    if (fare.getCode()==0) {
                        confirmVo.setFare(fare.getData(new TypeReference<BigDecimal>() {}));
                    }
                }
            }
        },threadPoolExecutor);
//        用户积分
        CompletableFuture<Void> integrationTask = CompletableFuture.runAsync(() -> {
            confirmVo.setIntegration(memberRespVo.getIntegration());
        }, threadPoolExecutor);
//        TODO 订单令牌
        //        订单令牌
        CompletableFuture<Void> tokenTask = CompletableFuture.runAsync(() -> {
            String token = UUID.randomUUID().toString().replace("-", "");
            confirmVo.setOrderToken(token);
            redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 30, TimeUnit.MINUTES);
        }, threadPoolExecutor);
        CompletableFuture.allOf(addressTask,itemVoList,integrationTask,tokenTask).get();
        return confirmVo;
    }

    @Override
    public BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException {
//        获取原请求信息,避免异步导致丢失
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        CompletableFuture<Void> changeAddr = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R r = memberFeignService.changeAddr(memberRespVo.getId(), addrId);
            if (r.getCode() != 0) {
                throw new RuntimeException("修改失败");
            }
        }, threadPoolExecutor);
//        查询新的运费信息
        CompletableFuture<BigDecimal> getFareTask = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R fare = wareFeignService.getFare(addrId);
            if (fare.getCode() == 0) {
                BigDecimal farePay = fare.getData(new TypeReference<BigDecimal>() {});
                return farePay;
            }
            else{
                return new BigDecimal(0);
            }
        }, threadPoolExecutor);
        CompletableFuture.allOf(changeAddr,getFareTask).get();
        return getFareTask.get();
    }
}
package com.alatus.mall.order.service.impl;

import com.alatus.common.to.MemberAddressTo;
import com.alatus.common.utils.R;
import com.alatus.common.vo.MemberRespVo;
import com.alatus.common.vo.SkuHasStockVo;
import com.alatus.mall.order.constant.OrderConstant;
import com.alatus.mall.order.feign.CartFeignService;
import com.alatus.mall.order.feign.MemberFeignService;
import com.alatus.mall.order.feign.WareFeignService;
import com.alatus.mall.order.interceptor.LoginUserInterceptor;
import com.alatus.mall.order.vo.OrderConfirmVo;
import com.alatus.mall.order.vo.OrderItemVo;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.alatus.common.utils.PageUtils;
import com.alatus.common.utils.Query;
import com.alatus.mall.order.dao.OrderDao;
import com.alatus.mall.order.entity.OrderEntity;
import com.alatus.mall.order.service.OrderService;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private CartFeignService cartFeignService;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private WareFeignService wareFeignService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );
        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
//        获取原请求信息,避免异步导致丢失
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
//        远程查询购物车所有选中的购物项
        CompletableFuture<Void> itemVoList = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            confirmVo.setItemVoList(cartFeignService.getCurrentCartItem());
        }, threadPoolExecutor).thenRunAsync(()->{
//        获取原请求信息,避免异步导致丢失
            RequestContextHolder.setRequestAttributes(requestAttributes);
//            查询库存
            List<OrderItemVo> itemList = confirmVo.getItemVoList();
            List<Long> ids = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R skuHasStock = wareFeignService.getSkuHasStock(ids);
            if(skuHasStock.getCode()==0){
                List<SkuHasStockVo> data = skuHasStock.getData(new TypeReference<List<SkuHasStockVo>>(){});
                if(data!=null&& !data.isEmpty()){
                    Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                    confirmVo.setStocks(collect);
                }
            }
        },threadPoolExecutor);
//        远程查询所有的用户地址列表
        CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            confirmVo.setAddress(memberFeignService.getAddress(memberRespVo.getId()));
        }, threadPoolExecutor).thenRunAsync(()->{
//            获取运费信息
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressTo> address = confirmVo.getAddress();
            for (MemberAddressTo memberAddressTo : address) {
                if(memberAddressTo.getDefaultStatus()==1){
                    R fare = wareFeignService.getFare(memberAddressTo.getId());
                    if (fare.getCode()==0) {
                        confirmVo.setFare(fare.getData(new TypeReference<BigDecimal>() {}));
                    }
                }
            }
        },threadPoolExecutor);
//        用户积分
        CompletableFuture<Void> integrationTask = CompletableFuture.runAsync(() -> {
            confirmVo.setIntegration(memberRespVo.getIntegration());
        }, threadPoolExecutor);
//        TODO 订单令牌
        //        订单令牌
        CompletableFuture<Void> tokenTask = CompletableFuture.runAsync(() -> {
            String token = UUID.randomUUID().toString().replace("-", "");
            confirmVo.setOrderToken(token);
            redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 30, TimeUnit.MINUTES);
        }, threadPoolExecutor);
        CompletableFuture.allOf(addressTask,itemVoList,integrationTask,tokenTask).get();
        return confirmVo;
    }

    @Override
    public BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException {
//        获取原请求信息,避免异步导致丢失
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        CompletableFuture<Void> changeAddr = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R r = memberFeignService.changeAddr(memberRespVo.getId(), addrId);
            if (r.getCode() != 0) {
                throw new RuntimeException("修改失败");
            }
        }, threadPoolExecutor);
//        查询新的运费信息
        CompletableFuture<BigDecimal> getFareTask = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R fare = wareFeignService.getFare(addrId);
            if (fare.getCode() == 0) {
                BigDecimal farePay = fare.getData(new TypeReference<BigDecimal>() {});
                return farePay;
            }
            else{
                return new BigDecimal(0);
            }
        }, threadPoolExecutor);
        CompletableFuture.allOf(changeAddr,getFareTask).get();
        return getFareTask.get();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值