当前位置: 首页 > news >正文

若依后台管理系统-v3.8.8-登录模块--个人笔记

各位编程爱好者们,你们好!今天让我们来聊聊若依系统在登录模块的一些业务逻辑,以及本人的一些简介和心得,那么废话不多说,让我们现在开始吧。

以下展示的这段代码,正是若依在业务层对应的登录代码(controller层)

/*** 登录方法* * @param loginBody 登录信息* @return 结果*/@PostMapping("/login")public AjaxResult login(@RequestBody LoginBody loginBody){AjaxResult ajax = AjaxResult.success();// 生成令牌String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),loginBody.getUuid());ajax.put(Constants.TOKEN, token);return ajax;}

1、我们可以看到在参数校验方面,貌似是没有进行校验的,那么如果我在填写账号的时候,使用表情包呢,会怎么样呢,哈哈哈,结果会这样。

tips:说明一个道理,大家在编写项目的时候呀,入参的这些地方还是得要加上必要的校验的,亦或者你自己使用AOP的方式来拦截,否则在没有想到可能会抛异常的地方没有对其可能抛出的异常进行处理的话,就会出现这种最原始的SQL异常了。这对我们的代码健壮性是不大好的。

2、AjaxResult ,这个看起来是若依自行封装的一个通用的response响应体。那么他是继承了HashMap的,我们可以很好的获取到本次请求的信息,以及设置相关的信息。其中还提供了链式的方法,可以保障我们以匿名类的方式多次赋值。

3、接下来让我们详细查看一下login方法,以下是主要的源代码(service层)

/*** 登录验证* * @param username 用户名* @param password 密码* @param code 验证码* @param uuid 唯一标识* @return 结果*/public String 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());// 生成tokenreturn tokenService.createToken(loginUser);}

这里就比较有意思了,让我们拆解成为几个部分聊一下。

(1)validateCaptcha 验证码的校验部分,在这里主要是开启的验证码的校验方式,然后通过配置合适的key在redis容器中进行获取,验证通过也就是没有抛异常,那么我们就正常的在redis中进行删除即可。

 /*** 校验验证码* * @param username 用户名* @param code 验证码* @param uuid 唯一标识* @return 结果*/public void validateCaptcha(String username, String code, String uuid){boolean captchaEnabled = configService.selectCaptchaEnabled();if (captchaEnabled){String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");String captcha = redisCache.getCacheObject(verifyKey);if (captcha == null){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));throw new CaptchaExpireException();}redisCache.deleteObject(verifyKey);if (!code.equalsIgnoreCase(captcha)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}}}

(2) loginPreCheck 登录前置校验部分,在登录部分,我们可以看到,这里考虑了蛮多的部分,包括用户密码和账号是均为空,或者密码和账号的长度的问题,以及当前用户登录的Ip是否被禁用等等。但是唯独没有校验用户使用表情包为用户名进行登录,然后没有拦截,在数据库(估计是版本的问题,无法处理emoj的查询),然后出现了上面的那种情况。

/*** 登录前置校验* @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) 现在让我们把目光聚焦在try的代码块中,这一块值得我们的学习和分析。在这里,若依使用了spring security 的方法,让我们一步一步来进行分析:

首先,我们为每一个用户都new了一个对象,用来管理用户的信息

UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);

(4)在这里我们可以发现,spring的源码对于pricipal是设置了final的关键字的,而他现在是Object类型,那么可以知道,他是想要每一个用户都有一个其独立的标识,当然如果用户传递的是一个对象的话,那么我们只需要保证这个对象的引用地址不被修改,当然的,我们可以修改这个对象里面的属性内容。

好的,那我们在来看看为啥他需要进行保存和清除呢?

我们知道,在这里进行保存无非就是为了在这套校验逻辑的内部中进行二次使用,以下是他在另外的一个地方的代码中被使用到。

通俗的来说就是我们在调用 authenticationManager.authenticate()时,  会触发spring security的机制,然后会自动调用到这个方法,下面我将一步步来进行说明。

-----------------------------------------------------------------------------------------------------------------------------

首先,我们选择到Providermanager的类中,因为这里默认是走它的。

塔默认提供了遍历循环获取到合适的provider,然后再进行调用 authenticate()方法进行下一步的调用。

这时我们就会来到了 AbstractUserDetailsAuthenticationProvider这个类中,但是实际上我们是到DaoAuthenticationProvider这个类中的,但是他在这里并没有重写这个方法,看得出来这里只是直接拿父类的来直接使用了。

那么我们就可以很直观的看到了,那就是相当于封住多了一层用来校验用户是否有创建一个类来实现这个方法,就是来判断用户是否有正常的实现UserDatailsService这个接口,如果没有找到实例,那么将会通过断言的方式抛出异常。从而无法进入到DaoAuthenticcationProvider,进行下一步的操作。

好的下面再让我们把目光放到实现了这个UserDatailsService的类中,我们可以看到passwordService中的validate()中就是我们上面提及到的登录校验中的内部获取上下文的逻辑,至此实现完美闭环

4、看到这里,是否我刚刚有一个方法是否是不是一直都没有提及,没有,就是他AsyncManager.me().execute()

这个东西在我提及中出现了不少,下面我们就来认识一下他, 其实他的主要作用是:异步记录用户登录成功的日志信息,从而避免登录过程变得十分缓慢。下面让我们来认识一下这个类吧。

package com.ruoyi.framework.manager;import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.ruoyi.common.utils.Threads;
import com.ruoyi.common.utils.spring.SpringUtils;/*** 异步任务管理器* * @author ruoyi*/
public class AsyncManager
{/*** 操作延迟10毫秒*/private final int OPERATE_DELAY_TIME = 10;/*** 异步操作任务调度线程池*/private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");/*** 单例模式*/private AsyncManager(){}private static AsyncManager me = new AsyncManager();public static AsyncManager me(){return me;}/*** 执行任务* * @param task 任务*/public void execute(TimerTask task){executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);}/*** 停止任务线程池*/public void shutdown(){Threads.shutdownAndAwaitTermination(executor);}
}

(1)在这里使用了SpringUtils.getBean()方法,用来获取名称为“scheduledExecutorService”的实例对象。那么到这里,相信大家就十分好奇了,这个待被注入的对象究竟做了什么呢,让我们来仔细分析一下看看。

好的,我们这里不难发现,若依的作者在这块使用的匿名类的方式来编写代码,我们看到作者在这里使用了 ScheduledThreadPoolExecutor作为实际的返回对象,然后里面的参数分别是初始化的核心线程池大小、以及初始化基础线程工厂,并初始化其未来线程构造的名字和创建守护进程。

在 Java 里,线程分成两种:

  • 用户线程(User Thread):正常的程序工作线程,比如处理请求、运算任务等。

  • 守护线程(Daemon Thread):一种辅助性质的线程,通常用于后台服务,比如监控、垃圾回收(GC 线程就是守护线程)。

  • 关键区别是

  • 所有用户线程都结束时,JVM会自动退出不管还有没有守护线程活着

  • 也就是说:守护线程不会阻止 JVM 退出,用户线程会阻止

所以在这里,我们使用了守护进程来处理异步日志同步的问题。

   第三个是初始化线程池的拒绝策略,也就是说,当线程池满了,在新的任务无法提交时,线程池会让提交任务的线程去执行这个任务,那么这样以后可以降低任务提交速度,防止过载。

   你可能会很好奇就是为啥会降低我们任务的提交速度呢,其实在实际的线程执行过程中,会有一定量的线程,然后主线程是命令和控制其他的属于线程池内的线程去执行对应的指令,那么当其他线程都被占据时,这时能用的线程可能就无了,那么有新的提交任务谁去完成呢,没错,那就是主线程自己干,哪怕我这时整个系统很慢,也不会至于停止的状态。如果你在这里使用了拒绝策略的话,往往可能会导致雪崩式的后果,像那些正常的支付模块,将会可能导致无法避免的后果。

    然后这里还写了一个匿名内部类来重写了afterExecute方法,那么afterExecute实际上是ThreadPoolExecutor的一个回调方法,也就是说每当线程池执行了一个任务后,都会执行这个方法,然后这里就会统一处理异常信息,也就是不管是否抛出异常,都会进行检查一遍。正常来说,如果线程池里的子线程出异常(比如RuntimeException),不会自动抛出来,主线程也收不到。
而这里通过重写 afterExecute,在每个任务执行完后,主动检查有没有异常,然后打印或处理。不然子线程偷偷挂了,你都不知道,排查问题非常痛苦。

    好的,然后让我们再回到AsyncManager这个类中,他提供了私有化的构造器,其意无非就是不想用户自己构建对象,然后这里初始化了静态的实例化对象,并且主要通过me()这个方法进行显示,然后我们拿到对象后就可以调用这个类里面的方法了。

     在 recordLogininfor() 方法内部,由于定义了一个匿名内部类(TimerTask 的实现),因此这里使用 final 修饰参数其实大有来头。我们知道,方法参数本质上是局部变量,一般存放在栈内存中,而内部类的实例(对象)则通常存储在堆内存中。这样就会出现一种情况:主方法 recordLogininfor() 执行完毕,局部变量(参数)随栈帧销毁。但内部类 TimerTask 可能还未执行完毕,甚至是延迟很久才执行。如果内部类要继续访问主方法的局部变量,但局部变量早已经销毁,就会存在悬空引用(dangling reference)的问题。为了避免这种隐患,Java 在编译时要求:传入内部类使用的外部变量**必须是 final 或 "effectively final"(实际上没有被修改过的变量)。这样,Java 编译器可以拷贝一份局部变量的副本到内部类对象中,确保即使外部栈帧销毁,内部类仍然可以安全访问这个值。而且 final 保证这个值不会在内部类执行前被修改,副本是可靠的。当然,即使你不显式写 final,只要你不修改这个变量,Java 编译器也会默认当成 effectively final 来处理,允许内部类引用。所以,这里显式加上 final,是为了提醒开发者注意变量不可变性的重要性,同时也体现了良好的编码规范。

tips:当 Java 编译器拷贝一份局部变量的副本给内部类时,这个副本是存放在内部类对象的堆内存里的,而不是栈。因为内部类(比如 TimerTask)本身就是一个对象实例,对象实例是存储在堆(heap)上的。副本跟着对象的生命周期走,因此也在堆上。栈(stack)是线程私有的,每次方法调用都会有一个栈帧,方法一结束,栈帧就被销毁了。如果副本还放在栈里,当方法退出后,副本也跟着栈帧销毁了,那内部类引用到的副本就成了野指针(dangling pointer),这就是严重的安全问题。把副本存在堆内存中,能保证即使方法退出,内部类对象依然可以正常使用这份副本数据。

    此外这个方法内部还有一个使用了StringBuilder类的这个可以大量避免了直接创建String来进行字符串的拼接,导致大量的浪费不必有的内存空间。

package com.ruoyi.framework.manager.factory;import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService;
import eu.bitwalker.useragentutils.UserAgent;/*** 异步工厂(产生任务用)* * @author ruoyi*/
public class AsyncFactory
{private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");/*** 记录登录信息* * @param username 用户名* @param status 状态* @param message 消息* @param args 列表* @return 任务task*/public static TimerTask recordLogininfor(final String username, final String status, final String message,final Object... args){final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));final String ip = IpUtils.getIpAddr();return new TimerTask(){@Overridepublic void run(){String address = AddressUtils.getRealAddressByIP(ip);StringBuilder s = new StringBuilder();s.append(LogUtils.getBlock(ip));s.append(address);s.append(LogUtils.getBlock(username));s.append(LogUtils.getBlock(status));s.append(LogUtils.getBlock(message));// 打印信息到日志sys_user_logger.info(s.toString(), args);// 获取客户端操作系统String os = userAgent.getOperatingSystem().getName();// 获取客户端浏览器String browser = userAgent.getBrowser().getName();// 封装对象SysLogininfor logininfor = new SysLogininfor();logininfor.setUserName(username);logininfor.setIpaddr(ip);logininfor.setLoginLocation(address);logininfor.setBrowser(browser);logininfor.setOs(os);logininfor.setMsg(message);// 日志状态if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)){logininfor.setStatus(Constants.SUCCESS);}else if (Constants.LOGIN_FAIL.equals(status)){logininfor.setStatus(Constants.FAIL);}// 插入数据SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);}};}/*** 操作日志记录* * @param operLog 操作日志信息* @return 任务task*/public static TimerTask recordOper(final SysOperLog operLog){return new TimerTask(){@Overridepublic void run(){// 远程查询操作地点operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);}};}
}

相关文章:

若依后台管理系统-v3.8.8-登录模块--个人笔记

各位编程爱好者们&#xff0c;你们好&#xff01;今天让我们来聊聊若依系统在登录模块的一些业务逻辑&#xff0c;以及本人的一些简介和心得&#xff0c;那么废话不多说&#xff0c;让我们现在开始吧。 以下展示的这段代码&#xff0c;正是若依在业务层对应的登录代码&#xf…...

Flip PDF Plus Corp7.7.22电子书制作软件

flip pdf plus corporate7.7.22中文版由FlipBuilder官方出品的一款企业级的翻页电子书制作软件&#xff0c;拥有丰富的模板&#xff0c;主题和动画场景&#xff0c;每本书最大页数1000页&#xff0c;每本书的最大大小1GB&#xff0c;即可以帮助企业用户制作好丰富的电子书籍。 …...

公路安全知识竞赛主持稿串词

合 &#xff1a;尊敬的各位领导、各位来宾 、各位选手 &#xff1a; 大家上午 好! 男 &#xff1a;安全就是生命&#xff0c;安全就是效益&#xff0c;安全是一切工作的重中之重&#xff01;安全生产只有满分&#xff0c;没有及格。只有安全生产这个环节不出差错&#xff0c;我…...

vscode 配置qt

工具&#xff1a;vscode、qttools、qtconfigure Search Mode改成基于cmake的。 # 在项目中指定Qt的路径 set(Qt5_DIR "/home/jp/qt-everywhere-src-5.12.9/arm-qt/lib/cmake/Qt5") # 用于指定 Qt5 的安装路径 find_package(Qt5 REQUIRED COMPONENTS Widgets)这样就…...

Node.js 事件循环和线程池任务完整指南​

在 Node.js 的运行体系中&#xff0c;事件循环和线程池是保障其高效异步处理能力的核心组件。事件循环负责调度各类异步任务的执行顺序&#xff0c;而线程池则承担着处理 CPU 密集型及部分特定 I/O 任务的工作。接下来&#xff0c;我们将结合图示&#xff0c;详细剖析两者的工作…...

Java之BigDecimal

BigDecimal 是 Java 中用于高精度计算的类&#xff0c;特别适合需要精确十进制运算的场景&#xff0c;如金融计算、货币运算、概率计算等。 为什么需要 BigDecimal类 解决浮点数精度问题&#xff1a;float 和 double 使用二进制浮点运算&#xff0c;无法精确表示某些十进制小数…...

Qt5与现代OpenGL学习(四)X轴方向旋转60度

把上面两张图像放到D盘1文件夹内&#xff1a; shader.h #ifndef SHADER_H #define SHADER_H#include <QDebug> #include <QOpenGLShader> #include <QOpenGLShaderProgram> #include <QString>class Shader { public:Shader(const QString& verte…...

基于LVS+Keepalived+NFS的高可用负载均衡集群部署

目录 项目功能 2 项目的部署 2.1 部署环境介绍 2.2 项目的拓扑结构 2.3 项目环境调试 2.4 项目的部署 2.4.1 安装软件; 2.4.2 NFS服务器配置 2.4.3 Web节点配置 2.5 项目功能的验证 2.6 项目对应服务使用的日志 项目功能 负载均衡功能 实现原理:基于LVS(D…...

人工智能数学基础(四):线性代数

线性代数是人工智能领域的核心数学工具之一&#xff0c;广泛应用于数据表示、模型训练和算法优化等多个环节。本文将系统梳理线性代数的关键知识点&#xff0c;并结合 Python 实例&#xff0c;助力读者轻松掌握这一重要学科。资源绑定附上完整资源供读者参考学习&#xff01; …...

基于C++的IOT网关和平台1:github项目ctGateway

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…...

LeetCode 2962.统计最大元素出现至少 K 次的子数组:滑动窗口

【LetMeFly】2962.统计最大元素出现至少 K 次的子数组&#xff1a;滑动窗口 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-subarrays-where-max-element-appears-at-least-k-times/ 给你一个整数数组 nums 和一个 正整数 k 。 请你统计有多少满足 「 nums 中…...

Nginx反向代理的负载均衡配置

Nginx 负载均衡详解 在互联网应用中&#xff0c;随着网站访问量的不断攀升&#xff0c;服务器的服务模式也需要进行相应升级。诸如分离数据库服务器、将图片作为单独服务等操作&#xff0c;这些都属于简单的数据负载均衡&#xff0c;其目的是将压力分散到不同机器上。而来自 We…...

案例速成GO+Socket,个人笔记

更多个人笔记&#xff1a;&#xff08;仅供参考&#xff0c;非盈利&#xff09; gitee&#xff1a; https://gitee.com/harryhack/it_note github&#xff1a; https://github.com/ZHLOVEYY/IT_note 文章目录 简单知识了解实现一个TCP 服务器与客户端&#xff08;聊天室&#x…...

篮球足球体育球员综合资讯网站模板

采用帝国CMS7.5新版核心。栏目和内容模板超多变换。后台操作简单&#xff0c;安全可靠&#xff0c;性能稳定。整站浏览效果高端大气&#xff0c;可以帮助你快速建立一个适合自己的软件下载类型的站点&#xff01; 演示地址&#xff1a;https://www.tmuban.com/store/620.html …...

HTTP(超文本传输协议)全面总结

HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是万维网&#xff08;World Wide Web&#xff09;应用中的基础协议&#xff0c;用于客户端与服务器之间的数据传输。随着互联网技术的发展&#xff0c;HTTP协议也经历了多个版本的更新&…...

OpenCV 图形API(72)图像与通道拼接函数-----根据指定的方式翻转图像(GMat)函数 flip()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 翻转一个2D矩阵&#xff0c;围绕垂直轴、水平轴或同时围绕两个轴。 该函数以三种不同的方式之一翻转矩阵&#xff08;行和列的索引是从0开始的&a…...

【报错问题】 macOS 的安全策略(Gatekeeper)阻止了未签名的原生模块(bcrypt_lib.node)加载

这个错误是由于 macOS 的安全策略&#xff08;Gatekeeper&#xff09;阻止了未签名的原生模块&#xff08;bcrypt_lib.node&#xff09;加载 导致的。以下是具体解决方案&#xff1a; 1. 临时允许加载未签名模块&#xff08;推荐先尝试&#xff09; 在终端运行以下命令&#x…...

keep-alive具体使用方法

什么是 Keep-Alive <keep-alive> 是 Vue.js 提供的一个内置组件&#xff0c;用于缓存动态组件实例&#xff0c;从而避免重复渲染已加载过的组件。它的主要功能是在切换组件时保留状态和 DOM 结构&#xff0c;提升性能。 工作原理 <keep-alive> 的核心在于维护一个…...

【C++11】包装器:function与bind

前言&#xff1a; 上文我们学了C11中一个新的表达式&#xff1a;Lambda表达式。Lambda表达式可以在函数内部定义&#xff0c;其本质是仿函数【C11】Lambda表达式-CSDN博客 本文我们来学习C11的下一个新语法&#xff1a;包装器 function function的定义为&#xff1a; templat…...

Educational Codeforces Round 178 div2(题解ABCDE)

A. Three Decks #1.由于最后三个数会相等&#xff0c;提前算出来和&#xff0c;%3判断&#xff0c;再判前两个数是否大于 #include<iostream> #include<vector> #include<stdio.h> #include<map> #include<string> #include<algorithm> #…...

mermaid 序列图 解析

sequenceDiagramparticipant UI as 用户界面participant Executor as 任务执行器participant StateMgr as 状态管理器participant Repo as 数据仓库UI->>Executor: 执行任务3350c74e...Executor->>StateMgr: 更新状态为"measuring"StateMgr->>Repo…...

DTO,VO,PO,Entity

1. DTO (Data Transfer Object) 定义 DTO 是数据传输对象&#xff0c;用于在不同系统或层之间传输数据。 目的 简化数据传输&#xff0c;降低耦合&#xff0c;通常只包含需要传输的字段&#xff0c;避免暴露内部实现细节。 使用场景 Controller 和 Service 或 远程调用 之…...

Proser:重新介绍

回想Proser的定位&#xff1a;一款直观的【协议发送】模拟软件。 现在间断更新下来&#xff0c;基本成了一款通信调试助手类软件 Proser 是一款支持串口与网络的通信调试助手&#xff0c;其独有的协议编辑器、数据检视、标尺等功能&#xff0c;让指令模拟与数据分析更加易用。…...

微信小程序 首页之轮播图和搜索框 代码分享

注意&#xff01;&#xff01;&#xff01; 只有样式&#xff0c;还没功能开发&#xff01;&#xff01;&#xff01; index.wxml <!-- 搜索框 --> <view class"search"><input placeholder"请输入搜索的内容"></input><imag…...

3D可视化编辑器模版

体验地址&#xff1a;http://mute.turntip.cn 整个搭建平台核心模块包含如下几个部分&#xff1a; 3D场景渲染 组件拖拽系统 元素编辑功能 状态管理 历史记录与撤销/重做 技术栈 前端框架与库 React 18 用于构建用户界面的JavaScript库 Next.js 14 React框架&#xff0c;提供服…...

foc控制 - clarke变换和park变换

1. foc控制框图 下图是foc控制框图&#xff0c;本文主要是讲解foc控制中的larke变换和park变换clarke变换将 静止的 a b c abc abc坐标系 变换到 静止的 α β αβ αβ坐标系&#xff0c;本质上还是以 定子 为基准的坐标系park变换 则将 α β αβ αβ坐标系 变换到 随 转…...

DeepSeek: 探索未来的深度学习搜索引擎

深度学习驱动的下一代搜索引擎&#xff1a;DeepSeek 在信息爆炸的时代&#xff0c;搜索引擎作为连接用户与互联网世界的桥梁&#xff0c;其重要性不言而喻。然而&#xff0c;随着用户需求的日益多样化和复杂化&#xff0c;传统搜索引擎在理解和满足用户需求方面逐渐显现出局限…...

如何在本地部署小智服务器:从源码到全模块运行的详细步骤

小智聊天机器人本地后台服务器源码全模块部署 作者&#xff1a;林甲酸 -不是小女子也不是女汉子 是大女子 更新日期&#xff1a;2025年4月29日 &#x1f3af; 前言&#xff1a;为什么要写这篇教程&#xff1f; 上周按照虾哥小智服务器的教程去部署本地后台&#xff0c;我用的是…...

基于论文的大模型应用:基于SmartETL的arXiv论文数据接入与预处理(四)

上一篇介绍了基于SmartETL框架实现arxiv采集处理的基本流程&#xff0c;通过少量的组件定制开发&#xff0c;配合yaml流程配置&#xff0c;实现了复杂的arxiv采集处理。 由于其业务流程复杂&#xff0c;在实际应用中还存在一些不足需要优化。 5. 基于Kafka的任务解耦设计 5.…...

GrapesJS 终极定制组件设计方案:扁平化对象属性编辑、多区域拖拽、多层嵌套与组件扩展实战

掌握 GrapesJS 复杂组件实用技巧&#xff0c;打造高复用、高交互的前端低代码组件体系 随着低代码可视化编辑需求日益提升&#xff0c;GrapesJS 作为优秀开源画布编辑器&#xff0c;灵活的组件机制是其核心优势。但在实际项目中&#xff0c;你可能会遇到&#xff1a; 如何编辑…...

Spring MVC 如何映射 HTTP 请求到 Controller 方法?

我们来详细分析一下如何在 Spring MVC 中将 HTTP 请求映射到 Controller 的处理方法&#xff08;Handler Methods&#xff09;上&#xff0c;以及 RequestMapping 注解的使用方法。 请求映射的核心&#xff1a;RequestMapping 注解 RequestMapping 是 Spring MVC 中最核心、最…...

vue.js中的一些事件修饰符【前端】

不要一直责怪过去的自己&#xff0c;他独自站在雾里也很迷茫。 目录 .stop&#xff1a;.prevent&#xff1a;.self&#xff1a;.once&#xff1a;.capture&#xff1a;.native&#xff1a;何时使用 .native&#xff1f; .stop&#xff1a; 作用&#xff1a;调用 event.stopPro…...

WEBSTORM前端 —— 第2章:CSS —— 第4节:盒子模型

目录 1.画盒子 2.Pxcook软件 3.盒子模型——组成 4.盒子模型 ——边框线 5.盒子模型——内外边距 6.盒子模型——尺寸计算 7.清除默认样式 8.盒子模型——元素溢出 9.外边距问题 ①合并现象 ②塌陷问题 10.行内元素——内外边距问题 11.盒子模型——圆角 12.盒子…...

Cursor —— AI编辑器 使用详解

Cursor - The AI Code Editor 一、Cursor 是什么&#xff1f; Cursor 是一款优秀的AI代码编辑器&#xff0c;它内置了 Deepseek-R1、GPT-4、Claude等 AI 模型。 简单说&#xff0c;就是&#xff1a;Cursor VS Code 编辑器 AI 大模型 Cursor 功能特性&#xff08;代码补全、…...

故障诊断——复现github代码ClassBD-CNN(BDCNN)

刚好看到这篇文献有github代码&#xff0c;而且也比较新&#xff0c;感觉可以当作自己论文中的对比方法。 https://www.sciencedirect.com/science/article/pii/S0888327024006484 &#xff08;论文地址&#xff09; &#xff08;看到4090&#xff0c;有点想劝退自己......&…...

局部和整体的关系

Prompt&#xff1a;为什么要研究局部和整体的关系呢&#xff1f;是因为很多情况下&#xff0c;局部就能表达出整体&#xff1f; 这是一个非常本质的问题&#xff0c;其实你已经接近数学和物理中“几何本质”的核心了。我们研究局部和整体的关系&#xff0c;是因为&#xff1a;…...

1.5 点云数据获取方式——双目立体相机

图1-5-1 双目立体相机 双目相机通过模拟人眼立体视觉,利用两个摄像头的视差信息计算物体深度,进而生成 3D 点云,具有成本低、体积小、信息丰富等优势,成为中...

Flume启动报错

报错1&#xff1a; 报错2&#xff1a; File Channel transaction capacity cannot be greater than the capacity of the channel capacit... 解决方案&#xff1a;删除配置...

leetcode 21. 合并两个有序链表(c++解法+相关知识点复习)

目录 题目 所需知识点复习 1.链表 1.1单链表 1.2哑结点&#xff08;Dummy Node&#xff09; 解答过程 1.循环双指针解法 2.递归解法 2025.4.29想到其他知识点会后续再继续补充。 题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表…...

链表反转_leedcodeP206

P206反转链表 原题 反转思路 将链表反转的过程分为两个区域&#xff1a; &#x1f7e6; 未反转区&#xff08;待处理&#xff09; 原链表中还没有处理&#xff08;还没有反转指针方向&#xff09;的部分&#xff0c;从 current 开始一直到链表尾部。 &#x1f7e9; 已反转…...

Laravel+API 接口

LaravelAPI 接口 网课连接&#xff1a;BIlibili. 中文文档. 1.RestFul Api编码风格 一、API设计 修改hosts&#xff0c;C:\Windows\System32\drivers\etc\hosts&#xff0c;增加127.0.0.1 api.lv8.com # Laravel 框架 用这个域名来测试&#xff08;推荐规范&#xff09; 在…...

在 Ubuntu 上离线安装 ClickHouse

在 Ubuntu 上离线安装 ClickHouse 的步骤如下: 一.安装验证 # 检查服务状态 sudo systemctl status clickhouse-server #删除默认文件 sudo rm /etc/clickhouse-server/users.d/default-password.xml # 使用客户端连接 clickhouse-client --password...

【AI微信小程序开发】掷骰子小程序项目代码:自设骰子数量和动画(含完整前端代码)

系列文章目录 【AI微信小程序开发】AI减脂菜谱小程序项目代码:根据用户身高/体重等信息定制菜谱(含完整前端+后端代码)【AI微信小程序开发】AI菜谱推荐小程序项目代码:根据剩余食材智能生成菜谱(含完整前端+后端代码)【AI微信小程序开发】图片工具小程序项目代码:图片压…...

Linux-02-VIM和VI编辑器

第一节:什么是VI和VIM编辑器: VI是Unix和类Unix操作系统中出现的通用的文本编辑器。VIM是从VI发展出来的一个性能更强大的文本编辑器可以主动的以字体颜色辨别语法的正确性,方便程序设计,VIM和VI编辑器完全兼容。使用:vi xxx文件 或者vim xxx文件,简单来说就是用来编辑文件的一…...

同为科技 智能PDU产品选型介绍-EN10/G801FR

随着各行业对数据中心机房重视程度的不断提高&#xff0c; 加强机柜微环境及电源计量、监控和管理则十分必要。在新型微模块化数据中心供配电系统建设中&#xff0c;UPS电源、智能PDU、监控管理系统、资产管理等产品早已成为IDC机房不可或缺的部分。其中&#xff0c;智能PDU通过…...

NS-SWIFT微调Qwen3

目录 一、NS-SWIFT简介 二、Qwen3简介 三、微调Qwen3 1、安装NS-SWIFT环境 2、准备训练数据 3、Lora微调 4、GROP训练 5、Megatron并行训练 一、NS-SWIFT简介 SWIFT&#xff08;Scalable lightWeight Infrastructure for Fine-Tuning&#xff09;是魔搭ModelScope开源社…...

借 AI 热潮,深挖 [风车 AI ] 为跨境电商打造的图片翻译黑科技

家人们&#xff0c;这几年 AI 技术简直像坐了火箭一样飞速发展&#xff0c;生活里、工作中到处都能看到它的身影。对咱们跨境行业来说&#xff0c;语言翻译一直是个让人头疼的大问题。今天咱就借着这股 AI 热潮&#xff0c;好好深挖一下风车 AI 为跨境打造的那些超厉害的翻译黑…...

uni-app 中封装全局音频播放器

在开发移动应用时&#xff0c;音频播放功能是一个常见的需求。无论是背景音乐、音效还是语音消息&#xff0c;音频播放都需要一个稳定且易于管理的解决方案。在 uni-app 中&#xff0c;虽然原生提供了 uni.createInnerAudioContext 方法用于音频播放&#xff0c;但直接使用它可…...

Uniapp:设置TabBar

目录 一、setTabBarBadge:增加文本二、removeTabBarBadge:移除文本三、showTabBarRedDot:显示红点四、hideTabBarRedDot:隐藏红点一、setTabBarBadge:增加文本 为 tabBar 某一项的右上角添加文本。 uni.setTabBarBadge({index: 0,text: 1 })参数类型必填说明indexNumber…...

如何查看k8s获取系统是否清理过docker镜像

k8s集群某个节点down掉后&#xff0c;pod就会漂移到其他节点&#xff0c;但是在该节点却又执行了拉取镜像操作&#xff0c;明明该节点之前部署过该容器的&#xff0c;不知为什么又拉取了一次镜像&#xff08;镜像拉取配置的优先使用本地&#xff09;&#xff0c;所以怀疑是触发…...