feat:前端输入工号/姓名等模糊匹配用户

This commit is contained in:
2026-03-03 16:49:35 +08:00
parent b29b56e44e
commit b456a43700
3 changed files with 240 additions and 18 deletions

View File

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

View File

@@ -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