通过rediss实现用户菜单智能推荐

本人用的框架 SpringCloud+ redis+Oauth2+Security

前言: 整体使用过滤器的思想,获取Request,然后从数据库查到菜单名称和路由以及计算点击次数,最后以list的形式存在redis,设计定时任务,在一定时间后,将redis的数据存在数据库(mysql或者oracle)中。 

设计时出现的问题(必看!!):

(一)、因为是微服务框架,所以在设计时想到的是在GateWay使用GlobalFilter对所有服务的请求进行拦截,但是有一个问题是,因为GateWay的pom文件依赖不允许有spring-web也就没办法使用fegin或者其他方式查询数据库,也就获取不到菜单的信息,所以舍弃了这种方法

(二)、那就使用基础模块,让每个服务都去依赖这个模块,就变相的达到了,控制每一个服务的方式。那又没办法想GateWay那样直接实现GlobalFilter拦截所有请求,但是又想到可以将拦截器加在security里,等每次认证结束后,经过过滤器,对请求进行处理,这样就达到了所有的目的

(三)、如果您只是单体的springBoot项目,那就更简单了,直接实现HandlerInterceptor,然后加到bean里让spring管理

一、先写拦截器内容

@Slf4j
public class UserFavoriteFunctionFilter extends OncePerRequestFilter {// 排除过滤的 uri 地址,nacos自行添加private final IgnoreWhiteProperties ignoreWhite;public UserFavoriteFunctionFilter(IgnoreWhiteProperties ignoreWhite) {this.ignoreWhite = ignoreWhite;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//  /gAcDeptController/listString urlPath = request.getRequestURI();log.info("Absolute path:{}", urlPath);// 跳过不需要统计的路径List<String> whites = ignoreWhite.getWhites();if (CollUtil.isEmpty(whites)){filterChain.doFilter(request, response);return;}if (StringUtils.matches(urlPath, whites)) {log.info("Skip path:{}", urlPath);filterChain.doFilter(request, response);return;}RemoteSystemService remoteSystemService = SpringUtils.getBean(RemoteSystemService.class);RedisService redisService = SpringUtils.getBean(RedisService.class);String prefixKey = "userFavorite:";BigDecimal userId = SecurityUtils.getUserId();// 获取uri的前半部分String[] split = urlPath.split("/");String ControllerPath = split[1]; // gAcDeptController//  从 G_AC_PERMISSION 查出当前菜单的 perm_noResponseData<String> data = remoteSystemService.getPermNo(ControllerPath);if (ObjectUtil.isNull(data)){filterChain.doFilter(request, response);return;}String permNo = data.getData();// 从redis查询当前的用户菜单点击量String key = prefixKey+userId;List<clickCountVo> clickCountVos = redisService.getCacheList(key);if (CollUtil.isNotEmpty(clickCountVos)){Map<String, clickCountVo> clickCountMap = clickCountVos.stream().collect(Collectors.toMap(clickCountVo::getName,  // 键映射函数vo -> vo            // 值映射函数,直接使用对象本身));clickCountVo clickCountVo = clickCountMap.get(permNo);if (ObjectUtil.isNotNull(clickCountVo)) {// 当前的点击量BigDecimal count = clickCountVo.getCount();AtomicLong atomicLong = new AtomicLong(count.longValue());long l = atomicLong.incrementAndGet();clickCountVo.setCount(new BigDecimal(l));clickCountVo.setTime(new Date());}else {clickCountVo clickVo = new clickCountVo();clickVo.setName(permNo);clickVo.setTime(new Date());clickVo.setCount(BigDecimal.ONE);clickCountVos.add(clickVo);}}else {clickCountVo countVo = new clickCountVo();countVo.setName(permNo);countVo.setTime(new Date());countVo.setCount(BigDecimal.ONE);clickCountVos.add(countVo);}redisService.deleteObject(key);redisService.setCacheList(key, clickCountVos);filterChain.doFilter(request, response);}}

二、创建一个Vo保存菜单信息和点击量

@Data
public class clickCountVo {private String name;private BigDecimal count;@JsonFormat(pattern = "yyyy-MM-dd")private Date time;
}

三、有一些路径我们不需要拦截的,可以在nacos配置一下

@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties {private List<String> whites = new ArrayList<>();public List<String> getWhites(){return whites;}public void setWhites(List<String> whites){this.whites = whites;}
}

四、最重要的,将我们自定义的拦截器加到Scurity里,这里还搭配了Oauth2.0

    @Bean@Order(Ordered.HIGHEST_PRECEDENCE)SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {AntPathRequestMatcher[] requestMatchers = permitAllUrl.getUrls().stream().map(AntPathRequestMatcher::new).toList().toArray(new AntPathRequestMatcher[] {});http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers).permitAll().anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.authenticationEntryPoint(resourceAuthExceptionEntryPoint).bearerTokenResolver(starBearerTokenExtractor).jwt()).addFilterAfter(new UserFavoriteFunctionFilter(whiteProperties),BearerTokenAuthenticationFilter.class).headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)).csrf(AbstractHttpConfigurer::disable);return http.build();}

.addFilterAfter(new UserFavoriteFunctionFilter(whiteProperties),BearerTokenAuthenticationFilter.class) 这个是添加我们自定义的过滤器的,熟悉Oauth2.0认证的都熟悉BearerTokenAuthenticationFilter,这个过滤器是当用户每一次想资源服务器请求时都会经过的过滤器,这个过滤器也负责处理token以及将用户认证信息存到SecurityContextHolder中,所以我们在这个过滤器后面加上我们自定义的过滤器UserFavoriteFunctionFilter(这个说起来都是泪,我一点一点debug后才发现addFilterAfter这个方法

然后你就找一个既又redis又有springWeb依赖的公共模块,将代码放进去就行了。

后面还有一个定时任务的功能,这个主要是为了防止redis数据太多,我们公司是TOB,基本没有什么用户量,也没有高并发什么的,暂时就没有写这个功能。

附带两个查询的方法,返回前端展示

@RestController
@RequestMapping("/userClickController")
@Tag(name = "获取用户常用菜单功能")
public class UserClickController {@Autowiredprivate RedisService redisService;@GetMapping("/getTop10Info")@Operation(summary = "获取点击量最多的前10个菜单信息")public List<clickCountVo> getTop10Info(){String  key = "userFavorite:";BigDecimal userId = SecurityUtils.getUserId();key = key+userId;List<clickCountVo> cacheList = redisService.getCacheList(key);// 按照点击量排序。如果点击量一样就按照时间排序 都是降序return cacheList.stream().sorted(Comparator.comparing(clickCountVo::getCount).reversed().thenComparing(Comparator.comparing(clickCountVo::getTime).reversed())).limit(10).collect(Collectors.toList());}@GetMapping("/getLastWeekInfo")@Operation(summary = "获取最近一周点击量的菜单信息")public List<clickCountVo> getLastWeekInfo(){String  key = "userFavorite:";BigDecimal userId = SecurityUtils.getUserId();key = key+userId;List<clickCountVo> cacheList = redisService.getCacheList(key);if (CollUtil.isNotEmpty(cacheList)){// 获取上一周的时间DateTime dateTime = DateUtil.lastWeek();// 按照点击量排序。如果点击量一样就按照时间排序 都是降序return cacheList.stream().filter(da -> da.getTime().toInstant().isAfter(dateTime.toInstant())).sorted(Comparator.comparing(clickCountVo::getCount).reversed().thenComparing(Comparator.comparing(clickCountVo::getTime).reversed())).collect(Collectors.toList());}return cacheList;}}

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

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

相关文章

Search for documents with similar texts

题意&#xff1a;搜索具有相似文本的文档 问题背景&#xff1a; I have a document with three attributes: tags, location, and text. 我有一份文档&#xff0c;包含三个属性&#xff1a;标签、位置和文本。 Currently, I am indexing all of them using LangChain/pgvecto…

快速了解《大模型赋能下的AI2.0数字人平台》白皮书

在生成式AI和大模型的赋能下&#xff0c;数字人迎来AI 2.0时代。它能否成为每个人的“数字分身”&#xff0c;转化为新型的AI劳动力工具&#xff1f;商汤科技与上海市人工智能技术协会、零壹智库、增强现实核心技术产业联盟联合发布《大模型赋能下的AI 2.0数字人平台》。《白皮…

Kubernetes面试整理-PersistentVolumes和PersistentVolumeClaims的使用和配置

在 Kubernetes 中,PersistentVolumes (PV) 和 PersistentVolumeClaims (PVC) 提供了一种分离存储和使用存储的机制。PV 是集群中存储资源的抽象表示,而 PVC 是用户对存储资源的请求。通过这种机制,用户可以动态地申请和管理存储资源。 PersistentVolumes (PV) PersistentVol…

【D3.js in Action 3 精译】1.2.2 可缩放矢量图形(二)

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知 1.2.1 HTML 与 DOM1.2.2 SVG - 可缩放矢量图形 ✔️ 第一部分【第二部分】✔️第三部分&#xff08;精译中 ⏳&#xff09; 1.2.3 Canvas 与 WebGL&#x…

自动化任务:在IPython中创建和运行脚本

在数据科学和编程中&#xff0c;自动化任务是提高效率的关键。IPython提供了多种方法来创建和运行脚本&#xff0c;使得重复性任务可以被轻松自动化。本文将介绍如何在IPython中创建和运行脚本&#xff0c;帮助你更高效地完成工作。 1. 创建和保存IPython脚本 使用文本编辑器…

Spring Boot 中的微服务监控与管理

微服务的概述 微服务架构的优点和挑战 优点: 灵活性和可扩展性:微服务架构允许每个服务单独部署和扩展,这使得系统可以更灵活地适应不同的业务需求和负载变化。 使团队更加聚焦:每个微服务都有明确的职责,这使得开发团队可以更加聚焦,专注于开发他们的服务。 技术和框…

读AI新生:破解人机共存密码笔记16对人工智能的治理

1. 愚蠢的、情绪化的人类 1.1. 与完美理性所设定的不可企及的标准相比&#xff0c;我们都是极其愚蠢的&#xff0c;我们受制于各种情绪的起伏&#xff0c;这些情绪在很大程度上支配着我们的行为 1.2. 为了充分了解人类的认知&#xff0c;我们&#xff08;或者更确切地说&…

简易跨平台上传文件,前后端demo

前端文件 <!DOCTYPE html> <html> <head><title>文件上传</title> </head> <body> <h1>文件上传1-相对慢&#xff0c;需要等待本地选择的文件全部上传完成后&#xff0c;服务器再保存</h1> <form id"uploadForm…

ORA-01775: 同义词的循环链问题

一、问题描述 ORA-01775: 同义词的循环链问题 二、 原因分析 同义词对应的对象&#xff08;表等&#xff09;已删除&#xff0c;不存在了。 可能原因&#xff1a; 删除数据库对象&#xff0c;但是忘记删除同义词。删除一个用户&#xff0c;但忘记删除此用户中相关的同义词…

@Param参数

Param参数 当方法参数大于两个的时候必须传递&#xff0c;只有一个的时候可以不传。大于两个的时候也可以用#{arg0}和#{arg1}。。。来取值 Param&#xff08;&#xff09;括号里面的值对应sql语句中 # {} 里面的值 看AI的解释

模版方法模式详解:使用和实现的指南

目录 模版方法模式模版方法模式结构模版方法模式适合应用场景模版方法模式优缺点练手题目题目描述输入描述输出描述题解 模版方法模式 模板方法模式是一种行为设计模式&#xff0c; 它在超类中定义了一个算法的框架&#xff0c; 允许子类在不修改结构的情况下重写算法的特定步…

《昇思25天学习打卡营第3天|张量 Tensor》

文章目录 前言&#xff1a;今日所学&#xff1a;1. 创建张量2. 张量的属性3.张量索引与运算4. NumPy与Tensor的转换5. 稀疏张量 前言&#xff1a; 张量&#xff1f;张亮&#xff1f;张量是什么&#xff1f; 张量是一个可以用来表示在一些矢量、标量和其他张量之间的线性关系的…

高并发部署:基于 Gunicorn、Flask 和 Docker

一、准备工作 确保已经安装以下软件&#xff1a; DockerDocker ComposePython 3.x 二、创建 Flask 应用 首先&#xff0c;创建一个简单的 Flask 应用。创建一个新的目录并在其中创建以下文件&#xff1a; 1. app.py python fromflask importFlask, jsonifyapp Flask(__…

leetcode 第133场双周赛 100333.统计逆序对的数目【计数dp/滚动数组/前缀和优化】

分析&#xff1a; 先考虑如下问题。 求长度为n&#xff0c;逆序对为m的排列数量。 可以考虑dp&#xff0c;dp[i][j]定义为长度为i&#xff0c;逆序对为j的排列数量。 dp[1][0] 1; //枚举排列长度&#xff0c;或者认为枚举当前需要插到长度为i-1的排列中的数字 for(int i 1…

OpenAI封杀不支持地区API:违规封号,7月9日生效

OpenAI 在检测用户使用其 API 的地区后&#xff0c;提示所有不支持位置的用户 昨晚&#xff0c;很多大模型应用的开发者、程序员都收到了 OpenAI 的警告信&#xff0c;心里一惊。 OpenAI 在检测用户使用其 API 的地区后&#xff0c;提示所有不支持位置的用户&#xff1a;即将封…

冒泡排序、选择排序、插入排序~java版

1、冒泡排序&#xff08;Bubble Sort&#xff09; 冒泡排序的基本思想是多次遍历待排序序列&#xff0c;每次遍历时两两比较相邻元素&#xff0c;如果顺序不对则交换&#xff0c;直到整个序列有序为止。 public class BubbleSort {public static void bubbleSort(int[] arr) …

图书管理系统(附源码)

前言&#xff1a;前面一起和小伙伴们学习了较为完整的Java语法体系&#xff0c;那么本篇将运用这些知识连串在一起实现图书管理系统。 目录 一、总体设计 二、书籍与书架 书籍&#xff08;Book&#xff09; 书架&#xff08;Booklist&#xff09; 三、对图书的相关操作 I…

已解决问题 | 该扩展程序未列在 Chrome 网上应用店中,并可能是在您不知情的情况下添加的

在Chrome浏览器中&#xff0c;如果你看到“该扩展程序未列在 Chrome 网上应用店中&#xff0c;并可能是在您不知情的情况下添加的”这样的提示&#xff0c;通常是因为该扩展程序没有通过Chrome网上应用店进行安装。以下是解决这个问题的步骤&#xff1a; 解决办法&#xff1a;…

Spring Boot整合Redis缓存的最佳实践

Spring Boot整合Redis缓存的最佳实践 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代应用开发中&#xff0c;缓存是提升系统性能和响应速度的关键技术之…

kali/ubuntu安装vulhub

无须更换源&#xff0c;安装docker-compose apt install docker.io docker -vdocker-compose #提示没有&#xff0c;输入y安装mkdir -p /etc/docker vi /etc/docker/daemon.json #更换dockerhub国内源┌──(root㉿kali)-[/home/kali/vulhub-master/tomcat/CVE-2017-12615] …