栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

谷粒商城项目(学习笔记十五)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

谷粒商城项目(学习笔记十五)

第十二章:订单服务 一、RabbitMQ环境的搭建

1)、安装RabbitMQ

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management

2)、访问15672端口登录后台管理界面

默认的用户名和密码都是guest

3)、为order订单访问引入RabbitMQ服务

1.引入依赖

        
            org.springframework.boot
            spring-boot-starter-amqp
        

2.配置环境

spring:
  rabbitmq:
    host: 自己的地址
    port: 5672
    virtual-host: /
    publisher-/confirm/i-type: correlated
    publisher-returns: true
    template:
      mandatory: true
    listener:
      simple:
        acknowledge-mode: manual

3.添加@EnableRabbit注解

二、订单模块的前端整合

和前面一样,这里省略

三、整合spring session

使登录信息同步

1)、引入session的依赖

        
        
            org.springframework.boot
            spring-boot-starter-data-redis
            
                
                    io.lettuce
                    lettuce-core
                
            
        
        
            redis.clients
            jedis
        
        
            org.springframework.session
            spring-session-data-redis
        

2)、引入session配置和线程池配置

spring.session.store-type=redis
gulimall.thread.core-size=20
gulimall.thread.max-size=200
gulimall.thread.keep-alive-time=10

spring.redis.host=101.43.79.94

 3)、开启注解

@EnableRedisHttpSession
四、配置拦截器

1)、拦截order项目所有请求

@Configuration
public class OrderWebConfiguration implements WebMvcConfigurer {


    @Autowired
    LoginUserInterceptor interceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor).addPathPatterns("
    private List buildOrderItems(String orderSn) {
        //最后确定每个购物项的价格
        List currentUserCartItems = cartFeignService.getCurrentUserCartItems();
        if (currentUserCartItems != null && currentUserCartItems.size() > 0) {
            List itemEntities = currentUserCartItems.stream().map(cartItem -> {
                OrderItemEntity itemEntity = buildOrderItem(cartItem);
                itemEntity.setOrderSn(orderSn);
                return itemEntity;
            }).collect(Collectors.toList());
            return itemEntities;
        }

        return null;
    }
 
    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity itemEntity = new OrderItemEntity();
        //1、订单信息:订单号 v
        //2、商品的SPU信息  V
        Long skuId = cartItem.getSkuId();
        R r = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo data = r.getData(new TypeReference() {
        });
        itemEntity.setSpuId(data.getId());
        itemEntity.setSpuBrand(data.getBrandId().toString());
        itemEntity.setSpuName(data.getSpuName());
        itemEntity.setCategoryId(data.getCatalogId());
        //3、商品的sku信息  v
        itemEntity.setSkuId(cartItem.getSkuId());
        itemEntity.setSkuName(cartItem.getTitle());
        itemEntity.setSkuPic(cartItem.getImage());
        itemEntity.setSkuPrice(cartItem.getPrice());
        String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
        itemEntity.setSkuAttrsVals(skuAttr);
        itemEntity.setSkuQuantity(cartItem.getCount());
        //4、优惠信息[不做]
        //5、积分信息
        itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
        itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
        //6、订单项的价格信息
        itemEntity.setPromotionAmount(new BigDecimal("0"));
        itemEntity.setCouponAmount(new BigDecimal("0"));
        itemEntity.setIntegrationAmount(new BigDecimal("0"));
        //当前订单项的实际金额。 总额-各种优惠
        BigDecimal orign = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
        BigDecimal subtract = orign.subtract(itemEntity.getCouponAmount())
                .subtract(itemEntity.getPromotionAmount())
                .subtract(itemEntity.getIntegrationAmount());
        itemEntity.setRealAmount(subtract);

        return itemEntity;
    }

4)、获取金额

   private void computePrice(OrderEntity orderEntity, List itemEntities) {
        BigDecimal total = new BigDecimal("0.0");

        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal integration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");

        BigDecimal gift = new BigDecimal("0.0");
        BigDecimal growth = new BigDecimal("0.0");
        //订单的总额,叠加每一个订单项的总额信息
        for (OrderItemEntity entity : itemEntities) {
            coupon = coupon.add(entity.getCouponAmount());
            integration = integration.add(entity.getIntegrationAmount());
            promotion = promotion.add(entity.getPromotionAmount());
            total = total.add(entity.getRealAmount());
            gift = gift.add(new BigDecimal(entity.getGiftIntegration().toString()));
//            Integer growth = entity.getGiftGrowth();
            growth = growth.add(new BigDecimal(entity.getGiftGrowth().toString()));

        }
        //1、订单价格相关
        orderEntity.setTotalAmount(total);
        //应付总额
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setCouponAmount(coupon);

        //设置积分等信息
        orderEntity.setIntegration(gift.intValue());
        orderEntity.setGrowth(growth.intValue());
        orderEntity.setDeleteStatus(0);//未删除


    }

5)、保存订单

    
    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);

        List orderItems = order.getOrderItems();
        orderItemService.saveBatch(orderItems);
    }

6)、锁定库存

    
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {

        
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        orderTaskService.save(taskEntity);


        //1、按照下单的收货地址,找到一个就近仓库,锁定库存。
        //1、找到每个商品在哪个仓库都有库存
        List locks = vo.getLocks();

        List collect = locks.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            stock.setNum(item.getCount());
            //查询这个商品在哪里有库存
            List wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
            stock.setWareId(wareIds);
            return stock;
        }).collect(Collectors.toList());

        //2、锁定库存
        for (SkuWareHasStock hasStock : collect) {
            Boolean skuStocked = false;
            Long skuId = hasStock.getSkuId();
            List wareIds = hasStock.getWareId();
            if (wareIds == null || wareIds.size() == 0) {
                //没有任何仓库有这个商品的库存
                throw new NoStockException(skuId);
            }
            //1、如果每一个商品都锁定成功,将当前商品锁定了几件的工作单记录发给MQ
            //2、锁定失败。前面保存的工作单信息就回滚了。发送出去的消息,即使要解锁记录,由于去数据库查不到id,所以就不用解锁
            //     1: 1 - 2 - 1   2:2-1-2  3:3-1-1(x)
            for (Long wareId : wareIds) {
                //成功就返回1,否则就是0
                Long count = wareSkuDao.lockSkuStock(skuId, wareId, hasStock.getNum());
                if (count == 1) {
                    skuStocked = true;
                    //TODO 告诉MQ库存锁定成功
                    WareOrderTaskDetailEntity entity = new WareOrderTaskDetailEntity(null, skuId, "", hasStock.getNum(), taskEntity.getId(), wareId, 1);
                    orderTaskDetailService.save(entity);
                    StockLockedTo lockedTo = new StockLockedTo();
                    lockedTo.setId(taskEntity.getId());
                    StockDetailTo stockDetailTo = new StockDetailTo();
                    BeanUtils.copyProperties(entity, stockDetailTo);
                    //只发id不行,防止回滚以后找不到数据
                    lockedTo.setDetail(stockDetailTo);
//                    rabbitTemplate
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", lockedTo);
                    break;
                } else {
                    //当前仓库锁失败,重试下一个仓库
                }
            }
            if (skuStocked == false) {
                //当前商品所有仓库都没有锁住
                throw new NoStockException(skuId);
            }
        }


        //3、肯定全部都是锁定成功过的

        return true;
    }
七、分布式事务

1)、seata环境准备

官网文档地址

1.为数据库添加回调undo_log

-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.下载包

下载seata-server-1.3.0

2)、整合环境

1.导入依赖

        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-seata
        

2.注册配置中心

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "gulimall-seata"
    serverAddr = "端口:8848"
    group = "DEFAULT_GROUP"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }

3.配置file

store {
  ## store mode: file、db、redis
  mode = "file"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

4.配置seataconfig

@Configuration
public class MySeataConfig {

    @Autowired
    DataSourceProperties dataSourceProperties;

    @Bean
    public DataSource dataSource(DataSourceProperties dataSourceProperties){
        //properties.initializeDataSourceBuilder().type(type).build();
        HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        if (StringUtils.hasText(dataSourceProperties.getName())) {
            dataSource.setPoolName(dataSourceProperties.getName());
        }
        return new DataSourceProxy(dataSource);
    }
}

5.为分布式微服务使用代理数据源

@EnableAspectJAutoProxy(exposeProxy = true)

6.为分布式服务添加注解

全局事务添加全局注解

    @GlobalTransactional  //高并发

分支事务添加普通注解

    @Transactional
八、消息队列

1)、创建信息队列MyMQconfig

@Configuration
public class MyMQConfig {



    //@Bean Binding,Queue,Exchange

    
    @Bean
    public Queue orderDelayQueue() {

        Map arguments = new HashMap<>();
        
        arguments.put("x-dead-letter-exchange","order-event-exchange");
        arguments.put("x-dead-letter-routing-key","order.release.order");
        arguments.put("x-message-ttl",60000);
        //String name, boolean durable, boolean exclusive, boolean autoDelete, Map arguments
        Queue queue = new Queue("order.delay.queue", true, false, false,arguments);
        return queue;
    }

    @Bean
    public Queue orderReleaseOrderQueue() {
        Queue queue = new Queue("order.release.order.queue", true, false, false);
        return queue;
    }

    @Bean
    public Exchange orderEventExchange() {
        //String name, boolean durable, boolean autoDelete, Map arguments
       return new TopicExchange("order-event-exchange",true,false);
    }

    @Bean
    public Binding orderCreateOrderBingding() {
        //String destination, DestinationType destinationType, String exchange, String routingKey,
        //			Map arguments
        return new Binding("order.delay.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.create.order",
                null);

    }

    @Bean
    public Binding orderReleaseOrderBingding() {
        return new Binding("order.release.order.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.order",
                null);
    }


    
    @Bean
    public Binding orderReleaseOtherBingding() {
        return new Binding("stock.release.stock.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.other.#",
                null);
    }


    @Bean
    public Queue orderSeckillOrderQueue(){
        //String name, boolean durable, boolean exclusive, boolean autoDelete, Map arguments
        return new Queue("order.seckill.order.queue",true,false,false);
    }

    @Bean
    public Binding orderSeckillOrderQueueBinding(){
        
        return new Binding("order.seckill.order.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.seckill.order",
                null);
    }

2)、为ware配置MQ

1.引入包

        
            org.springframework.boot
            spring-boot-starter-amqp
        

2.配置开始注解

@EnableRabbit
spring:
  rabbitmq:
    host: 101.43.79.94
    port: 5672
    virtual-host: /
    publisher-/confirm/i-type: correlated
    publisher-returns: true
    template:
      mandatory: true
    listener:
      simple:
        acknowledge-mode: manual

3.配置类

通上面的MyMQconfig

@Configuration
public class MyRabbitConfig {

    
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

//    @RabbitListener(queues = "stock.release.stock.queue")
//    public void  handle(Message message){
//
//    }

    @Bean
    public Exchange stockEventExchange(){
        //String name, boolean durable, boolean autoDelete, Map arguments
        return  new TopicExchange("stock-event-exchange",true,false);
    }

    @Bean
    public Queue stockReleaseStockQueue(){
        //String name, boolean durable, boolean exclusive, boolean autoDelete, Map arguments
        return new Queue("stock.release.stock.queue",true,false,false);
    }

    @Bean
    public Queue stockDelayQueue(){
        
        Map args = new HashMap<>();
        args.put("x-dead-letter-exchange","stock-event-exchange");
        args.put("x-dead-letter-routing-key","stock.release");
        args.put("x-message-ttl",120000);
        return new Queue("stock.delay.queue",true,false,false,args);
    }

    @Bean
    public Binding stockReleaseBinding(){
        
        return  new Binding("stock.release.stock.queue",
                Binding.DestinationType.QUEUE,
                "stock-event-exchange",
                "stock.release.#",
                null);
    }

    @Bean
    public Binding stockLockedBinding(){
        
        return  new Binding("stock.delay.queue",
                Binding.DestinationType.QUEUE,
                "stock-event-exchange",
                "stock.locked",
                null);
    }
}

3)、解锁库存

    @Override
    public void unlockStock(StockLockedTo to) {


        StockDetailTo detail = to.getDetail();
        Long detailId = detail.getId();
        //解锁
        //1、查询数据库关于这个订单的锁定库存信息。
        //  有:证明库存锁定成功了
        //    解锁:订单情况。
        //          1、没有这个订单。必须解锁
        //          2、有这个订单。不是解锁库存。
        //                订单状态: 已取消:解锁库存
        //                          没取消:不能解锁
        //  没有:库存锁定失败了,库存回滚了。这种情况无需解锁
        WareOrderTaskDetailEntity byId = orderTaskDetailService.getById(detailId);
        if (byId != null) {
            //解锁
            Long id = to.getId();
            WareOrderTaskEntity taskEntity = orderTaskService.getById(id);
            String orderSn = taskEntity.getOrderSn();//根据订单号查询订单的状态
            R r = orderFeignService.getOrderStatus(orderSn);
            if (r.getCode() == 0) {
                //订单数据返回成功
                OrderVo data = r.getData(new TypeReference() {
                });
                if (data == null || data.getStatus() == 4) {
                    //订单不存在
                    //订单已经被取消了。才能解锁库存
                    //detailId
                    if (byId.getLockStatus() == 1) {
                        //当前库存工作单详情,状态1 已锁定但是未解锁才可以解锁
                        unLockStock(detail.getSkuId(), detail.getWareId(), detail.getSkuNum(), detailId);
                    }
                }
            } else {
                //消息拒绝以后重新放到队列里面,让别人继续消费解锁。
                throw new RuntimeException("远程服务失败");
            }

        } else {
            //无需解锁
        }

    }

4)、监听

监听自动解锁

@Service
@RabbitListener(queues = "stock.release.stock.queue")
public class StockReleaseListener {

    @Autowired
    WareSkuService wareSkuService;

    @RabbitHandler
    public void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {

        System.out.println("收到解锁库存的消息...");
        try{
            //当前消息是否被第二次及以后(重新)派发过来了。
//            Boolean redelivered = message.getMessageProperties().getRedelivered();
            wareSkuService.unlockStock(to);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }

    }

    @RabbitHandler
    public void handleOrderCloseRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {
        System.out.println("订单关闭准备解锁库存...");
        try{
            wareSkuService.unlockStock(orderTo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }

    }

}

监听关单

@RabbitListener(queues = "order.release.order.queue")
@Service
public class OrderCloseListener {

    @Autowired
    OrderService orderService;
    @RabbitHandler
    public void listener(OrderEntity entity, Channel channel, Message message) throws IOException {
        System.out.println("收到过期的订单信息:准备关闭订单"+entity.getOrderSn()+"==>"+entity.getId());
        try{
            orderService.closeOrder(entity);
            //手动调用支付宝收单;
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }

    }
}
九、整合支付宝付款

1)、引入阿里支付的服务

1.引入包

        
        
            com.alipay.sdk
            alipay-sdk-java
            4.21.10.ALL
        

2.封装支付工具类AlipayTemplate

修改为自己的密钥

2)、支付业务

1.抽取支付的vo

@Data
public class PayVo {
    private String out_trade_no; // 商户订单号 必填
    private String subject; // 订单名称 必填
    private String total_amount;  // 付款金额 必填
    private String body; // 商品描述 可空
}

2.修改前端页面,完善支付逻辑

		  
  • 支付宝
  • @Controller
    public class PayWebController {
    
        @Autowired
        AlipayTemplate alipayTemplate;
    
        @Autowired
        OrderService orderService;
    
    
        
        @ResponseBody
        @GetMapping(value = "/payOrder",produces = "text/html")
        public String payOrder(@RequestParam("orderSn") String orderSn) throws AlipayApiException {
    
    //        PayVo payVo = new PayVo();
    //        payVo.setBody();//订单的备注
    //        payVo.setOut_trade_no();//订单号
    //        payVo.setSubject();//订单的主题
    //        payVo.setTotal_amount();
            PayVo payVo = orderService.getOrderPay(orderSn);
            //返回的是一个页面。将此页面直接交给浏览器就行
            String pay = alipayTemplate.pay(payVo);
            System.out.println(pay);
            return pay;
        }
    }
    

    3)、整合会员服务

    1.引入thymeleaf模板和redis

            
                org.springframework.boot
                spring-boot-starter-thymeleaf
            
            
            
                org.springframework.boot
                spring-boot-starter-data-redis
                
                    
                        io.lettuce
                        lettuce-core
                    
                
            
            
                redis.clients
                jedis
            
            
                org.springframework.session
                spring-session-data-redis
            

    2.添加配置

    3.添加拦截器

    和之前一样,为登录无法访问

    4.添加网关

            - id: gulimall-member-route
              uri: lb://gulimall-member
              predicates:
                - Host=member.gulimall.com

    5)、渲染订单服务

    1.业务逻辑

    @Controller
    public class MemberWebController {
    
        @Autowired
        OrderFeignService orderFeignService;
    
        
        @GetMapping("/memberOrder.html")
        public String memberOrderPage(@RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum,
                                      Model model, HttpServletRequest request){
            //获取到支付宝给我们传来的所有请求数据;
    //        request。验证签名,如果正确可以去修改。
    
    
            //查出当前登录的用户的所有订单列表数据
            Map page =new HashMap<>();
            page.put("page",pageNum.toString());
            //
            R r = orderFeignService.listWithItem(page);
            System.out.println(JSON.toJSonString(r));
            model.addAttribute("orders",r);
            return "orderList";
        }
    }

    2.网络的请求拦截器

    配置GuliFeignConfig,和之前的一样,保存传入的cookies

    6)、异步通知

    1.配置通知业务

    @RestController
    public class OrderPayedListener {
    
    
        @Autowired
        AlipayTemplate alipayTemplate;
    
        @Autowired
        OrderService orderService;
        
        @PostMapping("/payed/notify")
        public String handleAlipayed(PayAsyncVo vo,HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException {
            //只要我们收到了支付宝给我们异步的通知,告诉我们订单支付成功。返回success,支付宝就再也不通知
    //        Map map = request.getParameterMap();
    //        for (String key : map.keySet()) {
    //            String value = request.getParameter(key);
    //            System.out.println("参数名:"+key+"==>参数值:"+value);
    //        }
            //验签
            Map params = new HashMap();
            Map requestParams = request.getParameterMap();
            for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
                String name = (String) iter.next();
                String[] values = (String[]) requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
                }
                //乱码解决,这段代码在出现乱码时使用
    //            valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }
    
            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayTemplate.getAlipay_public_key(), alipayTemplate.getCharset(), alipayTemplate.getSign_type()); //调用SDK验证签名
            if(signVerified){
                System.out.println("签名验证成功...");
                String result = orderService.handlePayResult(vo);
                return result;
            }else {
                System.out.println("签名验证失败...");
                return "error";
            }
    
    
    
        }
    }
    

    2.配置nginx

           location /payed/ {
    	proxy_set_header Host order.gulimall.com;
    	proxy_pass http://gulimall;
           }
    

    Field error in object 'payAsyncVo' on field 'notify_time'问题

    添加全局时间的配置

    spring.mvc.format.date=yyyy-MM-dd HH:mm:ss

    7)、收单

    30min没支付,自动收单

           
        private String timeout = "30m";
    
        alipayRequest.setBizContent("{"out_trade_no":""+ out_trade_no +"","
                    + ""total_amount":""+ total_amount +"","
                    + ""subject":""+ subject +"","
                    + ""body":""+ body +"","
                    + ""timeout_express":""+timeout+"","
                    + ""product_code":"FAST_INSTANT_TRADE_PAY"}");

    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/683009.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号