ruoyi框架使用自定义用户表登录
背景
有的时候我们做框架升级或改造的时候,需要用到原来的部分表,比如只是用ruoyi的框架,然后登录的用户逻辑还是想用自己的表,那么接下来这边文章将介绍修改逻辑。
修改教程
1、SysLoginController.java
大家找到这个login方法,这是controller的入口。
/*** 登录方法* * @param loginBody 登录信息* @return 结果*/@PostMapping("/login")public AjaxResult login(@RequestBody LoginBody loginBody){// 退出接口查看LogoutSuccessHandlerImpl.onLogoutSuccessAjaxResult ajax = AjaxResult.success();// 生成令牌,修改这个map返回自己的表的用户对象。// 如果这里不处理其余的逻辑就不需要修改,因为我这里要手动写入自己的日志Map<String, Object> map = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),loginBody.getUuid());LoginUser loginUser = (LoginUser)map.get("loginUser");loginUser.setLanguage(loginBody.getLanguage());bsOperateLogV2Service.createLoginLog(String.valueOf(loginUser.getUserId()),loginUser.getUsername(),1,String.valueOf(loginUser.getDeptId()));ajax.put(Constants.TOKEN, map.get("token"));return ajax;}
2、SysLoginService.java
这里有一些验证,比如验证码,用户是否存在等,大家点击进入到loginPreCheck方法中,把查询用户修改为自己的表查询,然后把没有用的逻辑注释掉。
/*** 登录验证* * @param username 用户名* @param password 密码* @param code 验证码* @param uuid 唯一标识* @return 结果*/public Map<String, Object> login(String username, String password, String code, String uuid){// 验证码校验validateCaptcha(username, code, uuid);// 登录前置校验loginPreCheck(username, password);// 用户验证Authentication authentication = null;try{UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(authenticationToken);}catch (Exception e){if (e instanceof BadCredentialsException){//AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}else{//AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));throw new ServiceException(e.getMessage());}}finally{AuthenticationContextHolder.clearContext();}//AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));LoginUser loginUser = (LoginUser) authentication.getPrincipal();//recordLoginInfo(loginUser.getUserId());Map<String, Object> map = new HashMap<>();map.put("token",tokenService.createToken(loginUser));map.put("loginUser",loginUser);// 生成tokenreturn map;}/*** 登录前置校验* @param username 用户名* @param password 用户密码*/public void loginPreCheck(String username, String password){// 用户名或密码为空 错误if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));throw new UserNotExistsException();}// 密码如果不在指定范围内 错误
// if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
// || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
// {
// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
// throw new UserPasswordNotMatchException();
// }
// // 用户名不在指定范围内 错误
// if (username.length() < UserConstants.USERNAME_MIN_LENGTH
// || username.length() > UserConstants.USERNAME_MAX_LENGTH)
// {
// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
// throw new UserPasswordNotMatchException();
// }
// // IP黑名单校验
// String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
// if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
// {
// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
// throw new BlackListException();
// }}
3、loadUserByUsername方法
大家找到UserDetailsServiceImpl.loadUserByUsername方法,这是核心。
@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
// 这是自己的页面输入的密码,没有加密,比如123456String inputPassword = usernamePasswordAuthenticationToken.getCredentials().toString();// 这里是自定义的表查询,用户和密码验证,如果还有其他的验证都可以在这里进行添加LambdaQueryWrapper wrapper = new LambdaQueryWrapper<BladeUserV2DO>().eq(BladeUserV2DO::getAccount,username)//PasswordUtil是自己的加密工具,你用什么加密就保持原来的就行.eq(BladeUserV2DO::getPassword, PasswordUtil.encrypt(inputPassword)).eq(BladeUserV2DO::getIsDeleted,0);// 这里写自己的登录验证逻辑BladeUserV2DO bladeUserV2DO = bladeUserV2Mapper.selectOne(wrapper);
// SysUser user = userService.selectUserByUserName(username);if (StringUtils.isNull(bladeUserV2DO)){log.info("登录用户:{} 不存在.", username);throw new UserPasswordNotMatchException();}BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();// 这里一定要把加密后的密码放进去,后面的验证中会一直用这个密码// 这个是123456加密后的密码,这里给框架都是完全通过的密码,// 因为我们在方法开始的时候自己进行密码验证,能走到这里都是验证通过的bladeUserV2DO.setPassword(passwordEncoder.encode(inputPassword));passwordService.validate(bladeUserV2DO);return createLoginUser(bladeUserV2DO);}public UserDetails createLoginUser(BladeUserV2DO bladeUserV2DO){BladeUserRespVo bladeUserRespVo = bladeUserV2Mapper.getBladeUserInfo(bladeUserV2DO.getId());SysUser user = new SysUser();user.setUserId(bladeUserV2DO.getId());user.setDeptId(0L);if(StringUtils.isNotEmpty(bladeUserV2DO.getDeptId())){user.setDeptId(Long.parseLong(bladeUserV2DO.getDeptId()));}user.setEmail(bladeUserV2DO.getEmail());user.setDelFlag("0");user.setNickName(bladeUserV2DO.getRealName());user.setUserName(bladeUserV2DO.getAccount());user.setPhonenumber(bladeUserV2DO.getPhone());user.setCreateTime(bladeUserV2DO.getCreateTime());user.setRoleId(0L);// 必须复制加密后的密码,后续框架一直在验证// 上面已经把123456对应的加密字符串保存进来,这里直接复制进去// 后续的框架一直在验证user.setPassword(bladeUserV2DO.getPassword());if(StringUtils.isNotEmpty(bladeUserV2DO.getRoleId())){user.setRoleId(Long.parseLong(bladeUserV2DO.getRoleId()));}user.setSex(bladeUserV2DO.getSex()+"");// LoginUser直接修改代码,把自己需要添加的字段都写进去,生成对应的set和get方法就行return new LoginUser(user.getUserId(), user.getDeptId(),user, permissionService.getMenuPermission(user),bladeUserRespVo.getIsSuperAdmin(),bladeUserRespVo.getIsAdmin(),bladeUserV2DO.getAccount());}public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions,int isSuperAdmin,int isAdmin, String account){this.userId = userId;this.deptId = deptId;this.user = user;this.permissions = permissions;//增加自己的字段this.isSuperAdmin = isSuperAdmin;this.isAdmin = isAdmin;this.account = account;}
4、SysPasswordService.java
这里的validate方法中,把没有用的逻辑注释掉。
/*** 登录账户密码错误次数缓存键名* * @param username 用户名* @return 缓存键key*/private String getCacheKey(String username){return CacheConstants.PWD_ERR_CNT_KEY + username;}public void validate(BladeUserV2DO bladeUserV2DO){Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();String username = usernamePasswordAuthenticationToken.getName();String password = usernamePasswordAuthenticationToken.getCredentials().toString();
// Integer retryCount = redisCache.getCacheObject(getCacheKey(username));
//
// if (retryCount == null)
// {
// retryCount = 0;
// }
//
// if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
// {
// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
// MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)));
// throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
// }if (!matches(bladeUserV2DO, password)){
// retryCount = retryCount + 1;
// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
// MessageUtils.message("user.password.retry.limit.count", retryCount)));
// redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);throw new UserPasswordNotMatchException();}else{clearLoginRecordCache(username);}}
5、PasswordUtil.java
如果你用的md5加密就不需要我的这个工具类
package com.ruoyi.common.utils;import jodd.util.StringUtil;
import org.apache.commons.codec.Charsets;
import org.springframework.lang.Nullable;
import org.springframework.util.DigestUtils;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class PasswordUtil extends DigestUtils {private static final char[] HEX_CODE = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};public PasswordUtil() {}public static String md5Hex(final String data) {return DigestUtils.md5DigestAsHex(data.getBytes(Charsets.UTF_8));}public static String md5Hex(final byte[] bytes) {return DigestUtils.md5DigestAsHex(bytes);}public static String sha1Hex(String data) {return sha1Hex(data.getBytes(Charsets.UTF_8));}public static String sha1Hex(final byte[] bytes) {return digestHex("SHA-1", bytes);}public static String sha224Hex(String data) {return sha224Hex(data.getBytes(Charsets.UTF_8));}public static String sha224Hex(final byte[] bytes) {return digestHex("SHA-224", bytes);}public static String sha256Hex(String data) {return sha256Hex(data.getBytes(Charsets.UTF_8));}public static String sha256Hex(final byte[] bytes) {return digestHex("SHA-256", bytes);}public static String sha384Hex(String data) {return sha384Hex(data.getBytes(Charsets.UTF_8));}public static String sha384Hex(final byte[] bytes) {return digestHex("SHA-384", bytes);}public static String sha512Hex(String data) {return sha512Hex(data.getBytes(Charsets.UTF_8));}public static String sha512Hex(final byte[] bytes) {return digestHex("SHA-512", bytes);}public static String digestHex(String algorithm, byte[] bytes) {try {MessageDigest md = MessageDigest.getInstance(algorithm);return encodeHex(md.digest(bytes));} catch (NoSuchAlgorithmException var3) {var3.printStackTrace();}return null;}public static String hmacMd5Hex(String data, String key) {return hmacMd5Hex(data.getBytes(Charsets.UTF_8), key);}public static String hmacMd5Hex(final byte[] bytes, String key) {return digestHMacHex("HmacMD5", bytes, key);}public static String hmacSha1Hex(String data, String key) {return hmacSha1Hex(data.getBytes(Charsets.UTF_8), key);}public static String hmacSha1Hex(final byte[] bytes, String key) {return digestHMacHex("HmacSHA1", bytes, key);}public static String hmacSha224Hex(String data, String key) {return hmacSha224Hex(data.getBytes(Charsets.UTF_8), key);}public static String hmacSha224Hex(final byte[] bytes, String key) {return digestHMacHex("HmacSHA224", bytes, key);}public static byte[] hmacSha256(String data, String key) {return hmacSha256(data.getBytes(Charsets.UTF_8), key);}public static byte[] hmacSha256(final byte[] bytes, String key) {return digestHMac("HmacSHA256", bytes, key);}public static String hmacSha256Hex(String data, String key) {return hmacSha256Hex(data.getBytes(Charsets.UTF_8), key);}public static String hmacSha256Hex(final byte[] bytes, String key) {return digestHMacHex("HmacSHA256", bytes, key);}public static String hmacSha384Hex(String data, String key) {return hmacSha384Hex(data.getBytes(Charsets.UTF_8), key);}public static String hmacSha384Hex(final byte[] bytes, String key) {return digestHMacHex("HmacSHA384", bytes, key);}public static String hmacSha512Hex(String data, String key) {return hmacSha512Hex(data.getBytes(Charsets.UTF_8), key);}public static String hmacSha512Hex(final byte[] bytes, String key) {return digestHMacHex("HmacSHA512", bytes, key);}public static String digestHMacHex(String algorithm, final byte[] bytes, String key) {SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm);try {Mac mac = Mac.getInstance(secretKey.getAlgorithm());mac.init(secretKey);return encodeHex(mac.doFinal(bytes));} catch (InvalidKeyException | NoSuchAlgorithmException var5) {var5.printStackTrace();}return null;}public static byte[] digestHMac(String algorithm, final byte[] bytes, String key) {SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm);try {Mac mac = Mac.getInstance(secretKey.getAlgorithm());mac.init(secretKey);return mac.doFinal(bytes);} catch (InvalidKeyException | NoSuchAlgorithmException var5) {var5.printStackTrace();}return null;}public static String encodeHex(byte[] bytes) {StringBuilder r = new StringBuilder(bytes.length * 2);byte[] var2 = bytes;int var3 = bytes.length;for(int var4 = 0; var4 < var3; ++var4) {byte b = var2[var4];r.append(HEX_CODE[b >> 4 & 15]);r.append(HEX_CODE[b & 15]);}return r.toString();}public static boolean slowEquals(@Nullable String a, @Nullable String b) {return a != null && b != null ? slowEquals(a.getBytes(Charsets.UTF_8), b.getBytes(Charsets.UTF_8)) : false;}public static boolean slowEquals(@Nullable byte[] a, @Nullable byte[] b) {if (a != null && b != null) {if (a.length != b.length) {return false;} else {int diff = a.length ^ b.length;for(int i = 0; i < a.length; ++i) {diff |= a[i] ^ b[i];}return diff == 0;}} else {return false;}}public static String hex(String data) {return StringUtil.isBlank(data) ? "" : sha1Hex(data);}public static String encrypt(String data) {return StringUtil.isBlank(data) ? "" : sha1Hex(md5Hex(data));}
}
6、完结
相关文章:
ruoyi框架使用自定义用户表登录
背景 有的时候我们做框架升级或改造的时候,需要用到原来的部分表,比如只是用ruoyi的框架,然后登录的用户逻辑还是想用自己的表,那么接下来这边文章将介绍修改逻辑。 修改教程 1、SysLoginController.java 大家找到这个login方…...
计算机视觉与深度学习-卷积神经网络-卷积图像去噪边缘提取-卷积-[北邮鲁鹏]
目录标题 参考学习链接卷积的定义卷积的性质叠加性平移不变性交换律结合律分配律标量 边界填充边界填充方法 - 常数填充最常用常数填充零填充(zero padding)拉伸镜像 卷积示例单位脉冲核无变化平移平滑锐化 卷积核平均卷积核高斯卷积核高斯卷积核定义高斯…...
JS手动实现发布者-订阅者模式
发布-订阅模式是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。具体过程是:订阅者把自己想订阅的事件注册到调度中心,当发布者更新该事件时通知调度中心,由调度中…...
【含面试题】MySQL死锁日志分析与解决的Java代码实现
AI绘画关于SD,MJ,GPT,SDXL百科全书 面试题分享点我直达 2023Python面试题 2023最新面试合集链接 2023大厂面试题PDF 面试题PDF版本 java、python面试题 项目实战:AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI…...
解决方案:TSINGSEE青犀+智能分析网关助力智慧仓储智能化监管
为全面保障物流仓储的安全性与完整性,解决仓库管理难题,优化物流仓储方式,提升仓储效率,降低人工成本,旭帆科技推出智慧仓储AI视频智能分析方案,利用物联网、大数据、云计算等技术,对仓储管理进…...
进程间通信
#include <unistd.h> int pipe(int pipefd[2]); 功能:创建一个匿名管道,用于进程间通信 参数: -int pipefd[2]:传出参数 pipefd[0]对应的是管道的读端 pipefd[0]对应的是管道的写端 返回值: 成功返回0,失败返回-…...
Ubuntu 22.04.3 LTS安装
最近换电脑了,准备重新装一下ubuntu。多年前装过ubuntu很老的版本,现在发现官网最新的LTS版本是 Ubuntu 22.04.3 LTS 版本。那重新装的话,肯定装最新的版本了。这里我记录下自己的安装过程,作为以后的笔记查看。 我的环境&#x…...
记一次manjaro-i3系统sogoupinying候选词无法正常显示中文(变方框了)问题解决方案
记一次manjaro-i3系统sogoupinying候选词无法正常显示中文(变方框了)问题解决方案 前言解决方案 前言 今天早上发现公司电脑显卡驱动好像坏了,各种折腾完了干脆把系统搞黑屏无法开机了,时间有限懒再修了,于是重装了系…...
Lua学习笔记:词法分析
前言 本篇在讲什么 Lua的词法分析 本篇需要什么 对Lua语法有简单认知 对C语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践,轻理论,快速上手 提供全流程的源码内容 ★提高阅读体验★ 👉 ♠ 一级标题…...
flask服务鉴权
基本认证(Basic Authentication): 这是一种简单的鉴权方式,需要客户端发送用户名和密码,服务器验证后允许或拒绝访问。可以使用 Flask-BasicAuth 扩展来实现。首先,安装扩展: pip install Fla…...
【2023华为杯B题】DFT类矩阵的整数分解逼近(思路及代码下载)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
基于微信小程序的校园生活管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言运行环境学生微信端的主要功能有:管理员的主要功能有:具体实现截图视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝1…...
SQL server 创建存储过程
SQL Server如何创建存储过程 存储过程: 可以理解为完成特定功能的一组 SQL 语句集,存储在数据库中,经过第一次编译,之后的运行不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数…...
一文了解亚马逊云科技适用于 Amazon Lightsail 的托管数据库
Amazon Lightsail 是亚马逊云科技提供的一种易上手使用、月度价格经济实惠,并包括了计算实例、容器、存储、数据库的虚拟专用服务器。在创建时可以进行业务蓝图选择,可选择包含多种操作系统(Linux/Windows 等)或操作系统加上典型应…...
【antd Col】奇怪的TypeError: Cannot read properties of undefined (reading ‘then‘)
现象 修改antd的Col组件的layouts属性为span后,并通过监听resize事件对span列宽进行动态变化时,报错TypeError: Cannot read properties of undefined (reading ‘then‘)。 补充示例一 由于我使用了飞冰ice.js,且在以下过程中写了如下语句…...
requests处理 multipart/form-data 请求以及 boundary值问题
requests处理 multipart/form-data 请求以及 boundary值问题 前言1. 请求需要携带本地资源2. 请求需要携带json3. 总结 前言 关于 Content-type: multipart/form-data可以看一下这篇文章, 分析特别详细 HTTP协议之multipart/form-data请求分析 put和post区别不大, 只是上传资…...
FBX文件结构解读【文本格式】
FBX 格式几乎受到所有 3D 引擎的支持,是 Autodesk 开发的 3D 模型的专有格式。它支持顶点、索引、法线、UV坐标、材质和动画。 FBX还支持许多其他类型的信息,但它们对游戏引擎几乎没有用处。 推荐:用 NSDT编辑器 快速搭建可编程3D场景 有两种…...
JS基础语法
JS是一门面向对象的编程语言,运行在客户端的脚本语言,可以基于Node.js进行服务器端编程 JS的作用: 表单动态校验网页特效服务端开发 浏览器执行JS: 浏览器分为两部分:渲染引擎和JS引擎 渲染引擎用来解析HTML和CSS,…...
【Zabbix监控一】zabbix的原理与安装
利用一个优秀的监控软件,我们可以: ●通过一个友好的界面进行浏览整个网站所有的服务器状态 ●可以在 Web 前端方便的查看监控数据 ●可以回溯寻找事故发生时系统的问题和报警情况 总结:zabbix主要功能 监控,cpu负载,内存使用&a…...
图的十字链表存储结构
1.其实就是邻接表和逆邻接表的结合,说明白点,就是用箭头表示出弧头,弧尾,以及他们之间的关系 2.顶点结构 3.弧结构 3.这样根据上面的结点十字链表结构就很好分析了...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
