商城项目整合支付宝支付功能细化修改-----商城项目

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 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;
    }
}


	
		
		
    
    
	
	
    
收银台
  • [[${session.get('loginUser').getNickname()}]]退出
  • 我的订单
  • 支付帮助
订单提交成功,请尽快付款!订单号:[[${submitOrder.getOrderEntity().getOrderSn()}]] 应付金额[[${#numbers.formatDecimal(submitOrder.getOrderEntity().getPayAmount(),1,2)}]]
推荐使用 扫码支付请您在24小时内完成支付,否则订单会被自动取消(库存紧订单请参见详情页时限) 订单详细

  • 打白条
  • 可用额度 7275.38元 白条还款日 2018-01-27 优惠随机立减(最高10元)
  • 支付28.90
  1. 不分期

    0服务费

  2. 3期

    9.48元/期

  3. 6期

    4.94元/期

  4. 12期

    2.35元/期

  5. 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
  1. 不分期

    0服务费

  2. 3期

    9.48元/期

  3. 6期

    4.94元/期

  4. 12期

    2.35元/期

  5. 24期

    1.44元/期

  • 谷粒学院小金库 未开通小金库
  • 招商银行 信用卡(4337) 优惠单单减最高99元
  • 请输入6位数字支付密码

    忘记支付密码?

Copyright @2004-2017 谷粒学院gulimall.com 版权所有

你可能感兴趣的:(电商项目,#,Spring-Boot框架,#,Spring-Cloud框架,java,servlet,spring,spring,boot,spring,cloud,分布式,jvm)