package com.alatus.mall.order.template; import com.alatus.mall.order.vo.PayVo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradePagePayRequest; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @Data @ConfigurationProperties(prefix = "alatusmall.alipay") public class AlipayTemplate { public String app_id; public String merchant_private_key; public String alipay_public_key; public String notify_url; public String return_url; public String sign_type; public String charset; public String gatewayUrl; public String log_path; public String pay(PayVo payVo) throws AlipayApiException { //获得初始化的AlipayClient AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,app_id,merchant_private_key, "json",charset,alipay_public_key,sign_type); //设置请求参数 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(return_url); alipayRequest.setNotifyUrl(notify_url); //商户订单号,商户网站订单系统中唯一订单号,必填 String out_trade_no = payVo.getOut_trade_no(); //付款金额,必填 String total_amount = payVo.getTotal_amount(); //订单名称,必填 String subject = payVo.getSubject(); //商品描述,可空 String body = payVo.getBody(); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"total_amount\":\""+ total_amount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); //请求 return alipayClient.pageExecute(alipayRequest).getBody(); } }
package com.alatus.mall.order.template; import com.alatus.mall.order.vo.PayVo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradePagePayRequest; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @Data @ConfigurationProperties(prefix = "alatusmall.alipay") public class AlipayTemplate { public String app_id; public String merchant_private_key; public String alipay_public_key; public String notify_url; public String return_url; public String sign_type; public String charset; public String gatewayUrl; public String log_path; public String pay(PayVo payVo) throws AlipayApiException { //获得初始化的AlipayClient AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,app_id,merchant_private_key, "json",charset,alipay_public_key,sign_type); //设置请求参数 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(return_url); alipayRequest.setNotifyUrl(notify_url); //商户订单号,商户网站订单系统中唯一订单号,必填 String out_trade_no = payVo.getOut_trade_no(); //付款金额,必填 String total_amount = payVo.getTotal_amount(); //订单名称,必填 String subject = payVo.getSubject(); //商品描述,可空 String body = payVo.getBody(); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"total_amount\":\""+ total_amount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); //请求 return alipayClient.pageExecute(alipayRequest).getBody(); } }
package com.alatus.mall.order.web; import com.alatus.mall.order.service.OrderService; import com.alatus.mall.order.template.AlipayTemplate; import com.alipay.api.AlipayApiException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class PayWebController { @Autowired private OrderService orderService; @Autowired private AlipayTemplate alipayTemplate; @GetMapping(value = "/payOrder",produces = "text/html") @ResponseBody public String payOrder(@RequestParam("orderSn")String orderSn) throws AlipayApiException { return alipayTemplate.pay(orderService.getOrderPay(orderSn)); } }
package com.alatus.mall.order.web; import com.alatus.mall.order.service.OrderService; import com.alatus.mall.order.template.AlipayTemplate; import com.alipay.api.AlipayApiException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class PayWebController { @Autowired private OrderService orderService; @Autowired private AlipayTemplate alipayTemplate; @GetMapping(value = "/payOrder",produces = "text/html") @ResponseBody public String payOrder(@RequestParam("orderSn")String orderSn) throws AlipayApiException { return alipayTemplate.pay(orderService.getOrderPay(orderSn)); } }
package com.alatus.mall.order.service; import com.alatus.mall.order.vo.OrderConfirmVo; import com.alatus.mall.order.vo.OrderSubmitVo; import com.alatus.mall.order.vo.PayVo; import com.alatus.mall.order.vo.SubmitOrderResponseVo; 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 * @email [email protected] * @date 2024-03-12 13:50:51 */ public interface OrderService extends IService
{ PageUtils queryPage(Map params); OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException; BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException; SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo); OrderEntity getOrderByOrderSn(String orderSn); void closeOrder(OrderEntity order); PayVo getOrderPay(String orderSn); PageUtils queryPageWithItem(Map params); }
package com.alatus.mall.order.service; import com.alatus.mall.order.vo.OrderConfirmVo; import com.alatus.mall.order.vo.OrderSubmitVo; import com.alatus.mall.order.vo.PayVo; import com.alatus.mall.order.vo.SubmitOrderResponseVo; 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 * @email [email protected] * @date 2024-03-12 13:50:51 */ public interface OrderService extends IService{ PageUtils queryPage(Map params); OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException; BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException; SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo); OrderEntity getOrderByOrderSn(String orderSn); void closeOrder(OrderEntity order); PayVo getOrderPay(String orderSn); PageUtils queryPageWithItem(Map params); }
package com.alatus.mall.order.service.impl; import com.alatus.common.exception.NoStockException; import com.alatus.common.to.MemberAddressTo; import com.alatus.common.to.mq.OrderTo; import com.alatus.common.utils.R; import com.alatus.common.vo.MemberRespVo; import com.alatus.common.vo.OrderItemVo; import com.alatus.common.vo.SkuHasStockVo; import com.alatus.mall.order.config.RabbitMQConfigProperties; import com.alatus.mall.order.entity.OrderItemEntity; import com.alatus.mall.order.enums.OrderStatusEnum; import com.alatus.mall.order.feign.ProductFeignService; import com.alatus.mall.order.service.OrderItemService; import com.alatus.mall.order.to.OrderCreateTo; 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.*; import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.*; 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.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @Service("orderService") public class OrderServiceImpl extends ServiceImpl
implements OrderService { @Autowired private MemberFeignService memberFeignService; @Autowired private CartFeignService cartFeignService; @Autowired private OrderItemService orderItemService; @Autowired private ThreadPoolExecutor threadPoolExecutor; @Autowired private WareFeignService wareFeignService; @Autowired private StringRedisTemplate redisTemplate; @Autowired private ProductFeignService productFeignService; @Autowired private RabbitTemplate rabbitTemplate; @Autowired private RabbitMQConfigProperties rabbitMQConfigProperties; private ThreadLocal submitVoThreadLocal = new ThreadLocal<>(); @Override public PageUtils queryPage(Map params) { IPage page = this.page( new Query ().getPage(params), new QueryWrapper () ); 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 itemVoList = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setItemVoList(cartFeignService.getCurrentCartItem()); }, threadPoolExecutor).thenRunAsync(()->{ // 获取原请求信息,避免异步导致丢失 RequestContextHolder.setRequestAttributes(requestAttributes); // 查询库存 List itemList = confirmVo.getItemVoList(); List ids = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList()); R skuHasStock = wareFeignService.getSkuHasStock(ids); if(skuHasStock.getCode()==0){ List data = skuHasStock.getData(new TypeReference >(){}); if(data!=null&& !data.isEmpty()){ Map
collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock)); confirmVo.setStocks(collect); } } },threadPoolExecutor); // 远程查询所有的用户地址列表 CompletableFuture addressTask = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setAddress(memberFeignService.getAddress(memberRespVo.getId())); }, threadPoolExecutor).thenRunAsync(()->{ // 获取运费信息 RequestContextHolder.setRequestAttributes(requestAttributes); List 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 () {})); } } } },threadPoolExecutor); // 用户积分 CompletableFuture integrationTask = CompletableFuture.runAsync(() -> { confirmVo.setIntegration(memberRespVo.getIntegration()); }, threadPoolExecutor); // TODO 订单令牌 // 订单令牌 CompletableFuture 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 changeAddr = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R r = memberFeignService.changeAddr(memberRespVo.getId(), addrId); if (r.getCode() != 0) { throw new RuntimeException("修改失败"); } }, threadPoolExecutor); // 查询新的运费信息 CompletableFuture getFareTask = CompletableFuture.supplyAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R fare = wareFeignService.getFare(addrId); if (fare.getCode() == 0) { BigDecimal farePay = fare.getData(new TypeReference () {}); return farePay; } else{ return new BigDecimal(0); } }, threadPoolExecutor); CompletableFuture.allOf(changeAddr,getFareTask).get(); return getFareTask.get(); } @Override @Transactional public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) { SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo(); submitVoThreadLocal.set(orderSubmitVo); // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); responseVo.setCode(0); // 验证令牌是否合法,验证和删除必须保证原子性 String orderToken = orderSubmitVo.getOrderToken(); String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; // 原子删除锁和验证 Long result = redisTemplate.execute(new DefaultRedisScript (script, Long.class),Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()),orderToken); if(result == 0L){ // 令牌验证失败 responseVo.setCode(1); return responseVo; } else{ // 令牌验证成功 OrderCreateTo order = createOrder(); BigDecimal payAmount = order.getOrderEntity().getPayAmount(); BigDecimal payPrice = orderSubmitVo.getPayPrice(); if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){ // 金额对比成功 // 保存订单 saveOrder(order); // 锁定库存,只要没锁到,就抛异常让它回滚 WareSkuLockVo wareSkuLockVo = new WareSkuLockVo(); wareSkuLockVo.setOrderSn(order.getOrderEntity().getOrderSn()); List orderItemVoList = order.getOrderItemEntities().stream().map(orderItemEntity -> { OrderItemVo orderItemVo = new OrderItemVo(); orderItemVo.setSkuId(orderItemEntity.getSkuId()); orderItemVo.setCount(orderItemEntity.getSkuQuantity()); orderItemVo.setTitle(orderItemEntity.getSkuName()); return orderItemVo; }).collect(Collectors.toList()); wareSkuLockVo.setLocks(orderItemVoList); R stockResult = wareFeignService.orderLockStock(wareSkuLockVo); // 订单号,skuId,skuName if (stockResult.getCode()==0) { // 锁定成功 responseVo.setOrderEntity(order.getOrderEntity()); // TODO 远程扣除积分 // 订单发送消息给MQ rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getCreateKey(),order.getOrderEntity()); return responseVo; } else{ // 锁定失败 throw new NoStockException(0L); } } else{ responseVo.setCode(2); return responseVo; } } } @Override public OrderEntity getOrderByOrderSn(String orderSn) { return this.getOne(new QueryWrapper ().eq("order_sn",orderSn)); } @Override public void closeOrder(OrderEntity order) { OrderEntity orderEntity = this.getById(order.getId()); // 关单状态 if(orderEntity.getStatus()==OrderStatusEnum.CREATE_NEW.getCode()){ OrderEntity entity = new OrderEntity(); entity.setId(orderEntity.getId()); entity.setStatus(OrderStatusEnum.CANCLED.getCode()); this.updateById(entity); // 发送给MQ解锁库存消息 OrderTo orderTo = new OrderTo(); BeanUtils.copyProperties(orderEntity,orderTo); try{ // 发一条消息应该做一次记录到日志 rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getStockReleaseKey(),orderTo); }catch (Exception e){ // TODO 没发送成功的消息要重新发送 rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getStockReleaseKey(),orderTo); } } } @Override public PayVo getOrderPay(String orderSn) { // 获取当前订单的支付信息 PayVo payVo = new PayVo(); payVo.setTotal_amount(this.getOrderByOrderSn(orderSn).getPayAmount().setScale(2,BigDecimal.ROUND_UP).toString()); payVo.setOut_trade_no(orderSn); List orderItems = orderItemService.getItemsByOrderSn(orderSn); OrderItemEntity orderItemEntity = orderItems.get(0); payVo.setSubject(orderItemEntity.getSpuName()); payVo.setBody(orderItemEntity.getSkuAttrsVals()); return payVo; } @Override public PageUtils queryPageWithItem(Map params) { Long id = LoginUserInterceptor.loginUser.get().getId(); IPage page = this.page( new Query ().getPage(params), new QueryWrapper ().eq("member_id",id) .orderByDesc("id") ); // 转换为 OrderConfirmVo 分页信息 IPage orderSn = page.convert(orderEntity -> { OrderCreateTo orderPageVo = new OrderCreateTo(); orderPageVo.setOrderEntity(orderEntity); // 获取订单项返回 List orderItemEntities = orderItemService.list(new QueryWrapper ().eq("order_sn", orderEntity.getOrderSn())); orderPageVo.setOrderItemEntities(orderItemEntities); // 额外的字段转换 orderPageVo.setFare(orderEntity.getFreightAmount()); orderPageVo.setPayPrice(orderEntity.getTotalAmount()); return orderPageVo; }); return new PageUtils(orderSn); } private void saveOrder(OrderCreateTo order) { OrderEntity orderEntity = order.getOrderEntity(); List orderItemEntities = order.getOrderItemEntities(); orderEntity.setModifyTime(new Date()); save(orderEntity); orderItemService.saveBatch(orderItemEntities); } // 创建订单 private OrderCreateTo createOrder(){ OrderCreateTo orderCreateTo = new OrderCreateTo(); // 利用mybatisPlus的IdWorker生成一个不会重复的订单号 String orderSn = IdWorker.getTimeId(); // 构建订单 OrderEntity orderEntity = buildOrder(orderSn); orderCreateTo.setOrderEntity(orderEntity); // 设置订单项 List orderItemEntities = buildOrderItems(orderSn); orderCreateTo.setOrderItemEntities(orderItemEntities); // 验证价格 if(!orderItemEntities.isEmpty()){ computePrice(orderEntity,orderItemEntities); } return orderCreateTo; } // 计算价格 private void computePrice(OrderEntity orderEntity, List orderItemEntities) { // 订单价格相关 BigDecimal totalAmount = new BigDecimal("0.0"); // 优惠卷 BigDecimal coupon = new BigDecimal("0.0"); // 积分 BigDecimal integration = new BigDecimal("0.0"); // 折扣 BigDecimal promotion = new BigDecimal("0.0"); Integer giftIntegration = 0; Integer giftGrowth = 0; for (OrderItemEntity orderItemEntity : orderItemEntities) { totalAmount = totalAmount.add(orderItemEntity.getRealAmount()); coupon = coupon.add(orderItemEntity.getCouponAmount()); integration = integration.add(orderItemEntity.getIntegrationAmount()); promotion = promotion.add(orderItemEntity.getPromotionAmount()); giftIntegration += orderItemEntity.getGiftIntegration(); giftGrowth += orderItemEntity.getGiftGrowth(); } // 设置总额 orderEntity.setTotalAmount(totalAmount); orderEntity.setPromotionAmount(promotion); orderEntity.setCouponAmount(coupon); orderEntity.setIntegrationAmount(integration); // 设置积分和成长值 orderEntity.setGrowth(giftGrowth); orderEntity.setIntegration(giftIntegration); // 设置订单删除状态 orderEntity.setDeleteStatus(0); // 加上运费就是最终总价 orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount())); } // 构建订单项 private List buildOrderItems(String orderSn) { // 获取购物车订单项 List currentCartItem = cartFeignService.getCurrentCartItem(); if(!currentCartItem.isEmpty()){ return currentCartItem.stream().map(cartItem -> { // 最后一次再确认订单购物项价格 OrderItemEntity orderItemEntity = buildOrderItem(cartItem); orderItemEntity.setOrderSn(orderSn); return orderItemEntity; }).collect(Collectors.toList()); } return null; } // 构建每一个订单项 private OrderItemEntity buildOrderItem(OrderItemVo cartItem) { OrderItemEntity orderItemEntity = new OrderItemEntity(); // 订单信息,订单号 // 商品的SPU信息 R spuInfo = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId()); SpuInfoVo spuInfoData = spuInfo.getData(new TypeReference (){}); orderItemEntity.setSpuPic(cartItem.getImage()); orderItemEntity.setSpuBrand(spuInfoData.getBrandId().toString()); orderItemEntity.setSpuId(spuInfoData.getId()); orderItemEntity.setSpuName(spuInfoData.getSpuName()); orderItemEntity.setCategoryId(spuInfoData.getCatalogId()); // 商品的SKU信息 orderItemEntity.setSkuId(cartItem.getSkuId()); orderItemEntity.setSkuName(cartItem.getTitle()); orderItemEntity.setSkuPic(cartItem.getImage()); orderItemEntity.setSkuPrice(cartItem.getPrice()); orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(),";")); orderItemEntity.setSkuQuantity(cartItem.getCount()); // 积分信息 orderItemEntity.setGiftGrowth(cartItem.getPrice().intValue()*cartItem.getCount()); orderItemEntity.setGiftIntegration(cartItem.getPrice().intValue()*cartItem.getCount()); // 总价 // TODO 商品的优惠信息 // 促销 orderItemEntity.setPromotionAmount(new BigDecimal("0.0")); // 优惠卷 orderItemEntity.setCouponAmount(new BigDecimal("0.0")); // 积分优惠 orderItemEntity.setIntegrationAmount(new BigDecimal("0.0")); // 最终价格 BigDecimal price = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())); // 减去所有的优惠信息 price = price.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount()); orderItemEntity.setRealAmount(price); return orderItemEntity; } private OrderEntity buildOrder(String orderSn){ // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); OrderEntity entity = new OrderEntity(); entity.setMemberId(memberRespVo.getId()); entity.setMemberUsername(memberRespVo.getUsername()); // 设置订单号 entity.setOrderSn(orderSn); // 获取物流收货地址信息 OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get(); // 设置运费信息 R fareData = wareFeignService.getFare(orderSubmitVo.getAddrId()); BigDecimal fare = fareData.get("data", new TypeReference (){}); entity.setFreightAmount(fare); // 获取用户当前收货默认地址 MemberAddressTo defaultAddr = memberFeignService.getDefaultAddr(memberRespVo.getId()); entity.setReceiverCity(defaultAddr.getCity()); entity.setReceiverDetailAddress(defaultAddr.getDetailAddress()); entity.setReceiverName(defaultAddr.getName()); entity.setReceiverPhone(defaultAddr.getPhone()); entity.setReceiverPostCode(defaultAddr.getPostCode()); entity.setReceiverProvince(defaultAddr.getProvince()); entity.setReceiverRegion(defaultAddr.getRegion()); // 设置订单相关状态 entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode()); entity.setAutoConfirmDay(7); return entity; } }
package com.alatus.mall.order.service.impl; import com.alatus.common.exception.NoStockException; import com.alatus.common.to.MemberAddressTo; import com.alatus.common.to.mq.OrderTo; import com.alatus.common.utils.R; import com.alatus.common.vo.MemberRespVo; import com.alatus.common.vo.OrderItemVo; import com.alatus.common.vo.SkuHasStockVo; import com.alatus.mall.order.config.RabbitMQConfigProperties; import com.alatus.mall.order.entity.OrderItemEntity; import com.alatus.mall.order.enums.OrderStatusEnum; import com.alatus.mall.order.feign.ProductFeignService; import com.alatus.mall.order.service.OrderItemService; import com.alatus.mall.order.to.OrderCreateTo; 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.*; import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.*; 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.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @Service("orderService") public class OrderServiceImpl extends ServiceImplimplements OrderService { @Autowired private MemberFeignService memberFeignService; @Autowired private CartFeignService cartFeignService; @Autowired private OrderItemService orderItemService; @Autowired private ThreadPoolExecutor threadPoolExecutor; @Autowired private WareFeignService wareFeignService; @Autowired private StringRedisTemplate redisTemplate; @Autowired private ProductFeignService productFeignService; @Autowired private RabbitTemplate rabbitTemplate; @Autowired private RabbitMQConfigProperties rabbitMQConfigProperties; private ThreadLocal submitVoThreadLocal = new ThreadLocal<>(); @Override public PageUtils queryPage(Map params) { IPage page = this.page( new Query ().getPage(params), new QueryWrapper () ); 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 itemVoList = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setItemVoList(cartFeignService.getCurrentCartItem()); }, threadPoolExecutor).thenRunAsync(()->{ // 获取原请求信息,避免异步导致丢失 RequestContextHolder.setRequestAttributes(requestAttributes); // 查询库存 List itemList = confirmVo.getItemVoList(); List ids = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList()); R skuHasStock = wareFeignService.getSkuHasStock(ids); if(skuHasStock.getCode()==0){ List data = skuHasStock.getData(new TypeReference >(){}); if(data!=null&& !data.isEmpty()){ Map
collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock)); confirmVo.setStocks(collect); } } },threadPoolExecutor); // 远程查询所有的用户地址列表 CompletableFuture addressTask = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setAddress(memberFeignService.getAddress(memberRespVo.getId())); }, threadPoolExecutor).thenRunAsync(()->{ // 获取运费信息 RequestContextHolder.setRequestAttributes(requestAttributes); List 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 () {})); } } } },threadPoolExecutor); // 用户积分 CompletableFuture integrationTask = CompletableFuture.runAsync(() -> { confirmVo.setIntegration(memberRespVo.getIntegration()); }, threadPoolExecutor); // TODO 订单令牌 // 订单令牌 CompletableFuture 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 changeAddr = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R r = memberFeignService.changeAddr(memberRespVo.getId(), addrId); if (r.getCode() != 0) { throw new RuntimeException("修改失败"); } }, threadPoolExecutor); // 查询新的运费信息 CompletableFuture getFareTask = CompletableFuture.supplyAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R fare = wareFeignService.getFare(addrId); if (fare.getCode() == 0) { BigDecimal farePay = fare.getData(new TypeReference () {}); return farePay; } else{ return new BigDecimal(0); } }, threadPoolExecutor); CompletableFuture.allOf(changeAddr,getFareTask).get(); return getFareTask.get(); } @Override @Transactional public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) { SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo(); submitVoThreadLocal.set(orderSubmitVo); // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); responseVo.setCode(0); // 验证令牌是否合法,验证和删除必须保证原子性 String orderToken = orderSubmitVo.getOrderToken(); String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; // 原子删除锁和验证 Long result = redisTemplate.execute(new DefaultRedisScript (script, Long.class),Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()),orderToken); if(result == 0L){ // 令牌验证失败 responseVo.setCode(1); return responseVo; } else{ // 令牌验证成功 OrderCreateTo order = createOrder(); BigDecimal payAmount = order.getOrderEntity().getPayAmount(); BigDecimal payPrice = orderSubmitVo.getPayPrice(); if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){ // 金额对比成功 // 保存订单 saveOrder(order); // 锁定库存,只要没锁到,就抛异常让它回滚 WareSkuLockVo wareSkuLockVo = new WareSkuLockVo(); wareSkuLockVo.setOrderSn(order.getOrderEntity().getOrderSn()); List orderItemVoList = order.getOrderItemEntities().stream().map(orderItemEntity -> { OrderItemVo orderItemVo = new OrderItemVo(); orderItemVo.setSkuId(orderItemEntity.getSkuId()); orderItemVo.setCount(orderItemEntity.getSkuQuantity()); orderItemVo.setTitle(orderItemEntity.getSkuName()); return orderItemVo; }).collect(Collectors.toList()); wareSkuLockVo.setLocks(orderItemVoList); R stockResult = wareFeignService.orderLockStock(wareSkuLockVo); // 订单号,skuId,skuName if (stockResult.getCode()==0) { // 锁定成功 responseVo.setOrderEntity(order.getOrderEntity()); // TODO 远程扣除积分 // 订单发送消息给MQ rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getCreateKey(),order.getOrderEntity()); return responseVo; } else{ // 锁定失败 throw new NoStockException(0L); } } else{ responseVo.setCode(2); return responseVo; } } } @Override public OrderEntity getOrderByOrderSn(String orderSn) { return this.getOne(new QueryWrapper ().eq("order_sn",orderSn)); } @Override public void closeOrder(OrderEntity order) { OrderEntity orderEntity = this.getById(order.getId()); // 关单状态 if(orderEntity.getStatus()==OrderStatusEnum.CREATE_NEW.getCode()){ OrderEntity entity = new OrderEntity(); entity.setId(orderEntity.getId()); entity.setStatus(OrderStatusEnum.CANCLED.getCode()); this.updateById(entity); // 发送给MQ解锁库存消息 OrderTo orderTo = new OrderTo(); BeanUtils.copyProperties(orderEntity,orderTo); try{ // 发一条消息应该做一次记录到日志 rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getStockReleaseKey(),orderTo); }catch (Exception e){ // TODO 没发送成功的消息要重新发送 rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getStockReleaseKey(),orderTo); } } } @Override public PayVo getOrderPay(String orderSn) { // 获取当前订单的支付信息 PayVo payVo = new PayVo(); payVo.setTotal_amount(this.getOrderByOrderSn(orderSn).getPayAmount().setScale(2,BigDecimal.ROUND_UP).toString()); payVo.setOut_trade_no(orderSn); List orderItems = orderItemService.getItemsByOrderSn(orderSn); OrderItemEntity orderItemEntity = orderItems.get(0); payVo.setSubject(orderItemEntity.getSpuName()); payVo.setBody(orderItemEntity.getSkuAttrsVals()); return payVo; } @Override public PageUtils queryPageWithItem(Map params) { Long id = LoginUserInterceptor.loginUser.get().getId(); IPage page = this.page( new Query ().getPage(params), new QueryWrapper ().eq("member_id",id) .orderByDesc("id") ); // 转换为 OrderConfirmVo 分页信息 IPage orderSn = page.convert(orderEntity -> { OrderCreateTo orderPageVo = new OrderCreateTo(); orderPageVo.setOrderEntity(orderEntity); // 获取订单项返回 List orderItemEntities = orderItemService.list(new QueryWrapper ().eq("order_sn", orderEntity.getOrderSn())); orderPageVo.setOrderItemEntities(orderItemEntities); // 额外的字段转换 orderPageVo.setFare(orderEntity.getFreightAmount()); orderPageVo.setPayPrice(orderEntity.getTotalAmount()); return orderPageVo; }); return new PageUtils(orderSn); } private void saveOrder(OrderCreateTo order) { OrderEntity orderEntity = order.getOrderEntity(); List orderItemEntities = order.getOrderItemEntities(); orderEntity.setModifyTime(new Date()); save(orderEntity); orderItemService.saveBatch(orderItemEntities); } // 创建订单 private OrderCreateTo createOrder(){ OrderCreateTo orderCreateTo = new OrderCreateTo(); // 利用mybatisPlus的IdWorker生成一个不会重复的订单号 String orderSn = IdWorker.getTimeId(); // 构建订单 OrderEntity orderEntity = buildOrder(orderSn); orderCreateTo.setOrderEntity(orderEntity); // 设置订单项 List orderItemEntities = buildOrderItems(orderSn); orderCreateTo.setOrderItemEntities(orderItemEntities); // 验证价格 if(!orderItemEntities.isEmpty()){ computePrice(orderEntity,orderItemEntities); } return orderCreateTo; } // 计算价格 private void computePrice(OrderEntity orderEntity, List orderItemEntities) { // 订单价格相关 BigDecimal totalAmount = new BigDecimal("0.0"); // 优惠卷 BigDecimal coupon = new BigDecimal("0.0"); // 积分 BigDecimal integration = new BigDecimal("0.0"); // 折扣 BigDecimal promotion = new BigDecimal("0.0"); Integer giftIntegration = 0; Integer giftGrowth = 0; for (OrderItemEntity orderItemEntity : orderItemEntities) { totalAmount = totalAmount.add(orderItemEntity.getRealAmount()); coupon = coupon.add(orderItemEntity.getCouponAmount()); integration = integration.add(orderItemEntity.getIntegrationAmount()); promotion = promotion.add(orderItemEntity.getPromotionAmount()); giftIntegration += orderItemEntity.getGiftIntegration(); giftGrowth += orderItemEntity.getGiftGrowth(); } // 设置总额 orderEntity.setTotalAmount(totalAmount); orderEntity.setPromotionAmount(promotion); orderEntity.setCouponAmount(coupon); orderEntity.setIntegrationAmount(integration); // 设置积分和成长值 orderEntity.setGrowth(giftGrowth); orderEntity.setIntegration(giftIntegration); // 设置订单删除状态 orderEntity.setDeleteStatus(0); // 加上运费就是最终总价 orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount())); } // 构建订单项 private List buildOrderItems(String orderSn) { // 获取购物车订单项 List currentCartItem = cartFeignService.getCurrentCartItem(); if(!currentCartItem.isEmpty()){ return currentCartItem.stream().map(cartItem -> { // 最后一次再确认订单购物项价格 OrderItemEntity orderItemEntity = buildOrderItem(cartItem); orderItemEntity.setOrderSn(orderSn); return orderItemEntity; }).collect(Collectors.toList()); } return null; } // 构建每一个订单项 private OrderItemEntity buildOrderItem(OrderItemVo cartItem) { OrderItemEntity orderItemEntity = new OrderItemEntity(); // 订单信息,订单号 // 商品的SPU信息 R spuInfo = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId()); SpuInfoVo spuInfoData = spuInfo.getData(new TypeReference (){}); orderItemEntity.setSpuPic(cartItem.getImage()); orderItemEntity.setSpuBrand(spuInfoData.getBrandId().toString()); orderItemEntity.setSpuId(spuInfoData.getId()); orderItemEntity.setSpuName(spuInfoData.getSpuName()); orderItemEntity.setCategoryId(spuInfoData.getCatalogId()); // 商品的SKU信息 orderItemEntity.setSkuId(cartItem.getSkuId()); orderItemEntity.setSkuName(cartItem.getTitle()); orderItemEntity.setSkuPic(cartItem.getImage()); orderItemEntity.setSkuPrice(cartItem.getPrice()); orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(),";")); orderItemEntity.setSkuQuantity(cartItem.getCount()); // 积分信息 orderItemEntity.setGiftGrowth(cartItem.getPrice().intValue()*cartItem.getCount()); orderItemEntity.setGiftIntegration(cartItem.getPrice().intValue()*cartItem.getCount()); // 总价 // TODO 商品的优惠信息 // 促销 orderItemEntity.setPromotionAmount(new BigDecimal("0.0")); // 优惠卷 orderItemEntity.setCouponAmount(new BigDecimal("0.0")); // 积分优惠 orderItemEntity.setIntegrationAmount(new BigDecimal("0.0")); // 最终价格 BigDecimal price = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())); // 减去所有的优惠信息 price = price.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount()); orderItemEntity.setRealAmount(price); return orderItemEntity; } private OrderEntity buildOrder(String orderSn){ // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); OrderEntity entity = new OrderEntity(); entity.setMemberId(memberRespVo.getId()); entity.setMemberUsername(memberRespVo.getUsername()); // 设置订单号 entity.setOrderSn(orderSn); // 获取物流收货地址信息 OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get(); // 设置运费信息 R fareData = wareFeignService.getFare(orderSubmitVo.getAddrId()); BigDecimal fare = fareData.get("data", new TypeReference (){}); entity.setFreightAmount(fare); // 获取用户当前收货默认地址 MemberAddressTo defaultAddr = memberFeignService.getDefaultAddr(memberRespVo.getId()); entity.setReceiverCity(defaultAddr.getCity()); entity.setReceiverDetailAddress(defaultAddr.getDetailAddress()); entity.setReceiverName(defaultAddr.getName()); entity.setReceiverPhone(defaultAddr.getPhone()); entity.setReceiverPostCode(defaultAddr.getPostCode()); entity.setReceiverProvince(defaultAddr.getProvince()); entity.setReceiverRegion(defaultAddr.getRegion()); // 设置订单相关状态 entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode()); entity.setAutoConfirmDay(7); return entity; } }
收银台
- [[${session.get('loginUser').getNickname()}]]退出
- 我的订单
- 支付帮助
- 订单提交成功,请尽快付款!订单号:[[${submitOrder.getOrderEntity().getOrderSn()}]] 应付金额[[${#numbers.formatDecimal(submitOrder.getOrderEntity().getPayAmount(),1,2)}]]元
- 推荐使用 扫码支付请您在24小时内完成支付,否则订单会被自动取消(库存紧订单请参见详情页时限) 订单详细
打白条
- 可用额度 7275.38元 白条还款日 2018-01-27 优惠随机立减(最高10元)
- 支付28.90元
不分期
0服务费
3期
9.48元/期
6期
4.94元/期
12期
2.35元/期
24期
1.44元/期
谷粒学院小金库 未开通小金库
招商银行 信用卡(4337) 优惠单单减最高99元
请输入6位数字支付密码
忘记支付密码?Copyright @2004-2017 谷粒学院gulimall.com 版权所有
![]()
收银台
- [[${session.get('loginUser').getNickname()}]]退出
- 我的订单
- 支付帮助
- 订单提交成功,请尽快付款!订单号:[[${submitOrder.getOrderEntity().getOrderSn()}]] 应付金额[[${#numbers.formatDecimal(submitOrder.getOrderEntity().getPayAmount(),1,2)}]]元
- 推荐使用 扫码支付请您在24小时内完成支付,否则订单会被自动取消(库存紧订单请参见详情页时限) 订单详细
打白条
- 可用额度 7275.38元 白条还款日 2018-01-27 优惠随机立减(最高10元)
- 支付28.90元
不分期
0服务费
3期
9.48元/期
6期
4.94元/期
12期
2.35元/期
24期
1.44元/期
谷粒学院小金库 未开通小金库
招商银行 信用卡(4337) 优惠单单减最高99元
请输入6位数字支付密码
忘记支付密码?Copyright @2004-2017 谷粒学院gulimall.com 版权所有
![]()