feat:前端输入工号/姓名等模糊匹配用户
This commit is contained in:
@@ -5,6 +5,7 @@ import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
@@ -12,6 +13,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
@@ -23,16 +26,29 @@ public class CacheConfig {
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
cacheManager.setCaches(Arrays.asList(
|
||||
// 用户名缓存
|
||||
new ConcurrentMapCache("userNames"),
|
||||
// 查询任务树列表缓存
|
||||
new ConcurrentMapCache("taskTreeListCache"),
|
||||
// 同步待办缓存
|
||||
new ConcurrentMapCache("syncTodo")
|
||||
// 可以添加其他缓存配置
|
||||
));
|
||||
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {
|
||||
@Override
|
||||
protected Cache createConcurrentMapCache(String name) {
|
||||
// 为 allUsers 创建带过期时间的缓存
|
||||
if ("allUsers".equals(name)) {
|
||||
return new ExpiringCache(name, 60, TimeUnit.MINUTES);
|
||||
} else if ("userSearch".equals(name)) {
|
||||
return new ExpiringCache(name, 60, TimeUnit.MINUTES);
|
||||
}
|
||||
// 其他缓存使用默认实现
|
||||
return super.createConcurrentMapCache(name);
|
||||
}
|
||||
};
|
||||
|
||||
// 预声明缓存名称(可选)
|
||||
cacheManager.setCacheNames(Arrays.asList(
|
||||
"userNames",
|
||||
"taskTreeListCache",
|
||||
"syncTodo",
|
||||
"allUsers",
|
||||
"userSearch"
|
||||
));
|
||||
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@@ -41,15 +57,75 @@ public class CacheConfig {
|
||||
return new SimpleCacheErrorHandler() {
|
||||
@Override
|
||||
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
|
||||
log.warn("缓存获取失败, key: {}, 错误: {}", key, exception.getMessage());
|
||||
// 不抛出异常,继续执行
|
||||
log.warn("缓存获取失败, cache: {}, key: {}, 错误: {}",
|
||||
cache != null ? cache.getName() : "unknown", key, exception.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
|
||||
log.warn("缓存存入失败, key: {}, 错误: {}", key, exception.getMessage());
|
||||
// 不抛出异常,继续执行
|
||||
log.warn("缓存存入失败, cache: {}, key: {}, 错误: {}",
|
||||
cache != null ? cache.getName() : "unknown", key, exception.getMessage());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义过期缓存实现
|
||||
*/
|
||||
public static class ExpiringCache extends ConcurrentMapCache {
|
||||
|
||||
private final long ttl; // 过期时间(毫秒)
|
||||
private final ConcurrentHashMap<Object, CacheEntry> cacheMap = new ConcurrentHashMap<>();
|
||||
|
||||
public ExpiringCache(String name, long ttl, TimeUnit timeUnit) {
|
||||
super(name);
|
||||
this.ttl = timeUnit.toMillis(ttl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object lookup(Object key) {
|
||||
CacheEntry entry = cacheMap.get(key);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查是否过期
|
||||
if (System.currentTimeMillis() > entry.expiryTime) {
|
||||
cacheMap.remove(key);
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value) {
|
||||
if (value != null) {
|
||||
cacheMap.put(key, new CacheEntry(value, System.currentTimeMillis() + ttl));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evict(Object key) {
|
||||
cacheMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cacheMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存条目,包含值和过期时间
|
||||
*/
|
||||
private static class CacheEntry {
|
||||
private final Object value;
|
||||
private final long expiryTime;
|
||||
|
||||
public CacheEntry(Object value, long expiryTime) {
|
||||
this.value = value;
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.sdm.common.service;
|
||||
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.sdm.common.common.SdmResponse;
|
||||
import com.sdm.common.common.ThreadLocalContext;
|
||||
import com.sdm.common.entity.req.system.UserListReq;
|
||||
import com.sdm.common.entity.req.system.UserQueryReq;
|
||||
import com.sdm.common.entity.resp.PageDataResp;
|
||||
import com.sdm.common.entity.resp.system.CIDUserResp;
|
||||
import com.sdm.common.feign.impl.system.SysUserFeignClientImpl;
|
||||
import com.sdm.common.feign.inter.system.ISysUserFeignClient;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -18,8 +22,136 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
public class UserNameCacheService {
|
||||
|
||||
@Autowired
|
||||
private SysUserFeignClientImpl sysUserFeignClient;
|
||||
private final SysUserFeignClientImpl sysUserFeignClient;
|
||||
private final CacheManager cacheManager; // 注入CacheManager
|
||||
|
||||
public UserNameCacheService(SysUserFeignClientImpl sysUserFeignClient,
|
||||
CacheManager cacheManager) {
|
||||
this.sysUserFeignClient = sysUserFeignClient;
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// /**
|
||||
// * 初始化加载缓存
|
||||
// */
|
||||
// @PostConstruct
|
||||
// public void init() {
|
||||
// refreshCache();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 定时刷新缓存 - 每10分钟执行一次
|
||||
// */
|
||||
// @Scheduled(fixedDelay = 600000)
|
||||
// @CacheEvict(value = "users", allEntries = true) // 清空所有用户相关的缓存
|
||||
// public void refreshCache() {
|
||||
// try {
|
||||
// UserListReq userListReq = new UserListReq();
|
||||
// userListReq.setTenantId(ThreadLocalContext.getTenantId());
|
||||
// userListReq.setCurrent(1);
|
||||
// userListReq.setSize(9999);
|
||||
// SdmResponse<PageDataResp<List<CIDUserResp>>> pageDataRespSdmResponse = sysUserFeignClient.listUser(userListReq);
|
||||
// if (pageDataRespSdmResponse.isSuccess() && pageDataRespSdmResponse.getData() != null) {
|
||||
// List<CIDUserResp> users = pageDataRespSdmResponse.getData().getData();
|
||||
// if (users != null) {
|
||||
// this.allUsers = new CopyOnWriteArrayList<>(users);
|
||||
// log.info("缓存刷新成功,用户数:{}", users.size());
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// log.error("缓存刷新失败", e);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取所有用户列表 - 缓存1小时
|
||||
* 第一次调用时加载,之后1小时内从缓存获取
|
||||
*/
|
||||
@Cacheable(value = "allUsers", key = "'userList'")
|
||||
public List<CIDUserResp> getAllUsers() {
|
||||
log.info("缓存未命中,从用户系统加载全量数据...");
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
UserListReq userListReq = new UserListReq();
|
||||
userListReq.setTenantId(ThreadLocalContext.getTenantId());
|
||||
userListReq.setCurrent(1);
|
||||
userListReq.setSize(9999);
|
||||
SdmResponse<PageDataResp<List<CIDUserResp>>> pageDataRespSdmResponse = sysUserFeignClient.listUser(userListReq);
|
||||
if (pageDataRespSdmResponse.isSuccess() && pageDataRespSdmResponse.getData() != null) {
|
||||
List<CIDUserResp> users = pageDataRespSdmResponse.getData().getData();
|
||||
if (CollectionUtils.isNotEmpty(users)) {
|
||||
log.info("加载完成,用户数:{},耗时:{}ms", users.size(), System.currentTimeMillis() - startTime);
|
||||
return users;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
} catch (Exception e) {
|
||||
log.error("加载用户列表失败", e);
|
||||
return Collections.emptyList(); // 返回空列表,避免缓存null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键词搜索用户 - 结果缓存1小时
|
||||
* key = "search_" + keyword,比如搜索"张三"会缓存为 "search_张三"
|
||||
*/
|
||||
@Cacheable(value = "userSearch", key = "'search_' + #keyword")
|
||||
public List<CIDUserResp> searchUsers(String keyword) {
|
||||
log.info("===== 缓存未命中,执行搜索:keyword={} =====", keyword);
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// 1. 先获取所有用户(这个方法会单独缓存)
|
||||
List<CIDUserResp> allUsers = getAllUsers();
|
||||
|
||||
// 2. 如果没有关键词,返回所有用户
|
||||
if (StringUtils.isEmpty(keyword)) {
|
||||
log.info("空关键词搜索,返回全部用户,数量:{},耗时:{}ms",
|
||||
allUsers.size(), System.currentTimeMillis() - startTime);
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
// 3. 根据关键词过滤
|
||||
String lowerKeyword = keyword.trim().toLowerCase();
|
||||
List<CIDUserResp> result = allUsers.parallelStream()
|
||||
.filter(user -> matchesKeyword(user, lowerKeyword))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.info("搜索完成,keyword:{},结果数:{},耗时:{}ms",
|
||||
keyword, result.size(), System.currentTimeMillis() - startTime);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("搜索用户失败,keyword:{}", keyword, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动清除所有缓存
|
||||
*/
|
||||
@CacheEvict(value = "allUsers", key = "'userList'")
|
||||
public void clearAllCaches() {
|
||||
log.info("手动清除所有缓存");
|
||||
}
|
||||
|
||||
private boolean matchesKeyword(CIDUserResp user, String lowerKeyword) {
|
||||
if (user == null) return false;
|
||||
|
||||
if (user.getUsername() != null && user.getUsername().toLowerCase().contains(lowerKeyword)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user.getNickname() != null && user.getNickname().toLowerCase().contains(lowerKeyword)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取用户名 - 带缓存 TODO 后续加入Redis的时候改成Redis
|
||||
|
||||
Reference in New Issue
Block a user