【黑马SpringCloud微服务开发与实战】(四)微服务02

SpringCloud从入门到精通:实战篇 首先,我们要知道SpringCloud并不是一种具体的技术,而是一种分布式微服务框架,主要基于Spring Boot实现,目的是为了快速构建分布式系统的一站式解决方案集合。SpringCloud是由Netflix提出并开源的,它基于Spring Boot框架,集成了许多常见的微服务组件。 阅读详情

1. 什么是网关

在这里插入图片描述
在这里插入图片描述

2. 网关路由——快速入门

在这里插入图片描述

在这里插入图片描述

server:
  port: 8080
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.10.100:8848
    gateway:
      routes:
        - id: item # 路由规则id,自定义,唯一
          uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
          predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
            - Path=/items/**,/search/** # 这里是以请求路径作为判断规则
        - id: cart
          uri: lb://cart-service
          predicates:
            - Path=/carts/**
        - id: user
          uri: lb://user-service
          predicates:
            - Path=/users/**,/addresses/**
        - id: trade
          uri: lb://trade-service
          predicates:
            - Path=/orders/**
        - id: pay
          uri: lb://pay-service
          predicates:
            - Path=/pay-orders/**

需要干的两件事,第一把商品的searchcontroller复制到item服务里面去。这里我不用之前的8858了,因为不知道为啥连接不上,就用我之前自己配的nacos了。

在这里插入图片描述

在这里插入图片描述
前后端联调也没问题-正常登录

在这里插入图片描述

3. 网关路由——路由属性

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
全局生效过滤器:

在这里插入图片描述

4. 网关登录校验——思路分析

在这里插入图片描述

在这里插入图片描述

4. 网关登录校验——自定义GlobalFilter

在这里插入图片描述

在这里插入图片描述

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered{
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //TODO 模拟登录校验的逻辑
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        System.out.println("headers = " + headers);
        // 放行
        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        return 0;
    }

}

5. 网关登录校验——自定义GatewayFilter

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<PrintAnyGatewayFilterFactory.Config> {
    @Override
    public GatewayFilter apply(Config config) {
        return new OrderedGatewayFilter(new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 获取config值
                String a = config.getA();
                String b = config.getB();
                String c = config.getC();
                // 编写过滤器逻辑
                System.out.println("a = " + a);
                System.out.println("b = " + b);
                System.out.println("c = " + c);
                // 放行
                return chain.filter(exchange);
            }
        },1);
    }

    // 自定义配置属性,成员变量名称很重要,下面会用到
    @Data
    static class Config{
        private String a;
        private String b;
        private String c;
    }
    // 将变量名称依次返回,顺序很重要,将来读取参数时需要按顺序获取
    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("a", "b", "c");
    }
    // 返回当前配置类的类型,也就是内部的Config
    @Override
    public Class<Config> getConfigClass() {
        return Config.class;
    }
}

5. 网关登录校验——实现登录校验

@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private final JwtTool jwtTool;

    private final AuthProperties authProperties;

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取Request
        ServerHttpRequest request = exchange.getRequest();
        // 2.判断是否不需要拦截
        if(isExclude(request.getPath().toString())){
            // 无需拦截,直接放行
            return chain.filter(exchange);
        }
        // 3.获取请求头中的token
        String token = null;
        List<String> headers = request.getHeaders().get("authorization");
        if (!CollUtils.isEmpty(headers)) {
            token = headers.get(0);
        }
        // 4.校验并解析token
        Long userId = null;
        try {
            userId = jwtTool.parseToken(token);
        } catch (UnauthorizedException e) {
            // 如果无效,拦截
            ServerHttpResponse response = exchange.getResponse();
            response.setRawStatusCode(401);
            return response.setComplete();
        }

        // TODO 5.如果有效,传递用户信息
        System.out.println("userId = " + userId);
        // 6.放行
        return chain.filter(exchange);
    }

    private boolean isExclude(String antPath) {
        for (String pathPattern : authProperties.getExcludePaths()) {
            if(antPathMatcher.match(pathPattern, antPath)){
                return true;
            }
        }
        return false;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

在这里插入图片描述

6. 网关登录校验——网关传递用户到微服务

在这里插入图片描述

  // TODO 5.如果有效,传递用户信息
  System.out.println("userId = " + userId);
  String userInfo = userId.toString();
  ServerWebExchange swe = exchange.mutate()
          .request(builder -> builder.header("user-info", userInfo))
          .build();

  // 6.放行
  return chain.filter(swe);

在这里插入图片描述
在这里插入图片描述

public class UserInfoInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.获取请求头中的用户信息
        String userInfo = request.getHeader("user-info");
        // 2.判断是否为空
        if (StrUtil.isNotBlank(userInfo)) {
            // 不为空,保存到ThreadLocal
            UserContext.setUser(Long.valueOf(userInfo));
        }
        // 3.放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserContext.removeUser();
    }
}

@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor());
    }
}

不过,需要注意的是,这个配置类默认是不会生效的,因为它所在的包是com.hmall.common.config,与其它微服务的扫描包不一致,无法被扫描到,因此无法生效。
基于SpringBoot的自动装配原理,我们要将其添加到resources目录下的META-INF/spring.factories文件中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.hmall.common.config.MyBatisConfig,\
  com.hmall.common.config.MvcConfig,\
  com.hmall.common.config.JsonConfig
    @Override
    public List<CartVO> queryMyCarts() {
        // 1.查询我的购物车列表
        List<Cart> carts = lambdaQuery().eq(Cart::getUserId, UserContext.getUser()).list();
//        List<Cart> carts = lambdaQuery().eq(Cart::getUserId, 1L).list();
        if (CollUtils.isEmpty(carts)) {
            return CollUtils.emptyList();
        }

        // 2.转换VO
        List<CartVO> vos = BeanUtils.copyList(carts, CartVO.class);

        // 3.处理VO中的商品信息
        handleCartItems(vos);

        // 4.返回
        return vos;
    }

分别用jack和rose登录效果:
在这里插入图片描述

7. 网关登录校验——openfeign传递用户信息

在这里插入图片描述
在这里插入图片描述

public class DefaultFeignConfig {

    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }

    @Bean
    public RequestInterceptor userInfoRequestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                // 获取登录用户
                Long userId = UserContext.getUser();
                if(userId == null) {
                    // 如果为空则直接跳过
                    return;
                }
                // 如果不为空则放入请求头中,传递给下游微服务
                template.header("user-info", userId.toString());
            }
        };
    }
}

提交订单会删除rose对应为2用户的购物车:
在这里插入图片描述
在这里插入图片描述

8. 配置管理——什么是配置管理

在这里插入图片描述
在这里插入图片描述

9. 配置管理——共享配置

在这里插入图片描述

在这里插入图片描述

shared-jdbc.yaml

spring:
  datasource:
    url: jdbc:mysql://${hm.db.host:192.168.150.101}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: ${hm.db.un:root}
    password: ${hm.db.pw:123}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto

在这里插入图片描述

shared-log.yaml

logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"

在这里插入图片描述

shared-swagger.yaml

knife4j:
  enable: true
  openapi:
    title: ${hm.swagger.title:黑马商城接口文档}
    description: ${hm.swagger.description:黑马商城接口文档}
    email: ${hm.swagger.email:zhanghuyi@itcast.cn}
    concat: ${hm.swagger.concat:虎哥}
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - ${hm.swagger.package}

配置文件里面

hm:
  db:
    database: hm-cart

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  <!--nacos配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>

在cart-service中的resources目录新建一个bootstrap.yaml文件:

spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.10.100:8848 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

cart模块的yaml文件修改后:

server:
  port: 8082

feign:
  okhttp:
    enabled: true # 开启OKHttp功能
hm:
  db:
    database: hm-cart
  swagger:
    title: "黑马商城购物车服务接口文档"
    package: com.hmall.cart.controller

9. 配置管理——配置热更新

在这里插入图片描述
在这里插入图片描述

@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {

    private Integer maxAmount;
}

    private void checkCartsFull(Long userId) {
        int count = lambdaQuery().eq(Cart::getUserId, userId).count();
        if (count >= cartProperties.getMaxAmount()) {
            throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", 10));
        }
    }

配置nacos

cart-service.yaml

hm:
  cart:
    maxAmount: 10 # 购物车商品数量上限

还有一节后面补充吧,电脑有点卡。。。。。。刷算法去

推荐 7 个牛哄哄 Spring Cloud 实战项目 把一个大型的单个应用程序和服务拆分为数个甚至数十个的支持微服务,这就是微服务架构的架构概念,通过将功能分解到各个离散的服务中以实现对解决方案的解耦。 关于微服务相关的学习资料不多,而 GitHub 上的开源项目可以作为你微服务之旅的第一站。本文推荐 7 个非常火的微服务项目,从入门到实战,这篇文章值得收藏。 01.不二之选 第一个推荐的项目是高赞教程:PiggyMetrics ,这个开源项目是你开启微服务之旅的不二之选。 PiggyMetrics是一个很全面的微服务实践入门的实例集,它可以指导开.. 阅读详情

相关推荐

黑马商城项目—最新SpringCloud开发实战—功能实现详细学习笔记(微服务篇)

服务拆分后会遇到2个问题:1,服务地址有很多且将来可能发生变化,前端无法确定请求的服务地址端口2,每个服务都需要登录的用户信息,如果每个服务都去登录不仅复杂而且可能泄露密钥以上问题可以用网关解决前端只需要去请求网关,然后就可以访问到对应的微服务,包括3个功能,网关还有一些优点:网关可以避免微服务地址暴露给前端,也是一种对微服务的保护,对于前端来说,网关的作用将后端的微服务架构变成了单体架构(前端将整个后端视为黑盒首先微服务启动后在注册中心进行注册,然后网关会去拉取服务列表,

冷脸萌萌的技术实践 3033

SpringCloud微服务项目实战(一)---搭建SpringBoot项目

问题:新建子模块后,pom文件的图标是xml文件标识,而不是m标识,而且目录结构是com-xxx-xxx这样一层一层的,而不是package包名一起的解决:idea工作台左侧右键pom文件,点击Add As Maven Project,如下图问题:删除子模块后,再新增同样名称的子模块,pom文件不生效且出现了删除线。解决:File–Settings–Build–Maven–IgnoredFiles中将对勾取消,如下图。

xiaobai178的博客 6019

SpringCloud微服务技术栈实战教程

来源B站:黑马程序员SpringCloud微服务技术栈实战教程,涵盖springcloud微服务架构+Nacos配置中心+分布式事务等之前我们学习的项目一是单体项目,可以满足小型项目或传统项目的开发。而在互联网时代,越来越多的一线互联网公司都在使用微服务技术。从谷歌搜索指数来看,国内从自2016年底开始,微服务热度突然暴涨:接下来几天,我们就一起来揭开它的神秘面纱。计划是这样的,课前资料中给大家准备了一个。

weixin_43837329的博客 1237

Spring Cloud 微服务项目实战 -

Milestone 版本:在大版本正式发布前的里程碑版本,例如 2021.0.0-M1,M1 代表当前大版本的第一个里程碑版本,M2 代表第二个迭代里程碑,以此类推。使用模板的好处是可以对优惠券消费规则做一层抽象,比如满减类、打折类这些优惠券只是具体的优惠金额不同,但是玩法类似,把相类似的玩法功能抽象成一个模板,就可以简化具体优惠券的创建和核销流程。建议: 当框架有重大版本更新时,建议你先按兵不动,等大版本做了一两次迭代之后,明显的 Bug 修复得差不多了了,再应用到项目中也不迟。

小工匠 4万+

基于SpringBoot+SpringCloud+Vue前后端分离项目实战 --开篇

如何高效学习Java? 毕业设计项目应该怎么做?入门实战项目应该怎么做? 做Java开发都应该学习哪些框架技术? 我想来跟你聊聊为什么要学习此专栏?我们经常说,看一个事儿千万不要直接陷入细节里,你应该先鸟瞰其全貌,这样能够帮助你从高维度理解问题。同样,当你迷茫想努力没有方向时,最事半功倍的方法是:找人带你! 因为过来人,更懂得如何学、如何做、如何少走弯路! 那今天就给大家带来一门专栏课程,由 天罡gg 和 经海路大白狗 两位实力大牛合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!

天罡gg的专栏 2万+

springcloud实战项目框架搭建完整版

通过案例学习了基于Spring cloud进行web微服务项目的开发,干货很多。Spring cloud体系提供了完整的微服务开发的解决方案,整合了多个优秀组件,能够很好的帮助我们提高开发效率。

【黑马SpringCloud微服务开发与实战】(三)微服务01

本文介绍了黑马商城项目的微服务化改造过程。首先指导完成环境配置,包括MySQL安装、前后端启动(后端8080端口、前端18080端口)。然后从单体架构过渡到微服务架构,基于SpringCloud进行服务拆分。重点演示了商品服务的拆分步骤:创建item-service子模块,配置pom.xml依赖和application.yml文件,复制相关代码层。最终成功启动并测试商品微服务(8081端口)。项目采用模块化结构,包含hm-common公共模块和多个业务服务模块,遵循单一职责等拆分原则。

xtreallydance的博客 1466

车载监控系统升级指南:如何用XS9950A替换TP9950实现高清视频解码(附配置代码)

本文提供了从TP9950升级至XS9950A车载视频解码芯片的完整实战指南。详细解析了硬件设计调整、PCB布局要点,并重点分享了通过I2C配置寄存器实现高清HDCCTV视频解码与同轴音频集成的软件驱动代码与调试技巧,助力工程师平滑完成系统升级。

weixin_29268637的博客 364

SpringCloud微服务技术栈实战教程 黑马商城企业级项目

SpringCloud微服务技术栈实战教程,涵盖springcloud微服务架构+Nacos配置中心+分布式服务等 SpringCloud及SpringCloudAlibaba是目前最流行的微服务技术栈。但大家学习起来的感受就是组件很多,不知道该如何应用。这套《微服务实战课》从一个单体项目入手,带领大家从服务拆分时机、服务拆分原则、拆分遇到的问题入手,结合实际项目讲解。带你体会从单体到微服务中碰到的各种问题,并利用SpringCloud和SpringCloudAlibaba中的组件解决这些问题。让你

我亦无他,唯手熟尔 7572

斑马打印机状态监控进阶:不用SDK也能获取打印状态的3种野路子

本文介绍了三种不依赖官方SDK的斑马打印机状态监控方法,包括SGD命令、TCP端口嗅探和共享打印机日志分析。这些方案适用于老旧系统或网络受限环境,帮助运维人员实时掌握打印状态,提升设备管理效率。特别适合工业自动化和物流仓储场景中的应急处理。

weixin_42628688的博客 426

landsat8数据下载教程

下载landsat数据教程

weixin_44001203的博客 1万+

某马2024SpringCloud微服务开发与实战 bug记录与微服务知识拆解(MybatisPlus、Docker、MQ、ES、Redis)第五章

介绍了使用Sentinel进行请求限流、线程隔离、服务熔断等操作和分布式事务一致性处理方案seata 背景色是愧花黄绿

Java、Python、AI、前沿技术等领域的技术总结和经验分享。 1万+

binning模式下和normal模式下相同曝光参数相同场景加权亮度差异消除方案优化(一)

摘要:针对ISP参数设置中浮点权重系数需转换为整数的问题,提出整数邻域搜索方案。该方法在浮点最优解附近(±1/±2)进行局部搜索,通过逐阶微调找到误差最小的整数权重组合,避免直接取整导致的误差反弹(误差从0.8%降至0.35%)。方案结合范围约束(1~15)和单调性约束(暗端递减、亮端递增),兼顾计算效率(单图耗时<10ms)与硬件兼容性,显著优于暴力搜索和简单取整,满足工程落地需求。

大熊 94

某马2024SpringCloud微服务开发与实战 bug记录与微服务知识拆解(MybatisPlus、Docker、MQ、ES、Redis)第三章

2024最新SpringCloud微服务开发与实战bug记录与微服务知识拆解Day3 封面颜色是槐花黄绿

Java、Python、AI、前沿技术等领域的技术总结和经验分享。 1万+

基于机器学习的网络入侵检测系统设计与实现

本文设计并实现了一个基于机器学习的网络入侵检测系统,采用随机森林模型结合KMM算法优化,使用CICIDS2017数据集进行训练。系统包含完整的数据预处理流程、特征工程、模型调优和远程部署方案。实验结果表明,该系统能有效识别多种网络攻击类型,在检测准确率和误报率方面表现优异。研究为网络安全防护提供了新的技术思路和实用工具,具有重要的应用价值。

huanghm88的专栏 315

AE(自动曝光)调试策略:曝光曲线设计与过曝控制

自动曝光(AE, Auto Exposure)系统是图像质量控制链路中的核心模块之一,其性能直接决定了成像亮度是否稳定、细节是否完整、场景切换是否自然。在现代移动终端影像系统中,AE 不仅需要适应不同光照强度和复杂环境,还需兼顾人脸优先、HDR触发、视频帧率控制等多目标优化需求。本文基于高通 Snapdragon 和 MTK Dimensity 等主流平台的调试经验,系统梳理了 AE 模型设计原则、曝光曲线配置、测光策略选择与过曝抑制机制,并结合典型实战案例,提供可复制的工程落地路径。

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 3089

某马2024SpringCloud微服务开发与实战 bug记录与微服务知识拆解(MybatisPlus、Docker、MQ、ES、Redis)第一章

>我这份文档是对官方文档的补充,比如官方文档没有记载P10的请求参数,这种东西没有什么营养,又容易漏写些东西,因此我记录了下来,您可以通过文字的目录快捷需要的东西

Java、Python、AI、前沿技术等领域的技术总结和经验分享。 1万+

【Matlab算法】MATLAB实现的语音信号降噪算法(附MATLAB完整代码)

语音信号降噪是语音处理领域中的一个关键问题。在实际应用中,语音信号常常被各种环境噪声污染,这不仅影响了语音的质量,还可能导致后续处理(如语音识别)的性能下降。因此,开发有效的降噪算法对于提高语音通信质量、改善语音识别系统性能等具有重要意义。 本文将介绍并实现两种经典的语音信号降噪算法:谱减法和维纳滤波。这两种方法都是基于频域处理的技术,具有实现简单、计算效率高的特点。通过MATLAB实现这些算法,我们可以直观地比较它们的性能和降噪效果。

Albert_Lsk的博客 1326

黑马头条 SpringBoot+SpringCloud+ Nacos等企业级微服务架构项目

《黑马头条》项目采用的是SpringBoot+springcloud当下最流行的微服务为项目架构,配合spring cloud alibaba nacos作为项目的注册和配置中心。新课程采用快速开发的模式,主要解决真实企业开发的一些应用场景。深入挖掘技术和业务的解决方案,做到更加的通用,能举一反三,充分满足互联网行业中复杂的微服务技术要求

我亦无他,唯手熟尔 3万+

物联网系列③——基于ESP8266与点灯科技平台的温湿度计设计(接入小爱同学)

物联网系列③——基于ESP8266与点灯科技平台的温湿度计设计(接入小爱同学)一、设计目标二、电路展示三、存在的问题四、代码五、需要注意的问题 一、设计目标       前一阵子偶然接触到点灯科技平台,产生了制作一块ESP8266电路板,拥有以下功能: 使用CH340对ESP8266进行程序下载 使用DHT11进行温湿度数据读取 使用点灯科技平台接入物联网,可通过手机APP获得室内温湿度 接入小爱同学,通过语音获取室内温湿度信息 Mico US

weixin_43278295的博客 8852

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

某马2024SpringCloud微服务开发与实战 bug记录与微服务知识拆解(MybatisPlus、Docker、MQ、ES、Redis)第二章

2024最新SpringCloud微服务开发与实战bug记录与微服务知识拆解Day2

Java、Python、AI、前沿技术等领域的技术总结和经验分享。 7433

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【SpringCloud】黑马微服务学习笔记

关于微服务SpringCloud的学习笔记,记录了学习的内容及过程中一些问题的解决

猿来是你 6359

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

上一篇: 【黑马SpringCloud微服务开发与实战】(三)微服务01
下一篇: 【黑马SpringCloud微服务开发与实战】(五)微服务保护
夜斗小神社
博客等级 码龄7年 3718粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值