fix (farm): 首页,基地/地块 (补充提交)

- 优先从缓存中获取地块信息
- 项目启动,预加载地块
This commit is contained in:
达富斌
2025-11-12 23:43:35 +08:00
parent 3692ceee4c
commit 297fd4edb2
4 changed files with 161 additions and 43 deletions

View File

@@ -218,34 +218,11 @@ public class HomeServiceImpl implements HomeService {
List<PlotInfoDO> plotList = plotInfoService.getPlotInfos(baseObj.getId(), baseObj.getName()); List<PlotInfoDO> plotList = plotInfoService.getPlotInfos(baseObj.getId(), baseObj.getName());
List<PlotInfo> targetList = BeanUtils.toBean(plotList, PlotInfo.class); List<PlotInfo> targetList = BeanUtils.toBean(plotList, PlotInfo.class);
targetList.stream().forEach(e -> { targetList.forEach(e -> {
List<PlotPopupFrame> plotPopupFrame = this.getPlotPopupFrame(e.getPlotId(), e.getManager()); List<PlotPopupFrame> plotPopupFrame = this.getPlotPopupFrame(e.getPlotId(), e.getManager());
e.setPlotPopupFrames(plotPopupFrame.get(0)); e.setPlotPopupFrames(plotPopupFrame.get(0));
}); });
// for (PlotInfoDO e : plotList) {
// String employeeName = personnelManagementService.getEmployeeNameMap(employeeNameMap, e.getEmployeeId());
// 地块详情 (包含计划信息)
// PlotOrCropInfo plotOrCropInfo = this.getPlotOrCropInfoList(e,"地块详情",employeeName);
// 地块浮框信息
// List<PlotPopupFrame> plotPopupFrame = this.getPlotPopupFrame(e.getId(), employeeNameMap);
// 户主流转信息
// List<PlotLesseeDetailsRespVO> 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; return targetList;
} }

View File

@@ -90,20 +90,31 @@ public class PlotInfoCacheService {
} }
try { 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<Object>) connection -> { List<Object> results = stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (PlotInfoDO plotInfo : plotInfoList) { for (PlotInfoDO plotInfo : plotInfoList) {
if (plotInfo != null) { if (plotInfo != null) {
String plotKey = buildPlotKey(plotInfo.getId()); String plotKey = buildPlotKey(plotInfo.getId());
Map<String, String> hashData = toHashData(plotInfo); Map<String, String> hashData = toHashData(plotInfo);
log.debug("设置Hash键: {}", plotKey);
// 批量设置 Hash 字段 // 批量设置 Hash 字段
connection.hashCommands().hMSet( connection.hashCommands().hMSet(
plotKey.getBytes(StandardCharsets.UTF_8), plotKey.getBytes(StandardCharsets.UTF_8),
hashData.entrySet().stream() hashData.entrySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
e -> e.getKey().getBytes(StandardCharsets.UTF_8), 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() CACHE_TTL.getSeconds()
); );
// 更新索引 // 更新所有索引
updateIndexInPipeline(connection, plotInfo); updateAllIndexesInPipeline(connection, plotInfo);
} }
} }
return null; return null;
}); });
log.debug("批量缓存完成,管道操作结果数量: {}", results.size());
log.debug("批量缓存地块信息(Hash)成功,数量: {}", plotInfoList.size()); log.debug("批量缓存地块信息(Hash)成功,数量: {}", plotInfoList.size());
} catch (Exception e) { } catch (Exception e) {
log.error("批量缓存地块信息失败", e); log.error("批量缓存地块信息失败", e);
} }
@@ -456,19 +469,27 @@ public class PlotInfoCacheService {
// ==================== 管道操作辅助方法 ==================== // ==================== 管道操作辅助方法 ====================
/**
* 更新索引(确保包含集合索引)
*/
private void updateIndexInPipeline(RedisConnection connection, PlotInfoDO plotInfo) { private void updateIndexInPipeline(RedisConnection connection, PlotInfoDO plotInfo) {
// 租户索引集合
if (StringUtils.hasText(plotInfo.getTenantId())) { if (StringUtils.hasText(plotInfo.getTenantId())) {
String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId()); String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId());
connection.setCommands().sAdd(tenantKey.getBytes(StandardCharsets.UTF_8), connection.setCommands().sAdd(tenantKey.getBytes(StandardCharsets.UTF_8),
plotInfo.getId().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())) { if (StringUtils.hasText(plotInfo.getBaseId())) {
String baseKey = buildBasePlotsKey(plotInfo.getBaseId()); String baseKey = buildBasePlotsKey(plotInfo.getBaseId());
connection.setCommands().sAdd(baseKey.getBytes(StandardCharsets.UTF_8), connection.setCommands().sAdd(baseKey.getBytes(StandardCharsets.UTF_8),
plotInfo.getId().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())) { if (StringUtils.hasText(plotInfo.getPlotId())) {
String plotIdIndexKey = buildPlotIdIndexKey(plotInfo.getPlotId()); String plotIdIndexKey = buildPlotIdIndexKey(plotInfo.getPlotId());
connection.stringCommands().set( 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) { private void cleanupIndexesInPipeline(RedisConnection connection, PlotInfoDO plotInfo) {
if (StringUtils.hasText(plotInfo.getTenantId())) { if (StringUtils.hasText(plotInfo.getTenantId())) {
String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId()); String tenantKey = buildTenantPlotsKey(plotInfo.getTenantId());
@@ -519,18 +596,18 @@ public class PlotInfoCacheService {
// ==================== 键构建方法 ==================== // ==================== 键构建方法 ====================
private String buildPlotKey(String id) { private String buildPlotKey(String id) {
return CACHE_PREFIX + "info:" + id; return CACHE_PREFIX + "info:" + id; // plots:info:{id}
} }
private String buildTenantPlotsKey(String tenantId) { private String buildTenantPlotsKey(String tenantId) {
return CACHE_PREFIX + "tenant:" + tenantId; return CACHE_PREFIX + "tenant:" + tenantId; // plots:tenant:{tenantId}
} }
private String buildBasePlotsKey(String baseId) { private String buildBasePlotsKey(String baseId) {
return CACHE_PREFIX + "base:" + baseId; return CACHE_PREFIX + "base:" + baseId; // plots:base:{baseId}
} }
private String buildPlotIdIndexKey(String plotId) { private String buildPlotIdIndexKey(String plotId) {
return CACHE_PREFIX + "index:plotId:" + plotId; return CACHE_PREFIX + "index:plotId:" + plotId; // plots:index:plotId:{plotId}
} }
} }

View File

@@ -56,6 +56,11 @@ public interface PlotInfoService {
*/ */
List<PlotInfoDO> getPlotInfos(String baseId); List<PlotInfoDO> getPlotInfos(String baseId);
List<PlotInfoDO> getPlotInfos(String baseId,String baseName); List<PlotInfoDO> getPlotInfos(String baseId,String baseName);
/**
* 通过基地ID获取地块信息(缓存优先)
*/
List<PlotInfoDO> getPlotInfosByCatch(String baseId);
/** /**
* 地块集合 * 地块集合
* @param baseId * @param baseId

View File

@@ -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.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.baseinfo.BaseInfoDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.plotinfo.PlotInfoDO; 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.baseinfo.BaseInfoMapper;
import cn.iocoder.yudao.module.farm.dal.mysql.plotinfo.PlotInfoMapper; 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.enums.CommonConstants;
import cn.iocoder.yudao.module.farm.service.personnelmanagement.PersonnelManagementService; import cn.iocoder.yudao.module.farm.service.personnelmanagement.PersonnelManagementService;
import cn.iocoder.yudao.module.farm.service.productionplan.ProductionPlanService; import cn.iocoder.yudao.module.farm.service.productionplan.ProductionPlanService;
import cn.iocoder.yudao.module.farm.utils.BigDecimalUtil; import cn.iocoder.yudao.module.farm.utils.BigDecimalUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*; 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.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; 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 芋道源码 * @author 芋道源码
*/ */
@Slf4j
@Service @Service
@Validated @Validated
public class PlotInfoServiceImpl implements PlotInfoService { public class PlotInfoServiceImpl implements PlotInfoService {
@@ -152,12 +149,14 @@ public class PlotInfoServiceImpl implements PlotInfoService {
@Override @Override
public List<PlotInfoDO> getPlotInfos(String baseId) { public List<PlotInfoDO> getPlotInfos(String baseId) {
List<PlotInfoDO> list = this.getPlotInfosByCatch(baseId);
// 员工集合 <id,name> // 员工集合 <id,name>
Map<String, String> employeeNameMap = personnelManagementService.getEmployeeNameMap(); Map<String, String> employeeNameMap = personnelManagementService.getEmployeeNameMap();
List<PlotInfoDO> list = plotInfoMapper.selectList("base_id", baseId);
list.forEach(e -> { list.forEach(e -> {
e.setEmployeeName(employeeNameMap.get(e.getEmployeeId())); e.setEmployeeName(employeeNameMap.get(e.getEmployeeId()));
e.setManager(employeeNameMap.get(e.getEmployeeId()));
e.setPlotArea(BigDecimalUtil.roundedViaMath(e.getPlotArea()));
}); });
return list; return list;
@@ -165,10 +164,10 @@ public class PlotInfoServiceImpl implements PlotInfoService {
@Override @Override
public List<PlotInfoDO> getPlotInfos(String baseId, String baseName) { public List<PlotInfoDO> getPlotInfos(String baseId, String baseName) {
List<PlotInfoDO> list = this.getPlotInfosByCatch(baseId);
// 员工集合 <id,name> // 员工集合 <id,name>
Map<String, String> employeeNameMap = personnelManagementService.getEmployeeNameMap(); Map<String, String> employeeNameMap = personnelManagementService.getEmployeeNameMap();
List<PlotInfoDO> list = plotInfoMapper.selectList("base_id", baseId);
list.forEach(e -> { list.forEach(e -> {
e.setEmployeeName(employeeNameMap.get(e.getEmployeeId())); e.setEmployeeName(employeeNameMap.get(e.getEmployeeId()));
e.setManager(employeeNameMap.get(e.getEmployeeId())); e.setManager(employeeNameMap.get(e.getEmployeeId()));
@@ -179,6 +178,66 @@ public class PlotInfoServiceImpl implements PlotInfoService {
return list; return list;
} }
@Override
public List<PlotInfoDO> getPlotInfosByCatch(String baseId) {
// 参数校验
if (!StringUtils.hasText(baseId)) {
return Collections.emptyList();
}
List<PlotInfoDO> 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<PlotInfoDO> fallbackToDatabase(String baseId) {
try {
List<PlotInfoDO> result = plotInfoMapper.selectList("base_id", baseId);
return result != null ? result : Collections.emptyList();
} catch (Exception e) {
log.error("降级查询数据库也失败,基地ID: {}", baseId, e);
return Collections.emptyList();
}
}
@Override @Override
public List<PlotInfoDO> getPlotInfos() { public List<PlotInfoDO> getPlotInfos() {
LambdaQueryWrapper<PlotInfoDO> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<PlotInfoDO> queryWrapper = new LambdaQueryWrapper<>();