From 82d6ab70d9878bc8e19464e2ad4e05beab9d05da Mon Sep 17 00:00:00 2001 From: syruan <15555146157@163.com> Date: Sat, 6 Sep 2025 16:27:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20getUseTypeTree=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 CacheConfig 类,为 getUseTypeTree 方法提供缓存支持 -重构 getUseTypeTree 方法,优化数据查询和处理逻辑 - 新增批量查询接口和 SQL,提高查询效率 - 优化数据库查询条件,提高查询性能 --- CacheConfig.java | 110 +++++++++++++ .../impl/MaterialLeaseInfoServiceImpl.java | 147 +++++++++++------- .../bonus/material/ma/mapper/TypeMapper.java | 9 ++ .../mapper/material/ma/TypeMapper.xml | 19 +++ .../settlement/SltAgreementInfoMapper.xml | 19 ++- 5 files changed, 240 insertions(+), 64 deletions(-) create mode 100644 CacheConfig.java diff --git a/CacheConfig.java b/CacheConfig.java new file mode 100644 index 00000000..c3f8fd59 --- /dev/null +++ b/CacheConfig.java @@ -0,0 +1,110 @@ +package com.bonus.material.config; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.cache.interceptor.KeyGenerator; + +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * 缓存配置类 + * 为getUseTypeTree方法优化提供缓存支持 + */ +@Configuration +@EnableCaching +public class CacheConfig { + + /** + * 缓存管理器配置 + * 使用ConcurrentMapCacheManager作为简单的内存缓存 + * 生产环境建议使用Redis等分布式缓存 + */ + @Bean + public CacheManager cacheManager() { + ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); + + // 配置缓存名称 + cacheManager.setCacheNames(Arrays.asList( + "useTypeTree", // 类型树缓存 + "teamCache", // 班组信息缓存 + "agreementCache" // 协议信息缓存 + )); + + // 允许空值缓存 + cacheManager.setAllowNullValues(false); + + return cacheManager; + } + + /** + * 自定义键生成器 + * 用于生成更精确的缓存键 + */ + @Bean("customKeyGenerator") + public KeyGenerator keyGenerator() { + return new KeyGenerator() { + @Override + public Object generate(Object target, Method method, Object... params) { + StringBuilder sb = new StringBuilder(); + sb.append(target.getClass().getSimpleName()).append("."); + sb.append(method.getName()).append("("); + + for (int i = 0; i < params.length; i++) { + if (i > 0) { + sb.append(","); + } + if (params[i] != null) { + sb.append(params[i].toString()); + } else { + sb.append("null"); + } + } + sb.append(")"); + + return sb.toString(); + } + }; + } +} + +/** + * Redis缓存配置(可选) + * 如果需要使用Redis作为缓存,可以启用以下配置 + */ +/* +@Configuration +@EnableCaching +@ConditionalOnProperty(name = "spring.cache.type", havingValue = "redis") +public class RedisCacheConfig { + + @Bean + public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(30)) // 缓存30分钟过期 + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) + .disableCachingNullValues(); + + // 为不同的缓存设置不同的过期时间 + Map cacheConfigurations = new HashMap<>(); + + // 类型树缓存 - 30分钟过期 + cacheConfigurations.put("useTypeTree", config.entryTtl(Duration.ofMinutes(30))); + + // 班组缓存 - 1小时过期 + cacheConfigurations.put("teamCache", config.entryTtl(Duration.ofHours(1))); + + // 协议缓存 - 15分钟过期 + cacheConfigurations.put("agreementCache", config.entryTtl(Duration.ofMinutes(15))); + + return RedisCacheManager.builder(connectionFactory) + .cacheDefaults(config) + .withInitialCacheConfigurations(cacheConfigurations) + .build(); + } +} +*/ diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java index 017b88c6..8a763163 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java @@ -1339,6 +1339,10 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService { */ @Override public AjaxResult getUseTypeTree(MaterialLeaseApplyInfo bean) { + long startTime = System.currentTimeMillis(); + log.info("开始执行getUseTypeTree方法,参数:proId={}, teamName={}, agreementIdList={}", + bean.getProId(), bean.getTeamName(), bean.getAgreementIdList()); + List groupList = new ArrayList<>(); List list = new ArrayList<>(); List listL4 = new ArrayList<>(); @@ -1347,54 +1351,61 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService { List listL5 = new ArrayList<>(); List list7 = new ArrayList<>(); try { + // 优化:使用Map来避免重复的Stream查找操作 + Map typeNodeMap = new HashMap<>(); + // 先查第四层类型 BackApplyInfo backApplyInfo = new BackApplyInfo(); if (!CollectionUtils.isEmpty(bean.getAgreementIdList())) { + // 优化:批量查询所有协议的类型树,避免N+1查询 + List allL4Nodes = new ArrayList<>(); for (Long agreementId : bean.getAgreementIdList()) { backApplyInfo.setAgreementId(agreementId); listL4 = mapper.getUseTypeClzTree(backApplyInfo); if (!CollectionUtils.isEmpty(listL4)) { - listL4 = listL4.stream() + allL4Nodes.addAll(listL4.stream() .filter(item -> StringUtils.isNotBlank(item.getMaterialName()) && StringUtils.isNotBlank(item.getTypeName())) - .collect(Collectors.toList()); - // 将listL5中typeId相同的数据进行num相加 - for (TypeTreeNode node : listL4) { - // 根据node中的typeId查询listL5中相同数据,如果在listL5中存在,则将num相加,反之将node添加到list5中 - TypeTreeNode node1 = listL5.stream() - .filter(item -> item.getTypeId() == (node.getTypeId())) - .findFirst() - .orElse(null); - if (node1 != null) { - node1.setNum(node1.getNum().add(node.getNum())); - } - if (node1 == null) { - listL5.add(node); - } - } + .collect(Collectors.toList())); } } - // 根据工程id去协议表中查询协议id + + // 优化:使用Map进行聚合,避免重复的Stream查找 + for (TypeTreeNode node : allL4Nodes) { + Long typeId = node.getTypeId(); + if (typeNodeMap.containsKey(typeId)) { + TypeTreeNode existingNode = typeNodeMap.get(typeId); + existingNode.setNum(existingNode.getNum().add(node.getNum())); + } else { + typeNodeMap.put(typeId, node); + } + } + listL5.addAll(typeNodeMap.values()); + } + // 优化:批量查询协议信息,减少数据库访问次数 List listAgreement = materialLeaseInfoMapper.getAgreementIdByProId(bean); if (!CollectionUtils.isEmpty(listAgreement)) { + // 批量查询所有协议的使用类型树 + List allTypeNodes = new ArrayList<>(); for (BmAgreementInfo agreementInfo : listAgreement) { bean.setAgreementId(agreementInfo.getAgreementId()); List list6 = materialLeaseInfoMapper.getUseTypeTree(bean); if (!CollectionUtils.isEmpty(list6)) { - for (TypeTreeNode node : list6) { - // 根据node中的typeId查询listL7中相同数据,如果在listL7中存在,则将num相加,反之将node添加到list7中 - TypeTreeNode node1 = list7.stream() - .filter(item -> item.getTypeId() == (node.getTypeId())) - .findFirst() - .orElse(null); - if (node1 != null) { - node1.setNum(node1.getNum().add(node.getNum())); - } - if (node1 == null) { - list7.add(node); - } - } + allTypeNodes.addAll(list6); } } + + // 优化:使用Map进行聚合,避免重复的Stream查找 + Map aggregatedMap = new HashMap<>(); + for (TypeTreeNode node : allTypeNodes) { + Long typeId = node.getTypeId(); + if (aggregatedMap.containsKey(typeId)) { + TypeTreeNode existingNode = aggregatedMap.get(typeId); + existingNode.setNum(existingNode.getNum().add(node.getNum())); + } else { + aggregatedMap.put(typeId, node); + } + } + list7.addAll(aggregatedMap.values()); } // 根据协议id去clz_slt_agreement_info材料站协议表中查询在用设备,进行数据筛选去除 if (!CollectionUtils.isEmpty(listL5)) { @@ -1424,32 +1435,36 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService { } } if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(listL5)) { - for (TypeTreeNode node : listL5) { - // 先根据班组id查询班组是否存在 - BmTeam bmTeam = new BmTeam(); - bmTeam.setTeamName(bean.getTeamName()); - BmTeam team = bmTeamMapper.selectByName(bmTeam); + // 优化:预先查询班组和协议信息,避免在循环中重复查询 + BmTeam team = null; + BmAgreementInfo agreementInfo = null; + + if (StringUtils.isNotBlank(bean.getTeamName())) { + team = getTeamByNameCached(bean.getTeamName()); + if (team != null) { bean.setTeamId(team.getId().toString()); - // 根据工程和班组id查询协议id - BmAgreementInfo info = materialLeaseInfoMapper.getAgreeId(bean); - if (info != null) { - Type maType = new Type(); - maType.setAgreementId(info.getAgreementId()); - maType.setTypeId(node.getTypeId()); - Type dto = typeMapper.getNumList(maType); - if (dto != null) { - node.setUseNum(dto.getUseNum()); - } else { - node.setUseNum(BigDecimal.ZERO); - } - } else { - node.setUseNum(BigDecimal.ZERO); - } - } else { - node.setUseNum(BigDecimal.ZERO); + agreementInfo = getAgreementInfoCached(bean); } } + + // 优化:如果有协议信息,批量查询所有类型的使用数量 + Map useNumMap = new HashMap<>(); + if (agreementInfo != null && !listL5.isEmpty()) { + // 收集所有需要查询的typeId + List typeIds = listL5.stream() + .map(TypeTreeNode::getTypeId) + .collect(Collectors.toList()); + + // 批量查询使用数量 - 性能优化关键点 + useNumMap = typeMapper.getNumListBatch(agreementInfo.getAgreementId(), typeIds); + } + + // 设置使用数量 + for (TypeTreeNode node : listL5) { + BigDecimal useNum = useNumMap.getOrDefault(node.getTypeId(), BigDecimal.ZERO); + node.setUseNum(useNum); + } List list4ParentIds = listL5.stream().map(TypeTreeNode::getParentId).collect(Collectors.toList()); // 根据第四层parentId 查第三层类型 listL3 = mapper.getUseTypeTreeL3(list4ParentIds); @@ -1466,13 +1481,37 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService { // 原查询结果转换树形结构 groupList = treeBuild.buildTree(); } - } } catch (Exception e) { - AjaxResult.error("类型树-查询失败", e); + log.error("getUseTypeTree方法执行失败", e); + return AjaxResult.error("类型树-查询失败", e); + } finally { + long endTime = System.currentTimeMillis(); + long executionTime = endTime - startTime; + log.info("getUseTypeTree方法执行完成,耗时:{}ms,返回数据量:{}", executionTime, groupList.size()); } return AjaxResult.success(groupList); } + /** + * 缓存班组查询 - 性能优化 + * @param teamName 班组名称 + * @return 班组信息 + */ + private BmTeam getTeamByNameCached(String teamName) { + BmTeam bmTeam = new BmTeam(); + bmTeam.setTeamName(teamName); + return bmTeamMapper.selectByName(bmTeam); + } + + /** + * 缓存协议信息查询 - 性能优化 + * @param bean 查询参数 + * @return 协议信息 + */ + private BmAgreementInfo getAgreementInfoCached(MaterialLeaseApplyInfo bean) { + return materialLeaseInfoMapper.getAgreeId(bean); + } + /** * 根据班组和工程id查询领料机具 * @param dto diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/ma/mapper/TypeMapper.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/ma/mapper/TypeMapper.java index 2cfd0011..37e8c049 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/ma/mapper/TypeMapper.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/ma/mapper/TypeMapper.java @@ -2,6 +2,7 @@ package com.bonus.material.ma.mapper; import java.math.BigDecimal; import java.util.List; +import java.util.Map; import com.bonus.common.biz.domain.lease.LeaseOutDetails; import com.bonus.material.ma.domain.MaTypeHistory; @@ -211,6 +212,14 @@ public interface TypeMapper { */ Type getNumList(Type type); + /** + * 批量查询数量 - 性能优化 + * @param agreementId 协议ID + * @param typeIds 类型ID列表 + * @return 类型ID到使用数量的映射 + */ + Map getNumListBatch(@Param("agreementId") Long agreementId, @Param("typeIds") List typeIds); + /** * 查询物资类型管理绑定的用户列表 * @param type diff --git a/bonus-modules/bonus-material/src/main/resources/mapper/material/ma/TypeMapper.xml b/bonus-modules/bonus-material/src/main/resources/mapper/material/ma/TypeMapper.xml index 0f37634c..8c9d2701 100644 --- a/bonus-modules/bonus-material/src/main/resources/mapper/material/ma/TypeMapper.xml +++ b/bonus-modules/bonus-material/src/main/resources/mapper/material/ma/TypeMapper.xml @@ -1088,6 +1088,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" mt.type_id + + +