阿里云存储和用户认证(预约挂号项目第九部分)

一、阿里云存储oss

用户验证需要上传证件图片、首页轮播也需要上传图片,因此我们要做文件服务,阿里云oss是一个很好的分布式文件服务系统,所以我们只需要集成阿里云oss即可

1、开通“对象存储OSS”服务
(1)申请阿里云账号
(2)实名验证
(3)开通“对象存储OSS”服务
(4)进入管理控制台

在这里插入图片描述

1.1创建Bucket
选择:标准存储、公共读、不开通

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、使用SDK文档

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3、文件服务实现

3.1搭建service-oss模块
3.1.1 搭建service-oss模块
搭建过程参考service-user模块

3.1.2 修改配置
1、修改pom.xml,引入阿云oss依赖

<dependencies>
    <!-- 阿云oss依赖 -->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
    </dependency>

    <!-- 日期工具栏依赖 -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
    </dependency>
</dependencies>

2、添加配置文件application.properties:

# 服务端口
server.port=8205
# 服务名
spring.application.name=service-oss

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848


3.1.3 启动类

在这里插入图片描述

//取消数据源自动配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@ComponentScan(basePackages = {"com.atguigu"})
public class ServiceOssApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceOssApplication.class, args);
    }
}

3.1.4配置网关,在网关服务中配置

#设置路由id
spring.cloud.gateway.routes[5].id=service-oss
#设置路由的uri
spring.cloud.gateway.routes[5].uri=lb://service-oss
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[5].predicates= Path=/*/oss/**

3.2 测试SDK :

在这里插入图片描述

package com.fan.yygh.oss;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;

public class Demo {

    public static void main(String[] args) throws Exception {
         // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        String accessKeyId = "xxx";
        String accessKeySecret = "xxx";

        // 填写Bucket名称,例如examplebucket。
        String bucketName = "fanbucket-test";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 创建存储空间。
            ossClient.createBucket(bucketName);

        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

在这里插入图片描述

封装controller接口
controller.FileApiController:

在这里插入图片描述

2、创建com.atguigu.yygh.oss.utils.ConstantOssPropertiesUtils配置类

@Component
public class ConstantOssPropertiesUtils implements InitializingBean {

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;

    @Value("${aliyun.oss.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.oss.secret}")
    private String secret;

    @Value("${aliyun.oss.bucket}")
    private String bucket;

    public static String EDNPOINT;
    public static String ACCESS_KEY_ID;
    public static String SECRECT;
    public static String BUCKET;

    @Override
    public void afterPropertiesSet() throws Exception {
        EDNPOINT=endpoint;
        ACCESS_KEY_ID=accessKeyId;
        SECRECT=secret;
        BUCKET=bucket;
    }
}

3、创建接口类实现类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

二、用户人证

1、需求分析

用户登录成功后都要进行身份验证,验证通过后才可以预约挂号
验证过程:用户填写信息(姓名、证件类型、证件号码和证件照片)==> 平台审批
用户验证设计接口:
1、提交验证
2、上传证件图片
3、获取提交验证信息

2、api接口
操作模块:service-user

获取当前用户工具类
在common-util模块添加工具类

//获取当前用户信息工具类
public class AuthContextHolder {
    //获取当前用户id
    public static Long getUserId(HttpServletRequest request) {
        //从header获取token
        String token = request.getHeader("token");
        //jwt从token获取userid
        Long userId = JwtHelper.getUserId(token);
        return userId;
    }
    //获取当前用户名称
    public static String getUserName(HttpServletRequest request) {
        //从header获取token
        String token = request.getHeader("token");
        //jwt从token获取userid
        String userName = JwtHelper.getUserName(token);
        return userName;
    }
}

在这里插入图片描述

controller:
添加controller方法
在UserInfoApiController类添加方法

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

//用户人证接口
@PostMapping("auth/userAuth")
public Result userAuth(@RequestBody UserAuthVo userAuthVo, HttpServletRequest request) {
    //传递两个参数,第一个参数用户id,第二个参数验证数据vo对象
    userInfoService.userAuth(AuthContextHolder.getUserId(request),userAuthVo);
    return Result.ok();
}

//获取用户id信息接口
@GetMapping("auth/getUserInfo")
public Result getUserInfo(HttpServletRequest request) {
    Long userId = AuthContextHolder.getUserId(request);
    UserInfo userInfo = userInfoService.getById(userId);
    return Result.ok(userInfo);
}

在这里插入图片描述

 //用户人证接口
    @Override
    public void userAuth(Long userId, UserAuthVo userAuthVo) {
        //根据用户id查询用户信息
        UserInfo userInfo = baseMapper.selectById(userId);
        //设置验证信息
        //验证人姓名
        userInfo.setName(userAuthVo.getName());
        userInfo.setCertificatesType(userAuthVo.getCertificatesType());
        userInfo.setCertificatesNo(userAuthVo.getCertificatesNo());
        userInfo.setCertificatesUrl(userAuthVo.getCertificatesUrl());
        userInfo.setAuthStatus(AuthStatusEnum.AUTH_RUN.getStatus());
        //进行信息更新
        baseMapper.updateById(userInfo);

    }

在这里插入图片描述

前端:

3、前端
3.1封装api请求
在/api/userInfo.js添加方法:

在这里插入图片描述

getUserInfo() {
return request({
  url: `${api_name}/auth/getUserInfo`,
  method: `get`
})
},

saveUserAuah(userAuth) {
  return request({
    url: `${api_name}/auth/userAuth`,
    method: 'post',
    data: userAuth
  })
}

3.2 页面展示
创建/pages/user/index.vue组件

在这里插入图片描述

在这里插入图片描述

测试:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、就诊人管理

1、需求分析
预约下单需要选择就诊人,因此我们要实现就诊人管理,前端就诊人管理其实就是要实现一个完整的增删改查

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

添加:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.fan.yygh.user.api;

import com.fan.yygh.common.result.Result;
import com.fan.yygh.common.utils.AuthContextHolder;
import com.fan.yygh.model.user.Patient;
import com.fan.yygh.user.service.PatientService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestController
@Api("就诊人接口")
@RequestMapping("/api/user/patient")
public class PatientController {
    @Resource
    private PatientService patientService;

    //获取就诊人列表
    @GetMapping("auth/findAll")
    @ApiOperation("//获取就诊人列表") //需要参数:账号拥有者的userId
    public Result findAll(HttpServletRequest request){
        //获取当前登录用户userId
        Long userId = AuthContextHolder.getUserId(request);
        //根据当前登录用户查询关联的其他就诊者列表
        List<Patient> list = patientService.findAllUserId(userId);
        return Result.ok(list);
    }
    //根据id获取就诊人信息
    @ApiOperation("//根据id获取就诊人信")
    @GetMapping("auth/get/{id}")
    public Result getPatient(@PathVariable Long id){
        Patient patient = patientService.getPatientId(id);//此方法getPatientId需要单独补充信息。
        return Result.ok(patient);
    }
    @ApiOperation("//添加就诊人")
    @PostMapping("auth/save") //此方法比较特殊,需要多一个参数
    public Result savePatient(@RequestBody Patient patient,
                              HttpServletRequest request){
        //获取当前登录用户id,补全信息,即将其他就诊人绑定到当前登录账户上。所以需要userid参数
        Long userId = AuthContextHolder.getUserId(request);
        patient.setUserId(userId);
        patientService.save(patient);
        return Result.ok();
    }
    @ApiOperation("//删除就诊人")
    @DeleteMapping("auth/remove/{id}")
    public Result deletePatient(@PathVariable Long id){
        patientService.removeById(id);
        return Result.ok();
    }
    @ApiOperation("修改就诊人信息")
    @PutMapping("auth/update")
    public Result updatePatient(@RequestBody Patient patient){
        //根据id修改,此对象包含id
        patientService.updateById(patient);
        return Result.ok();
    }
}

3、前端
3.1 封装api请求
创建/api/patient.js文件

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.2 列表
添加/pages/patient/index.vue组件

在这里插入图片描述

3.3 添加与修改
添加/pages/patient/add.vue组件

在这里插入图片描述

在这里插入图片描述

3.4 详情与删除
添加/pages/patient/show.vue组件

四、平台用户管理

前面我们做了用户登录、用户验证与就诊人,现在我们需要把这些信息在我们的平台管理系统做一个统一管理
操作模块:service-user

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
使用枚举类处理验证状态,将数字编码变成文字的验证状态;

package com.fan.yygh.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fan.yygh.common.exception.YyghException;
import com.fan.yygh.common.result.ResultCodeEnum;
import com.fan.yygh.enums.AuthStatusEnum;
import com.fan.yygh.common.helper.JwtHelper;
import com.fan.yygh.model.user.UserInfo;
import com.fan.yygh.user.mapper.UserInfoMapper;
import com.fan.yygh.user.service.UserInfoService;
import com.fan.yygh.vo.user.LoginVo;
import com.fan.yygh.vo.user.UserAuthVo;
import com.fan.yygh.vo.user.UserInfoQueryVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Service //先继承《M是mapper,T是实体类》后实现
public class UserInfoImpl extends ServiceImpl<UserInfoMapper, UserInfo>
        implements UserInfoService {
    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private RedisTemplate<String,String> redisTemplate;

    @Override
    public IPage<UserInfo> selectPage(Page<UserInfo> pageParam,
                                      UserInfoQueryVo userInfoQueryVo) {
        //UserInfoQueryVo获取条件值
        String name = userInfoQueryVo.getKeyword(); //用户名称
        Integer status = userInfoQueryVo.getStatus();//用户状态
        Integer authStatus = userInfoQueryVo.getAuthStatus(); //验证状态
        String createTimeBegin = userInfoQueryVo.getCreateTimeBegin(); //开始时间
        String createTimeEnd = userInfoQueryVo.getCreateTimeEnd(); //结束时间
        //对条件值进行非空判断
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if(!StringUtils.isEmpty(name)) {
            wrapper.like("name",name);
        }
        if(!StringUtils.isEmpty(status)) {
            wrapper.eq("status",status);
        }
        if(!StringUtils.isEmpty(authStatus)) {
            wrapper.eq("auth_status",authStatus);
        }
        if(!StringUtils.isEmpty(createTimeBegin)) {
            wrapper.ge("create_time",createTimeBegin);
        }
        if(!StringUtils.isEmpty(createTimeEnd)) {
            wrapper.le("create_time",createTimeEnd);
        }
        //调用mapper的方法,这里为何使用IPage
        IPage<UserInfo> pages = baseMapper.selectPage(pageParam, wrapper);
        //编号变成对应的中文值的 封装
        pages.getRecords().stream().forEach( item -> {
            //盗用方法进行封装
            this.packageUserInfo(item);
        });
        return pages;
    }

    //编号变成文字的对应值  封装
    private UserInfo packageUserInfo(UserInfo userInfo) {
        //处理验证状态的编码,这里使用枚举类进行装换
        userInfo.getParam().put("authStatusString",
                //根据用户数字状态  转换
                AuthStatusEnum.getStatusNameByStatus(userInfo.getAuthStatus()));
        /*整数类intValue()方法
intValue()方法在java.lang包中可用。

intValue()方法用于返回此Integer对象表示的值,该值转换为int类型(通过强制转换)。

intValue()方法是一个非静态方法,只能通过类对象访问,如果尝试使用类名称访问该方法,则会收到错误消息。

从整数转换为int时,intValue()方法不会引发异常。*/
        //处理用户状态 0  1
        String statusString = userInfo.getStatus().intValue()==0 ? "锁定" : "正常";
        userInfo.getParam().put("statusString",statusString);
        return userInfo;
    }

    //用户验证接口
    @Override
    public void userAuth(Long userId, UserAuthVo userAuthVo) {
        //根据用户id查询用户信息
        System.out.println("userId:"+userId);
        UserInfo userInfo = baseMapper.selectById(userId);
        System.out.println("userInfo:"+userInfo);
        //设置验证信息
        //验证人姓名
        userInfo.setName(userAuthVo.getName());
        userInfo.setCertificatesType(userAuthVo.getCertificatesType());
        userInfo.setCertificatesNo(userAuthVo.getCertificatesNo());
        userInfo.setCertificatesUrl(userAuthVo.getCertificatesUrl());
        userInfo.setAuthStatus(AuthStatusEnum.AUTH_RUN.getStatus());
        //进行信息更新
        baseMapper.updateById(userInfo);

    }

    //判断数据库是否存在微信的扫描人信息,根据openid判断
    @Override
    public UserInfo selectWxInfoOpenId(String openid) {
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("openid",openid);
        UserInfo userInfo = baseMapper.selectOne(wrapper);
        return userInfo;
    }

    //用户手机号登录接口
    @Override
    public Map<String, Object> loginUser(LoginVo loginVo) {
        //从vo中获取参数
        String phone = loginVo.getPhone();
        String code = loginVo.getCode();
        //参数校验
        if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)){
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);//ResultCodeEnum枚举类
        }
        //TODO 校验校验验证码
        //校验校验验证码
        String mobleCode = redisTemplate.opsForValue().get(phone);
        if(!code.equals(mobleCode)) {
            throw new YyghException(ResultCodeEnum.CODE_ERROR);
        }


        //绑定手机号码
        UserInfo userInfo = null;
        if(!StringUtils.isEmpty(loginVo.getOpenid())) {
            userInfo = this.selectWxInfoOpenId(loginVo.getOpenid());
            if(null != userInfo) {
                userInfo.setPhone(loginVo.getPhone());
                this.updateById(userInfo);
            } else {
                throw new YyghException(ResultCodeEnum.DATA_ERROR);
            }
        }

        if(userInfo == null){
            //判断是否第一次登录,根据手机号查询数据库,如果不存在相同的手机号就是第一次登录
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("phone",phone);
             userInfo = baseMapper.selectOne(wrapper);
            if(null==userInfo ){//第一次使用这个手机号登录
                //添加信息到数据库
                userInfo = new UserInfo();
                userInfo.setName("");
                userInfo.setPhone(phone);
                userInfo.setStatus(1);//设置可用
                //this.save(userInfo);//保存到数据库
                baseMapper.insert(userInfo);
            }
        }

        //校验是否被禁用
        if(userInfo.getStatus() == 0) {
            throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
        }

        //返回给页面的map信息
        HashMap<String, Object> map = new HashMap<>();
        String name = userInfo.getName();
        if (StringUtils.isEmpty(name)) {//如果名字为空
            name = userInfo.getNickName();//用昵称代替名字
        }
        if(StringUtils.isEmpty(name)){
            name = userInfo.getPhone();//如果名字为空,用手机号代替名字
        }
        map.put("name",name);//返回name和token
        //利用工具生成token
        String token = JwtHelper.createToken(userInfo.getId(), name);
        map.put("token",token);//token  被写错了
        return map;  //记得返回map
    }
}

后台管理系统的前端:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

修改网关:
在这里插入图片描述

2.用户锁定的后台管理:

2、锁定
2.1 api接口
添加controller方法

在这里插入图片描述

在这里插入图片描述

2.2.1 封装api请求
在/api/user/userInfo.js文件添加方法

在这里插入图片描述

2.2.2 添加组件
修改/views/user/userInfo/list.vue组件
在这里插入图片描述

在这里插入图片描述

3.详情页面:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4、用户列表

在这里插入图片描述
4.1 添加路由

{
  path: 'userInfo/authList',
  name: '验证审批列表',
        component: () =>import('@/views/user/userInfo/authList'),
  meta: { title: '验证审批列表', icon: 'table' }
}

4.2 添加组件
添加/views/user/userInfo/authList.vue组件

在这里插入图片描述

5、用户审批

5.1 api接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

 @Override
    public void approval(Long userId, Integer authStatus) {
        System.out.println(userId+":"+authStatus);
        //当页面点击通过按钮或者不通过按钮的时候,参数传过来是2/-1
        if(authStatus.intValue() ==2 || authStatus.intValue() == -1){
            UserInfo userInfo = baseMapper.selectById(userId);
            userInfo.setAuthStatus(authStatus);//忘了写
            int i = baseMapper.updateById(userInfo);//可以用baseMapper进行更改
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 访问阿里云OSS(对象存储服务)图片失败可能有多种原因。 首先,可能是由于网络连接问题导致无法访问阿里云OSS。在网络连接不稳定、慢或中断的情况下,访问OSS图片会失败。此时,可以尝试检查网络连接,并确保网络状况良好。 其次,可能是由于OSS对象不存在或被意外删除导致无法访问。在请求访问OSS图片时,如果对象不存在,将返回错误信息。此时,可以检查对象的路径和名称是否正确,并确保对象未被删除。 另外,可能是由于访问权限限制导致无法访问OSS图片。阿里云OSS提供了丰富的权限控制机制,可以通过私有、公共读、公共读写等权限设置来控制对象的访问。如果没有正确的权限设置,可能无法正常访问OSS图片。此时,可以检查访问对象的权限设置,并确保拥有足够的访问权限。 最后,可能是由于错误的请求方式或参数导致无法访问OSS图片。在请求OSS图片时,需要正确地使用API接口、请求方法和参数。如果请求方式或参数错误,阿里云OSS将无法正确处理请求并返回相应的图片。此时,可以检查请求方式和参数是否正确,并参考阿里云OSS API文档进行调整。 综上所述,访问阿里云OSS图片失败可能是由于网络连接问题、对象不存在、访问权限限制或错误的请求方式或参数等原因导致的。可以根据具体情况检查相关因素,并采取相应的措施来解决问题。 ### 回答2: 访问阿里云OSS图片失败可能是由于多种原因引起的。 首先,可能是由于网络连接问题导致无法访问OSS图片。这可能是由于网络连接不稳定、网络信号弱或者代理服务器的问题。建议检查网络连接,确认网络正常后再次尝试访问。 其次,可能是由于OSS图片的地址设置错误导致无法访问。检查图片的URL地址是否正确,确认是否包含了正确的域名、桶名和图片路径。 此外,还可能是由于权限设置不正确导致无法访问。在阿里云OSS中,需要正确设置桶的访问权限以及图片的访问权限。确保桶的读取权限设置为公共读或者授权用户有访问权限,并且图片的权限也正确配置。 最后,还可能是由于阿里云OSS服务端故障导致无法访问。在这种情况下,建议联系阿里云客服或者开发者支持团队,寻求帮助并报告问题,他们将会及时处理并解决问题。 综上所述,访问阿里云OSS图片失败可能是由于网络连接问题、URL地址设置错误、权限设置不正确或者服务端故障等原因引起的。根据具体情况,可以逐步排查并解决问题。 ### 回答3: 当访问阿里云OSS图片失败时,可能有以下几个原因: 1. 链接错误:请确保访问阿里云OSS的链接地址正确,包括域名、路径等信息是否填写正确。 2. 访问权限限制:在阿里云OSS中,每个存储桶都有自己的访问权限设置。请确保您拥有访问该图片的权限。 3. 存储桶设置问题:您需要确认存储桶是否处于正常状态。可能是存储桶已被删除、权限设置有误等情况导致无法访问。 4. 图片不存在:如果您输入的图片路径错误或者该图片已被删除,将导致访问失败。 5. 网络问题:可能是网络连接不稳定或者网络延迟导致无法正常访问阿里云OSS。此时,您可以尝试刷新页面或者稍后再试。 6. 阿里云OSS服务端故障:可能是阿里云OSS服务端出现故障导致无法正常访问。此时,您可以联系阿里云技术支持反馈问题,等待解决。 总结起来,访问阿里云OSS图片失败可能是链接错误、访问权限限制、存储桶设置问题、图片不存在、网络问题或阿里云OSS服务端故障等原因导致的。如果您遇到该问题,可以逐一排查以上原因,找到具体原因后采取相应的解决措施。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值