SpringBoot+SpringCloud 学习系列

SpringBoot 学习 一 thymeleaf使用

加入thymeleaf依赖


  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>

编辑一个不起眼的controller


@Controller
public class HelloSpringBoot {
​
   
​
    @RequestMapping("/index")
    public String index(ModelMap map ){
        map.addAttribute("message","hello thymeleaf");
        return "index";
    }
​
    @RequestMapping("/")
    @ResponseBody
    public String hello(){
        return "hello springboot";
    }
}

编辑一个不起眼的index.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
</head>
<body>
    <td th:text="${message}"></td>
    <h2 th:text="${message}"></h2>
</body>
</html>

springboot静态资源的文件格式为 : src\main\resources\templates\index.html 一般将html文件放在 templates文件下,也可以在thymleaf配置文件中自定义templates文件名 springboot学习系列借鉴文章大多来自 该作者 http://blog.didispace.com/springbootaoplog/ 收工。啊,普通程序员的快乐往往就是这么简单。

 

SpringBoot 学习 二 springboot 集成jpa

加入jpa必须依赖


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

application.properties配置


spring.datasource.url=jdbc:mysql://localhost:3306/posp?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
spring.datasource.username=java
spring.datasource.password=java
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
​
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true

spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下:

  • create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。

  • create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。

  • update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。

  • validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 spring.jpa.show-sql=true 显示执行sql

一个不起眼的测试demo


不起眼的sql

INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (804, '李诚', '男', '1958-12-02', '副教授', '计算机系');
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (856, '张旭', '男', '1969-03-12', '讲师', '电子工程系');
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (825, '王萍', '女', '1972-05-05', '助教', '计算机系');
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (831, '刘冰', '女', '1977-08-14', '助教', '电子工程系');

一个不起眼的实体类


@Data
@Entity
@Table(name="teacher")
public class Teacher {
    @Id
    private String tno;
​
    private String tname;
​
    private String tsex;
​
    private Date tbirthday;
​
    private String prof;
​
    private String depart;
​
}
​

注意:这里的@Id 注解一定要有否则启动抛错 'No identifier specified for entity'

一个不起眼的repository


@Repository
public interface TeacherRepository extends JpaRepository<Teacher,String> {
​
    List<Teacher> findByDEPART(String depart);
​
    @Query(value = "select  * from  teacher WHERE depart = ?",nativeQuery = true)
    List<Teacher> findTeacher(String depart);
}

一个不起眼的controller


@RestController
public class TeacherController {
​
    @Autowired
    TeacherRepository teacherRepository;
​
    @RequestMapping(value = "/findByDepart",method = RequestMethod.GET)
    public List<Teacher> findByDepart(String depart){
        return teacherRepository.findByDepart(depart);
    }
​
    @RequestMapping(value = "/findByDepart1",method = RequestMethod.GET)
    public List<Teacher> findByDepart1(String depart){
        return teacherRepository.findTeacher(depart);
    }
}

收工。啊,普通程序员的快乐往往就是这么简单。

 

SpringBoot 学习 三 springboot +jpa多数据源配置

建立一个Spring配置类


定义两个DataSource用来读取配置文件(application.property)中的多数据源,这里定义一个primary,一个slave

@Configuration
public class DataSourceConfig {
​
    @Bean
    @Qualifier("primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    @Primary
    public DataSource primaryDataSource(){
        return DataSourceBuilder.create().build();
    }
​
    @Bean
    @Qualifier("slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource salveDataSource(){
        return DataSourceBuilder.create().build();
    }
}

建立JPA配置


注意主数据源和从数据源repository路径的配置,和entity路径的配置。这里用@Primary区分主数据源

/**
主数据源配置
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactoryPrimary",
        transactionManagerRef="transactionManagerPrimary",
        basePackages= { "com.myc.springboot.study.study.service.primary" }) //设置Repository所在位置
public class PrimaryConfig {
​
    @Autowired @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;
​
    @Autowired
    private HibernateProperties hibernateProperties;
​
    @Autowired
    private JpaProperties jpaProperties;
​
​
    @Primary
    @Bean(name = "entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }
​
    @Primary
    @Bean(name = "entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
        Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
                jpaProperties.getProperties(), new HibernateSettings());
​
        return builder
                .dataSource(primaryDataSource)
                .properties(properties)
                .packages("com.myc.springboot.study.study.domain.primary") //设置实体类所在位置
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }
​
​
    @Primary
    @Bean(name = "transactionManagerPrimary")
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }
}
​
/**
从数据源配置
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactorySlave",
        transactionManagerRef="transactionManagerSlave",
        basePackages= { "com.***.***.***.***.service.slave" }) //设置Repository所在位置
public class SlaveConfig {
​
    @Autowired @Qualifier("slaveDataSource")
    private DataSource slaveDataSource;
​
    @Autowired
    private HibernateProperties hibernateProperties;
​
    @Autowired
    private JpaProperties jpaProperties;
​
​
    @Bean(name = "entityManagerSlave")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactorySlave(builder).getObject().createEntityManager();
    }
​
    @Bean(name = "entityManagerFactorySlave")
    public LocalContainerEntityManagerFactoryBean entityManagerFactorySlave (EntityManagerFactoryBuilder builder) {
        Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
                jpaProperties.getProperties(), new HibernateSettings());
​
        return builder
                .dataSource(slaveDataSource)
                .properties(properties)
                .packages("com.myc.springboot.study.study.domain.slave") //设置实体类所在位置
                .persistenceUnit("slavePersistenceUnit")
                .build();
    }
​
    @Bean(name = "transactionManagerSlave")
    public PlatformTransactionManager transactionManagerSlave(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactorySlave(builder).getObject());
    }
}

注意: 在配置主数据源和从数据源时,一是两处路径的配置,二是@Bean名称别冲突,三是 @Primary即主数据源只能有一个

配置文件


#注意这里的jdbc-url 通常是 url,但是这里需要用jdbc-url,不然启动会抛错
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/posp?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
spring.datasource.primary.username=java
spring.datasource.primary.password=java
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
​
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
spring.datasource.slave.username=java
spring.datasource.slave.password=java
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
​
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true

测试数据sql


insert into student (sno, sname, ssex, sbirthday, sclass) values (108, '曾华'
                                                                , '男', '1977-09-01', 95033);
insert into student (sno, sname, ssex, sbirthday, sclass) values (105, '匡明'
                                                                , '男', '1975-10-02', 95031);
insert into student (sno, sname, ssex, sbirthday, sclass) values (107, '王丽'
                                                                , '女', '1976-01-23', 95033);
insert into student (sno, sname, ssex, sbirthday, sclass) values (101, '李军'
                                                                , '男', '1976-02-20', 95033);
insert into student (sno, sname, ssex, sbirthday, sclass) values (109, '王芳'
                                                                , '女', '1975-02-10', 95031);
insert into student (sno, sname, ssex, sbirthday, sclass) values (103, '陆君'
                                                                , '男', '1974-06-03', 95031);

一个不起眼的测试 controller


@RestController
public class StudentController {
​
    @Autowired
    private StudentRepository studentRepository;
​
    @RequestMapping(value = "/getStudentByClass",method = RequestMethod.GET)
    public List<Student> getStudentByClass(String class_){
        return studentRepository.findBySclass(class_);
    }
}

码云地址 https://gitee.com/BoMumyc/spboot-study study 收工。啊,普通程序员的快乐往往就是这么简单。

 

SpringBoot 学习 四 springboot Aop处理请求日志

引入依赖


      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

log aop配置


@Aspect
@Component
public class LogAspect {
​
    private Logger logger = LoggerFactory.getLogger(getClass());
​
    @Pointcut("execution(* com.myc.demo.web..*.*(..))")
    public void log(){}
​
    @Before("log()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
​
        // 记录下请求内容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
​
    }
​
    @AfterReturning(returning = "ret", pointcut = "log()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("RESPONSE : " + ret);
    }
​
}

测试类


@RestController
public class HelloController {
​
    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String hello(){
        return "hello springboot";
    }
​
    @RequestMapping(value = "/index",method = RequestMethod.GET)
    public String idex(){
        return "hello index";
    }
}

码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809 收工。啊,普通程序员的快乐往往就是这么简单。

 

SpringCloud 学习一 服务注册与发现

注册中心 Eureka


  • pom文件

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.myc</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
​
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    </properties>
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
​
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
​
</project>
​

在启动类上添加注解 @EnableEurekaServer


@SpringBootApplication
@EnableEurekaServer
public class DemoApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
​
}
> @EnableEurekaServer 注册中心标志

配置文件配置 application.properties


server.port=9000
​
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
​

false 表示自己是注册中心

服务方配置 与上类似


区别在于 ,一,启动类的@EnableEurekaServer注解变为@EnableEurekaClient。二,配置文件(application.properties)的不同具体如下:

spring.application.name=service-04
server.port=8001
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
​

注意端口不要重复

 

码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-03 demo-0809-04

SpringCloud 学习二Ribbon 和feign


pom文件加入ribbon


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
​

allication.property配置


spring.application.name=service-06
server.port=8003
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/

启动类加入 restTemplate


@SpringBootApplication
@EnableDiscoveryClient
public class Demo080906Application {
​
    public static void main(String[] args) {
        SpringApplication.run(Demo080906Application.class, args);
    }
​
​
    @Bean
    @LoadBalanced
        RestTemplate restTemplate(){
        return new RestTemplate();
    }
​
}

测试类


@RestController
public class ConsumerController {
​
    @Autowired
    RestTemplate restTemplate;
​
    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String hello(){
        return restTemplate.getForObject("http://service-04/hello?name=唱跳rap篮球" , String.class);
    }
​
​
}

 

码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-06


feign pom配置 加入


 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>

不同版本的springcloud feign略有不同 ,使用时注意版本 ,本文springcloud版本为:Greenwich.SR2

application.properties 配置


spring.application.name=service-05
server.port=8002
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true

注意要加入最后两行配置,否则调用其他服务会抛错 'Load balancer does not have available server for client'

feign 服务


@FeignClient("SERVICE-04")
public interface ComputeClient {
​
    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello(@RequestParam(value = "name") String name);
​
}

简单的测试类


@RestController
public class ConsumerController {
​
    @Autowired
    ComputeClient computeClient;
​
    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String hello(){
        return computeClient.hello("张三");
    }
}

码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-05

 

SpringCloud 学习三 hystrix 断路器


feign中使用断路器


加入依赖


 <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
 </dependency>

feign 中已经有hystrix依赖了所以不用加,但是用到缺少javanica这个依赖,不同的版本是有区别的,比如我用的这个spirngcloud版本,feign中是没有javanica这个依赖的,而别的版本或许有比如Finchley.M8这个版本就有,因此运用是注意

开启断路器---在application.properties中加入


feign.hystrix.enabled=true

修改代码


@FeignClient(value = "SERVICE-04",fallback = ConsumerClientImpl.class)
public interface ConsumerClient {
​
    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello(@RequestParam(value = "name") String name);
​
}
​
@Component
public class ConsumerClientImpl implements ConsumerClient {
    @Override
    public String hello(String name) {
        return "server time out error!!!";
    }
}
​
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = {com.myc.demo080905.ConsumerClient.class})
@EnableCircuitBreaker
public class Demo080905Application {
​
    public static void main(String[] args) {
        SpringApplication.run(Demo080905Application.class, args);
    }
​
}

码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-05

Ribbon中用hystrix


加入依赖


   <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
​
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
        </dependency>

修改之前的代码


@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class Demo080906Application {
​
    public static void main(String[] args) {
        SpringApplication.run(Demo080906Application.class, args);
    }
    @Bean
    @LoadBalanced
        RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
​
​
@RestController
public class ConsumerController {
​
    @Autowired
    RestTemplate restTemplate;
    @HystrixCommand(fallbackMethod = "helloCallBack")
    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String hello(){
        return restTemplate.getForObject("http://service-04/hello?name=唱跳rap篮球" , String.class);
    }
​
    public String helloCallBack(){
        return "servce time out error";
    }
}
​

码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-06

SpringCloud 学习四 zuul


加入依赖


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
​
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

启动类加入注解


@EnableZuulProxy
@SpringCloudApplication
public class ApiGatewayApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
​
    @Bean
    public AccessFilter accessFilter() {
        return new AccessFilter();
    }
    
}

这里用了@SpringCloudApplication注解,它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker,主要目的还是简化配置。

application.properties文件配置 ----- serviceId的映射


​
spring.application.name=api-gateway
server.port=5000
    zuul.routes.api-a.path=/api-05/**
zuul.routes.api-a.serviceId=service-05
​
zuul.routes.api-b.path=/api-06/**
zuul.routes.api-b.serviceId=service-06
​
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/

zuul 服务过滤 ------小作用demo


public class AccessFilter extends ZuulFilter {
​
    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
​
    @Override
    public String filterType() {
        return "pre";
    }
​
    @Override
    public int filterOrder() {
        return 0;
    }
​
    @Override
    public boolean shouldFilter() {
        return true;
    }
​
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
​
        log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
​
        Object accessToken = request.getParameter("accessToken");
        if(accessToken == null) {
            log.info("access token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            } catch (IOException e) {
            }
            return null;
        }
        log.info("access token ok");
        return null;
    }
}

自定义过滤器的实现,需要继承ZuulFilter,需要重写实现下面四个方法: filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下: pre:可以在请求被路由之前调用 routing:在路由请求时候被调用 post:在routing和error过滤器之后被调用 error:处理请求时发生错误时被调用 filterOrder:通过int值来定义过滤器的执行顺序 shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。 run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值