SpringBootWeb案例_02

Web后端开发_05

SpringBootWeb案例_02

1.新增员工

1.1需求

在新增用户时,我们需要保存用户的基本信息,并且还需要上传的员工的图片,目前我们先完成第一步操作,保存用户的基本信息。

在这里插入图片描述

1.2 接口文档

基本信息

请求路径:/emps

请求方式:POST

接口描述:该接口用于添加员工的信息

请求参数

参数格式:application/json

参数说明:

名称类型是否必须备注
usernamestring必须用户名
namestring必须姓名
gendernumber必须性别, 说明: 1 男, 2 女
imagestring非必须图像
deptIdnumber非必须部门id
entrydatestring非必须入职日期
jobnumber非必须职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师

请求数据样例:

{
  "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg",
  "username": "linpingzhi",
  "name": "林平之",
  "gender": 1,
  "job": 1,
  "entrydate": "2022-09-18",
  "deptId": 1
}

响应数据

参数格式:application/json

参数说明:

参数名类型是否必须备注
codenumber必须响应码,1 代表成功,0 代表失败
msgstring非必须提示信息
dataobject非必须返回的数据

响应数据样例:

{
    "code":1,
    "msg":"success",
    "data":null
}

1.3思路

在这里插入图片描述

1.4功能开发

EmpController.java

@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;
    @PostMapping
    public Result save(@RequestBody Emp emp) {
        log.info("新增员工,emp:{}", emp);
        empService.save(emp);
        return Result.success();
    }
}

EmpService.java

public interface EmpService {
    /**
     * 新增员工
     * @param emp
     */
    void save(Emp emp);
}

EmpServiceImpl.java

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;
    @Override
    public void save(Emp emp) {
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.insert(emp);
    }
}

EmpMapper.java

@Mapper
public interface EmpMapper {
    /**
     * 新增员工
     * @param emp
     */
    void insert(Emp emp);
}

EmpMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bowen.mapper.EmpMapper">
    <insert id="insert">
        insert into tlias.emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)
        VALUES (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime},#{updateTime})
    </insert>
</mapper>

1.5 功能测试

代码开发完成后,重启服务器,打开Postman发送 POST 请求,

请求路径:http://localhost:8080/emps

在这里插入图片描述

1.6 前后端联调

功能测试通过后,再进行通过打开浏览器,测试后端功能接口:

在这里插入图片描述

在这里插入图片描述

2.文件上传(阿里云OSS)

黑马程序员2023新版JavaWeb开发教程

(P146 Day11-02. 案例-文件上传-简介——P150 Day11-06. 案例-文件上传-阿里云OSS-集成)

2.1简介

  • 文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程。
  • 文件上传在项目中应用非常广泛,例如发微博、发微信朋友圈都用到了文件上传功能

在这里插入图片描述

2.1.1前端页面三要素
  • 表单项file
  • post方式(由于上传文件较大,所以使用post)
  • 编码格式multipart/form-data(默认编码格式不适用于传输大型的二进制数据文件)

在这里插入图片描述

upload.html放在resources/static目录下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>

    <form action="/upload" method="post" enctype="multipart/form-data">
        姓名: <input type="text" name="username"><br>
        年龄: <input type="text" name="age"><br>
        头像: <input type="file" name="image"><br>
        <input type="submit" value="提交">
    </form>

</body>
</html>

在这里插入图片描述

2.1.2服务端接收文件

UploadController.java

@Slf4j
@RestController
public class UploadController {
    @PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image) {
        log.info("文件上传:{},{},{}", username, age, image);
        return Result.success();
    }
}

打断点进行调试

在这里插入图片描述

使用谷歌浏览器进行上传文件

在这里插入图片描述

可以看到生成的临时文件的路径

在这里插入图片描述

location C:\Users\1900\AppData\Local\Temp\tomcat.8080.6952090206307874716\work\Tomcat\localhost\ROOT

在文件资源管理器中打开该目录

在这里插入图片描述

将三个临时文件复制到一个桌面新建的文件夹中,并将其改名为1.txt2.txt3.txt,用记事本打开三个文件,可以看到

在这里插入图片描述

放行断点,临时文件自动被删除,说明还需要在服务端写存储文件的方法

2.1.3小结

前端页面三要素

  • 表单项type= "file"
  • 表单提交方式post(由于上传文件较大,所以使用post)
  • 表单的enctype属性multipart/form-data(默认编码格式不适用于传输大型的二进制数据文件)

服务端接收文件

  • MultipartFile

2.2本地存储

在服务端,接收到上传上来的文件之后,将文件存储在本地服务器磁盘中。

UploadController.java上传文件的controller控制层

@Slf4j
@RestController
public class UploadController {
    @PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image) throws IOException {
        log.info("文件上传:{},{},{}", username, age, image);
        //获取原始文件名
        String filename = image.getOriginalFilename();

        //构建唯一的文件名(不能重复)- uuid(通用唯一识别码) 7e8c6178-3320-4638-b4d9-19de7cb733f4
        int index = filename.lastIndexOf(".");
        String extname = filename.substring(index);
        String newFileName = UUID.randomUUID().toString() + extname;

        //将文件存储在服务器的磁盘目录中 E:\images
        image.transferTo(new File("E:\\images\\" + newFileName));
        return Result.success();
    }
}

测试UUID,演示UUID的生成

@SpringBootTest
class TliasWebManagementApplicationTests {
    @Test
    public void testUuid() {
        for (int i = 0; i < 1000; i++) {
            String uuid = UUID.randomUUID().toString();
            System.out.println(uuid);
        }
    }
}

在这里插入图片描述

org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field image exceeds its maximum permitted size of 1048576 bytes.

在SpringBoot中,文件上传,默认单个文件允许最大大小为1M。如果需要上传大文件,可以进行如下配置:

application.properties

#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB

测试上传文件

在这里插入图片描述

MultipartFile提供的常见方法

  • String getOriginalFilename();//获取原始文件名
  • void transferTo(File dest);//将接收的文件转存到磁盘文件中
  • long getSize();//获取文件的大小,单位:字节
  • byte[] getBytes();//获取文件内容的字节组数
  • InputStream getInputStream();//或缺接收到的文件内容的输入流

2.3阿里云OSS

阿里云是阿里巴巴集团旗下全球领先的云计算公司,也是国内最大的云服务提供商。

阿里云对象存储OSS ( object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。

2.3.1第三方服务-通用思路

在这里插入图片描述

2.3.2阿里云OSS-使用步骤

在这里插入图片描述

Bucket:存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间。

SDK:Software Development Kit的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK。

2.3.2.1.注册阿里云

注册账号后需要实名认证,流程不在赘述。

2.3.2.2.充值

新账号可免费体验三个月OSS对象存储服务,40G的OSS资源包首年9¥,5年45¥

image-20231201163605975

image-20231201163230990

image-20231201163334721

2.3.2.3.开通对象存储服务OSS

免费使用三个月对象存储OSS

2.3.2.4.创建Bucket

打开对象存储oss控制台

在这里插入图片描述

创建OSS Bucket空间

在这里插入图片描述

进入Bucket

在这里插入图片描述

Bucket空间长这样

在这里插入图片描述

2.3.2.5.获取AccessKey

在这里插入图片描述

创建AccessKey,请务必保存好AccessKey

在这里插入图片描述

在这里插入图片描述

2.3.2.6.参照官方SDK编写入门程序

打开阿里云SDK

在这里插入图片描述

https://help.aliyun.com/zh/oss/developer-reference/java-installation?spm=a2c4g.11186623.0.0.15744f33WrUden

在这里插入图片描述

看上传文件SDK

在这里插入图片描述

拷贝文件代码到src/test/java/com/bowen文件夹中

在这里插入图片描述

对代码进行简单修改

image-20231129170705758

测试的图片

image-20231129170743052

测试结果

image-20231129170839686

2.4阿里云OSS-集成

1.基本流程

image-20231129190501762

步骤

  • 引入阿里云OSS上传文件工具类(由官方的示例代码改造而来)
  • 上传图片接口开发
2.AliOSSUtils.java工具类
/**
 * 阿里云 OSS 工具类
 */
@Component
public class AliOSSUtils {

    private String endpoint = "地域节点";
    private String accessKeyId = "AccessKey ID";
    private String accessKeySecret = "AccessKey Secret";
    private String bucketName = "存储空间名称";

    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile file) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}
3.UploadController.java
@Slf4j
@RestController
public class UploadController {
    @Autowired
    private AliOSSUtils aliOSSUtils;
    //阿里云OSS
    @PostMapping("/upload")
    public Result upload(MultipartFile image) throws IOException {
        log.info("文件上传,文件名:{}",image.getOriginalFilename());
        String url = aliOSSUtils.upload(image);//调用阿里云OSS工具类,将上传来的文件存入阿里云
        log.info("文件上传完成,文件访问的url:{}",url);
        return Result.success(url);//将图片上传完成的url返回,用于浏览器回显展示
    }
}
4.API测试

image-20231129195734184

5.前端联调,新增员工

image-20231129200020460

我这个前端好像有一丢丢问题

image-20231129200110737

6.查看数据库保存的url

image-20231129200311963

2.5小结

  1. 文件上床介绍

  2. 前端页面三要素(file表单项、post方式、multipart/form-data)

  3. 服务端接收文件(MultipartFile)

  4. 文件存储方式

    • 本地存储(问题:无法直接访问、磁盘空间限制、磁盘损坏)

    • 云存储OSS

3.修改员工

3.1需求

image-20231129202259264

3.2查询回显

3.2.1接口文档

根据ID查询

基本信息

请求路径:/emps/{id}

请求方式:GET

接口描述:该接口用于根据主键ID查询员工的信息

请求参数

参数格式:路径参数

参数说明:

参数名类型是否必须备注
idnumber必须员工ID

请求参数样例:

/emps/1
响应数据

参数格式:application/json

参数说明:

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 1 成功 , 0 失败
msgstring非必须提示信息
dataobject必须返回的数据
|- idnumber非必须id
|- usernamestring非必须用户名
|- namestring非必须姓名
|- passwordstring非必须密码
|- entrydatestring非必须入职日期
|- gendernumber非必须性别 , 1 男 ; 2 女
|- imagestring非必须图像
|- jobnumber非必须职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师
|- deptIdnumber非必须部门id
|- createTimestring非必须创建时间
|- updateTimestring非必须更新时间

响应数据样例:

{
  "code": 1,
  "msg": "success",
  "data": {
    "id": 2,
    "username": "zhangwuji",
    "password": "123456",
    "name": "张无忌",
    "gender": 1,
    "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg",
    "job": 2,
    "entrydate": "2015-01-01",
    "deptId": 2,
    "createTime": "2022-09-01T23:06:30",
    "updateTime": "2022-09-02T00:29:04"
  }
}
3.2.2思路

3.2.3实现

EmpController.java控制层

@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;
	@GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
        log.info("根据ID查询员工信息:{}", id);
        Emp emp = empService.getById(id);
        return Result.success(emp);
    }
}

EmpServiceImpl.java员工 接口的实现类

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;
	@Override
    public Emp getById(Integer id) {
        return empMapper.getById(id);
    }
}

EmpService.java接口

public interface EmpService {
    /**
     * 根据id查询员工
     *
     * @param id
     * @return
     */
    Emp getById(Integer id);
}

EmpMapper.java员工的Mapper层,由于SQL语句比较简单,直接用注解@Select

@Mapper
public interface EmpMapper {
	/**
     * 根据ID查询员工
     *
     * @param id
     * @return
     */
    @Select("select * from emp where id = #{id};")
    Emp getById(@Param("id") Integer id);
}
3.2.4API测试

image-20231129205218105

3.2.5前端联调

image-20231129205332915

3.3修改员工

3.3.1需求

查询到回显数据后,需要修改信息

3.3.2接口文档
基本信息

请求路径:/emps

请求方式:PUT

接口描述:该接口用于修改员工的数据信息

请求参数

参数格式:application/json

参数说明:

名称类型是否必须备注
idnumber必须id
usernamestring必须用户名
namestring必须姓名
gendernumber必须性别, 说明: 1 男, 2 女
imagestring非必须图像
deptIdnumber非必须部门id
entrydatestring非必须入职日期
jobnumber非必须职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师

请求数据样例:

{
  "id": 1,
  "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg",
  "username": "linpingzhi",
  "name": "林平之",
  "gender": 1,
  "job": 1,
  "entrydate": "2022-09-18",
  "deptId": 1
}
响应数据

参数格式:application/json

参数说明:

参数名类型是否必须备注
codenumber必须响应码,1 代表成功,0 代表失败
msgstring非必须提示信息
dataobject非必须返回的数据

响应数据样例:

{
    "code":1,
    "msg":"success",
    "data":null
}
3.3.3思路

image-20231129210646247

3.3.4实现

EmpController.java控制层

@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;
    @PutMapping
    public Result update(@RequestBody Emp emp){
        log.info("更新员工信息,emp:{}", emp);
        empService.update(emp);
        return Result.success();
    }
}

EmpServiceImpl.java员工 接口的实现类

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;
    @Override
    public void update(Emp emp) {
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.update(emp);
    }
}

EmpService.java接口

public interface EmpService {
    /**
     * 更新员工数据
     * @param emp
     */
    void update(Emp emp);
}

EmpMapper.java员工的Mapper层

@Mapper
public interface EmpMapper {
    /**
     * 更新员工
     * @param emp
     */
    void update(Emp emp);
}

EmpMapper.xml由于要动态SQL,所以在xml文件中写SQL语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bowen.mapper.EmpMapper">
    <!--更新员工-->
    <update id="update">
        update emp
        <set>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="password != null and password != ''">
                password = #{password},
            </if>
            <if test="name != null and name != ''">
                name=#{name},
            </if>
            <if test="gender != null">
                gender=#{gender},
            </if>
            <if test="image != null and image !=''">
                image=#{image},
            </if>
            <if test="job != null">
                job=#{job},
            </if>
            <if test="entrydate != null">
                entrydate=#{entrydate},
            </if>
            <if test="deptId != null">
                dept_id=#{deptId},
            </if>
            <if test="updateTime != null">
                update_time=#{updateTime}
            </if>
        </set>
        where id = #{id};

    </update>
</mapper>
3.3.5API调试

image-20231129214416570

3.3.6前端联调

image-20231129214601275

image-20231129215010774

可以看到修改成功!!!

image-20231129215039207

4.配置文件

4.1参数配置化

4.1.1问题分析

项目中的参数配置过于分散,不方便集中的管理和维护

自定义的阿里云OSS配置信息application.properties

冷知识:alt + 鼠标左键 可以同时编辑多行

image-20231129221923936

image-20231129222217530

  • @value注解通常用于外部配置的属性注入,具体用法为:@Value("${配置文件中的key)")

image-20231129223800021

4.2yml配置文件

  • SpringBoot提供了多种属性配置方式

    • application.properties

      server.port=8080
      server.address=127.0.0.1
      
    • application.yml

      server:
        port: 9000
        address: 127.0.0.1
      
    • application.yaml

      server:
        port: 9000
        address: 127.0.0.1
      
  • 常见配置文件格式对比

4.3yml基本语法

  • 大小写敏感
  • 数值前边必须有空格,作为分隔符
  • 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)缩进的空格
  • 数目不重要,只要相同层级的元素左侧对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略

4.4yml数据格式

  • 对象/Map集合

    user:
      name: zhangsan
      age: 18
      password: 123456
    
  • 数组/List/Set集合

    hobby:
      - java
      - game
      - sport
    

4.5yml配置

  • application.yml中的配置案例相关的配置项

    #端口配置
    server:
      port: 8080
    #mybatis的配置
    mybatis:
      mapper-locations: classpath:mappers/*xml
      type-aliases-package: com.bowen.mybatis.entity
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        map-underscore-to-camel-case: true
    spring:
      #数据库连接信息
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/tlias
        username: root
        password: 密码
      #文件上传的配置
      servlet:
        multipart:
          max-file-size: 10MB
          max-request-size: 100MB
    #阿里云配置
    aliyun:
      oss:
        endpoint: 地域节点
        accessKeyId: AccessKey ID
        accessKeySecret: AccessKey Secret
        bucketName: 存储空间名称
    
    

4.6@ConfigurationProperties

4.6.1问题分析

image-20231129232209930

4.6.2解决方案

使用@ConfigurationProperties注解,前提:配置文件中key的名字=实体类中的属性名

image-20231129232442805

在utils下创建AliOSSProperties.java阿里云OSS的实体类

@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
}

重新编写AliOSSUtils.java中的代码

@Component
public class AliOSSUtils {
    @Autowired
    private AliOSSProperties aliOSSProperties;

    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile file) throws IOException {
        //获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();

        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}
4.6.3解决警告

image-20231129234451058

pom.xml文件中配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

引入依赖后可以看到灰色的提示

image-20231129235008044

application.yml中配置aliyun也有了提示信息

image-20231129235419040

@ConfigurationProperties@Value区别

相同点

  • 都是用来注入外部配置的属性的

不通点

  • @Value注解只能一个一个的进行外部属性的注入
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}

解决警告

在这里插入图片描述

pom.xml文件中配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

引入依赖后可以看到灰色的提示

在这里插入图片描述

application.yml中配置aliyun也有了提示信息

在这里插入图片描述

4.7@ConfigurationProperties@Value区别

相同点

  • 都是用来注入外部配置的属性的

不同点

  • @Value注解只能一个一个的进行外部属性的注入
  • @ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中
  • 49
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

甲柒

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值