SpringMVC_02

springmvc第二天

学习目标

  • 掌握图片上传(重点)
  • 了解SpringMVC的统一异常处理
  • 掌握SpringMVC拦截器(重点)
  • 将面面项目改成ssm架构(重点)

第一章 - SpringMVC 实现文件上传

知识点-文件上传介绍

1.目标

  • 掌握文件上传的要求

2.路径

  1. 文件上传概述
  2. 文件上传要求
  3. 常见的文件上传jar包和框架

3.讲解

3.1文件上传概述以及使用场景

​ 就是把客户端(浏览器)的文件保存一份到服务器 说白了就是文件的拷贝

​ 常见的使用场景:上传头像、上传各种照片、上传word、Excel等等文件

3.2文件上传要求
3.2.1 浏览器端要求(通用浏览器的要求)
  • 表单提交方式 post
  • 提供文件上传框(组件) input type=“file”
  • 表单的entype属性必须为 multipart/form-data(没有这个属性值的话, 文件的内容是提交不过去的)
3.2.2 服务器端要求
  1. 获取客户端上传的文件

    1. 准备一个目录存储客户端上传的文件
    2. 将客户端上传的文件写入到准备好的目录中

注意:

  • 若表单使用了 multipart/form-data ,使用原生request.getParameter()去获取参数的时候都为null

我们做文件上传一般会借助第三方组件(jar, 框架 SpringMVC)实现文件上传.

3.3常见的文件上传jar包和框架

​ serlvet3.0(原生的文件上传的API)

​ commons-fileupload : apache出品的一款专门处理文件上传的工具包 (我们肯定不会直接使用)

​ struts2(底层封装了:commons-fileupload)

​ SpringMVC(底层封装了:commons-fileupload)

4.小结

案例-springmvc 传统方式文件上传

1.需求

  • 使用springmvc 完成传统方式文件上传

2.步骤

  1. 把commons-fileupload坐标导入进来
  2. 在控制器的方法的形参里面定义和文件相关的变量 MultipartFile
  3. 把文件存到服务器
  4. 配置文件解析器

3.实现

  • 创建Maven工程,添加依赖
<dependencies>
    <!--文件上传组件的依赖-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    <!--springmvc的依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
  • 创建前端页面
<h1>一,springmvc传统方式文件上传</h1>
<form action="springmvc/fileUpload02" method="post" enctype="multipart/form-data">
    图片: <input type="file" name="upload"/><br/>
    图片描述:<input type="text" name="pdesc"/>
    <input type="submit" value="上传"/>
</form>
  • 控制器
package com.itheima.controller;

import com.itheima.utils.UploadUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

/**
 * 包名:com.itheima.controller
 * @author Leevi
 * 日期2020-08-15  08:46
 * 1. 获取客户端传入的文件描述信息
 * 2. 获取客户端上传的文件,存放到file文件夹中
 *    2.1 获取客户端上传的文件   MultipartFile upload
 *    2.2 创建一个file文件夹
 *    2.3 使用输出流将客户端上传的文件,输出到file文件夹中
 */
@RestController
@RequestMapping("/file")
public class FileController {
    @PostMapping("/upload")
    public String upload(String pdesc, MultipartFile upload, HttpServletRequest request){
        System.out.println("文件描述是:" + pdesc);

        //1. 获取存放文件的目录路径
        String realPath = request.getSession().getServletContext().getRealPath("file/"+ UploadUtils.getDir());
        File file = new File(realPath);
        //判断file对应的文件夹是否真实存在于硬盘中
        if (!file.exists()) {
            //不存在,则将其在硬盘中创建出来
            file.mkdirs();
        }

        //2. 将客户端上传的文件,写入到file文件夹中
        //2.1 获取客户端上传的那个文件的文件名
        String filename = upload.getOriginalFilename();
        //将文件名,进行重命名,重命名成唯一的文件名--->UUID
        String uuidName = UploadUtils.getUUIDName(filename);
        try {
            //2.2 将客户端上传的文件写入到文件夹中
            upload.transferTo(new File(file,uuidName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "success";
    }
}
  • 在springmvc.xml配置文件解析器

    注意:文件上传的解析器id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他字段也将无法绑定)

<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 设置上传文件的最大尺寸为 5MB --> 
    <property name="maxUploadSize" value="5242880"></property>
</bean>

4.小结

案例-springmvc 跨服务器方式的文件上传(了解)

1.需求

  • 了解使用springmvc 跨服务器方式的文件上传

2.分析

2.1分服务器的目的

在实际开发中,我们会有很多处理不同功能的服务器(注意:此处说的不是服务器集群)。 例如:

​ 应用服务器:负责部署我们的应用
​ 数据库服务器:运行我们的数据库
​ 缓存和消息服务器:负责处理高并发访问的缓存和消息
​ 文件服务器:负责存储用户上传文件的服务器。

分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPitWs6k-1631055157773)(img/tu_2.png)]

2.2跨服务器方式的文件上传图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-leSq8KGZ-1631055157809)(img/tu_3.png)]

  • 准备两个服务器(默认情况下,tomcat是不允许其他服务器往它里面写入数据的), 修改tomcat的的conf目录下的web.xml, 添加readonly参数为false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGgMfyFH-1631055157816)(img/tu_4.png)]

3.实现

  • 添jersey依赖 (跨服务器上传图片的代码)
<dependencies>
    <!--引入文件上传的依赖-->
    <!--文件上传组件的依赖-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    <!--springmvc的依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>

    <!--跨服务器上传的文件的依赖-->
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-core</artifactId>
        <version>1.18.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.18.1</version>
    </dependency>
</dependencies>
  • 前端页面
<h1>二,springmvc 跨服务器方式的文件上传</h1>
<form action="springmvc/fileUpload03" method="post" enctype="multipart/form-data">
    图片: <input type="file" name="upload"/><br/>
    图片描述:<input type="text" name="pdesc"/>
    <input type="submit" value="上传"/>
</form>
  • 控制器
package com.itheima.controller;

import com.itheima.utils.UploadUtils;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
 * 包名:com.itheima.controller
 *
 * @author Leevi
 * 日期2020-08-15  09:53
 */
@RestController
@RequestMapping("/file")
public class FileController {
    @PostMapping("/upload")
    public String upload(String pdesc, MultipartFile upload){
        System.out.println("pdesc="+pdesc);
        //1. 获得文件名
        String filename = upload.getOriginalFilename();
        //2. 获得随机文件名
        String uuidName = UploadUtils.getUUIDName(filename);

        //3. 使用跨服务器文件上传的方式,将客户端上传的文件写入到文件服务器中
        Client client = Client.create();
        //连接文件服务器
        WebResource resource = client.resource("http://localhost:8833/file/"+uuidName);
        //通过resource将客户端上传的文件写入到文件服务器
        try {
            resource.put(upload.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "success";
    }
}

4.小结

第二章-SpringMVC 中的异常处理(了解)

知识点-SpringMVC 中的异常处理

1.目标

  • 掌握SpringMVC的统一异常处理

2.分析

​ 系统中异常包括两类:编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PMP8Pl5V-1631055157826)(img/tu_5.png)]

​ springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。

3.代码实现

3.1自定义异常处理器
package com.itheima.handler;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 包名:com.itheima.handler
 * @author Leevi
 * 日期2020-08-15  10:30
 * 1. 自定义一个异常处理器类,实现HandlerExceptionResolver接口
 * 2. 重写resolveException方法,对异常进行统一处理
 * 3. 在springmvc的配置文件中,配置异常处理器
 */
public class GlobalExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg",ex.getMessage());
        //打印异常信息
        ex.printStackTrace();
        modelAndView.setViewName("error");
        return modelAndView;
    }
}
3.2配置异常处理器
  • 在springmvc.xml配置
<!--配置异常处理器-->
<bean id="sysExceptionResolver" class="com.itheima.handler.GlobalExceptionHandler"></bean>

4.小结

第三章-SpringMVC 中的拦截器(掌握)

知识点-SpringMVC入门

1.目标

  • 掌握SpringMVC基本使用

2.路径

  1. 拦截器概述
  2. 自定义拦截器入门

3.讲解

3.1.拦截器概述

​ Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器(自己编写的Controller)进行预处理和后处理。用户可以自己定义一些拦截器来实现特定的功能。谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但
是也有区别,接下来我们就来说说他们的区别:

类别使用范围拦截范围
拦截器SpringMVC项目只会拦截访问的控制器方法的请求
过滤器任何web项目任何资源(servlet,控制器,jsp,html等)

​ 我们要想自定义拦截器, 要求必须实现: HandlerInterceptor 接口。

3.2.自定义拦截器入门
  • 编写一个普通类实现 HandlerInterceptor 接口
package com.itheima.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 包名:com.itheima.interceptor
 *
 * @author Leevi
 * 日期2020-08-15  10:57
 * 1. 自定义一个拦截器实现HandlerInterceptor接口
 * 2. 重写它的方法
 *    preHandle:在执行处理器方法之前执行
 *    postHandle:执行处理器方法之后执行
 *    afterCompletion:这次请求完成之后执行
 * 3. 在springmvc的配置文件中,配置拦截器
 */
public class PermissionInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进行权限校验....");
        //返回true表示放行,返回false表示拦截
        return true;
    }

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("权限校验的posthandle方法执行了...");
    }
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("权限校验的afterCompletion方法执行了...");
    }
}
  • 在springmvc.xml配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!--排除某些请求-->
        <mvc:exclude-mapping path="/hello/sayHaha.do"/>
        <bean id="interceptor2" class="com.itheima.interceptor.CheckNameInterceptor">	</bean>
    </mvc:interceptor>
</mvc:interceptors>

4.小结

知识点-自定义拦截器进阶

1.目标

  • 掌握自定义拦截器的高级使用

2.路径

  1. 拦截器的放行
  2. 拦截后跳转
  3. 拦截器的路径
  4. 拦截器的其它方法
  5. 多个拦截器执行顺序

3.讲解

3.1拦截器的放行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YL6LGqyq-1631055157833)(img/tu_6.png)]

3.2拦截后跳转

​ 拦截器的处理结果,莫过于两种:

​ 放行: 如果后面还有拦截器就执行下一个拦截器,如果后面没有了拦截器,就执行Controller方法

​ 拦截: 但是注意,拦截后也需要返回到一个具体的结果(页面,Controller)。

  • 在preHandle方法返回false,通过request进行转发,或者通过response对象进行重定向,输出
package com.itheima.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 包名:com.itheima.interceptor
 * @author Leevi
 * 日期2020-08-15  11:12
 */
public class CheckNameInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("校验用户名...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("校验用户名的postHandler方法...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("校验用户名的afterCompletion方法...");
    }
}
3.3 拦截器的路径
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--
			拦截访问所有controller的请求
		-->
        <mvc:mapping path="/**"/>
        <!--排除某些请求-->
        <mvc:exclude-mapping path="/hello/sayHaha.do"/>
        <bean id="interceptor2" class="com.itheima.interceptor.CheckNameInterceptor"></bean>
    </mvc:interceptor>

    <mvc:interceptor>
        <!--
			拦截所有满足"/hello/say*"的请求
		-->
        <mvc:mapping path="/hello/say*"/>
        <bean id="intercepter01" class="com.itheima.interceptor.PermissionInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
3.4拦截器的其它方法
  • afterCompletion  在目标方法完成视图层渲染后执行。
  • postHandle  在目标方法执行完毕获得了返回值后执行。
  • preHandle 被拦截的目标方法执行之前执行。
package com.itheima.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 包名:com.itheima.interceptor
 *
 * @author Leevi
 * 日期2020-08-15  11:12
 */
public class CheckNameInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("校验用户名...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("校验用户名的postHandler方法...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("校验用户名的afterCompletion方法...");
    }
}
3.5 多个拦截器执行顺序

​ 回想多个过滤器的执行顺序:

  1. 如果采用配置文件方式配置过滤器,那么就按照过滤器的配置先后顺序执行

     2. 如果采用注解方式配置过滤器,那么就按照类名的排序执行
    

​ 我们可以配置多个拦截器, 所以就存在一个优先级问题了.多个拦截器的优先级是按照配置的顺序决定的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZZRZvNx-1631055157838)(img/tu_9.jpg)]

  • 配置顺序
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!--排除某些请求-->
        <mvc:exclude-mapping path="/hello/sayHaha.do"/>
        <bean id="interceptor2" class="com.itheima.interceptor.CheckNameInterceptor"></bean>
    </mvc:interceptor>

    <mvc:interceptor>
        <!--用于指定拦截路径-->
        <mvc:mapping path="/hello/say*"/>
        <bean id="intercepter01" class="com.itheima.interceptor.PermissionInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

4.小结

第四章-将面面项目改成SSM

第一步-引入依赖

  1. spring相关依赖
    1. spring-webmvc
    2. spring-jdbc
    3. aop依赖
  2. mybatis整合spring的依赖
  3. log相关依赖
  4. jackson相关的依赖
  5. druid连接池依赖
  6. 文件上传的依赖
  7. 并且去掉heimaMvc的依赖
<properties> 
    <spring.version>5.0.2.RELEASE</spring.version>  
    <!--日志打印框架-->  
    <slf4j.version>1.6.6</slf4j.version>  
    <log4j.version>1.2.12</log4j.version>   
</properties>  
<dependencies>
	<dependency> 
      <groupId>org.springframework</groupId>  
      <artifactId>spring-webmvc</artifactId>  
      <version>${spring.version}</version> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework</groupId>  
      <artifactId>spring-jdbc</artifactId>  
      <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId>  
      <artifactId>jackson-databind</artifactId>  
      <version>2.9.0</version> 
    </dependency>  
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId>  
      <artifactId>jackson-core</artifactId>  
      <version>2.9.0</version> 
    </dependency>  
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId>  
      <artifactId>jackson-annotations</artifactId>  
      <version>2.9.0</version> 
    </dependency>
    <!-- log start -->  
    <dependency> 
      <groupId>log4j</groupId>  
      <artifactId>log4j</artifactId>  
      <version>${log4j.version}</version> 
    </dependency>  
    <dependency> 
      <groupId>org.slf4j</groupId>  
      <artifactId>slf4j-api</artifactId>  
      <version>${slf4j.version}</version> 
    </dependency>  
    <dependency> 
      <groupId>org.slf4j</groupId>  
      <artifactId>slf4j-log4j12</artifactId>  
      <version>${slf4j.version}</version> 
    </dependency>  
    <!-- log end --> 
    <!--
           关键: mybatis整合spring的依赖
        -->  
    <dependency> 
      <groupId>org.mybatis</groupId>  
      <artifactId>mybatis-spring</artifactId>  
      <version>1.3.0</version> 
    </dependency>
    <!--druid-->  
    <dependency> 
      <groupId>com.alibaba</groupId>  
      <artifactId>druid</artifactId>  
      <version>1.1.5</version> 
    </dependency>  
    <!--SpringAOP相关的坐标-->  
    <dependency> 
      <groupId>org.aspectj</groupId>  
      <artifactId>aspectjweaver</artifactId>  
      <version>1.8.7</version> 
    </dependency> 
    
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
	</dependency>
</dependencies>

第二步-删除mybatis的核心配置文件

第三步-编写spring整合mybatis的配置文件

  1. 整合mybatis
  2. 配置事务管理者
  3. 加载事务注解驱动

第三步-编写spring的主配置文件

  1. 包扫描
  2. 加载mvc注解驱动
  3. 导入application-mybatis.xml

第四步-修改web.xml配置

  1. 将heimaMVC中的DispatcherServlet的配置改成SpringMVC中的DispatcherServlet的配置
  2. 将解决乱码的过滤器的配置改成SpringMVC中的解决乱码的过滤器配置
  3. 删除项目中自己定义的解决乱码的过滤器

第五步-修改各个controller的代码

  1. 将原本heimaMvc的Controller注解改成springMvc中的RestController注解
  2. 使用Autowired注解注入Service对象
  3. 在各个Controller类上添加RequestMapping注解
  4. 将Controller类的各个方法上的RequestMapping注解改成SpringMVC中的RequestMapping注解,并且修改路径
  5. 将各个方法的获取请求参数的方式改成SpringMVC中的获取请求参数
  6. 将各个方法的响应数据给客户端的方式改成SpringMVC的响应数据方式

第六步-修改各个Service的代码

  1. 将所有的Service的实现类改名成"XXXImpl"
  2. 给各个实现类添加接口,然后Controller中一定要以接口类型接收Service的对象
  3. 各个Service的实现类商添加Service注解进行IOC
  4. 使用Autowired注解注入Dao对象
  5. 将方法中所有涉及到SqlSession的代码全部删掉,直接就能使用Dao对象
  6. 要进行事务控制的类或者方法上添加Transactional注解

第七步-修改文件上传的代码

  1. 将原来的获取上传文件的代码改成SpringMVC的文件上传代码
  2. 在创建spring文件上传的配置文件,配置文件解析器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值