记慕课学习秒杀系统之DAO层(一)

作为一名初学框架的菜鸟,记录这一次在慕课学习整个框架基础功能的过程,与大家共勉!

本项目利用SSM框架,完成了秒杀项目简单的增删改查功能,对初学框架的小菜鸟(比如我)有非常好的指导作用。
项目开发所用工具:IDEA开发环境,jdk1.8,Mysql 8.0.11,maven 3.5.3,以及mysql的可视化工具Navicat

一、创建Maven项目,添加项目依赖

1.创建Maven webapp项目

原教程是通过命令行创建Maven项目骨架,我这里直接利用IDEA新建Maven项目
IDEA创建Maven Web项目
注意Create from archetype要打勾,不然无法选择;
另外要注意对照好骨架的名称:maven-archetype-webapp

建成项目后的目录结构如下所示
项目初步目录结构
因为Maven默认创建项目的Servlet版本较低,因此需要修改web.xml的头信息来修改Servlet版本;查询官方文档可以看到Servlet最新版本的web.xml配置头信息,复制替换进web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="true">
  <!-- 修改Servlet版本-->
</web-app>

紧接着,打开项目结构,添加必要的文件夹
打开项目结构
整体项目目录结构如下所示
整体项目目录结构
这里介绍一下各文件夹的作用:

main:项目主体部分				test:单元测试
---java:存放java源文件				---java:存放java单元测试源文件
---resources:存放一些资源配置信息		---resources:存放一些资源配置信息
---sql:存放sql脚本文件

2.添加项目依赖

打开pom.xml文件,编写代码及相关注释如下:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.seckill</groupId>
  <artifactId>seckill</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>seckill Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <!-- 使用junit4:支持注解方式-->
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- 补全项目依赖 -->
    <!-- 1:java日志:slf4j,log4j,logback,common-logging
        slf4j是规范/接口
        日志实现:log4j,logback,common-logging
        使用:slf4j+logback
     -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.12</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.1.1</version>
    </dependency>
    <!-- 实现slf4j接口并整合 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.1</version>
    </dependency>
    <!--2:数据库相关依赖 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.11</version>
      <!-- 指定作用域:项目运行时执行 -->
      <scope>runtime</scope> 
    </dependency>
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>
    <!-- DAO框架:MyBatis-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.2</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>
    <!-- Servlet web相关依赖 -->
    <!-- 标签库 -->
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.5.4</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <!-- 4:Spring依赖-->
    <!-- 1)Spring核心依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <!-- 2)Spring dao层依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <!-- 3)Spring web相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
    <!-- 4)Spring test相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.1.0.RELEASE</version>
    </dependency>
  </dependencies>
  
  <build>
    <finalName>seckill</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.7.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.20.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

二、DAO层设计与开发

1.数据库设计与编码

在sql文件夹中创建schema.sql文件,编写sql语句,完成数据库的初始化工作

-- 数据库初始化脚本

-- 创建数据库
CREATE  DATABASE seckill;
-- 使用数据库
use seckill;
-- 创建秒杀库存表
CREATE  TABLE  seckill(
`seckill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品库存id',
`name` varchar(120) NOT NULL  COMMENT '商品名称',
`number` int NOT NULL  COMMENT '库存数量',
`start_time` timestamp NOT NULL  COMMENT '秒杀开启时间',
`end_time` timestamp NOT NULL  COMMENT '秒杀结束时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (seckill_id),
key idx_start_time(start_time),
key idx_end_time(end_time),
key idx_create_time(create_time)
)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表';

-- 初始化数据
insert into seckill(name,number,start_time,end_time)
values
  ('1000元秒杀iphone6',100,'2018-12-01 00:00:00','2018-12-02 00:00:00'),
  ('500元秒杀ipad2',200,'2018-12-01 00:00:00','2018-12-02 00:00:00'),
  ('300元秒杀小米4',300,'2018-12-01 00:00:00','2018-12-02 00:00:00'),
  ('200元秒杀红米note',400,'2018-12-01 00:00:00','2018-12-02 00:00:00');

-- 秒杀成功明细表
-- 用户登录认证相关的信息
create table success_killed(
`seckill_id` bigint NOT NULL COMMENT '秒杀商品id',
`user_phone` bigint NOT NULL COMMENT '用户手机号',
`state` tinyint NOT NULL DEFAULT -1 COMMENT '状态标示:-1:无效 0:成功 1:已付款',
`create_time` timestamp NOT NULL COMMENT '创建时间',
PRIMARY KEY(seckill_id,user_phone),/*联合主键*/
key idx_create_time(create_time)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表';

在此我利用的是Navicat for MySQL可视化工具,导入上述sql文件建立数据库。
(通过这种方法创建数据库出现了中文乱码问题,数据库各项配置都为UTF-8也不能解决。后删掉数据库,用命令行方式进入数据库执行一系列操作成功解决问题,所以以后尽量用命令行方式执行数据库操作,Navicat只能作为可视化工具辅助工作)

2.DAO实体和接口编码

2.1 DAO实体编码

在mian/java下创建两个package:
org.seckill.bean
org.seckill.dao
分别存放实体bean和对应的dao接口
在这里插入图片描述
接着在bean中创建Seckill类(商品实体类),和SuccessKilled类(秒杀情况实体类),两个类各属性如下:

//商品实体类
public class Seckill {
    private long seckillId;		//商品ID
    private String name;		//商品名称
    private int number;			//商品数量/库存
    private Date startTime;		//秒杀开始时间
    private Date endTime;		//秒杀结束时间
    private Date createTime;		//该记录创建时间
//秒杀状态记录类
public class SuccessKilled {
    private long seckillId;		//商品ID
    private long userPhone;		//用户手机号
    private short state;		//状态标示:-1:无效 0:成功 1:已付款
    private Date creatTime;		//该记录创建时间
    private Seckill seckill;		//秒杀商品实体

另外需自动生成set/get方法,并重写toString方法.

2.2 DAO接口编码

在dao包下建立两个接口(注意命名规范)
SeckillDao
SuccessKilledDao
分别对应bean中的两个实体类

package org.seckill.dao;

import org.seckill.bean.Seckill;

import java.util.Date;
import java.util.List;

public interface SeckillDao {

    /**
     * 减库存
     * @param seckillId
     * @param killTime
     * @return
     */
    int reduceNumber(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);

    /**
     * 根据ID查询秒杀对象
     * @param seckillId
     * @return
     */
    Seckill queryById(long seckillId);

    /**
     * 根据偏移量查询秒杀商品列表
     * @param offset
     * @param limit
     * @return
     */
    List<Seckill> queryAll(@Param("offset") int offset, @Param("limit") int limit);
}
package org.seckill.dao;

import org.seckill.bean.SuccessKilled;

public interface SuccessKilledDao {

    /**
     * 插入购买明细,可过滤重复
     * @param seckillId
     * @param userPhone
     * @return
     */
    int insertSuccessKilled(long seckillId,long userPhone);

    /**
     * 根据id查询SuccessKilled并携带秒杀产品对象实例
     * @param seckillId
     * @return
     */
    SuccessKilled queryByIdWithSeckill(@Param("seckillId") long seckillId, @Param("userPhone") long userPhone);
}

2.3 基于MyBatis实现DAO编程

在resources文件夹中创建一个文件夹mapper,用来存放之前在dao文件夹中编写的dao类映射;另外再创建一个mybatis-config.xml文件,用于编写mybatis的各项配置信息。在这里插入图片描述
查询MyBatis的官方文档,找到最新版本xml配置文件的头信息,将其复制在mybatis-config.xml文件中,并配置一些全局属性
头信息:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

头信息+全局属性:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
        
<configuration>
    <!-- 配置全局属性 -->
    <settings>
        <!-- 使用JDBC的getGeneratedKeys获取数据库自增主键值 -->
        <setting name="useGeneratedKeys" value="true"/>
        <!-- 使用列别名替换列名 默认:true -->
        <setting name="useColumnLabel" value="true"/>
        <!-- 开启驼峰命名转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

然后在mapper文件夹中建立两个映射配置文件,注意与dao文件夹中的类名相同
SeckillDao.xml
SuccessKilledDao.xml
同样查询MyBatis官方文档,可以查到mapper有关的xml文件的头信息

首先编写SeckillDao.xml文件,主要就是为DAO接口方法提供sql语句,这就是Mybatis方便的地方之一:我们只用编写dao层的接口和xml文件的sql语句,具体的实现交给Mybatis自己处理。

<?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">
        
<!-- namespace为dao类的路径 -->
<mapper namespace="org.seckill.dao.SeckillDao">
    <!-- 目的:为DAO接口方法提供sql语句配置 -->
    <!-- id即为dao类接口名 -->
    <update id="reduceNumber">
        <!-- 具体sql语句 -->
        update
          seckill
        set
          number =number -1
        where seckill_id=#{seckillId}
        and start_time<![CDATA[<=]]>#{killTime}
        and end_time>=#{killTime}
        and number >0
    </update>
    
    <select id="queryById" resultType="Seckill" parameterType="long">
        select  seckill_id ,name,number,start_time,end_time,create_time
        from seckill
        where seckill_id=#{seckillId}
    </select>

    <select id="queryAll" resultType="Seckill">
        select  seckill_id ,name,number,start_time,end_time,create_time
        from seckill
        order by create_time DESC
        limit #{offset},#{limit}
    </select>
</mapper>

然后编写SuccessKilledDao.xml,这里采用了别名替换,实现对多张表的联合查询。这里也是Mybatis的优点之一:可以灵活的使用sql语句,更大的发挥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="org.seckill.dao.SuccessKilledDao">
    <insert id="insertSuccessKilled">
        insert ignore into success_killed(seckill_id,user_phone,state)
        values (#{seckillId},#{userPhone},0)
    </insert>
    <select id="queryByIdWithSeckill" resultType="SuccessKilled">
        select
               sk.seckill_id,
               sk.user_phone,
               sk.create_time,
               sk.state,
               s.seckill_id "seckill.seckill_id",
               s.name "seckill.name",
               s.number "seckill.number",
               s.start_time "seckill.start_time",
               s.end_time "seckill.end_time",
               s.create_time "seckill.create_Time"
        from success_killed sk
        inner join seckill s on sk.seckill_id=s.seckill_id
        where sk.seckill_id=#{seckillId} and sk.user_phone=#{userPhone}
    </select>

</mapper>

3.MyBati与Spring的整合

通过mybatis与spring的整合,我们可以得到实现以下目标:

  • 更少的编码:只写接口,不写实现
  • 更少的配置:自动扫描包;自动扫描xml配置文件;自动注入Spring容器
  • 足够的灵活性:自己定制SQL语句;自由传参;结果集自动赋值;XML提供SQL,DAO接口提供Mapper

首先在resources文件夹下再创建一个文件夹spring,用来存放与spring相关的文件
在spring文件夹中创建spring-dao.xml配置文件,同样查找官方文档,找到对应版本的xml头部,复制过来:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

这个文件就存放spring的与dao相关的配置,同时还有与mybatis整合的配置,详细代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 所有Spring使用数据库都会用到的相关配置 -->
    <!-- 1:配置数据库相关参数 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 2:数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0连接池的私有属性 -->
        <!-- 数据库最大连接对象 默认:15 -->
        <property name="maxPoolSize" value="30"/>
        <!-- 数据库最小连接对象 默认:3 -->
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit 默认:false -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 默认:0,表示无限等待-->
        <property name="checkoutTimeout" value="1000"/>
        <!-- 当获取连接失败时重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!-- 真正配置Mybatis整合Spring -->
    <!-- 3:配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置Mybatis全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- 扫描bean包 使用别名 -->
        <property name="typeAliasesPackage" value="org.seckill.bean"/>
        <!-- 扫描sql配置文件:mapper需要的xml文件 -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!-- 4:配置扫描DAO接口包,动态实现DAO接口,注入到spring容器中
      不给id是因为该api在其他地方无引用,故不必给出id-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 给出需要扫描DAO接口包 -->
        <property name="basePackage" value="org.seckill.dao"/>
    </bean>
</beans>

到这个地方,Dao层的编写算是完成了。接下来要写一些单元测试,来测试我们的整个程序。

在test文件夹中建立一个java文件夹,然后可以在SeckillDao中选中接口名,按Ctrl+Shift+T组合键快速自动创建测试类
在这里插入图片描述
目录结构及详细代码如下:
在这里插入图片描述

package org.seckill.dao;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.seckill.bean.Seckill;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import javax.xml.crypto.Data;

import java.util.Date;
import java.util.List;


/**
 * 配置Spring和Junit整合,为了Junit启动时加载SpringIOC容器
 * spring-test,junit
 */
@RunWith(SpringJUnit4ClassRunner.class)
//告诉junit spring配置文件
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class SeckillDaoTest {

    //注入DAO实现类依赖
    @Resource
    private SeckillDao seckillDao;

    @Test
    public void queryById() {
        long id=1000;
        Seckill seckill=seckillDao.queryById(id);
        System.out.println(seckill.getName());
        System.out.println(seckill);
    }

    @Test
    public void queryAll() {
        List<Seckill> seckills=seckillDao.queryAll(0,100);
        for(Seckill seckill:seckills){
            System.out.println(seckill);
        }
    }

    @Test
    public void reduceNumber() {
        Date killTime = new Date();
        int updateCount = seckillDao.reduceNumber(1000L, killTime);
        System.out.println("updateCount=" + updateCount);
    }
}

同样的,测试类SuccessKilledTest的详细代码如下:

package org.seckill.dao;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.seckill.bean.SuccessKilled;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

import static org.junit.Assert.*;

/**
 * 配置Spring和Junit整合,为了Junit启动时加载SpringIOC容器
 * spring-test,junit
 */
@RunWith(SpringJUnit4ClassRunner.class)
//告诉junit spring配置文件
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class SuccessKilledDaoTest {

    @Resource
    private  SuccessKilledDao successKilledDao;

    @Test
    public void insertSuccessKilled() {
        long id=1000L;
        long phone=13502181181L;
        int insertCount= successKilledDao.insertSuccessKilled(id,phone);
        System.out.println("insertCount="+insertCount);
    }

    @Test
    public void queryByIdWithSeckill() {
        long id=1000L;
        long phone=13502181181L;
        SuccessKilled successKilled=successKilledDao.queryByIdWithSeckill(id,phone);
        System.out.println(successKilled);
        System.out.println(successKilled.getSeckill());
    }
}

至此,秒杀系统DAO层编写完毕。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值