5.Redis 实现点赞 优化登陆(验证码 token..)

news2024/9/30 11:36:54
  1. Redis

(1)简介

  • Redis 是一个高性能的 key-value 数据库

  • 原子 – Redis的所有操作都是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。

  • 非关系形数据库

  • 数据全部存在内存中,性能高。

(2)数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

  • string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

  • Redis hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

  • Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

  • Redis 的 Set 是 string 类型的无序集合,集合是通过hash实现的

  • Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

(3)基本操作

@Test
public void testStrings() {
    String redisKey = "test:count";

    redisTemplate.opsForValue().set(redisKey, 1);

    System.out.println(redisTemplate.opsForValue().get(redisKey));
    System.out.println(redisTemplate.opsForValue().increment(redisKey));
    System.out.println(redisTemplate.opsForValue().decrement(redisKey));
}

@Test
public void testHashes() {
    String redisKey = "test:user";

    redisTemplate.opsForHash().put(redisKey, "id", 1);
    redisTemplate.opsForHash().put(redisKey, "username", "zhangsan");

    System.out.println(redisTemplate.opsForHash().get(redisKey, "id"));
    System.out.println(redisTemplate.opsForHash().get(redisKey, "username"));
}

@Test
public void testLists() {
    String redisKey = "test:ids";

    redisTemplate.opsForList().leftPush(redisKey, 101);
    redisTemplate.opsForList().leftPush(redisKey, 102);
    redisTemplate.opsForList().leftPush(redisKey, 103);

    System.out.println(redisTemplate.opsForList().size(redisKey));
    System.out.println(redisTemplate.opsForList().index(redisKey, 0));
    System.out.println(redisTemplate.opsForList().range(redisKey, 0, 2));

    System.out.println(redisTemplate.opsForList().leftPop(redisKey));
    System.out.println(redisTemplate.opsForList().leftPop(redisKey));
    System.out.println(redisTemplate.opsForList().leftPop(redisKey));
}

@Test
public void testSets() {
    String redisKey = "test:teachers";

    redisTemplate.opsForSet().add(redisKey, "刘备", "关羽", "张飞", "赵云", "诸葛亮");

    System.out.println(redisTemplate.opsForSet().size(redisKey));
    System.out.println(redisTemplate.opsForSet().pop(redisKey));
    System.out.println(redisTemplate.opsForSet().members(redisKey));
}

@Test
public void testSortedSets() {
    String redisKey = "test:students";

    redisTemplate.opsForZSet().add(redisKey, "唐僧", 80);
    redisTemplate.opsForZSet().add(redisKey, "悟空", 90);
    redisTemplate.opsForZSet().add(redisKey, "八戒", 50);
    redisTemplate.opsForZSet().add(redisKey, "沙僧", 70);
    redisTemplate.opsForZSet().add(redisKey, "白龙马", 60);

    System.out.println(redisTemplate.opsForZSet().zCard(redisKey));
    System.out.println(redisTemplate.opsForZSet().score(redisKey, "八戒"));
    System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey, "八戒"));
    System.out.println(redisTemplate.opsForZSet().reverseRange(redisKey, 0, 2));
}

多次访问同一个key
@Test
public void testBoundOperations() {
    String redisKey = "test:count";
    BoundValueOperations operations = redisTemplate.boundValueOps(redisKey);
    operations.increment();
    operations.increment();
    operations.increment();
    operations.increment();
    operations.increment();
    System.out.println(operations.get());
}

(4)spring 配置 redis

引入依赖

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

在 application.properties 中声明:访问哪个库,host地址,端口号

# RedisProperties
spring.redis.database=11
spring.redis.host=localhost
spring.redis.port=6379

在 config 下实现 RedisConfig 类

注入连接工厂才能访问数据库 RedisConnectionFactory factory

实例化 bean new RedisTemplate<>();

设置工厂后有访问数据库能力 template.setConnectionFactory(factory);

指定序列化方式(数据转化方式)

//定义自定义的redis对象
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){

        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        //主要配置 序列化的方式
        //设置key 的 序列化方式
        redisTemplate.setKeySerializer(RedisSerializer.string());
        //设置value的序列化方式
        redisTemplate.setValueSerializer(RedisSerializer.json());
        //设置hash 的 key序列化
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        //设置 hash 的 value 序列化
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        //出发 使其生效
        redisTemplate.afterPropertiesSet();
        return  redisTemplate;
    }

(5)Redis 事务 管理

事务内命令不会立即执行,提交后统一执行

使用编程式事务进行管理,声明式事务用的少

调用 redisTemplate ,方法内部做匿名实现

SessionCallback() 里方法execute重写,内部实现事务逻辑

启用事务 operations.multi();

提交事务 operations.exec();

// 编程式事务
@Test
public void testTransactional() {
    Object obj = redisTemplate.execute(new SessionCallback() {
        @Override
        public Object execute(RedisOperations operations) throws DataAccessException {
            String redisKey = "test:tx";

            operations.multi();

            operations.opsForSet().add(redisKey, "zhangsan");
            operations.opsForSet().add(redisKey, "lisi");
            operations.opsForSet().add(redisKey, "wangwu");

            System.out.println(operations.opsForSet().members(redisKey));

            return operations.exec();
        }
    });
    System.out.println(obj);
}

2.点赞

(1)业务层

生成redis key的工具 在 util 下实现 RedisKeyUtil,集合set存储谁给某个实体点的赞

public class RedisKeyUtil {
    private static final String SPLIT = ":";
    private static final String PREFIX_ENTITY_LIKE = "like:entity";
    private static final String PREFIX_USER_LIKE = "like:user";

    // 某个实体的赞
    // like:entity:entityType:entityId -> set(userId)
    public static String getEntityLikeKey(int entityType, int entityId) { //实体类型  实体ID
        return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId;
    }

}

Service 下实现 LikeService

@Service
public class LikeService {

    @Autowired
    private RedisTemplate redisTemplate;

    // 点赞
    public void like(int userId, int entityType, int entityId) {
        //获取key
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType,entityId);
        //判断当前用户是否点过赞   即userid 是否在set中
        if(redisTemplate.opsForSet().isMember(entityLikeKey,userId)){
            redisTemplate.opsForSet().remove(entityLikeKey,userId);
        }else {
            redisTemplate.opsForSet().add(entityLikeKey,userId);
        }
    }

    // 查询某实体点赞的数量
    public long findEntityLikeCount(int entityType, int entityId){
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType,entityId);
        return redisTemplate.opsForSet().size(entityLikeKey);
    }

    // 查询某人对某实体的点赞状态
    public int findEntityLikeStatus(int userId, int entityType, int entityId) {
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType,entityId);
        return redisTemplate.opsForSet().isMember(entityLikeKey,userId)? 1:0 ;
    }
}

(2)表现层

Controller 下实现 LikeController

  1. 获取当前用户

  1. 调用service点赞方法

  1. 获取数量和状态

  1. 放入map

  1. 返回json格式数据

@Controller
public class LikeController {
    @Autowired
    private LikeService likeService;
    @Autowired
    private HostHolder hostHolder;

    @RequestMapping(path = "/like", method = RequestMethod.POST)
    @ResponseBody
    public String like(int entityType, int entityId){
        User user = hostHolder.getUser();
        //点赞
        likeService.like(user.getId(), entityType,entityId);
        //更新点赞数量
        long likeCount = likeService.findEntityLikeCount(entityType,entityId);
        //查询状态
        int likeStatus = likeService.findEntityLikeStatus(user.getId(),entityType,entityId);

        Map<String,Object> map = new HashMap<>();
        map.put("likeCount", likeCount);
        map.put("likeStatus", likeStatus);

        return CommunityUtil.getJSONString(0, null, map);
    }
}

帖子详情页面赞的数量的显示

修改 DiscussPostController 下的 getDiscussPost

//根据 帖子id 查询帖子内容 评论 评论的回复
    @RequestMapping(path = "/detail/{discussPostId}",method = RequestMethod.GET)
    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page){
        //根据帖子id查询帖子
        DiscussPost post = discussPostService.findDiscussPostById(discussPostId);

        model.addAttribute("post",post);
        //根据userid查询user
        User user =userService.findUserById(post.getUserId());
        model.addAttribute("user",user);
        // 点赞数量
        long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, discussPostId);
        model.addAttribute("likeCount", likeCount);
        // 点赞状态
        int likeStatus = hostHolder.getUser() == null ? 0 :
                likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, discussPostId);
        model.addAttribute("likeStatus", likeStatus);

        //查评论的分页信息
        page.setLimit(5);
        page.setPath("/discuss/detail/" + discussPostId);
        page.setRows(post.getCommentCount());

        //评论:给帖子的评论
        //回复:给评论的评论

        //获取所有评论
        List<Comment> commentList = commentService.findCommentsByEntity
                (ENTITY_TYPE_POST,post.getId(), page.getOffset(),page.getLimit());
        //用于封装 每条评论及每条评论的回复。。。
        List<Map<String,Object>> commentVoList = new ArrayList<>();

        //每一条评论 找到评论的作者。找到该评论的回复,回复的作者,回复的用户
        for (Comment comment:commentList) {
            Map<String,Object> commentVo = new HashMap<>();
            //存入评论内容
            commentVo.put("comment",comment);
            //放入 作者
            commentVo.put("user",userService.findUserById(comment.getUserId()));
            
            // 点赞数量
            likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, comment.getId());
            commentVo.put("likeCount", likeCount);
            // 点赞状态
            likeStatus = hostHolder.getUser() == null ? 0 :
                    likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, comment.getId());
            commentVo.put("likeStatus", likeStatus);

            //获取该评论的所有回复
            List<Comment> replyList = commentService.findCommentsByEntity
                    (ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
            //用于封装 每一条回复的 作者 回复咪表
            List<Map<String, Object>> replyVoList = new ArrayList<>();
            if(replyVoList != null){
                for (Comment reply: replyList) {
                    Map<String,Object> replyVo = new HashMap<>();
                    //回复
                    replyVo.put("reply", reply);
                    // 放入 回复的作者
                    replyVo.put("user", userService.findUserById(reply.getUserId()));
                    //回复目标
                    User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
                    replyVo.put("target", target);
                    // 点赞数量
                    likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, reply.getId());
                    replyVo.put("likeCount", likeCount);
                    // 点赞状态
                    likeStatus = hostHolder.getUser() == null ? 0 :
                            likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId());
                    replyVo.put("likeStatus", likeStatus);

                    //将 单条回复放入 此 评论 总的 回复表
                    replyVoList.add(replyVo);
                }
            }
            //将回复总表 嵌入 单条评论
            commentVo.put("replys", replyVoList);

            //回复数量
            int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId());
            commentVo.put("replyCount", replyCount);
            commentVoList.add(commentVo);
        }

        model.addAttribute("comments", commentVoList);
        return "/site/discuss-detail";
    }

3.使用Redis存储验证码

LoginController.getKaptcha

        //  老方法 验证码 存入session
        //session.setAttribute("kaptcha", text);

        // 验证码的归属 一个验证码 绑定 一个 kaptchaOwner
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        cookie.setMaxAge(60);
        cookie.setPath(contextPath);
        response.addCookie(cookie);
        //存入redis
        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);

LoginController.login

// 检查验证码 String kaptcha = (String) session.getAttribute("kaptcha");
        //获取验证码
        String kaptcha =null;
        if(StringUtils.isNotBlank(kaptchaOwner)){//是否存在
                String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
                kaptcha = (String) redisTemplate.opsForValue().get(redisKey);
        }
        //比对验证码
        if(StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equals(code)){
            model.addAttribute("codeMsg", "验证码不正确!");
            return "/site/login";
        }

4. 使用Redis存储登录凭证

UserService

  1. login 生成登录凭证

// 生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
        //loginTicketMapper.insertLoginTicket(loginTicket);

        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        redisTemplate.opsForValue().set(redisKey, loginTicket);
  1. logout 退出登录,ticket取出来再存进去

public void logout(String ticket) {
        //loginTicketMapper.updateStatus(ticket, 1);
        String redisKey  = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        loginTicket.setStatus(1);
        redisTemplate.opsForValue().set(redisKey,loginTicket);
    }
  1. LoginTicket 查询凭证

public LoginTicket findLoginTicket(String ticket) {
       // return loginTicketMapper.selectByTicket(ticket);
        String redisKey  = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        return  loginTicket;
    }

5.使用Redis缓存用户数据

查用户时: 先查缓存 在查mysql

UserService

// 1.优先从缓存中取值
private User getCache(int userId) {
    String redisKey = RedisKeyUtil.getUserKey(userId);
    return (User) redisTemplate.opsForValue().get(redisKey);
}

// 2.取不到时初始化缓存数据
private User initCache(int userId) {
    User user = userMapper.selectById(userId);
    String redisKey = RedisKeyUtil.getUserKey(userId);
    redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
    return user;
}

// 3.数据变更时清除缓存数据
private void clearCache(int userId) {
    String redisKey = RedisKeyUtil.getUserKey(userId);
    redisTemplate.delete(redisKey);
}

public User findUserById(int id) {
//        return userMapper.selectById(id);
    User user = getCache(id);
    if (user == null) {
        user = initCache(id);
    }
    return user;
}

public int activation(int userId, String code) {
    User user = userMapper.selectById(userId);
    if (user.getStatus() == 1) {
        return ACTIVATION_REPEAT;
    } else if (user.getActivationCode().equals(code)) {
        userMapper.updateStatus(userId, 1);
        clearCache(userId);
        return ACTIVATION_SUCCESS;
    } else {
        return ACTIVATION_FAILURE;
    }
}

public int updateHeader(int userId, String headerUrl) {
//        return userMapper.updateHeader(userId, headerUrl);
    int rows = userMapper.updateHeader(userId, headerUrl);
    clearCache(userId);
    return rows;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker中安装MySQL 8

前言 上一期在Windows中安装好了Docker环境&#xff0c;这一期在Docker中完成MySQL 8安装和配置。 启动Docker Desktop后在cmd窗口中输入docker -v即可查看到安装的docker版本 Docker启动容器的原理流程 Docker启动一个容器(应用)时&#xff0c;大致原理流程如下图&#x…

安全技术与防火墙工具iptables

目录 安全技术 安全技术 补充防水墙 防火墙的分类 按保护范围划分 按实现方式划分 按网络协议划分 iptables iptables的五表五链 三种报文流向 iptables基本语法 数据包常见的控制类型 iptables的基本选项 显示扩展模块 保存规则 持久保存规则 加载规则 开机自…

上岸!轻轻松松打工!Python数据分析证

俗话说的好&#xff0c;活到老学到&#x1f914;这个大内卷的时代掌握一项技能还是很重要&#x1f648;的&#xff0c;这不趁着下班时间的功夫&#xff0c;偷偷去考了个证 就是一个不论含金量&#xff0c;还是对实习和求职都非常有益的一个双协会认证的高含金量证书~BDA数据分析…

电影订票网站的设计与开发

技术&#xff1a;Java、JSP等摘要&#xff1a;随着科技的发展&#xff0c;时代的进步&#xff0c;互联网已经成为了人们生活中不可缺少的一部分&#xff0c;网上购物已然是一种时代的象征。纵观市场&#xff0c;电影行业的发展尤为迅速&#xff0c;电影种类和数量的增多导致客流…

Portraiture全新4.0最新版人像磨皮插件更新内容

Portraiture是一款智能磨皮插件&#xff0c;为Photoshop和Lightroom添加一键磨皮美化功能&#xff0c;快速对照片中皮肤、头发、眉毛等部位进行美化&#xff0c;无需手动调整&#xff0c;大大提高P图效率。全新4版本&#xff0c;升级AI算法&#xff0c;并独家支持多人及全身模式…

ROS小车研究笔记2/11/2023:使用ssh远程登录小车

1 SSH简介&#xff1a; SSH全称Secure Shell&#xff0c;是一种建立在应用层的安全网络协议。其安全性又非对称加密(RSA)实现 对称加密&#xff1a;使用同一密钥对信息进行加密和解密&#xff0c;但是一旦该密钥被窃取就会威胁通信安全 非对称加密&#xff1a;使用公钥和私钥。…

【Java基础】018 -- 面向对象阶段项目下(拼图小游戏扩展)

文章目录切换游戏图片的业务分析:1&#xff0c;所需要的技术点2&#xff0c;分析业务逻辑项目实现步骤&#xff1a;添加组件绑定事件&#xff1a;代码实现登录界面的业务分析&#xff1a;1&#xff0c;所需要的技术点2&#xff0c;分析业务逻辑项目实现步骤&#xff1a;主界面设…

Day885.NextKeyLock加锁规则 -MySQL实战

NextKeyLock加锁规则 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于NextKeyLock加锁规则的内容。 加锁规则前提说明&#xff1a; MySQL 后面的版本可能会改变加锁策略&#xff0c;所以这个规则只限于截止到现在的最新版本&#xff0c;即 5.x 系列 <5.7.24&#…

搭建流媒体推流/拉流服务(RTMP/RTSP/HLS/HTTP-FLV)

一、什么是流媒体流媒体&#xff08;streaming media&#xff09;是指将一连串的媒体数据压缩后&#xff0c;经过网上分段发送数据&#xff0c;在网上即时传输影音以供观赏的一种技术与过程&#xff0c;此技术使得数据包得以像流水一样发送&#xff1b;如果不使用此技术&#x…

ffmpeg转码转封装小工具开发

如下图所示&#xff0c;是本人开发的一个转码转封装小工具 其中目标文件视频编码格式支持&#xff1a;H264&#xff0c;H265&#xff0c;VP8&#xff0c;VP9。 目标文件封装格式支持&#xff1a;mp4,mkv,avi,mov,flv。 目标文件音频编码格式支持两个&#xff0c;COPY和AAC&am…

安全寒假作业nginx反向代理+负载均衡上传webshell重难点+apache漏洞

1.应用场景 负载均衡作为现今解决web应用承载大流量访问问题的一种方案&#xff0c;在真实环境中得到广泛的部署。实现负载均衡的方式有很多种&#xff0c;比如 DNS 方式、HTTP 重定向方式、IP 负载均衡方式、反向代理方式等等。 比如基于dns的负载均衡&#xff1a; 当然还有…

LTD212次升级 | 官网社区支持PC端展示 • 官网新增证件查询应用,支持条形码扫码查询

1、新增证件查询应用&#xff0c;支持条形码扫码查询&#xff1b; 2、新增用户社区PC端功能&#xff1b; 01证件查询应用 1、新增证件查询应用功能 支持证件信息录入、打印功能&#xff0c;支持条形码扫码识别。 后台管理操作路径&#xff1a;官微中心 - 应用 - 证件查询 …

这才是CSDN最系统完整的网络安全学习路线(建议收藏)

01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

linux head命令(head指令)(获取文件或管道输出结果前n行,默认前10行)与sed命令区别

head命令是一个在Linux系统中常用的命令&#xff0c;用于读取文件的前几行&#xff08;默认读取前10行&#xff09; 文章目录使用方法读取文件的前10行&#xff1a;head filename读取文件的前n行&#xff1a;head -n行数 filename读取多个文件的前几行&#xff1a;head -n 行数…

六、Java框架之SpringBoot

黑马课程 文章目录1. SpringBoot入门1.1 SpringBoot入门案例步骤1&#xff1a;创建SpringBoot项目高版本springboot常见错误步骤2&#xff1a;创建BookController步骤3&#xff1a;启动服务器并运行程序pom.xml示例1.2 官网创建SpringBoot1.3 SpringBoot工程快速启动问题导入打…

网络安全实验室6.解密关

6.解密关 1.以管理员身份登录系统 url&#xff1a;http://lab1.xseclab.com/password1_dc178aa12e73cfc184676a4100e07dac/index.php 进入网站点击忘记密码的链接&#xff0c;进入到重置密码的模块 输入aaa&#xff0c;点击抓包&#xff0c;发送到重放模块go 查看返回的链接…

VScode 结合clangd 构建linux源代码阅读环境

1、背景介绍上一篇文章&#xff1a;VScode 结合Global构建linux源代码阅读环境 &#xff0c;介绍了在VS Code工具中通过remote-ssh远程登陆到Linux远程服务器&#xff0c;使用Global构建linux源代码阅读环境&#xff0c;对linux kernel代码进行解析&#xff0c;实现全局搜索、自…

Vue2创建移动端项目

一、Vscode Vscode 下载安装以及常用的插件 1、Vscode 下载 下载地址&#xff1a;Vscode 中文语言插件 搜索 chinese 主题 Atom 主题 文件图标主题 搜索 icon 源代码管理插件GitLens 搜索 GitLens Live Server _本地服务器 搜索 Live Server Prettier - Code formatt…

405 Method Not Allowed,CORS跨域,来也自动化OCR接口报错,Nginx处理OPTIONS请求

在使用来也的OCR接口时&#xff0c;请求报错了&#xff0c;提示CORS跨域。POST请求&#xff0c;请求url&#xff1a;http://mage.uibot.com.cn/v1/mage/ocr/bills&#xff0c;官方文档&#xff1a;请求与响应&#xff1a;预检请求OPTIONS正式请求错误信息看控制台和网络栏位内容…

【沁恒WCH CH32V307V-R1开发板输出DAC实验】

【沁恒WCH CH32V307V-R1开发板输出DAC实验】1. 前言2. 软件配置2.1 安装MounRiver Studio3. DAC项目测试3.1 打开DAC工程3.2 编译项目4. 下载验证4.1 接线4.2 演示效果5. 小结1. 前言 数字/模拟转换模块&#xff08;DAC&#xff09;&#xff0c;包含 2 个可配置 8/12 位数字输入…