** 短信解决方案_阿里大于短信_短信微服务_ActiveMQ*

SpringBoot框架与短信解决方案

今日内容:

  1. 掌握Spring Boot框架的搭建方法
  2. 能够使用阿里大于发送短信
  3. 运用SpringBoot、阿里大于和ActiveMQ开发短信微服务
  4. 完成品优购用户注册功能(短信验证码认证)

SpringBoot 入门

1. 什么是SpringBoot ?
	* Spring Boot 是 Spring 社区较新的一个项目。该项目的目的是帮助开发者更容易的创建基于 Spring 的应用程序和服务,让更多人的人更快的对 Spring 进行入门体验,为 Spring生态系统提供了一种固定的、约定优于配置风格的框架。
	
2. 为什么要使用SpringBoot?

	1.  Spring 诞生时是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring 为企业级 Java 开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的 Java 对象(Plain Old Java Object,POJO)实现了 EJB 的功能;
	
	2.  虽然 Spring 的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring 用 XML配置,而且是很多 XML 配置。Spring 2.5 引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式 XML 配置。Spring 3.0 引入了基于 Java 的配置,这是一种类型安全的可重构配置方式,可以代替 XML。所有这些配置都代表了开发时的损耗。因为在思考Spring 特性配置和解决业务问题之间需要进行思维切换,所以写配置挤占了写应用程序逻辑的时间。和所有框架一样,Spring 实用,但与此同时它要求的回报也不少。
	
	3. 除此之外,项目的依赖管理也是件吃力不讨好的事情。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪个版本和其他库不会有冲突,这难题实在太棘手。并且,依赖管理也是一种损耗,添加依赖不是写应用程序代码。一旦选错了依赖的版本,随之而来的不兼容问题毫无疑问会是生产力杀手
	
	4.   正因为如此,SpringBoot才随着出现,它为了解决这一系列的问题才受到了人们的欢迎。


3. SpringBoot的特点:
	1. 为基于 Spring 的开发提供更快的入门体验
	2. 开箱即用,没有代码生成,也无需 XML 配置。同时也可以修改默认值来满足特定的需求。
	3. 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。
	4. Spring Boot 并不是不对 Spring 功能上的增强,而是提供了一种快速使用 Spring的方式。

Spring Boot 入门程序

  1. 入门程序

    1. pom.xml:
      <?xml version="1.0" encoding="UTF-8"?>

      4.0.0

           <groupId>cn.itcast.demo</groupId>
           <artifactId>springBootDemo</artifactId>
           <version>1.0-SNAPSHOT</version>
       
           <!--设置java版本-->
           <properties>
               <java.version>1.8</java.version>
           </properties>
       
       
           <!--启动器-->
           <parent>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-parent</artifactId>
               <version>1.4.0.RELEASE</version>
           </parent>
       
           <dependencies>
               <!--WEB工程相关包-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-web</artifactId>
                   <version>1.4.0.RELEASE</version>
               </dependency>
           </dependencies>
       
       	</project>
       * 注意:
       	1. 在pom.xml中的这两个文件,一个是启动器,一个是web工程相关工具包,导入了web工程的那个就导入了基本上所有web工程所用到的包,且不会有包版本冲突等问题;
       	2. 如何实现的呢?原理: 依赖传递。 通过SpringBoot继承了这些包,然后我们导入了web工程包的时候就相当于继承了这些包了;
      
    2. 引导类:Application.java:
      package cn.itcast.demo;

       import org.springframework.boot.SpringApplication;
       import org.springframework.boot.autoconfigure.SpringBootApplication;
       
       @SpringBootApplication
       public class Application {
           public static void main(String[] args) {
               SpringApplication.run(Application.class,args);
           }
       }
      
      • 注意: @SpringBootApplication 其实就是以下三个注解的总和

          1. @Configuration: 用于定义一个 java 配置类 application-solr.xml
          2. @EnableAutoConfiguration :Spring Boot 会自动根据你 jar 包的依赖来自动配置项目。
          3. @ComponentScan: 告诉 Spring 哪个 packages 的用注解标识的类 会被 spring 自动扫描并且装入 bean 容器。
        
    3. 测试代码:HelloWorldController.java:
      package cn.itcast.demo;

       import org.springframework.web.bind.annotation.RequestMapping;
       import org.springframework.web.bind.annotation.RestController;
       @RestController
       public class HelloWorldController {
       
       
           @RequestMapping("/info")
           public String findString(){
               return "HelloWorld!";
           }
       
       }
      
    4. 先运行Application.java,然后打开浏览器搜索:localhost:8080/info 即可看到" helloworld!"

  2. 如何修改Tomcat端口:

    1. 创建resources/application.properties:
      server.port=8088

    2. 运行Application.java后即可发现端口号已经覆盖改变,登录需要输入新的ip地址;

  3. 如何读取配置文件信息:

    1. 在 src/main/resources 下的 application.properties 增加配置
      url=http://www.baidu.cn

    2. 在类中读取这个配置信息,修改 HelloWorldController
      //注入可连接配置文件内容的env
      @Autowired
      private Environment env;

      @RequestMapping("/info")
      public String info(){
      return “HelloWorld~~”+env.getProperty(“url”); //env调用getProperty进行取值
      }

  4. 热部署

    1. 概述:

      • 我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大量的时间,能不能在我修改代码后不重启就能生效呢?可以,在pom.xml 中添加如下配置就可以实现这样的功能,我们称之为热部署。
    2. 代码:

      org.springframework.boot
      spring-boot-devtools

    3. 在idea中如果热部署失败:

      1. 两步:

        1. CTRL + SHIFT + A --> 查找make project automatically --> 选中
        2. CTRL + SHIFT + A --> 查找Registry --> 找到并勾选 compiler.automake.allow.when.app.running
      2. 在pom.xml中添加上 spring开发工具包

         <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-devtools</artifactId>
         <optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
         <scope>runtime</scope>
         </dependency>
        
      3. 如果使用的是Intellij IEDA开发工具,还需要到设置里将project automatically勾选上,File->Setting->Build,…->Compiler 将右侧project automatically勾上

与ActiveMQ整合:

  1. 注意:SpringBoot采用的是内置的类似于ActiveMQ服务,所以登浏览器查看不到发送消息和接受消息的详细信息;

  2. 使用内嵌服务:

    1. pom.xml:
      <?xml version="1.0" encoding="UTF-8"?>

      4.0.0

           <groupId>cn.itcast.demo</groupId>
           <artifactId>springBootDemo</artifactId>
           <version>1.0-SNAPSHOT</version>
       
           <!--设置java版本-->
           <properties>
               <java.version>1.8</java.version>
           </properties>
       
       
           <!--启动器-->
           <parent>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-parent</artifactId>
               <version>1.4.0.RELEASE</version>
           </parent>
       
           <dependencies>
               <!--WEB工程相关包-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-web</artifactId>
                   <version>1.4.0.RELEASE</version>
               </dependency>
       
               <!--热部署-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-devtools</artifactId>
               </dependency>
               <!--ActiveMQ起步依赖-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-activemq</artifactId>
               </dependency>
       
           </dependencies>
       
       </project>
      
    2. 消息生产者: QueueController.java

       package cn.itcast.demo;
      
       import org.springframework.beans.factory.annotation.Autowired;
       import org.springframework.jms.core.JmsMessagingTemplate;
       import org.springframework.web.bind.annotation.RequestMapping;
       import org.springframework.web.bind.annotation.RestController;
       @RestController
       public class QueueController {
       
           @Autowired
           private JmsMessagingTemplate jmsMessagingTemplate;
       
           @RequestMapping("/send")
           public void send(String text){
               jmsMessagingTemplate.convertAndSend("itcast", text);
           }
       }
      
    3. 引导类:Application:
      package cn.itcast.demo;

       import org.springframework.boot.SpringApplication;
       import org.springframework.boot.autoconfigure.SpringBootApplication;
       @SpringBootApplication
       public class Application {
           public static void main(String[] args) {
               SpringApplication.run(Application.class,args);
           }
       }
      
    4. 消费者:Consumer.java:
      package cn.itcast.demo;

       import org.springframework.jms.annotation.JmsListener;
       import org.springframework.stereotype.Component;
       @Component
       public class Consumer {
           @JmsListener(destination = "itcast")
           public  void readMessage(String text){
               System.out.println("接收到消息:"+text);
           }
       }
      
    5. 注意:

      1. 内嵌服务是内置的ActiveMQ的,所以浏览器查记录查不到的;
  3. 使用外部服务:

    1. application.properties:
      spring.activemq.broker-url=tcp://192.168.25.128:61616

      • 这里通过修改配置文件后,将ActiveMQ替换掉了,使用了外置服务后就连接到了外界服务器中的ActiveMQ,就可以在浏览器中查看记录了;
    2. 其他地方与内嵌一致,创建的时候,队列名称不能一样,可以共用在同一个类中;

短信发送平台 - 阿里大于

  1. 简介:

    • 阿里大于是 阿里云旗下产品,融合了三大运营商的通信能力,通过将传统通信业务和能力与互联网相结合,创新融合阿里巴巴生态内容,全力为中小企业和开发者提供优质服务阿里大于提供包括短信、语音、流量直充、私密专线、店铺手机号等个性化服务。通过阿里大于打通三大运营商通信能力,全面融合阿里巴巴生态,以开放 API 及 SDK 的方式向开发者提供通信和数据服务,更好地支撑企业业务发展和创新服务。
    • 短信的发送流程大同小异,所以学习了阿里大于的短信发送流程,那么其他的也差不多类似;
    • 目前阿里大于不能个人发送短信,必须有工商营业执照;
  2. 注册账户:

    1. 首先我们先进入“阿里大于” www.alidayu.comhttps://dayu.aliyun.com/)
    2. 注册账号后,再在手机下载“阿里云”应用,登陆,然后进行在线实名认证。
  3. 登录系统并操作:

    1. 使用刚才注册的账号进行登陆。
    2. 点击进入控制台
    3. 点击使用 短信服务
    4. 申请签名
    5. 申请模板
    6. 创建 accessKey
  4. SDK安装

    1. 从阿里云通信官网上下载 Demo 工程
    2. 解压后导入 Eclipce/idea
    3. aliyun-java-sdk-core/dysmsapi 的两个工程就是阿里云通信的依赖 jar 源码,我们将其安装到本地仓库
    4. (删除 aliyun-java-sdk-core 的单元测试类)
    5. 本地 jar 包安装后 alicom-dysms-api 工程引入依赖


      com.aliyun
      aliyun-java-sdk-dysmsapi
      1.0.0-SNAPSHOT


      com.aliyun
      aliyun-java-sdk-core
      3.2.5

  5. 发送短信测试:

    1. 打开 SmsDemo,替换提示的相应几处代码
      这个 accessKeyId 和 accessSecret 到刚才申请过的
      手机号,短信签名和模板号
      模板参数${number} ${name}
      number 是我们申请模板时写的参数
      执行 main 方法我们就可以在手机收到短信啦
    2. 执行main方法即可收到短信啦~

短信微服务:

1. 需求分析:
	* 构建一个通用的短信发送服务(独立于品优购的单独工程),接收 activeMQ 的消息(MAP类型) 消息包括手机号(mobile)、短信模板号(template_code)、签名(sign_name)、参数字符串(param )。
	* 使用短信微服务,它对阿里大于进行了又一层的封装,它独立于项目之外,所有项目通过调用它的方法均可以使用短信发送功能,就可以不用直接调用阿里大于,这样就不需要每个项目都用到阿里大于的账户密码等信息;
	* 提高了可重用性,效率性,易使用性以及安全性;

2. 加了Compont注解后才能进入Bean;

注册时手机验证码思路:

  1. 点击发送验证码逻辑

    1. 生成一个6位随机数字
    2. 将生成的验证码存入reids以手机号为key,以验证码为值
    3. 将短信内容发送到activeMQ
  2. 校验验证码是否正确

    1. 用户注册前进行校验
    2. 获取用户填写的验证码,与reids中的验证码进行比较;

短信微服务发送手机验证码:实战分析:

1. 实现短信发送,首先建立短信微服务:
	1. resources/application.properties:
		# 配置端口
		server.port=9003
		# 配置activeMQ消息中间件ip地址
		spring.activemq.broker-url=tcp://192.168.25.128:61616
		# 配置账户id和密码
		accessKeyId=''
		accessKeySecret=''
	2. 引导类:
		package cn.itcast.sms;
		import org.springframework.boot.SpringApplication;
		import org.springframework.boot.autoconfigure.SpringBootApplication;
		@SpringBootApplication
		public class Application {
		    public static void main(String[] args) {
		        SpringApplication.run(Application.class,args);
		    }
		}
	3. 监听接收消息类:
			package cn.itcast.sms;
			import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
			import com.aliyuncs.exceptions.ClientException;
			import org.springframework.beans.factory.annotation.Autowired;
			import org.springframework.jms.annotation.JmsListener;
			import org.springframework.stereotype.Component;
			import java.util.Map;
			@Component
			public class SmsListener {
			    @Autowired
			    private SmsUtil smsUtil;
			    @JmsListener(destination = "sms")
			    public void sendSms(Map<String,String> map){
			        try {
			            SendSmsResponse response=smsUtil.sendSms(map.get("mobile"),
			                    map.get("template_code"),
			                    map.get("sign_name"),
			                    map.get("param"));
			            System.out.println("code:"+response.getCode());
			            System.out.println("message:"+response.getMessage());
			        } catch (ClientException e) {
			            e.printStackTrace();
			        }
			    }
			}
		
		* 分析:
			1. 设置监听的时候,在方法中导入接收preperties文件中的参数配置信息,获取发件人手机号,内容,以及短信账户密码等,当接收到了监听时,调用下面的工具类方法发送消息;


	4. 阿里大于的jar包发送短信demo:
				package cn.itcast.sms;
	
				import com.aliyuncs.DefaultAcsClient;
				import com.aliyuncs.IAcsClient;
				import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
				import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
				import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
				import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
				import com.aliyuncs.dysmsapi.transform.v20170525.SendSmsResponseUnmarshaller;
				import com.aliyuncs.exceptions.ClientException;
				import com.aliyuncs.http.FormatType;
				import com.aliyuncs.http.HttpResponse;
				import com.aliyuncs.profile.DefaultProfile;
				import com.aliyuncs.profile.IClientProfile;
				import org.springframework.beans.factory.annotation.Autowired;
				import org.springframework.core.env.Environment;
				import org.springframework.stereotype.Component;
				
				import java.nio.charset.Charset;
				import java.text.SimpleDateFormat;
				import java.util.Date;
				import java.util.UUID;
				
				/**
				 * Created on 17/6/7.
				 * 短信API产品的DEMO程序,工程中包含了一个SmsDemo类,直接通过
				 * 执行main函数即可体验短信产品API功能(只需要将AK替换成开通了云通信-短信产品功能的AK即可)
				 * 工程依赖了2个jar包(存放在工程的libs目录下)
				 * 1:aliyun-java-sdk-core.jar
				 * 2:aliyun-java-sdk-dysmsapi.jar
				 * <p>
				 * 备注:Demo工程编码采用UTF-8
				 * 国际短信发送请勿参照此DEMO
				 */
				@Component
				public class SmsUtil {
				
				    //产品名称:云通信短信API产品,开发者无需替换
				    static final String product="Dysmsapi";
				    //产品域名,开发者无需替换
				    static final String domain="dysmsapi.aliyuncs.com";
				
				    // TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
				    //通过配置文件注入可以不用写死,提高程序的灵活性
				    @Autowired
				    private Environment environment;
				    //static final String accessKeyId = "yourAccessKeyId";
				    //static final String accessKeySecret = "yourAccessKeySecret";
				
				    public SendSmsResponse sendSms(String mobile,String template_code,String sign_name,String param) throws ClientException {
				
				        //从配置文件中注入
				        String accessKeyId=environment.getProperty("accessKeyId");
				        String accessKeySecret=environment.getProperty("accessKeySecret");
				
				        //可自助调整超时时间
				        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
				        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
				
				        //初始化acsClient,暂不支持region化
				        IClientProfile profile=DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
				        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
				        IAcsClient acsClient=new DefaultAcsClient(profile);
				
				        //组装请求对象-具体描述见控制台-文档部分内容
				        SendSmsRequest request=new SendSmsRequest();
				        //必填:待发送手机号
				        request.setPhoneNumbers(mobile);
				        //必填:短信签名-可在短信控制台中找到
				        request.setSignName(sign_name);
				        //必填:短信模板-可在短信控制台中找到
				        request.setTemplateCode(template_code);
				        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
				        request.setTemplateParam(param);
				
				        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
				        //request.setSmsUpExtendCode("90997");
				
				        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
				        request.setOutId("yourOutId");
				
				        //hint 此处可能会抛出异常,注意catch
				        SendSmsResponse sendSmsResponse=acsClient.getAcsResponse(request);
				
				        return sendSmsResponse;
				    }
				
				
				    public QuerySendDetailsResponse querySendDetails(String mobile,String bizId) throws ClientException {
				
				        //从配置文件中注入
				        String accessKeyId=environment.getProperty("accessKeyId");
				        String accessKeySecret=environment.getProperty("accessKeySecret");
				
				
				        //可自助调整超时时间
				        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
				        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
				
				        //初始化acsClient,暂不支持region化
				        IClientProfile profile=DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
				        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
				        IAcsClient acsClient=new DefaultAcsClient(profile);
				
				        //组装请求对象
				        QuerySendDetailsRequest request=new QuerySendDetailsRequest();
				        //必填-号码
				        request.setPhoneNumber(mobile);
				        //可选-流水号
				        request.setBizId(bizId);
				        //必填-发送日期 支持30天内记录查询,格式yyyyMMdd
				        SimpleDateFormat ft=new SimpleDateFormat("yyyyMMdd");
				        request.setSendDate(ft.format(new Date()));
				        //必填-页大小
				        request.setPageSize(10L);
				        //必填-当前页码从1开始计数
				        request.setCurrentPage(1L);
				
				        //hint 此处可能会抛出异常,注意catch
				        QuerySendDetailsResponse querySendDetailsResponse=acsClient.getAcsResponse(request);
				
				        return querySendDetailsResponse;
				    }
				
				
				    //因为是工具类所以暂时不用main方法
				/*    public static void main(String[] args) throws ClientException, InterruptedException {
				
				        //发短信
				        SendSmsResponse response = sendSms();
				        System.out.println("短信接口返回的数据----------------");
				        System.out.println("Code=" + response.getCode());
				        System.out.println("Message=" + response.getMessage());
				        System.out.println("RequestId=" + response.getRequestId());
				        System.out.println("BizId=" + response.getBizId());
				
				        Thread.sleep(3000L);
				
				        //查明细
				        if(response.getCode() != null && response.getCode().equals("OK")) {
				            QuerySendDetailsResponse querySendDetailsResponse = querySendDetails(response.getBizId());
				            System.out.println("短信明细查询接口返回数据----------------");
				            System.out.println("Code=" + querySendDetailsResponse.getCode());
				            System.out.println("Message=" + querySendDetailsResponse.getMessage());
				            int i = 0;
				            for(QuerySendDetailsResponse.SmsSendDetailDTO smsSendDetailDTO : querySendDetailsResponse.getSmsSendDetailDTOs())
				            {
				                System.out.println("SmsSendDetailDTO["+i+"]:");
				                System.out.println("Content=" + smsSendDetailDTO.getContent());
				                System.out.println("ErrCode=" + smsSendDetailDTO.getErrCode());
				                System.out.println("OutId=" + smsSendDetailDTO.getOutId());
				                System.out.println("PhoneNum=" + smsSendDetailDTO.getPhoneNum());
				                System.out.println("ReceiveDate=" + smsSendDetailDTO.getReceiveDate());
				                System.out.println("SendDate=" + smsSendDetailDTO.getSendDate());
				                System.out.println("SendStatus=" + smsSendDetailDTO.getSendStatus());
				                System.out.println("Template=" + smsSendDetailDTO.getTemplateCode());
				            }
				            System.out.println("TotalCount=" + querySendDetailsResponse.getTotalCount());
				            System.out.println("RequestId=" + querySendDetailsResponse.getRequestId());
				        }
				
				    }*/
				}


2. 微服务建立好了之后,开始建立用户注册页面,下面是分析步骤,代码较多不展示啦~
	1. 首先前端web层,后端interface,service层建立,导入相关文件,配置好相关信息;
	2. 后端提供几个方法:
		1. 发送短信验证码
			* 发送短信验证码的时候导入相关template,首先生成六位随机数验证码,然后将验证码存入reids,配置参数properties文件,读取配置然后将短信验证码的内容和其他信息发送给activeMQ;

		2. 校验短信验证码:
			* 在reids中取值,如果正确返回true,错误返回false;


	3. 前端使用angulerJS,
		1. 注册用户:
			* 在controller.js中进行判断,第一次判断两次输入密码是否一致,如果不一致则返回提示不一致,如果信息一致则提交到后台检验验证码是否正确,如果正确注册成功,如果错误返回false.
		2. 发送验证码:
			* 在controler.js中提供方法,前端点击发送验证码时调用此方法,先判断手机号是否为"" 或null,如果格式正确则在controller.java中提供的正则表达式进行校验,验证手机号的工具类可以提取到公共项目中重复使用;如果验证通过则调用service中的生成校验码方法生成校验码并调用微服务短信发送给用户手机号,如果不格式不对则返回结果;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗余

码字来之不易,您的鼓励我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值