原文链接:
https://blog.csdn.net/qq_41889508/article/details/123592124
首先感谢原文作者,在原有代码做了一点点小的修改,作为记录。直接贴代码
1、pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
2、HotSerachController
package net.beidousky.web.app.controller;
import net.beidousky.web.app.domain.Result;
import net.beidousky.web.app.service.impl.HotSearchService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 热搜及最近搜索
* 2022-10-26
* jwb
*/
@RequestMapping("/app/serach")
@RestController
public class HotSerachController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private HotSearchService hotSearchService;
/**
* 删除redis
* @param key
* @return
*/
@GetMapping("/remove")
public Result removeRedis(String key){
redisTemplate.delete(key);
return Result.success();
}
/**
* 搜索
* @param query
* @return
*/
@GetMapping("/search")
public Result listProduct(String query) {
return Result.success(hotSearchService.search(query));
}
/**
* 热搜列表
* @return
*/
@ResponseBody
@GetMapping("/hotSearch")
public Result listHotSearch() {
return Result.success(hotSearchService.listHotSearch());
}
/**
* 最近搜索列表
* @return
*/
@ResponseBody
@GetMapping("/latelySearch")
public Result recentHotSearch() {
return Result.success(hotSearchService.listRecentSearch());
}
}
2、SearchRedisHelper
package net.beidousky.web.app.controller;
import net.beidousky.common.core.domain.model.LoginUser;
import net.beidousky.common.utils.SecurityUtils;
import net.beidousky.web.app.domain.Product;
import net.beidousky.web.app.domain.UserRecentSearch;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
public class SearchRedisHelper {
@Resource
private RedisTemplate redisTemplate;
/**
* 热搜的key
*/
public static final String HOT_SEARCH = "hotSearch";
/**
* 最近搜索的key
*/
public static final String RECENT_SEARCH = "latelySearch";
/**
* 最近搜索的大小
*/
public static final Integer CURRENT_SEARCH_SIZE = 3;
/**
* 热搜key的过期时间
*/
public static final Integer HOT_SEARCH_EXPIRE_TIME = 3;
/**
* 设置redis的过期时间
* expire其实是懒加载,不设置key的时候是不会执行的
*/
@PostConstruct
public void setHotSearchExpireTime() {
redisTemplate.expire(HOT_SEARCH, HOT_SEARCH_EXPIRE_TIME, TimeUnit.SECONDS);
}
/**
* redis添加最近搜索
* @param query
*/
public void addRedisRecentSearch(String query) {
UserRecentSearch userRecentSearch = new UserRecentSearch();
LoginUser loginUser = SecurityUtils.getLoginUser();
//用户id 当前用户
userRecentSearch.setUnionId(loginUser.getUserId());
//搜索信息
userRecentSearch.setSearchInfo(query);
//score为一个分值,需要把最近浏览的商品id 的分值设置为最大值,
//此处我们可以设置为当前时间Instant.now().getEpochSecond()
//这样最近浏览的商品id的分值一定最大,排在ZSet集合最前面。
ZSetOperations<String, UserRecentSearch> zSet = redisTemplate.opsForZSet();
//由于zset 的集合特性当插入已经存在的 v 值 (商品id) 时只会更新score 值,
zSet.add(RECENT_SEARCH, userRecentSearch, Instant.now().getEpochSecond());
//获取到全部用户的最近搜索记录,用reverseRangeWithScores方法,可以获取到根据score排序之后的集合
Set<ZSetOperations.TypedTuple<UserRecentSearch>> typedTuples = zSet.reverseRangeWithScores(RECENT_SEARCH, 0, -1);
//只得到当前用户的最近搜索记录,注意这里必须保证set集合的顺序
Set<UserRecentSearch> userRecentSearches = listRecentSearch();
if (userRecentSearches.size() > CURRENT_SEARCH_SIZE) {
//获取到最开始浏览的第一条
UserRecentSearch userRecentSearchLast = userRecentSearches.stream().reduce((first, second) -> second).orElse(null);
//删除最开始浏览的第一条
zSet.remove(RECENT_SEARCH, userRecentSearchLast);
}
}
/**
* 热搜列表
* @return
*/
public Set<Product> listHotSearch() {
//0 5 表示0-5下标对应的元素
return redisTemplate.opsForZSet().reverseRangeWithScores(HOT_SEARCH, 0, 10);
}
/**
* redis添加热搜
* @param query
*/
public void addRedisHotSearch(String query) {
//1:表示每调用一次,当前product的分数+1
// productList.forEach(product -> redisTemplate.opsForZSet().incrementScore(HOT_SEARCH, product, 1D));
redisTemplate.opsForZSet().incrementScore(HOT_SEARCH, query, 1D);
}
/**
* 最近搜索列表
* @return
*/
public Set<UserRecentSearch> listRecentSearch() {
LoginUser loginUser = SecurityUtils.getLoginUser();
Set<ZSetOperations.TypedTuple<UserRecentSearch>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(RECENT_SEARCH, 0, -1);
return Optional.ofNullable(typedTuples)
.map(tuples -> tuples.stream()
.map(ZSetOperations.TypedTuple::getValue)
.filter(Objects::nonNull)
.filter(userRecentSearch -> Objects.equals(userRecentSearch.getUnionId(),loginUser.getUserId()))
.collect(Collectors.collectingAndThen(
Collectors.toCollection(LinkedHashSet::new), LinkedHashSet::new)))
.orElseGet(LinkedHashSet::new);
}
}
3、HotSearchService
package net.beidousky.web.app.service.impl;
import net.beidousky.web.app.controller.SearchRedisHelper;
import net.beidousky.web.app.domain.Product;
import net.beidousky.web.app.domain.UserRecentSearch;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Set;
@Service
public class HotSearchService {
@Resource
private SearchRedisHelper searchRedisHelper;
/**
* 搜索
* @param query
* @return
*/
public String search(String query) {
//业务代码可用es.....此处略过....模拟数据库数据
searchRedisHelper.addRedisRecentSearch(query);
searchRedisHelper.addRedisHotSearch(query);
return query;
}
/**
* 热搜列表
* @return
*/
public Set<Product> listHotSearch() {
return searchRedisHelper.listHotSearch();
}
/**
* 最近搜索列表
* @return
*/
public Set<UserRecentSearch> listRecentSearch() {
return searchRedisHelper.listRecentSearch();
}
}
4、UserRecentSearch
package net.beidousky.web.app.domain;
import lombok.Data;
import java.io.Serializable;
@Data
public class UserRecentSearch implements Serializable {
/**
* 搜索信息
*/
private String searchInfo;
/**
* 用户id
*/
private Long unionId;
}
6、Product
package net.beidousky.web.app.domain;
import lombok.Data;
import java.io.Serializable;
@Data
public class Product implements Serializable {
//商品名称
private String productName;
}
7、Result
package net.beidousky.web.app.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.beidousky.common.constant.HttpStatus;
import net.beidousky.common.utils.StringUtils;
@Data
@ApiModel(value = "返回参数")
public class Result<T> {
@ApiModelProperty(value = "状态码", name = "code")
private int code;
@ApiModelProperty(value = "返回信息", name = "msg")
private String msg;
@ApiModelProperty(value = "返回数据", name = "data")
private T data = null;
public Result() {
}
/**
* 初始化一个新创建的 Response 对象
* @param code 状态码
* @param msg 返回内容
*/
public Result(int code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 初始化一个新创建的 Response 对象
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
public Result(int code, String msg, T data) {
this.code = code;
this.msg = msg;
if (StringUtils.isNotNull(data)) {
this.data = data;
}
}
/**
* 返回成功消息
* @return 成功消息
*/
public static Result success() {
return Result.success("操作成功");
}
/**
* 返回成功数据
* @return 成功消息
*/
public static <T> Result success(T data) {
return Result.success("操作成功", data);
}
/**
* 返回成功消息
* @param msg 返回内容
* @return 成功消息
*/
public static Result success(String msg) {
return Result.success(msg, null);
}
/**
* 返回成功消息
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static <T> Result success(String msg, T data) {
return new Result(HttpStatus.SUCCESS, msg, data);
}
/**
* 返回错误消息
* @return
*/
public static Result error() {
return Result.error("操作失败");
}
/**
* 返回错误消息
* @param msg 返回内容
* @return 警告消息
*/
public static Result error(String msg) {
return Result.error(msg, null);
}
/**
* 返回错误消息
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static <T> Result error(String msg, T data) {
return new Result(HttpStatus.ERROR, msg, data);
}
/**
* 返回错误消息
* @param code 状态码
* @param msg 返回内容
* @return 警告消息
*/
public static Result error(int code, String msg) {
return new Result(code, msg, null);
}
}
创作不易,大家帮忙关注一下公众号,感谢。