【探花交友】day02—完善个人信息

目录

1、完善用户信息

1.1、阿里云OSS

1.2、百度人脸识别

1.3、保存用户信息

1.4、上传用户头像

2、用户信息管理

2.1、查询用户资料

2.2、更新用户资料

3、统一token处理

3.1、代码存在的问题

3.2、解决方案

3.3、代码实现

4、统一异常处理

4.1、解决方案

4.2、代码实现

1、完善用户信息

用户在首次登录时需要完善个人信息,包括性别、昵称、生日、城市、头像等。其中,头像数据需要做图片上传,这里采用阿里云的OSS服务作为我们的图片服务器,并且对头像要做人脸识别,非人脸照片不得上传。

  • 首次登录时(手机号码不存在),需要创建用户存入数据库中

  • 客户端检测首次登录需要完善用户信息

    • 填写用户基本信息

    • 上传用户头像(需要人脸认证)

1.1、阿里云OSS

实现图片上传服务,需要有存储的支持,那么我们的解决方案将以下几种:

  1. 直接将图片保存到服务的硬盘(springmvc将的文件上传)

    1. 优点:开发便捷,成本低

    2. 缺点:扩容困难

  2. 使用分布式文件系统进行存储

    1. 优点:容易实现扩容

    2. 缺点:开发复杂度稍大(有成熟的产品可以使用,比如:FastDFS)

  3. 使用第三方的存储服务

    1. 优点:开发简单,拥有强大功能,免维护

    2. 缺点:付费

在本套课程中选用阿里云的OSS服务进行图片存储。

1.1.1、概述

对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

地址:对象存储 OSS_云存储服务_企业数据管理_存储-阿里云

1.1.2、账号申请

购买服务

使用第三方服务最大的缺点就是需要付费,下面,我们看下如何购买开通服务。

 

购买下行流量包: (不购买也可以使用,按照流量付费)

说明:OSS的上行流量是免费的,但是下行流量是需要购买的。

创建Bucket

使用OSS,首先需要创建Bucket,Bucket翻译成中文是水桶的意思,把存储的图片资源看做是水,想要盛水必须得有桶,就是这个意思了。

进入控制台,阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

选择Bucket后,即可看到对应的信息,如:url、消耗流量等 :

文件管理:

查看文件:

1.1.3、抽取模板工具

和发送短信类似,阿里云OSS也是采用自定义工具的形式进行封装

OssProperties

tanhua-autoconfig创建配置类

@Data
@ConfigurationProperties(prefix = "tanhua.oss")
public class OssProperties {

    private String accessKey; 
    private String secret;
    private String bucketName;
    private String url; //域名
    private String endpoint;
}

OssTemplate

tanhua-autoconfig创建模板对象

package com.tanhua.autoconfig.template;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.tanhua.autoconfig.properties.OssProperties;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

public class OssTemplate {

    private OssProperties properties;

    public OssTemplate(OssProperties properties) {
        this.properties = properties;
    }

    /**
     * 文件上传
     *   1:文件名称
     *   2:输入流
     */
    public String upload(String filename, InputStream is) {
        //3、拼写图片路径
        filename = new SimpleDateFormat("yyyy/MM/dd").format(new Date())
                +"/"+ UUID.randomUUID().toString() + filename.substring(filename.lastIndexOf("."));


        // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
        String endpoint = properties.getEndpoint();
        // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
        String accessKeyId = properties.getAccessKey();
        String accessKeySecret = properties.getSecret();

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

        // 填写Byte数组。
        // 填写Bucket名称和Object完整路径。Object完整路径中不能包含Bucket名称。
        ossClient.putObject(properties.getBucketName(), filename, is);

        // 关闭OSSClient。
        ossClient.shutdown();

        String url = properties.getUrl() +"/" + filename;
        return url;
    }
}

TanhuaAutoConfiguration

TanhuaAutoConfiguration加入配置

@EnableConfigurationProperties({
        SmsProperties.class,
        OssProperties.class
})
public class TanhuaAutoConfiguration {

    @Bean
    public SmsTemplate smsTemplate(SmsProperties properties) {
        return new SmsTemplate(properties);
    }

    @Bean
    public OssTemplate ossTemplate(OssProperties properties) {
        return new OssTemplate(properties);
    }
}

1.1.4、测试

tanhua-app-server加入配置内容,并测试

tanhua:  
  oss:
    accessKey: LTAI4GKgob9vZ53k2SZdyAC7
    secret: LHLBvXmILRoyw0niRSBuXBZewQ30la
    endpoint: oss-cn-beijing.aliyuncs.com
    bucketName: tanhua001
    url: https://tanhua001.oss-cn-beijing.aliyuncs.com/

编写测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class OssTest {

    @Autowired
    private OssTemplate template;

    @Test
    public void testTemplateUpload() throws FileNotFoundException {
        String path = "C:\\Users\\lemon\\Desktop\\课程资源\\02-完善用户信息\\03-资料\\2.jpg";
        FileInputStream inputStream = new FileInputStream(new File(path));
        String imageUrl = template.upload(path, inputStream);
        System.out.println(imageUrl);
    }
}

1.2、百度人脸识别

人脸识别(Face Recognition)基于图像或视频中的人脸检测、分析和比对技术,提供对您已获授权前提下的私有数据的人脸检测与属性分析、人脸对比、人脸搜索、活体检测等能力。灵活应用于金融、泛安防、零售等行业场景,满足身份核验、人脸考勤、闸机通行等业务需求

1.2.1、概述

地址:人脸识别_人脸识别_准确率99.99%_免费试用-百度AI开放平台

1.2.2、账号申请

账号登录注册

百度云AI支持百度账号登录,也可以支持云账号。按需注册即可

创建应用

按需创建应用

1.2.3、抽取模板工具

AipFaceProperties

@Data
@ConfigurationProperties("tanhua.aip")
public class AipFaceProperties {
    private String appId;
    private String apiKey;
    private String secretKey;

    @Bean
    public AipFace aipFace() {
        AipFace client = new AipFace(appId, apiKey, secretKey);
        // 可选:设置网络连接参数
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);
        return client;
    }
}

AipFaceTemplate

package com.tanhua.autoconfig.template;

import com.baidu.aip.face.AipFace;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;

public class AipFaceTemplate {

    @Autowired
    private AipFace client;

    /**
     * 检测图片中是否包含人脸
     *  true:包含
     *  false:不包含
     */
    public boolean detect(String imageUrl) {
        // 调用接口
        String imageType = "URL";

        HashMap<String, String> options = new HashMap<String, String>();
        options.put("face_field", "age");
        options.put("max_face_num", "2");
        options.put("face_type", "LIVE");
        options.put("liveness_control", "LOW");

        // 人脸检测
        JSONObject res = client.detect(imageUrl, imageType, options);
        System.out.println(res.toString(2));

        Integer error_code = (Integer) res.get("error_code");

        return error_code == 0;
    }
}

1.2.4、测试

tanhua-app-server加入百度AI的配置信息

tanhua:
  aip:
    appId: 27469915
    apiKey: sHiKW8u382xONufrmu0dHKdv
    secretKey: YsrzV2EVDRROdlZRAxDoTWGDH5L5cNrF

编写单元测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class FaceTest {


    @Autowired
    private AipFaceTemplate template;

    @Test
    public void detectFace() {
        String image = "https://tanhua001.oss-cn-beijing.aliyuncs.com/2021/04/19/a3824a45-70e3-4655-8106-a1e1be009a5e.jpg";
        boolean detect = template.detect(image);
    }
}

1.3、保存用户信息

1.3.1、接口文档

YAPI接口地址:http://192.168.136.160:3000/project/19/interface/api/88

1.3.2、需求分析

数据库表

tb_user_info(用户基本资料表)

  • 用户表和用户信息表是一对一的关系,两者采用主键关联的形式配置

  • 主键关联:用户表主键和用户资料表主键要保持一致(如:用户表id=1,此用户的资料表id=1)

执行流程

  1. 手机端发送请求到消费者

  2. 消费者构造数据,调用提供者

  3. 消费者完成业务处理,操作数据库

1.3.3、代码实现

UserInfo实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo implements Serializable {

    /**
     * 由于userinfo表和user表之间是一对一关系
     *   userInfo的id来源于user表的id
     */
    @TableId(type= IdType.INPUT)
    private Long id; //用户id
    private String nickname; //昵称
    private String avatar; //用户头像
    private String birthday; //生日
    private String gender; //性别
    private Integer age; //年龄
    private String city; //城市
    private String income; //收入
    private String education; //学历
    private String profession; //行业
    private Integer marriage; //婚姻状态
    private String tags; //用户标签:多个用逗号分隔
    private String coverPic; // 封面图片
    private Date created;
    private Date updated;

    //用户状态,1为正常,2为冻结
    @TableField(exist = false)
    private String userStatus = "1";
}

UserController

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserInfoService userInfoService;

    /**
     * 保存用户信息
     *   UserInfo
     *   请求头中携带token
     */
    @PostMapping("/loginReginfo")
    public ResponseEntity loginReginfo(@RequestBody UserInfo userInfo,
                                       @RequestHeader("Authorization") String token) {
        //1、解析token
        Claims claims = JwtUtils.getClaims(token);
        Integer id = (Integer) claims.get("id");
        //2、向userinfo中设置用户id
        userInfo.setId(Long.valueOf(id));
        //3、调用service
        userInfoService.save(userInfo);
        return ResponseEntity.ok(null);
    }
}

UserInfoService

@Service
public class UserInfoService {

    @DubboReference
    private UserInfoApi userInfoApi;

    public void save(UserInfo userInfo) {
        userInfoApi.save(userInfo);
    }
}

UserInfoApi

public interface UserInfoApi {
    public void save(UserInfo userInfo);
}

UserInfoApiImpl

@DubboService
public class UserInfoApiImpl implements  UserInfoApi {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Override
    public void save(UserInfo userInfo) {
        userInfoMapper.insert(userInfo);
    }
}

UserInfoMapper

public interface UserInfoMapper extends BaseMapper<UserInfo> {

}

1.4、上传用户头像

1.4.1、需求分析

接口文档

YAPI地址:http://192.168.136.160:3000/project/19/interface/api/91

文件上传采用POST传递,mvc中经过文件解析器转化为MultipartFile对象处理

后续请求中,请求headr中都有Authorization参数。

执行流程

1.4.2、代码实现

UserControler

  /**
     * 上传用户头像
     * @return
     */
    @PostMapping("/loginReginfo/head")
    public ResponseEntity head(MultipartFile headPhoto,@RequestHeader("Authorization") String token) throws IOException {
        //1、判断token是合法
        boolean verifyToken = JwtUtils.verifyToken(token);
        if(!verifyToken){
            return ResponseEntity.status(401).body(null);
        }
        //2、向userinfo中设置用户id
        Claims claims = JwtUtils.getClaims(token);
        Integer id = (Integer) claims.get("id");
        //3、调用service
        userInfoService.updateHead(headPhoto,id);
        return ResponseEntity.ok(null);
    }

UserInfoService

//更新用户头像
public void updateHead(MultipartFile headPhoto, Long id) throws IOException {
    //1、将图片上传到阿里云oss
    String imageUrl = ossTemplate.upload(headPhoto.getOriginalFilename(), headPhoto.getInputStream());
    //2、调用百度云判断是否包含人脸
    boolean detect = aipFaceTemplate.detect(imageUrl);
    //2.1 如果不包含人脸,抛出异常
    if(!detect) {
        throw new RuntimeException();
    }else{
        //2.2 包含人脸,调用API更新
        UserInfo userInfo = new UserInfo();
        userInfo.setId(id);
        userInfo.setAvatar(imageUrl);
        userInfoApi.update(userInfo);
    }
}

UserInfoApi

public void update(UserInfo userInfo);

UserInfoApiImpl

@Override
public void update(UserInfo userInfo) {
    userInfoMapper.updateById(userInfo);
}

2、用户信息管理

2.1、查询用户资料

2.1.1、接口文档

YAPI地址:http://192.168.136.160:3000/project/19/interface/api/265

参数userID: 用户id,当不传递时,查询当前用户的资料信息

2.1.2、代码实现

UserInfoVo

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoVo implements Serializable {

    private Long id; //用户id
    private String nickname; //昵称
    private String avatar; //用户头像
    private String birthday; //生日
    private String gender; //性别
    private String age; //年龄
    private String city; //城市
    private String income; //收入
    private String education; //学历
    private String profession; //行业
    private Integer marriage; //婚姻状态
}

UsersController

@RestController
@RequestMapping("/users")
public class UsersControler {

    @Autowired
    private UserInfoService userInfoService;

    /**
     * 查询用户资料
     */
    @GetMapping
    public ResponseEntity users(Long userID,@RequestHeader("Authorization") String token) {
        if(userID == null) {
            Claims claims = JwtUtils.getClaims(token);
			Integer id = (Integer) claims.get("id");
            userID = Long.valueOf(id);
        }
        UserInfoVo userInfo = userInfoService.findById(userID);
        return ResponseEntity.ok(userInfo);
    }
}

UserInfoService

//根据id查寻
public UserInfoVo findById(Long id) {
    UserInfo userInfo = userInfoApi.findById(id);

    UserInfoVo vo = new UserInfoVo();

    BeanUtils.copyProperties(userInfo,vo); //copy同名同类型的属性

    if(userInfo.getAge() != null) {
        vo.setAge(userInfo.getAge().toString());
    }

    return vo;
}

UserInfoApiImpl

@Override
public UserInfo findById(Long id) {
    return userInfoMapper.selectById(id);
}

2.2、更新用户资料

2.2.1、接口文档

YAPI地址:http://192.168.136.160:3000/project/19/interface/api/259

2.2.2、代码实现

UsersController

  /**
     * 更新用户资料
     * @return
     */
    @PutMapping
    public ResponseEntity updateUserInfo(@RequestBody UserInfo userInfo,@RequestHeader("Authorization") String token){
        //1、判断token是否合法
        boolean verifyToken = JwtUtils.verifyToken(token);
        if(!verifyToken){
            return ResponseEntity.status(401).body(null);
        }
        //2、获取用户id
        Claims claims = JwtUtils.getClaims(token);
        Integer id = (Integer) claims.get("id");
        //3、设置用户id
        userInfo.setId(Long.valueOf(id));
        userInfoService.update(userInfo);
        return ResponseEntity.ok(null);
    }

UserInfoService

//更新
public void update(UserInfo userInfo) {
    userInfoApi.update(userInfo);
}

UserInfoApiImpl

@Override
public void update(UserInfo userInfo) {
    userInfoMapper.updateById(userInfo);
}

3、统一token处理

3.1、代码存在的问题

3.2、解决方案

基于ThreadLocal + 拦截器的形式统一处理

拦截器(Interceptor)

  • 是一种动态拦截方法调用的机制;

  • 类似于Servlet 开发中的过滤器Filter,用于对处理器进行前置处理和后置处理。

ThreadLocal

  • 线程内部的存储类,赋予了线程存储数据的能力。

  • 线程内调用的方法都可以从ThreadLocal中获取同一个对象。

  • 多个线程中ThreadLocal数据相互隔离

Threadlocal使用方法很简单

ThreadLocal<T> threadLocal = new ThreadLocal<T>();
threadLocal.set() //将数据绑定到当前线程
threadLocal.get() //从当前线程中获取数据

3.3、代码实现

3.3.1、ThreadLocal工具类

定义ThreadLocal工具类,仅需要调用set方法即可将数据存入ThreadLocal中

/**
 * 工具类:实现向threadlocal存储数据的方法
 */
public class UserHolder {

    private static ThreadLocal<User> tl = new ThreadLocal<>();


    //将用户对象,存入Threadlocal
    public static void set(User user) {
        tl.set(user);
    }

    //从当前线程,获取用户对象
    public static User get() {
        return tl.get();
    }

    //从当前线程,获取用户对象的id
    public static Long getUserId() {
        return tl.get().getId();
    }

    //从当前线程,获取用户对象的手机号码
    public static String getMobile() {
        return tl.get().getMobile();
    }
}

3.3.2、定义拦截器

定义拦截器,在前置拦截方法preHandle中解析token并验证有效性,如果失效返回状态码401。如果有效,解析User对象,存入ThreadLocal中

public class TokenInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        //1、获取请求头
        String token = request.getHeader("Authorization");

        //2、使用工具类,判断token是否有效
        boolean verifyToken = JwtUtils.verifyToken(token);
        //3、如果token失效,返回状态码401,拦截 
        if(!verifyToken) {
            response.setStatus(401);
            return false;
        }
        //4、如果token正常可用,放行

        //解析token,获取id和手机号码,构造User对象,存入Threadlocal
        Claims claims = JwtUtils.getClaims(token);
        String mobile = (String) claims.get("mobile");
        Integer id = (Integer) claims.get("id");

        User user = new User();
        user.setId(Long.valueOf(id));
        user.setMobile(mobile);

        UserHolder.set(user);

        return true;
    }
}

3.3.3、注册拦截器

拦截器需要注册到MVC容器中

@Configuration
public class WebConfig  implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns(new String[]{"/user/login","/user/loginVerification"});
    }
}

3.3.4、测试

修改UsersController方法,从ThreadLoca中获取用户数据

/**
 * 查询用户资料
 */
@GetMapping
public ResponseEntity users(Long userID) {
    if(userID == null) {
        userID = UserHolder.getUserId();
    }
    UserInfoVo userInfo = userInfoService.findById(userID);
    return ResponseEntity.ok(userInfo);
}

4、统一异常处理

软件开发过程中,不可避免的是需要处理各种异常,常见的形式就是逐层向上抛出,web层进行处理。使用try {...} catch {...}很方便就能对异常做到业务处理

  1. 冗余代码多,影响代码可读性

  2. 异常处理和业务代码耦合

4.1、解决方案

SpringMVC提供了一套解决全局异常的处理方案,可以在代码无侵入的前提下完成异常处理。遵循逐层抛出,异常处理器统一处理的思路

 

4.2、代码实现

项目中可能存在不可预知的各种异常,如:空指针,数组越界等。针对这类异常,可以直接在异常处理器中统一处理;

还有一类是可预知的错误,如图片不合法,验证码错误等等。这类错误也可以理解为业务异常,可以通过自定义异常类来处理;

4.2.1、业务异常对象

为了方便操作,将一些常见的业务错误封装到ErrorResult对象中

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ErrorResult {

    private String errCode = "999999";
    private String errMessage;

    public static ErrorResult error() {
        return ErrorResult.builder().errCode("999999").errMessage("系统异常稍后再试").build();
    }

    public static ErrorResult fail() {
        return ErrorResult.builder().errCode("000001").errMessage("发送验证码失败").build();
    }

    public static ErrorResult loginError() {
        return ErrorResult.builder().errCode("000002").errMessage("验证码失效").build();
    }

    public static ErrorResult faceError() {
        return ErrorResult.builder().errCode("000003").errMessage("图片非人像,请重新上传!").build();
    }

    public static ErrorResult mobileError() {
        return ErrorResult.builder().errCode("000004").errMessage("手机号码已注册").build();
    }

    public static ErrorResult contentError() {
        return ErrorResult.builder().errCode("000005").errMessage("动态内容为空").build();
    }

    public static ErrorResult likeError() {
        return ErrorResult.builder().errCode("000006").errMessage("用户已点赞").build();
    }

    public static ErrorResult disLikeError() {
        return ErrorResult.builder().errCode("000007").errMessage("用户未点赞").build();
    }

    public static ErrorResult loveError() {
        return ErrorResult.builder().errCode("000008").errMessage("用户已喜欢").build();
    }

    public static ErrorResult disloveError() {
        return ErrorResult.builder().errCode("000009").errMessage("用户未喜欢").build();
    }
}

4.2.2 业务异常类

自定义业务异常类,针对业务错误之间抛出业务异常即可

/**
 * 自定义异常类
 */
@Data
public class BusinessException extends RuntimeException {

    private ErrorResult errorResult;

    public BusinessException(ErrorResult errorResult) {
        super(errorResult.getErrMessage());
        this.errorResult = errorResult;
    }
}

4.2.3、异常处理器

/**
 * 自定义统一异常处理
 *  1、通过注解,声明异常处理类
 *  2、编写方法,在方法内部处理异常,构造响应数据
 *  3、方法上编写注解,指定此方法可以处理的异常类型
 */
@ControllerAdvice
public class ExceptionAdvice {

    //处理业务异常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity handlerException(BusinessException be) {
        be.printStackTrace();
        ErrorResult errorResult = be.getErrorResult();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
    }

    //处理不可预知的异常
    @ExceptionHandler(Exception.class)
    public ResponseEntity handlerException1(Exception be) {
        be.printStackTrace();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ErrorResult.error());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/6879.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

「从零入门推荐系统」14:推荐系统冷启动

作者 | gongyouliu编辑 | gongyouliu作者在第2章《推荐系统基础介绍》中讲述推荐系统面临的挑战时提到冷启动是推荐系统的重要挑战之一。冷启动问题是推荐系统工程实践中非常重要的一个问题&#xff0c;只有解决好冷启动问题&#xff0c;推荐系统的用户体验才会更好。有很多读者…

首届“兴智杯”产业赛收官,文心大模型助推产业创新

由工业和信息化部、科学技术部、深圳市人民政府共同主办&#xff0c;中国信通院等单位承办的首届“兴智杯”全国人工智能创新应用大赛圆满收官。本次大赛受到国家部委、政府机构、科技企业、高校师生等社会各界密切关注。为了进一步激发创新活力&#xff0c;促进人工智能核心技…

ChatGPT 本地部署及搭建

这篇简要说下清华开源项目 ChatGLM 本地部署的详细教程。清华开源项目 ChatGLM-6B 已发布开源版本&#xff0c;这一项目可以直接部署在本地计算机上做测试&#xff0c;无需联网即可体验与 AI 聊天的乐趣。 项目地址&#xff1a;GitHub - THUDM/ChatGLM-6B: ChatGLM-6B&#xf…

创建网络数据集

目的&#xff1a;主要是用来做路径规划。 第一步&#xff1a;加载用作构建网络数据集的道路网数据到arcmap。 第二步&#xff1a;做打断处理。【如果线数据未做过打断处理&#xff0c;需要做这一步。】 有两种方式【1、编辑器里面的高级编辑器的打断相交线功能&#xff1b;2、…

带你玩转Python爬虫(胆小者勿进)千万别做坏事·······

这节课很危险&#xff0c;哈哈哈哈&#xff0c;逗你们玩的 目录 写在前面 1 了解robots.txt 1.1 基础理解 1.2 使用robots.txt 2 Cookie 2.1 两种cookie处理方式 3 常用爬虫方法 3.1 bs4 3.1.1 基础介绍 3.1.2 bs4使用 3.1.2 使用例子 3.2 xpath 3.2.1 xpath基础介…

AD20 PCB后期处理

•DRC检查•位号的调整•装配图制造输出•Gerber&#xff08;光绘&#xff09;文件输出•BOM输出•原理图PDF输出•文档规范存档1.电气性能检查 完成PCB的布局布线工作之后&#xff0c;接下来需要进行DRC检查&#xff0c;DRC检查主要是检查整板PCB布局布线与用户设置的规则约束…

最小的k个数(堆排序,快排)

原文&#xff1a; 最小的k个数 - 最小的k个数 - 力扣&#xff08;LeetCode&#xff09; class Solution { public: vector<int> getLeastNumbers(vector<int>& arr, int k) { vector<int> vec(k, 0); if (k 0) { // 排除 0 的情况 …

WT588D软件操作教程二

1、音频输出模式设置 设置音频的输出方式为 DAC(外接功放模式)和 PWM(直接驱动扬声器模式)。 点击“操作”→“选项”,在选项界面里设置音频输出模式。 2、BUSY 设置 设置 BUSY 端( I/O 口 P17)在播放音频时输出电平状态为高或低。 点击“操作”→“选项”,在“忙信号输…

ArcEngine 添加标题

样例 做法【这个很简单&#xff0c;看一下就能懂】 代码 private void 添加标题ToolStripMenuItem_Click(object sender, EventArgs e){{ IGraphicsContainer graphicsContainer mainPageLayoutControl1.PageLayout as IGraphicsContainer;IEnvelope envelope ne…

javaweb实现登录和注册(前端转数据到后端,servlet到mysql验证的案例)

一、 myeclipse的tomcat的使用和驱动的放置 软件版本&#xff1a; 编译软件myeclipse2014 数据库mysql2014 驱动mysql-connector-java-5.1.47 1、myeclipse的tomcat的使用 新建立一个java web 项目&#xff0c;在src下面新建里一个servlet类&#xff08;名叫register&#x…

电子学会2023年3月青少年软件编程(图形化)等级考试试卷(四级)真题,含答案解析

青少年软件编程(图形化)等级考试试卷(四级) 分数:100 题数:24 一、单选题(共10题,共30分) 1. 编写一段程序,从26个英文字母中,随机选出10个加入列表a。空白处应填入的代码是?( )

数字工厂项目实施注意事项有哪些

借助数字工厂管理系统&#xff0c;电子制造企业可以规范和优化整个企业内部业务流程&#xff0c;标准化企业业务数据&#xff0c;实现企业管理信息化;可以更高效的管理及分配企业资源&#xff0c;更高效的运营。基于供应链管理的数字工厂系统&#xff0c;在实施过程中需要注意些…

Windows 使用很久以后,C盘空间不足,怎么办

C:\User\某用户\AppData\Local\Tmp 把这个文件夹下的文件删除掉

写在28岁,回看3年前的自己,庆幸当时入了软件测试这行

为什么会学习软件测试&#xff1f; 已经28岁了&#xff0c;算一下快过去3年了&#xff0c;刚毕业那会工作了一年&#xff0c;因为自己当时很迷茫&#xff08;觉得自己挺废的&#xff09;&#xff0c;所以就没去工作就一直在家&#xff0c;家里固定每个月给点生活费&#xff0c…

SimpleDataFormat.parse转换日期错误-多线程

最近使用线程池批量操作数据&#xff0c;中间用到了SimpleDataFormat转换时间&#xff0c;部分数据转换不正确&#xff0c;甚至2023年转成了7223年&#xff0c;原因是SimpleDataFormat不是线程安全的类&#xff0c;所以可以加锁进行处理 我是将sdf作为参数放入多线程&#xff0…

降噪蓝牙耳机哪个品牌好?降噪蓝牙耳机排行推荐

随着蓝牙耳机品牌越来越多&#xff0c;型号更是让人眼花缭乱&#xff0c;各种功能也是层出不穷。但是很多人在眼花缭乱的耳机中并不知道如何选择合适的&#xff0c;下面是我根据多年的耳机使用经验总结的几款值得推荐的降噪蓝牙耳机&#xff0c;快速来看。 1.南卡A2真无线降噪…

【蓝桥杯嵌入式】蓝桥杯第十届省赛真题,程序题全解析(含代码)

&#x1f38a;【蓝桥杯嵌入式】专题正在持续更新中&#xff0c;原理图解析✨&#xff0c;各模块分析✨以及历年真题讲解✨都在这儿哦&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f; &#x1f38f;【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题…

C#和Lua的交互

1.C#调用Lua 1.1C#调用Lua文件中的全局变量 using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using XLua;/* *创建者: *创建时间: *描述:XLua管理器 *版本: */ public class XLuaManager {public static LuaEnv le;//Lua环…

计讯物联智慧景区应用解决方案,开启交互式智慧旅游新篇章

方案背景 后疫情时代&#xff0c;旅游市场逐步回暖。随着游客的旅游需求趋向个性化、多元化&#xff0c;景区的数字化转型升级势在必行。在此背景下&#xff0c;计讯物联充分发挥5G、云计算、物联网、大数据等技术的应用价值&#xff0c;以技术创新推动业务创新&#xff0c;面…