diff --git a/src/main/java/com/coscoshipping/ebtp/system/dict/service/impl/DictProjectServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/dict/service/impl/DictProjectServiceImpl.java index 30c46bd..db9e612 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/dict/service/impl/DictProjectServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/dict/service/impl/DictProjectServiceImpl.java @@ -2,6 +2,7 @@ package com.coscoshipping.ebtp.system.dict.service.impl; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.chinaunicom.mall.ebtp.common.base.service.impl.BaseServiceImpl; @@ -76,13 +77,9 @@ public class DictProjectServiceImpl extends BaseServiceImpl selectDictList(DictProject dictProject) { - if (dictProject != null && dictProject.getCode() != null && dictProject.getParentType() != null) { - return this.list(Wrappers.lambdaQuery() - .eq(DictProject::getCode, dictProject.getCode()) - .eq(DictProject::getParentType, dictProject.getParentType()) - .last("limit 1")); - } - return ListUtil.empty(); + return this.list(Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(dictProject.getCode()), DictProject::getCode, dictProject.getCode()) + .eq(StrUtil.isNotBlank(dictProject.getParentType()), DictProject::getParentType, dictProject.getParentType())); } @Override diff --git a/src/main/java/com/coscoshipping/ebtp/system/org/config/OrgCacheConfig.java b/src/main/java/com/coscoshipping/ebtp/system/org/config/OrgCacheConfig.java new file mode 100644 index 0000000..fb14748 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/org/config/OrgCacheConfig.java @@ -0,0 +1,48 @@ +package com.coscoshipping.ebtp.system.org.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericToStringSerializer; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; + +import java.util.List; + +/** + * 组织机构缓存配置类 + * 配置专门的RedisTemplate用于组织机构缓存,使用Java原生序列化提高性能 + * + * @author system + */ +@Configuration +public class OrgCacheConfig { + + /** + * 组织机构专用RedisTemplate配置 + * 使用Java原生序列化,避免JSON转换开销 + * + * @param redisConnectionFactory Redis连接工厂 + * @return 配置好的RedisTemplate + */ + @Bean(name = "orgCacheRedisTemplate") + public RedisTemplate orgCacheRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + // 使用String序列化器处理key + redisTemplate.setKeySerializer(new GenericToStringSerializer<>(String.class)); + redisTemplate.setHashKeySerializer(new GenericToStringSerializer<>(String.class)); + + // 使用Java原生序列化器处理value,避免JSON转换 + JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(); + redisTemplate.setValueSerializer(jdkSerializer); + redisTemplate.setHashValueSerializer(jdkSerializer); + + // 启用事务支持 + redisTemplate.setEnableTransactionSupport(true); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/org/config/OrgCacheInitializer.java b/src/main/java/com/coscoshipping/ebtp/system/org/config/OrgCacheInitializer.java new file mode 100644 index 0000000..4db5ea8 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/org/config/OrgCacheInitializer.java @@ -0,0 +1,41 @@ +package com.coscoshipping.ebtp.system.org.config; + +import com.coscoshipping.ebtp.system.org.manager.OrgCacheManager; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +/** + * 组织机构缓存初始化器 + * 在系统启动时自动初始化组织机构数据到Redis缓存 + * + * @author system + */ +@Slf4j +@Component +@Order(1) // 设置执行顺序,确保在其他组件之前执行 +public class OrgCacheInitializer implements ApplicationRunner { + + @Autowired + private OrgCacheManager orgCacheManager; + + @Override + public void run(ApplicationArguments args) throws Exception { + log.info("系统启动 - 开始初始化组织机构缓存"); + + try { + // 初始化组织机构缓存 + orgCacheManager.initCache(); + log.info("系统启动 - 组织机构缓存初始化完成"); + + // 可选:执行性能测试(仅在需要时启用) + // orgCacheManager.performanceTest(100); + } catch (Exception e) { + log.error("系统启动 - 组织机构缓存初始化失败", e); + // 不抛出异常,避免影响系统启动 + } + } +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/org/manager/OrgCacheManager.java b/src/main/java/com/coscoshipping/ebtp/system/org/manager/OrgCacheManager.java new file mode 100644 index 0000000..052800a --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/org/manager/OrgCacheManager.java @@ -0,0 +1,163 @@ +package com.coscoshipping.ebtp.system.org.manager; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.coscoshipping.ebtp.system.org.entity.SysOrg; +import com.coscoshipping.ebtp.system.org.service.SysOrgService; +import com.coscoshipping.ebtp.system.org.util.CachePerformanceTest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * 组织机构缓存管理器 + * 负责组织机构数据的Redis缓存操作 + * + * @author system + */ +@Slf4j +@Component +public class OrgCacheManager { + + private static final String ORG_CACHE_KEY = "sys:org:all"; + private static final long CACHE_EXPIRE_TIME = 24; // 24小时过期 + + @Autowired + @Qualifier("cacheRedisTemplate") + private RedisTemplate redisTemplate; + + @Autowired + private SysOrgService sysOrgService; + + /** + * 初始化缓存 - 系统启动时调用 + */ + public void initCache() { + log.info("开始初始化组织机构缓存..."); + try { + // 从数据库查询所有组织机构数据 + List allOrgs = sysOrgService.list(new LambdaQueryWrapper() + .eq(SysOrg::getDeleteFlag, "normal") + .orderByAsc(SysOrg::getSort)); + + if (!CollectionUtils.isEmpty(allOrgs)) { + // 直接将List存入Redis缓存,无需JSON转换 + redisTemplate.opsForValue().set(ORG_CACHE_KEY, allOrgs, CACHE_EXPIRE_TIME, TimeUnit.HOURS); + log.info("组织机构缓存初始化完成,共缓存{}条记录", allOrgs.size()); + + // 可选:执行性能测试(仅在开发环境建议启用) + // CachePerformanceTest.comparePerformance(allOrgs, 100); + } else { + log.warn("数据库中没有找到组织机构数据"); + } + } catch (Exception e) { + log.error("初始化组织机构缓存失败", e); + } + } + + /** + * 从缓存中获取所有组织机构数据 + * + * @return 组织机构列表,如果缓存中没有数据则返回null + */ + @SuppressWarnings("unchecked") + public List getAllOrgsFromCache() { + try { + Object cacheData = redisTemplate.opsForValue().get(ORG_CACHE_KEY); + if (cacheData != null) { + log.debug("从缓存中获取组织机构数据"); + return (List) cacheData; + } else { + log.debug("缓存中没有组织机构数据"); + return null; + } + } catch (Exception e) { + log.error("从缓存获取组织机构数据失败", e); + return null; + } + } + + /** + * 刷新缓存 - 增删改操作后调用 + */ + public void refreshCache() { + log.info("开始刷新组织机构缓存..."); + try { + // 先删除旧缓存 + redisTemplate.delete(ORG_CACHE_KEY); + + // 重新初始化缓存 + initCache(); + + log.info("组织机构缓存刷新完成"); + } catch (Exception e) { + log.error("刷新组织机构缓存失败", e); + } + } + + /** + * 清除缓存 + */ + public void clearCache() { + try { + redisTemplate.delete(ORG_CACHE_KEY); + log.info("组织机构缓存已清除"); + } catch (Exception e) { + log.error("清除组织机构缓存失败", e); + } + } + + /** + * 检查缓存是否存在 + * + * @return true 如果缓存存在,false 如果缓存不存在 + */ + public boolean isCacheExists() { + try { + return Boolean.TRUE.equals(redisTemplate.hasKey(ORG_CACHE_KEY)); + } catch (Exception e) { + log.error("检查缓存是否存在时出错", e); + return false; + } + } + + /** + * 延长缓存过期时间 + */ + public void extendCacheExpire() { + try { + redisTemplate.expire(ORG_CACHE_KEY, CACHE_EXPIRE_TIME, TimeUnit.HOURS); + log.debug("组织机构缓存过期时间已延长"); + } catch (Exception e) { + log.error("延长缓存过期时间失败", e); + } + } + + /** + * 执行缓存性能测试 + * 比较JSON序列化与Java原生序列化的性能差异 + * + * @param testCount 测试次数,建议100-1000次 + */ + public void performanceTest(int testCount) { + try { + List allOrgs = sysOrgService.list(new LambdaQueryWrapper() + .eq(SysOrg::getDeleteFlag, "normal")); + + if (!CollectionUtils.isEmpty(allOrgs)) { + log.info("开始执行组织机构缓存性能测试..."); + CachePerformanceTest.comparePerformance(allOrgs, testCount); + log.info("组织机构缓存性能测试完成"); + } else { + log.warn("没有组织机构数据,无法进行性能测试"); + } + } catch (Exception e) { + log.error("执行缓存性能测试失败", e); + } + } +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java index 4cf93a0..4c7f5ff 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -26,6 +27,7 @@ import com.coscoshipping.ebtp.system.org.entity.SysOrg; import com.coscoshipping.ebtp.system.org.entity.vo.SysOrgVO; import com.coscoshipping.ebtp.system.org.service.SysOrgService; import com.coscoshipping.ebtp.system.org.util.CommonUtil; +import com.coscoshipping.ebtp.system.org.manager.OrgCacheManager; import cn.hutool.core.bean.BeanUtil; @@ -37,6 +39,9 @@ import cn.hutool.core.bean.BeanUtil; @Service public class SysOrgServiceImpl extends BaseServiceImpl implements SysOrgService { + @Autowired + private OrgCacheManager orgCacheManager; + @Override public IPage getPage(SysOrgVO SysOrgVO) { LambdaQueryWrapper query = buildQueryWrapper(SysOrgVO); @@ -79,9 +84,15 @@ public class SysOrgServiceImpl extends BaseServiceImpl imp return buildOrgTree(orgList, org); } - // 3. 没有条件,才全查 - List allOrgList = this.list(); - if (CollectionUtils.isEmpty(allOrgList)) return new ArrayList<>(); + // 3. 没有条件,才全查 - 优先从缓存获取 + List allOrgList = orgCacheManager.getAllOrgsFromCache(); + if (CollectionUtils.isEmpty(allOrgList)) { + // 缓存中没有数据,从数据库查询 + allOrgList = this.list(); + if (CollectionUtils.isEmpty(allOrgList)) return new ArrayList<>(); + // 如果数据库有数据但缓存没有,刷新缓存 + orgCacheManager.refreshCache(); + } return buildOrgTree(allOrgList, org); } @@ -165,7 +176,14 @@ public class SysOrgServiceImpl extends BaseServiceImpl imp SysOrg full = this.buildOrgFullPath(vo); vo.setOrgFullId(full.getOrgFullId()).setOrgFullName(full.getOrgFullName()); validEntityBeforeSave(vo, false); - return baseMapper.insert(vo) > 0; + boolean result = baseMapper.insert(vo) > 0; + + // 新增成功后刷新缓存 + if (result) { + orgCacheManager.refreshCache(); + } + + return result; } @Override @@ -175,7 +193,14 @@ public class SysOrgServiceImpl extends BaseServiceImpl imp vo.setOrgFullId(full.getOrgFullId()).setOrgFullName(full.getOrgFullName()); // 删除机构与人员关联 // roleMenuService.remove(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, vo.getRoleId())); - return baseMapper.updateById(vo) > 0; + boolean result = baseMapper.updateById(vo) > 0; + + // 更新成功后刷新缓存 + if (result) { + orgCacheManager.refreshCache(); + } + + return result; } @@ -186,7 +211,14 @@ public class SysOrgServiceImpl extends BaseServiceImpl imp CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("存在下级组织,不可删除", !orgList.isEmpty()); // 删除机构与人员关联 // roleMenuService.remove(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); - return this.removeById(orgId); + boolean result = this.removeById(orgId); + + // 删除成功后刷新缓存 + if (result) { + orgCacheManager.refreshCache(); + } + + return result; } @Override diff --git a/src/main/java/com/coscoshipping/ebtp/system/org/util/CachePerformanceTest.java b/src/main/java/com/coscoshipping/ebtp/system/org/util/CachePerformanceTest.java new file mode 100644 index 0000000..68ead24 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/org/util/CachePerformanceTest.java @@ -0,0 +1,113 @@ +package com.coscoshipping.ebtp.system.org.util; + +import com.chinaunicom.mall.ebtp.common.util.JsonUtils; +import com.coscoshipping.ebtp.system.org.entity.SysOrg; +import lombok.extern.slf4j.Slf4j; + +import java.io.*; +import java.util.List; + +/** + * 缓存性能测试工具类 + * 用于比较JSON序列化与Java原生序列化的性能差异 + * + * @author system + */ +@Slf4j +public class CachePerformanceTest { + + /** + * 测试JSON序列化性能 + * + * @param orgList 组织机构列表 + * @param testCount 测试次数 + * @return 平均耗时(纳秒) + */ + public static long testJsonSerialization(List orgList, int testCount) { + long startTime = System.nanoTime(); + + for (int i = 0; i < testCount; i++) { + // 序列化 + String json = JsonUtils.objectToJson(orgList); + // 反序列化 + List result = JsonUtils.jsonToList(json, SysOrg.class); + } + + long endTime = System.nanoTime(); + long avgTime = (endTime - startTime) / testCount; + + log.info("JSON序列化测试完成,测试次数: {}, 平均耗时: {} 纳秒", testCount, avgTime); + return avgTime; + } + + /** + * 测试Java原生序列化性能 + * + * @param orgList 组织机构列表 + * @param testCount 测试次数 + * @return 平均耗时(纳秒) + */ + @SuppressWarnings("unchecked") + public static long testJavaSerial(List orgList, int testCount) { + long startTime = System.nanoTime(); + + for (int i = 0; i < testCount; i++) { + try { + // 序列化 + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(orgList); + byte[] bytes = bos.toByteArray(); + oos.close(); + bos.close(); + + // 反序列化 + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + List result = (List) ois.readObject(); + ois.close(); + bis.close(); + } catch (Exception e) { + log.error("Java序列化测试失败", e); + } + } + + long endTime = System.nanoTime(); + long avgTime = (endTime - startTime) / testCount; + + log.info("Java原生序列化测试完成,测试次数: {}, 平均耗时: {} 纳秒", testCount, avgTime); + return avgTime; + } + + /** + * 比较两种序列化方式的性能 + * + * @param orgList 组织机构列表 + * @param testCount 测试次数 + */ + public static void comparePerformance(List orgList, int testCount) { + log.info("开始缓存性能测试,数据量: {} 条记录", orgList.size()); + + // 预热JVM + testJsonSerialization(orgList, 10); + testJavaSerial(orgList, 10); + + // 正式测试 + long jsonTime = testJsonSerialization(orgList, testCount); + long javaTime = testJavaSerial(orgList, testCount); + + // 计算性能提升 + double improvement = ((double) (jsonTime - javaTime) / jsonTime) * 100; + + log.info("=== 缓存性能测试结果 ==="); + log.info("JSON序列化平均耗时: {} 纳秒", jsonTime); + log.info("Java原生序列化平均耗时: {} 纳秒", javaTime); + log.info("性能提升: {:.2f}%", improvement); + + if (improvement > 0) { + log.info("Java原生序列化比JSON序列化快 {:.2f}%", improvement); + } else { + log.info("JSON序列化比Java原生序列化快 {:.2f}%", Math.abs(improvement)); + } + } +} \ No newline at end of file