Java, redis, springboot, ruoyi implement hot searches and recent searches

Original link:
https://blog.csdn.net/qq_41889508/article/details/123592124
First of all, I would like to thank the author of the original article for making some small modifications to the original code as a record. Directly paste the code
1, pom file

	<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);
    }
}


Creation is not easy, please help me follow the official account, thank you.
Insert image description here

Guess you like

Origin blog.csdn.net/qq_41060647/article/details/127528075