实现目标:
- 展示用户的搜索记录10条,按照搜索关键词的时间倒序
- 可以删除搜索记录
- 保存历史记录,保存10条,多余的则删除最久的历史记录
数据库的选择:
用户的搜索记录,需要给每一个用户都保存一份,数据量较大,要求加载速度快,通常这样的数据存储到mongodb更合适,不建议直接存储到关系型数据库中。与redis对比,MongoDB是结构化数据,而redis中只有keyValue。
实现思路:
实现
保存
1. 创建实体类
@Data
@Document("ap_user_search") // mongoDB的映射注解
public class ApUserSearch implements Serializable {
private static final long serialVersionUID = 1L;
private Integer userId;
private String keyword;
private Date createdTime;
}
2. 保存userSearch
@Autowired
private MongoTemplate mongoTemplate;
// keyword从dto中获取,userId从线程中获取
@Override
public void insert(String keyword, Integer userId){
// 1. 查询当前用户的搜索关键词
Query query = Query.query(Criteria
.where("userId").is(userId)
.and("keyword").is(keyword));
ApUserSearch apUserSearch = ApUsermongoTemplate.findOne(query, ApUserSearch.class);
// 2. 存在,更新创建时间
if(apUserSearch != null) {
apUserSerach.setCreatedTime(new Date());
mongoTemplate.save(apUserSearch);
return;
}
// 3. 不存在,判断当前历史总是是否超过10
apUserSearch = new ApUserSearch();
apUserSearch.setUserId(userId);
apUserSearch.setKeyWord(keyword);
apUserSearch.setCreatedTime(new Date());
// 重新排序
Query query1 = Query.query(Criteria
.where("userId").is(userId);
query1.with(Sort.by(Sort.Direction.DESC, "createdTime"));
List list = mongoTemplate.find(query1, ApUserSearch.class);
// 保存历史记录,保存10条,多余的则删除最久的历史记录
if(list == null || list.size() < 10){
mongoTemplate.save(apUserSearch);
}else{
ApUserSearch lastInfo = apUserSearchList.get(apUserSearchList.size() - 1);
// 替换
mongoTemplate.findAndReplace(
Query.query(Criteria.where("id").is(lastInfo.getId())
, apUserSearch);
}
}
3. 获取当前的用户
拦截器类,继承Ordered,GlobalFilter,其中重写filter方法,获取到了用户信息,存储到了header中,再重置请求
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
}
实现一个工具类AppThreadLocalUtil在utils的Service服务中,用于获取用户
public class AppThreadLocalUtil {
private final static ThreadLocal WM_USER_THREAD_LOCAL = new ThreadLocal();
//存入线程中
public static void setUser(ApUser apUser){
WM_USER_THREAD_LOCAL.set(apUser);
}
//从线程中获取
public static ApUser getUser(){
return WM_USER_THREAD_LOCAL.get();
}
//清理
public static void clear(){
WM_USER_THREAD_LOCAL.remove();
}
}
实现拦截器
public class AppTokenInterceptor implements HandlerInterceptor {
// 把用户解析放在线程中
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
if(userId != null){
//存入到当前线程中
ApUser apUser = new ApUser();
apUser.setId(Integer.valueOf(userId));
AppThreadLocalUtil.setUser(apUser);
}
return true;
}
// 清理线程
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
AppThreadLocalUtil.clear();
}
}
在config中的WebMvcConfig中配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AppTokenInterceptor()).addPathPatterns("/**");
}
}
4. 最后在ArticleSerachServiceImple中的search函数中添加
ApUser user = AppThreadLocalUtil.getUser();
if(user != null && dto.getFromIndex() == 0){ // 首页
// 异步调用 保存搜索记录
apUserSearchService.insert(dto.getSearchWords(), user.getId());
}
查询
List apUserSearches = mongoTemplate.find(Query.query(Criteria.where("userId").is(user.getId())).with(Sort.by(Sort.Direction.DESC, "createdTime")), ApUserSearch.class);
删除
//1.检查参数
if(dto.getId() == null){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
//2.判断是否登录
ApUser user = AppThreadLocalUtil.getUser();
if(user == null){
return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
}
//3.删除
mongoTemplate.remove(Query.query(Criteria.where("userId").is(user.getId()).and("id").is(dto.getId())),ApUserSearch.class);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);