微信公众号打通与登录的实现

今天实现一下与微信公众号进行对接,通过扫描二维码的方式来进行注册与登录,获取用户的微信唯一标识作为用户的username,下面我们开始编写。

骨架建立:

建包:

第一步还是先将骨架建好,与网关骨架差不多,我们需要的东西不多,如图:

controller:接收微信请求

handler:进行微信信息处理

redis:redis的工具类和配置文件

utils:一些工具类

依赖引入:

本次依赖如下:每个依赖我都写好注释了

    <dependencies><!-- 启动依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.4.2</version><exclusions><exclusion><artifactId>spring-boot-starter-logging</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><!-- 日志依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><version>2.4.2</version></dependency><!-- xml解析器--><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency><!-- xml和Java对象的序列化--><dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.18</version></dependency><!-- json序列化--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.12.7</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.7</version></dependency><!-- json序列化工具--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.6</version></dependency><!-- 操作Redis的依赖库--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.4.2</version></dependency><!-- 对象池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.9.0</version></dependency></dependencies>

配置文件:

没什么好说的,不懂大家问问ai,很简单

server:port: 1500spring:# redis配置redis:# Redis数据库索引(默认为0)database: 1# Redis服务器地址host: 127.0.0.1# Redis服务器连接端口port: 6379# Redis服务器连接密码(默认为空)password:# 连接超时时间timeout: 2slettuce:pool:# 连接池最大连接数max-active: 200# 连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0

 微信打通初测试:

阅读文档:

接入概述 | 微信开放文档

第一步: 

根据文档的要求,我们第一步需要配置服务器。点击箭头位置进入接口调试工具

 NATAPP-内网穿透 基于ngrok的国内高速内网映射工具

这是我使用的内网穿透工具,大家可以自行选择,因为微信公众号的对接需要443端口,我们需要内网穿透 。进入后购买免费的就可以了

修改你的地址和端口,端口为你服务的端口号 ,并保存你的authtoken

链接:https://pan.baidu.com/s/1Sr13Bea82z4oO_AbpDrKDA?pwd=1111 
提取码:1111

 下载它的客户端,解压后是一个exe文件,我们双击打开,输入命令。authtoken换成你自己的

start natapp.exe -authtoken (authtoken)

 成功会会出现以下界面,我们将Forwarding后的地址复制到测试号的地址中

 如下图所示:

第二步:

阅读文档发现我们需要一个方法接收微信服务器的访问,微信服务器会携带四个参数,我们需要将刚刚定义的token,与其携带的timestamo,nonce进行字典排序,并将其进行sha1加密,将加密后的字符串与signature参数进行比较,如果成功了,就证明这是来自微信的请求

下面我们在controller中实现这个逻辑,sha1加密是一个固定的写法,我这里不去细讲了,大家知道它进行了一次加密就好,在结尾我粘贴出来,文章里我不粘贴工具类了

CallBackController:
package com.yizhiliulianta.wx.controller;import com.yizhiliulianta.wx.utils.SHA1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
public class CallBackController {private static final String token = "yizhiliulianta";@GetMapping("callback")public String callback(@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam("echostr") String echostr) {log.info("get验签请求参数:signature:{},timestamp:{},nonce:{},echostr:{}",signature, timestamp, nonce, echostr);String shaStr = SHA1.getSHA1(token, timestamp, nonce, "");if (signature.equals(shaStr)) {return echostr;}return "unknown";}
}

大家可以阅读一下代码,和刚刚的过程是一样的,接收参数,加密,判断是否合法,最后返回,如果不合法返回unknown。

下面我们用aippost测试一下该方法是否成功:

成功了!我们继续

第三步:

我们将刚刚的方法修改为post请求,新增一个就好,并多添加一个参数为requestBody,因为我们在进行扫码成功时,微信会给我们返回一个请求体,为xml格式,并且我们需要将我们的返回格式也设置为xml格式,而且我们需要将echostr变为不必须的,不然会报错,代码如下:

    @PostMapping(value = "callback", produces = "application/xml;charset=UTF-8")public String callback(@RequestBody String requestBody,@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam(value = "echostr",required = false) String echostr) {log.info("接收到微信的请求:requestBody:{},signature:{},timestamp:{},nonce:{},echostr:{}",requestBody,signature,timestamp,nonce,echostr);return "unknown";}

下面我们测试一下,首先,我们在测试号的网页下,会有一个二维码,这里我就不截图了,当大家配置好网址,token和端口后,我们扫当前的二维码,并且关注,我们在后台会收到如下信息:

 我来分析一下都是什么,大家也可以看文档,都是有介绍的

ToUserName开发者微信号(本次服务的标识)
FromUserName发送方账号(一个OpenID)(也就是说,谁关注了我们)
CreateTime消息创建时间
MsgType消息类型
Event事件类型,subscribe(订阅)、unsubscribe(取消订阅)(证明该用户进行了关注动作)

下面当我们给该公众号发送一则消息,响应的内容如下:

 唯一改变的是消息的类型,变成了text,Event变成了Content,并包含着本次发的内容:你好

 第四步:

当我们接收到用户的消息,我们需要对其进行回复,因为我们接收的格式为xml,所以我们返回也需要xml。这里我们看一下文档,发现需要返回的内容如下:

 

我们需要知道发送方是谁,接收方是谁,因为我们给用户返回,所以发送方变成后台程序,而接收方变成了用户。

我们清楚了这个逻辑后,只需要将用户关注后,微信给我们推送的xml信息解析然后编写一个xml返回就好了

这里我使用一个工具类,还是贴在最后,重要的是逻辑和如何对接,代码如下:

    @PostMapping(value = "callback", produces = "application/xml;charset=UTF-8")public String callback(@RequestBody String requestBody,@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam(value = "echostr",required = false) String echostr) {log.info("接收到微信的请求:requestBody:{},signature:{},timestamp:{},nonce:{},echostr:{}",requestBody,signature,timestamp,nonce,echostr);Map<String, String> msgMap = MessageUtil.parseXml(requestBody);String toUserName = msgMap.get("ToUserName");String fromUserName = msgMap.get("FromUserName");String mag = "<xml>\n" +"  <ToUserName><![CDATA["+fromUserName+"]]></ToUserName>\n" +"  <FromUserName><![CDATA["+toUserName+"]]></FromUserName>\n" +"  <CreateTime>12345678</CreateTime>\n" +"  <MsgType><![CDATA[text]]></MsgType>\n" +"  <Content><![CDATA[你好,我是一支榴莲挞]]></Content>\n" +"</xml>\n";return mag;}

我们再来测试一下,给公众号发送一个消息,如图:

成功!到这里对接就已经成功了

思考: 

我们到这里打通了微信的对接,我们对接后需要什么?

一:用户关注后,我们需要给其反馈

二:我们需要获取用户的唯一标识作为用户名

三:我们需要根据用户不同的动作和发的信息做出不同的回应

我们目前实现了一,二也很简单,下面我们需要实现三,我的想法是用户发送验证码,我们给其一个随机的4位数,并将用户的唯一标识与验证码放入到redis里,然后用户进入auth服务,实现登录,需要输入刚刚我们发送的验证码,接着,我们在redis里根据用户发送的验证码进行查询,如果查询到了,登录成功,否则登录失败,这样我们就实现了整个登录的实现。

微信打通功能优化与完善:

本次我们使用工厂模式来进行解耦:

因为用户至少有两个动作,一个是关注的动作,一个是发送消息的动作。

枚举:

首先我们定义动作的枚举:

一个是用户关注事件,一个是消息事件

package com.yizhiliulianta.wx.handler;public enum WxChatMsgTypeEnum {SUBSCRIPE("event.subscribe","用户关注事件"),TEXT_MSG("text","接收用户文本消息");private String msgType;private String desc;WxChatMsgTypeEnum(String msgType, String desc) {this.msgType = msgType;this.desc = desc;}public static WxChatMsgTypeEnum getByMsgType(String msgType){for (WxChatMsgTypeEnum wxChatMsgTypeEnum : WxChatMsgTypeEnum.values()){if (wxChatMsgTypeEnum.msgType.equals(msgType)){return wxChatMsgTypeEnum;}}return null;}
}

 工厂设计:

接口规范:

首先我们需要一个接口规范,告诉每个实现类,需要执行什么方法

一:我需要知道你是干什么的,哪个实现类

二:处理微信的请求

代码如下:

package com.yizhiliulianta.wx.handler;import java.util.Map;public interface WxChatMsgHandler {WxChatMsgTypeEnum getMsgType();String dealMsg(Map<String, String> messageMap);}

工厂管理: 

package com.yizhiliulianta.wx.handler;import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Component
public class WxChatMsgFactory implements InitializingBean {@Resourceprivate List<WxChatMsgHandler> wxChatMsgHandlerList;private final Map<WxChatMsgTypeEnum,WxChatMsgHandler> handlerMap = new HashMap<>();public WxChatMsgHandler getHandlerByMsgType(String msgType){WxChatMsgTypeEnum msgTypeEnum = WxChatMsgTypeEnum.getByMsgType(msgType);return handlerMap.get(msgTypeEnum);}@Overridepublic void afterPropertiesSet() {for (WxChatMsgHandler wxChatMsgHandler : wxChatMsgHandlerList) {handlerMap.put(wxChatMsgHandler.getMsgType(),wxChatMsgHandler);}}
}

 首先我们需要一个工厂管理类,通过这个类来获取和管理每个实现接口的实现类。

首先我们通过Spring的自动注入,列出实现接口的所有类,并注入到一个集合中

我们应该怎么通过对应的枚举,来获取到对应的实现类呢,这里我使用实现InitializingBean接口,重写afterPropertiesSet方法,让我们在bean初始化后,进行处理的一个方法。

首先我们构建一个map集合,键为枚举值,值为实现类

在afterPropertiesSet方法中,循环所有实现类,并将实现类的枚举值,和实现类放入map集合,这样我们就可以通过枚举来获取对应的实现类了,那么getHandlerByMsgType也就理解了,就是通过传入的枚举值获取对应的枚举,并在map表中取到对应的实现类。

关注实现:

package com.yizhiliulianta.wx.handler;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@Slf4j
public class SubscribeMsgHandler implements WxChatMsgHandler{@Overridepublic WxChatMsgTypeEnum getMsgType() {return WxChatMsgTypeEnum.SUBSCRIPE;}@Overridepublic String dealMsg(Map<String, String> messageMap) {log.info("触发用户关注事件");String fromUserName = messageMap.get("FromUserName");String toUserName = messageMap.get("ToUserName");String subscribeContent = "谢谢您的关注,我是一支榴莲挞!";String replyContent = "<xml>\n" +"  <ToUserName><![CDATA["+fromUserName+"]]></ToUserName>\n" +"  <FromUserName><![CDATA["+toUserName+"]]></FromUserName>\n" +"  <CreateTime>12345678</CreateTime>\n" +"  <MsgType><![CDATA[text]]></MsgType>\n" +"  <Content><![CDATA["+subscribeContent+"]]></Content>\n" +"</xml>";return replyContent;}
}

这个代码应该不难理解,实现刚刚的接口,返回对应的枚举和实现处理请求。

处理请求的方式和刚刚的是一样的,不再去详细说了,相信大家可以看懂

验证码实现:

package com.yizhiliulianta.wx.handler;import com.yizhiliulianta.wx.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;@Component
@Slf4j
public class ReceiveTextMsgHandler implements WxChatMsgHandler {private static final String KEY_WORD = "验证码";private static final String LOGIN_PREFIX = "loginCode";@Resourceprivate RedisUtil redisUtil;@Overridepublic WxChatMsgTypeEnum getMsgType() {return WxChatMsgTypeEnum.TEXT_MSG;}@Overridepublic String dealMsg(Map<String, String> messageMap) {log.info("接收到文本消息事件");String content = messageMap.get("Content");if (!KEY_WORD.equals(content)) {return "";}//发送方 --》 接收方String fromUserName = messageMap.get("FromUserName");//接收方 --》 发送方String toUserName = messageMap.get("ToUserName");//随机四位数验证码Random r = new Random();int one = r.nextInt(10);  //产生0到9的随机数int two = r.nextInt(10);int three = r.nextInt(10);int four = r.nextInt(10);String num = "" + one + two + three + four;//将4个随机数组成一个字符串//存入redisString numKey = redisUtil.buildKey(LOGIN_PREFIX,num);redisUtil.setNx(numKey, fromUserName, 5L, TimeUnit.MINUTES);//返回内容String numContent = "您当前的验证码是:" + num + "(5分钟内有效)";String replyContent = "<xml>\n" +"  <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +"  <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +"  <CreateTime>12345678</CreateTime>\n" +"  <MsgType><![CDATA[text]]></MsgType>\n" +"  <Content><![CDATA[" + numContent + "]]></Content>\n" +"</xml>";return replyContent;}
}

这部分代码就是实现了给用户返回一个四位数的验证码,并将用户的id和验证码存储到redis的一个操作,首先取出content也就是用户发送的信息,如果是验证码这个信息就继续执行,然后生成一个4位随机数

定义redis的主键,与随机数进行构建一个放入redis的键,然后将用户唯一标识作为值放入redis,然后将处理的内容返回 

controller使用:

那该如何在controller中使用呢,重写后的代码如下:

    @PostMapping(value = "callback", produces = "application/xml;charset=UTF-8")public String callback(@RequestBody String requestBody,@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam(value = "echostr",required = false) String echostr) {log.info("接收到微信的请求:requestBody:{},signature:{},timestamp:{},nonce:{},echostr:{}",requestBody,signature,timestamp,nonce,echostr);Map<String, String> msgMap = MessageUtil.parseXml(requestBody);String msgType = msgMap.get("MsgType");String event = msgMap.get("Event");log.info("msgType:{},event:{}",msgType,event);StringBuilder sb = new StringBuilder();sb.append(msgType);if (event!=null){sb.append(".");sb.append(event);}String msgTypeKey = sb.toString();WxChatMsgHandler wxChatMsgHandler = wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);if (Objects.isNull(wxChatMsgHandler)){return "unknown";}String replyContent = wxChatMsgHandler.dealMsg(msgMap);log.info("replyContent:{}",replyContent);return replyContent;}

前面没有什么变化,但是我们需要先将微信的xml解析,通过分析msgtype来判断是什么动作,这里我使用了一个字符串的拼接,因为如果是信息而不是关注动作,那么event是为空的,所以经过判断我们就可以直接将msgTypeKey传入,获取到对应的实现类,然后调用其dealMsg,也就是处理方法,获取返回值,最后将其返回。

测试:

下面我们来测试一下整个流程

重启项目后,我们给公众号发送,验证码。

 成功接收!然后我们去redis里看看

 成功啦!到此为止,我们的微信服务彻底打通,并且与redis进行了集成

登录实现:

auth服务:

下面我们进行auth服务的登录验证逻辑,首先我们进行登录需要用户给我们传一个验证码,然后我们判断成功后,返回给其一个token,代码如下:

auth的controller层

    // 会话登录接口@RequestMapping("doLogin")public Result<SaTokenInfo> doLogin(@RequestParam("validCode") String validCode) {try {Preconditions.checkArgument(!StringUtils.isBlank(validCode),"验证码不能为空");SaTokenInfo tokenInfo = authUserDomainService.doLogin(validCode);return Result.ok(tokenInfo);}catch (Exception e){log.error("UserController.doLogin.error:{}", e.getMessage(), e);return Result.fail("用户登录失败");}}

auth的domain层:

下面就是执行doLogin的逻辑实现,代码如下:

    @Overridepublic SaTokenInfo doLogin(String validCode) {String loginKey = redisUtil.buildKey(LOGIN_PREFIX, validCode);String openId = redisUtil.get(loginKey);if (StringUtils.isBlank(openId)){return null;}AuthUserBO authUserBO = new AuthUserBO();authUserBO.setUserName(openId);this.register(authUserBO);StpUtil.login(openId);SaTokenInfo tokenInfo = StpUtil.getTokenInfo();return tokenInfo;}

大家记得把LOGIN_PREFIX这个redis的键复制过来。

首先还是先把redis里的主键构建处理,然后通过封装好的工具类,获取该键的值,也就是用户的唯一标识,我们新建一个authUserBO,将唯一标识作为用户名放入,并调用register也就是注册方法,将其插入数据库中

然后使用satoken的login方法,进行登录,并获取其对应的token进行返回。

这里我对register注册方法进行完善:代码如下

@Overridepublic Boolean register(AuthUserBO authUserBO) {AuthUser existAuthUser = new AuthUser();existAuthUser.setUserName(authUserBO.getUserName());//校验用户是否存在List<AuthUser> existUser = authUserService.queryByCondition(existAuthUser);if (existUser.size() > 0){return true;}AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToAuth(authUserBO);if (StringUtils.isNotBlank(authUser.getPassword())){authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(),salt));}//插入用户AuthUser user = authUserService.insert(authUser);AuthPermission authPermission = new AuthPermission();//获取对应权限信息authPermission.setPermissionKey(AuthConstant.PERMISSION_KEY);AuthPermission permissionResult = authPermissionService.queryByCondition(authPermission);//用户与权限做关联Long userId = user.getId();Long permissionResultId = permissionResult.getId();AuthUserPermission authUserPermission = new AuthUserPermission();authUserPermission.setUserId(userId);authUserPermission.setPermissionId(permissionResultId);authUserPermissionService.insert(authUserPermission);//放入redisString buildKey = redisUtil.buildKey(userPermissionPrefix, authUser.getUserName());List<AuthPermission> authPermissionList = new LinkedList<>();authPermissionList.add(permissionResult);redisUtil.set(buildKey,new Gson().toJson(authPermissionList));return true;}

就是在开头新增两个判断,首先在数据库查询有没有该用户,如果有的话直接返回,如果没有再进行注册,并对密码进行判空处理,因为我们使用的是微信登录,所以是没有密码的,不然会出现空指针问题

测试:

那么到这里,整个登录的逻辑完成了,下面让我们测试一下,重启服务并请出apipost

看一眼nacos,ok,都正常

获取一个验证码 

 这里还是通过网关来进行分配登录,将验证码作为参数传入,成功了!我们可以看到下方成功返回了token

 看一下redis 

 

成功!

这里有一个点需要注意,我们的redis配置是不同的索引,所以我们需要将wx服务的redis配置文件 和auth服务的redis配置文件索引相同,不然是无法获取的

 

到这里我们的登录服务彻底结束了,下一篇是微服务直接的调用,我们该如何在其他服务里知道,本次登录的用户是谁呢?这需要使用feign了

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

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

相关文章

玄机平台应急响应—MySQL应急

前言 这个是比较简单的&#xff0c;其实和MySQL没啥太大的关系&#xff0c;没涉及太多MySQL的知识。看一下它的flag要求吧。 flag1 它说黑客写入的shell&#xff0c;那我们就去它的网站目录去看看&#xff0c;果然有一个叫sh.php的文件。 flag1{ccfda79e-7aa1-4275-bc26-a61…

DeepDriving | CUDA编程-05:流和事件

本文来源公众号“DeepDriving”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;CUDA编程-05&#xff1a;流和事件 1 CUDA流 在CUDA中有两个级别的并发&#xff1a;内核级并发和网格级并发。前面的文章DeepDriving | CUDA编程-04&…

ESP32 BLE学习(0) — 基础架构

前言 &#xff08;1&#xff09;学习本文之前&#xff0c;需要先了解一下蓝牙的基本概念&#xff1a;BLE学习笔记&#xff08;0.0&#xff09; —— 基础概念&#xff08;0&#xff09; &#xff08;2&#xff09; 学习一款芯片的蓝牙肯定需要先简单了解一下该芯片的体系结构&a…

园区地图导航系统:技术原理、部署方案与智能化应用解析

随着智能化时代的到来&#xff0c;园区管理面临诸多挑战。维小帮园区地图导航系统&#xff0c;采用前沿技术&#xff0c;为园区提供全面的导航解决方案&#xff0c;极大提升了园区管理效率和用户体验。 一、园区地图导航系统的功能特点 维小帮园区地图导航系统&#xff0c;以其…

MongoDB 多层级查询

多层级查询 注意&#xff1a;要注意代码顺序 查询层级数据代码放前面&#xff0c;查询条件放后面 if (StringUtils.isBlank(params.getDocType())) {params.setDocType(DOC_TDCTYPE);}String docName mapper.findByDocInfo(params.getDocType());List<ExpertApprovalOpin…

智能化状态管理:自动状态流转处理模块

目录 基本背景介绍 具体实现 基本数据准备 基本数据表 状态转换常量 状态转换注解 任务处理模版 各任务实现逻辑 开启比对任务进行处理 降噪字段处理任务处理 开启业务数据比对处理 业务数据比对处理 开始核对数据生成最终报告处理 核对数据生成最终报告处理 状…

AI项目二十二:行人属性识别

若该文为原创文章&#xff0c;转载请注明原文出处。 分享一个行人属性分析系统&#xff0c;识别行人&#xff0c;并标记每个人的属性。 项目代码来自公众号渡码的项目。 本人用Win10复现完整项目&#xff0c;并记录过程。 源码会上传到github,可以自行下载测试。 Yinyifen…

Flutter 简化CustomPainter的绘制

文章目录 前言一、为何简化&#xff1f;1、通常做法&#xff08;1&#xff09;、绘制形状1&#xff08;2&#xff09;、绘制形状2&#xff08;3&#xff09;、界面显示 2、简化 二、完整代码三、使用示例1、绘制图形2、动态触发绘制 总结 前言 使用Flutter做界面时&#xff0c…

Linux DMA-Buf驱动框架

一、DMABUF 框架 dmabuf 是一个驱动间共享buf 的机制&#xff0c;他的简单使用场景如下&#xff1a; 用户从DRM&#xff08;显示驱动&#xff09;申请一个dmabuf&#xff0c;把dmabuf 设置给GPU驱动&#xff0c;并启动GPU将数据输出到dmabuf&#xff0c;GPU输出完成后&#xf…

大数据实训项目(小麦种子)-02、实训项目整体功能介绍与演示

文章目录 前言界面及功能描述实现功能描述技术选型界面展示首页界面功能1&#xff1a;HDFS&#xff0c;选择文件上传文件详细步骤 功能2&#xff1a;MapReduce预处理数据功能3&#xff1a;Hbase存储小麦种子数据并查询前10条记录功能4&#xff1a;Hive分析原始csv文件数据并ech…

RTA_OS基础功能讲解 2.9-警报器

RTA_OS基础功能讲解 2.9-警报器 文章目录 RTA_OS基础功能讲解 2.9-警报器一、警报器简介二、警报器配置2.1 激活一个任务2.2 设置一个事件2.3 执行回调函数2.4 递增一个(软件)计数器三、警报器设置3.1 绝对警报3.1.1 单次触发3.1.2 周期触发3.1.3 在过去设置警报3.1.4 将绝对…

swift微调牧歌数据电商多模态大语言模型

大规模中文多模态评测基准MUGE_数据集-阿里云天池多模态理解和生成评估挑战榜(MUGE)是由阿里巴巴达摩院智能计算实验室发起,由阿里云天池平台承办,并由浙江大学、清华大学等单位共同协办。 Mhttps://tianchi.aliyun.com/dataset/107332微调的是牧歌数据集,结果都不好,记录…

中望CAD 2025 (ZW3D2025) 简体中文修改版

名称&#xff1a;中望CAD 2025 (ZW3D2025) 简体中文修改版 描述&#xff1a;一款三维CAD设计工具&#xff0c;运行破解补丁ZW3D2025-2024-Patch执行修补。 链接&#xff1a;夸克网盘分享 &#x1f4c1; 大小&#xff1a;3.2GB &#x1f3f7; 标签&#xff1a;#PC软件 #CAD #设…

支付宝 沙盒demo使用

简介&#xff1a;支付宝沙箱环境是一个为开发者提供的模拟测试环境&#xff0c;用于在应用上线前进行接口功能开发和联调。在这个环境中&#xff0c;开发者可以模拟开放接口&#xff0c;进行开发调试工作&#xff0c;以确保应用上线后能顺利运行。 1. 配置沙盒 1. 1 沙箱控制…

【odoo15】前端自定义模态弹窗

概要 在odoo15或者在15之前&#xff0c;odoo前端的owl框架还没完全替换当前前端框架的时候&#xff0c;我们很多时候都是用js或者jq来直接操作dom&#xff0c;那么我们如果需要在前端用到一个模态弹窗&#xff0c;可以怎么解决呢&#xff1f; 方法1 直接用js原生的模态弹窗&am…

Oracle的这些BUG你要遇到,说明你是一个DBA老鸟...

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯及Greenplum备份恢复&#xff0c; 安装迁移&#xff0c;性能优化、故障…

【LVGL】Guider 界面分析

文章目录 前言架构创建 UI切换界面空间释放分析创建页面空间变化 前言 分析Gui Guider-1.7.2-GA 生成的 LVGL 界面切换&#xff0c;资源管理等处理 架构 所有控件存放于同一个结构体 lv_ui 内&#xff0c;每个页面都至少包含 screen_xxx 和 screen_xxx_del 两个成员 typede…

用HAL库改写江科大的stm32入门-7-1 ADC

实验目的:了解ADC基本概念 电路图&#xff1a; ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器&#xff0c;它可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁。 实验效果&#xff1a; &#xff0…

【html】学会这一套布局,让你的网页更加

很多小伙伴们在刚刚开始学习网页设计的时候不知道怎么布局今天给大家介绍一种非常实用且更加专业的一种布局。 灵感来源&#xff1a; 小米官网 布局图; 实例效果图&#xff1a; 这是一个简单的HTML模板&#xff0c;包括头部、内容区域和底部。 头部部分包括一个分为左右两部分…

【代码随想录】【算法训练营】【第39天】 [62]不同路径 [63]不同路径II [343]整数拆分 [96]不同的二叉搜索树

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 39&#xff0c;周六&#xff0c;坚持不住了~ 题目详情 [62] 不同路径 题目描述 62 不同路径 解题思路 前提&#xff1a;每次只能向下或者向右移动一步 思路&#xff1a;动态规划&#xff0…