From 297fd4edb207fdb32554a8222e702f95e5840d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=BE=E5=AF=8C=E6=96=8C?= Date: Wed, 12 Nov 2025 23:43:35 +0800 Subject: [PATCH] =?UTF-8?q?fix=20(farm):=20=E9=A6=96=E9=A1=B5,=E5=9F=BA?= =?UTF-8?q?=E5=9C=B0/=E5=9C=B0=E5=9D=97=20(=E8=A1=A5=E5=85=85=E6=8F=90?= =?UTF-8?q?=E4=BA=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优先从缓存中获取地块信息 - 项目启动,预加载地块 --- .../farm/service/home/HomeServiceImpl.java | 25 +---- .../plotinfo/PlotInfoCacheService.java | 93 +++++++++++++++++-- .../service/plotinfo/PlotInfoService.java | 5 + .../service/plotinfo/PlotInfoServiceImpl.java | 81 +++++++++++++--- 4 files changed, 161 insertions(+), 43 deletions(-) diff --git a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/home/HomeServiceImpl.java b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/home/HomeServiceImpl.java index 99e0afd9..718c788f 100644 --- a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/home/HomeServiceImpl.java +++ b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/home/HomeServiceImpl.java @@ -218,34 +218,11 @@ public class HomeServiceImpl implements HomeService { List plotList = plotInfoService.getPlotInfos(baseObj.getId(), baseObj.getName()); List targetList = BeanUtils.toBean(plotList, PlotInfo.class); - targetList.stream().forEach(e -> { + targetList.forEach(e -> { List plotPopupFrame = this.getPlotPopupFrame(e.getPlotId(), e.getManager()); e.setPlotPopupFrames(plotPopupFrame.get(0)); }); -// for (PlotInfoDO e : plotList) { -// String employeeName = personnelManagementService.getEmployeeNameMap(employeeNameMap, e.getEmployeeId()); - // 地块详情 (包含计划信息) -// PlotOrCropInfo plotOrCropInfo = this.getPlotOrCropInfoList(e,"地块详情",employeeName); - // 地块浮框信息 -// List plotPopupFrame = this.getPlotPopupFrame(e.getId(), employeeNameMap); - // 户主流转信息 -// List lesseeList = plotInfoService.getPlotLesseeList(e.getId()); - -// PlotInfo t = new PlotInfo(); -// t.setBaseId(e.getBaseId()); -// t.setBaseName(baseObj.getName()); -// t.setPlotId(e.getPlotId()); -// t.setPlotName(e.getPlotName()); -// t.setPlotArea(BigDecimalUtil.roundedViaMath(e.getPlotArea())); -// t.setManager(employeeName); -// t.setPoint(e.getPoint()); -// t.setPlotColor(e.getPlotColor()); -// t.setPlotPopupFrames(plotPopupFrame.get(0)); -// t.setPlotOrCropInfo(plotOrCropInfo); -// t.setLesseeList(lesseeList); -// targetList.add(t); -// } return targetList; } diff --git a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoCacheService.java b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoCacheService.java index 978aac16..ce38dbbe 100644 --- a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoCacheService.java +++ b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoCacheService.java @@ -90,20 +90,31 @@ public class PlotInfoCacheService { } try { + // 添加调试日志 + log.debug("开始批量缓存地块信息,数量: {}", plotInfoList.size()); + for (PlotInfoDO plotInfo : plotInfoList) { + if (plotInfo != null) { + log.debug("处理地块: id={}, tenantId={}, baseId={}, plotId={}", + plotInfo.getId(), plotInfo.getTenantId(), plotInfo.getBaseId(), plotInfo.getPlotId()); + } + } + // 使用管道批量操作 - stringRedisTemplate.executePipelined((RedisCallback) connection -> { + List results = stringRedisTemplate.executePipelined((RedisCallback) connection -> { for (PlotInfoDO plotInfo : plotInfoList) { if (plotInfo != null) { String plotKey = buildPlotKey(plotInfo.getId()); Map hashData = toHashData(plotInfo); + log.debug("设置Hash键: {}", plotKey); + // 批量设置 Hash 字段 connection.hashCommands().hMSet( plotKey.getBytes(StandardCharsets.UTF_8), hashData.entrySet().stream() .collect(Collectors.toMap( e -> e.getKey().getBytes(StandardCharsets.UTF_8), - e -> e.getValue().getBytes(StandardCharsets.UTF_8) + e -> e.getValue() != null ? e.getValue().getBytes(StandardCharsets.UTF_8) : "".getBytes() )) ); @@ -113,14 +124,16 @@ public class PlotInfoCacheService { CACHE_TTL.getSeconds() ); - // 更新索引 - updateIndexInPipeline(connection, plotInfo); + // 更新所有索引 + updateAllIndexesInPipeline(connection, plotInfo); } } return null; }); + log.debug("批量缓存完成,管道操作结果数量: {}", results.size()); log.debug("批量缓存地块信息(Hash)成功,数量: {}", plotInfoList.size()); + } catch (Exception e) { log.error("批量缓存地块信息失败", e); } @@ -456,19 +469,27 @@ public class PlotInfoCacheService { // ==================== 管道操作辅助方法 ==================== + /** + * 更新索引(确保包含集合索引) + */ private void updateIndexInPipeline(RedisConnection connection, PlotInfoDO plotInfo) { + // 租户索引集合 if (StringUtils.hasText(plotInfo.getTenantId())) { String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId()); connection.setCommands().sAdd(tenantKey.getBytes(StandardCharsets.UTF_8), plotInfo.getId().getBytes(StandardCharsets.UTF_8)); + connection.keyCommands().expire(tenantKey.getBytes(StandardCharsets.UTF_8), CACHE_TTL.getSeconds()); } + // 基地索引集合 if (StringUtils.hasText(plotInfo.getBaseId())) { String baseKey = buildBasePlotsKey(plotInfo.getBaseId()); connection.setCommands().sAdd(baseKey.getBytes(StandardCharsets.UTF_8), plotInfo.getId().getBytes(StandardCharsets.UTF_8)); + connection.keyCommands().expire(baseKey.getBytes(StandardCharsets.UTF_8), CACHE_TTL.getSeconds()); } + // plotId 索引 if (StringUtils.hasText(plotInfo.getPlotId())) { String plotIdIndexKey = buildPlotIdIndexKey(plotInfo.getPlotId()); connection.stringCommands().set( @@ -480,6 +501,62 @@ public class PlotInfoCacheService { } } + /** + * 在管道中更新所有索引 - 修复版本 + */ + private void updateAllIndexesInPipeline(RedisConnection connection, PlotInfoDO plotInfo) { + // 1. 更新租户索引集合 - 确保这里正确执行 + if (StringUtils.hasText(plotInfo.getTenantId())) { + String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId()); + log.debug("添加租户索引: {} -> {}", tenantKey, plotInfo.getId()); + + connection.setCommands().sAdd( + tenantKey.getBytes(StandardCharsets.UTF_8), + plotInfo.getId().getBytes(StandardCharsets.UTF_8) + ); + // 设置集合的过期时间 + connection.keyCommands().expire( + tenantKey.getBytes(StandardCharsets.UTF_8), + CACHE_TTL.getSeconds() + ); + } else { + log.debug("地块 {} 没有tenantId,跳过租户索引", plotInfo.getId()); + } + + // 2. 更新基地索引集合 + if (StringUtils.hasText(plotInfo.getBaseId())) { + String baseKey = buildBasePlotsKey(plotInfo.getBaseId()); + log.debug("添加基地索引: {} -> {}", baseKey, plotInfo.getId()); + + connection.setCommands().sAdd( + baseKey.getBytes(StandardCharsets.UTF_8), + plotInfo.getId().getBytes(StandardCharsets.UTF_8) + ); + // 设置集合的过期时间 + connection.keyCommands().expire( + baseKey.getBytes(StandardCharsets.UTF_8), + CACHE_TTL.getSeconds() + ); + } else { + log.debug("地块 {} 没有baseId,跳过基地索引", plotInfo.getId()); + } + + // 3. 更新 plotId 到主键的映射索引 + if (StringUtils.hasText(plotInfo.getPlotId())) { + String plotIdIndexKey = buildPlotIdIndexKey(plotInfo.getPlotId()); + log.debug("添加plotId索引: {} -> {}", plotIdIndexKey, plotInfo.getId()); + + connection.stringCommands().set( + plotIdIndexKey.getBytes(StandardCharsets.UTF_8), + plotInfo.getId().getBytes(StandardCharsets.UTF_8), + Expiration.from(CACHE_TTL), + RedisStringCommands.SetOption.UPSERT + ); + } else { + log.debug("地块 {} 没有plotId,跳过plotId索引", plotInfo.getId()); + } + } + private void cleanupIndexesInPipeline(RedisConnection connection, PlotInfoDO plotInfo) { if (StringUtils.hasText(plotInfo.getTenantId())) { String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId()); @@ -519,18 +596,18 @@ public class PlotInfoCacheService { // ==================== 键构建方法 ==================== private String buildPlotKey(String id) { - return CACHE_PREFIX + "info:" + id; + return CACHE_PREFIX + "info:" + id; // plots:info:{id} } private String buildTenantPlotsKey(String tenantId) { - return CACHE_PREFIX + "tenant:" + tenantId; + return CACHE_PREFIX + "tenant:" + tenantId; // plots:tenant:{tenantId} } private String buildBasePlotsKey(String baseId) { - return CACHE_PREFIX + "base:" + baseId; + return CACHE_PREFIX + "base:" + baseId; // plots:base:{baseId} } private String buildPlotIdIndexKey(String plotId) { - return CACHE_PREFIX + "index:plotId:" + plotId; + return CACHE_PREFIX + "index:plotId:" + plotId; // plots:index:plotId:{plotId} } } diff --git a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoService.java b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoService.java index 60fedfc0..ff503de5 100644 --- a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoService.java +++ b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoService.java @@ -56,6 +56,11 @@ public interface PlotInfoService { */ List getPlotInfos(String baseId); List getPlotInfos(String baseId,String baseName); + /** + * 通过基地ID获取地块信息(缓存优先) + */ + List getPlotInfosByCatch(String baseId); + /** * 地块集合 * @param baseId diff --git a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoServiceImpl.java b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoServiceImpl.java index 0138500a..c355524e 100644 --- a/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoServiceImpl.java +++ b/yudao-module-farm/src/main/java/cn/iocoder/yudao/module/farm/service/plotinfo/PlotInfoServiceImpl.java @@ -9,26 +9,22 @@ import cn.iocoder.yudao.module.farm.controller.admin.managementcenter.baseinfo.v import cn.iocoder.yudao.module.farm.controller.admin.managementcenter.baseinfo.vo.wx.RegularPlotInfoRespVO; import cn.iocoder.yudao.module.farm.dal.dataobject.baseinfo.BaseInfoDO; import cn.iocoder.yudao.module.farm.dal.dataobject.plotinfo.PlotInfoDO; -import cn.iocoder.yudao.module.farm.dal.dataobject.plotinfo.PlotLesseeDO; import cn.iocoder.yudao.module.farm.dal.mysql.baseinfo.BaseInfoMapper; import cn.iocoder.yudao.module.farm.dal.mysql.plotinfo.PlotInfoMapper; -import cn.iocoder.yudao.module.farm.dal.mysql.plotinfo.PlotLesseeMapper; import cn.iocoder.yudao.module.farm.enums.CommonConstants; import cn.iocoder.yudao.module.farm.service.personnelmanagement.PersonnelManagementService; import cn.iocoder.yudao.module.farm.service.productionplan.ProductionPlanService; import cn.iocoder.yudao.module.farm.utils.BigDecimalUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.commons.lang3.ObjectUtils; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; import java.util.*; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; @@ -40,6 +36,7 @@ import static cn.iocoder.yudao.module.farm.enums.ErrorCodeConstants.*; * * @author 芋道源码 */ +@Slf4j @Service @Validated public class PlotInfoServiceImpl implements PlotInfoService { @@ -152,12 +149,14 @@ public class PlotInfoServiceImpl implements PlotInfoService { @Override public List getPlotInfos(String baseId) { + List list = this.getPlotInfosByCatch(baseId); + // 员工集合 Map employeeNameMap = personnelManagementService.getEmployeeNameMap(); - - List list = plotInfoMapper.selectList("base_id", baseId); list.forEach(e -> { e.setEmployeeName(employeeNameMap.get(e.getEmployeeId())); + e.setManager(employeeNameMap.get(e.getEmployeeId())); + e.setPlotArea(BigDecimalUtil.roundedViaMath(e.getPlotArea())); }); return list; @@ -165,10 +164,10 @@ public class PlotInfoServiceImpl implements PlotInfoService { @Override public List getPlotInfos(String baseId, String baseName) { + List list = this.getPlotInfosByCatch(baseId); + // 员工集合 Map employeeNameMap = personnelManagementService.getEmployeeNameMap(); - - List list = plotInfoMapper.selectList("base_id", baseId); list.forEach(e -> { e.setEmployeeName(employeeNameMap.get(e.getEmployeeId())); e.setManager(employeeNameMap.get(e.getEmployeeId())); @@ -179,6 +178,66 @@ public class PlotInfoServiceImpl implements PlotInfoService { return list; } + @Override + public List getPlotInfosByCatch(String baseId) { + // 参数校验 + if (!StringUtils.hasText(baseId)) { + return Collections.emptyList(); + } + + List result = null; + + try { + // 1. 首先尝试从缓存获取 + result = pCatchService.getPlotsByBaseId(baseId); + + // 2. 如果缓存中存在数据,直接返回 + if (!CollectionUtils.isEmpty(result)) { + log.debug("从缓存获取地块信息成功,基地ID: {},数量: {}", baseId, result.size()); + return result; + } + + log.debug("缓存未命中,从数据库查询地块信息,基地ID: {}", baseId); + + // 3. 缓存中没有,从数据库查询 + result = plotInfoMapper.selectList("base_id", baseId); + + if (CollectionUtils.isEmpty(result)) { + log.debug("数据库中也未查询到地块信息,基地ID: {}", baseId); + return Collections.emptyList(); + } + + // 4. 将数据库查询结果写入缓存 + try { + pCatchService.batchCreateOrUpdatePlotInfo(result); + log.debug("成功将地块信息写入缓存,基地ID: {},数量: {}", baseId, result.size()); + } catch (Exception cacheException) { + // 缓存写入失败不应该影响主流程,只记录日志 + log.warn("写入缓存失败,但不影响数据返回,基地ID: {}", baseId, cacheException); + } + + return result; + + } catch (Exception e) { + log.error("获取地块信息异常,基地ID: {}", baseId, e); + // 发生异常时,尝试从数据库直接查询作为降级方案 + return fallbackToDatabase(baseId); + } + } + + /** + * 降级方案:直接从数据库查询 + */ + private List fallbackToDatabase(String baseId) { + try { + List result = plotInfoMapper.selectList("base_id", baseId); + return result != null ? result : Collections.emptyList(); + } catch (Exception e) { + log.error("降级查询数据库也失败,基地ID: {}", baseId, e); + return Collections.emptyList(); + } + } + @Override public List getPlotInfos() { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();