fix (farm): 集成测试,修复bug

- 首页统计
- 模型管理-模型周期 添加字段
- 添加限制地块删除,编辑
This commit is contained in:
达富斌
2025-09-11 15:23:27 +08:00
parent ad874184c8
commit ee44e537fa
11 changed files with 169 additions and 67 deletions

View File

@@ -150,6 +150,7 @@ public class CropFarmingCenterController {
public CommonResult<Boolean> restartPlanning(@RequestBody ProductionPlanUpdateReqVO updateReqVO) {
updateReqVO.setPlanStatus(CommonConstants.PLAN_STATUS_2);
// TODO: 2025/8/29 这里存在业务逻辑问题 ,等产品确认
// 如果地块, 物种 模型 被删除 ,则不可以重启
cropFarmingCenterService.restartProductionPlan(updateReqVO);
return success(true);
}

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.farm.dal.dataobject.modelcycle;
import lombok.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
@@ -39,6 +40,14 @@ public class ModelCycleDO extends BaseDO {
* 阶段天数
*/
private Integer stage;
/**
* 周期范围-开始日期 2025年09月10日
*/
private LocalDate cycleRangeStartDate;
/**
* 周期范围-结束日期 2025年09月10日
*/
private LocalDate cycleRangeEndDate;
/**
* 物候期描述
*/

View File

@@ -50,6 +50,7 @@ public class ModelManagementDO extends BaseDO {
* 生命周期
*/
private String cycle;
private String cycleName;
/**
* 产量参考
*/
@@ -91,12 +92,15 @@ public class ModelManagementDO extends BaseDO {
*/
private String taskId;
private int isRelease;
/**
* 引用次数
*/
private int quoteCount;
/**
* 物种图片(数据库存储为逗号分隔的字符串)
*/
private String speciesImage;
private String cycleName;
/**
* 物种图片集合(业务层使用)
*/

View File

@@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.farm.dal.dataobject.modelmanagement.ModelManageme
import java.util.List;
import static cn.iocoder.yudao.module.farm.enums.CommonConstants.LIFE_CYCLE_MAP;
/**
* 模型管理转换工具类
* 确保所有接口返回格式统一
@@ -30,7 +32,7 @@ public class ModelManagementConvertUtil {
respVO.setSpeciesId(modelManagementDO.getSpeciesId());
respVO.setSpeciesName(modelManagementDO.getSpeciesName());
respVO.setCycle(modelManagementDO.getCycle());
respVO.setCycleName(modelManagementDO.getCycleName());
respVO.setCycleName(LIFE_CYCLE_MAP.get(modelManagementDO.getCycle()));
respVO.setHarvest(modelManagementDO.getHarvest());
respVO.setDescription(modelManagementDO.getDescription());
respVO.setCreateTime(modelManagementDO.getCreateTime());

View File

@@ -99,6 +99,8 @@ public class BaseInfoServiceImpl implements BaseInfoService {
String baseId = updateReqVO.getId();
// 校验存在
validateBaseInfoExists(baseId);
// 校验基地面积
validateBaseArea(baseId, updateReqVO.getArea());
// 更新
BaseInfoDO updateObj = BeanUtils.toBean(updateReqVO, BaseInfoDO.class);
baseInfoMapper.updateById(updateObj);
@@ -134,6 +136,17 @@ public class BaseInfoServiceImpl implements BaseInfoService {
}
}
/**
* 修改后的基地面积 不可小于 其地块面积总和
* @param id
* @param updateBaseArea
*/
private void validateBaseArea(String id,BigDecimal updateBaseArea) {
if (!plotInfoService.validateTotalPlotArea(id,updateBaseArea)) {
throw exception(BASE_PLOT_AREA_ERRORS);
}
}
@Override
public BaseInfoDO getBaseInfo(String id) {
return baseInfoMapper.selectById(id);

View File

@@ -84,6 +84,7 @@ public class CropFarmingCenterServiceImpl implements CropFarmingCenterService {
@Override
public void createProductionPlan(ProductionPlanSaveReqVO reqVO) {
// 模版类型-模型ID
String speciesModelId = reqVO.getSpeciesModelId();
Map<String, String> baseNameMap = baseInfoMapper.selectList().stream().collect(toMap(BaseInfoDO::getId, BaseInfoDO::getName, (v1, v2) -> v1));
Map<String, String> plotNameMap = plotInfoService.getPlotInfos().stream().collect(toMap(PlotInfoDO::getPlotId, PlotInfoDO::getPlotName, (v1, v2) -> v1));
@@ -91,40 +92,38 @@ public class CropFarmingCenterServiceImpl implements CropFarmingCenterService {
List<ProductionPlanDO> list = new ArrayList<>();
for (ProductionPlanSaveReqVO.CultivationArea e : cultivationAreaList) {
if (speciesModelId.equals("")) {
continue; // TODO: 2025/9/10 放在外面
}
// 引用模型-模型ID
String taskId = IdUtil.fastUUID();
// 随机字符串 + 地块名称
String planNumber = StrHelperUtil.generate() + "-" + plotNameMap.getOrDefault(e.getPlotId(), "");
ProductionPlanDO obj = new ProductionPlanDO();
obj.setId(taskId);
obj.setPlanNumber(planNumber);
obj.setPlantSpeciesCode(reqVO.getPlantSpeciesCode());
obj.setProductionStartDate(reqVO.getProductionStartDate());
obj.setProductionEndDate(reqVO.getProductionEndDate());
obj.setHarvestStartDate(reqVO.getHarvestStartDate());
obj.setPlanStatus(CommonConstants.PLAN_STATUS_1);
obj.setPlanSubStatus(CommonConstants.PLAN_STATUS_1);
obj.setPlantingDate(e.getPlantingDate());
obj.setPlantingQuantity(e.getPlantingQuantity());
obj.setPredictedYields(e.getPredictedYields());
obj.setTotalArea(e.getTotalArea());
obj.setPredictedArea(e.getPredictedArea());
// obj.setRemainingArea(e.getRemainingArea());
obj.setUsagePercentage(e.getUsagePercentage());
obj.setRemark(e.getRemark());
obj.setBaseId(e.getBaseId());
obj.setBaseName(baseNameMap.getOrDefault(e.getBaseId(), ""));
obj.setPlotId(e.getPlotId());
obj.setPlotName(plotNameMap.getOrDefault(e.getPlotId(), ""));
obj.setTenantId(getLoginUser().getTenantId().toString());
// 随机字符串 + 地块名称
obj.setPlanNumber(StrHelperUtil.generate() + "-" + plotNameMap.getOrDefault(e.getPlotId(), ""));
String taskId = IdUtil.fastUUID();
obj.setId(taskId);
// 创建模型案例
speciesModelId = modelManagementService.createModelManagementCase(speciesModelId, taskId);
if (speciesModelId.equals("")) {
return;
}
obj.setSpeciesModelId(speciesModelId);
// 创建模型案例 (创建引用模版)
String newSpeciesModelId = modelManagementService.createModelManagementCase(speciesModelId, obj);
obj.setSpeciesModelId(newSpeciesModelId);
list.add(obj);
}
productionPlanMapper.insertBatch(list);
@@ -227,6 +226,7 @@ public class CropFarmingCenterServiceImpl implements CropFarmingCenterService {
String plotId = productionPlanRespVO.getPlotId();
PlotInfoDO plotIObj = plotInfoService.getPlotInfoByPlotId(plotId);
productionPlanRespVO.setPlotManger(employeeNameMap.getOrDefault(plotIObj.getEmployeeId(),"系统"));
// 管理周期
return productionPlanRespVO;
}
@@ -363,8 +363,7 @@ public class CropFarmingCenterServiceImpl implements CropFarmingCenterService {
if (baseObj != null) {
String employeeId = baseObj.getManager();
ModelManagementDO modelManagementDo = modelManagementService.getModelManagement("id", planObj.getSpeciesModelId());
String managementCycleName = "";
String managementCycleName = modelManagementService.getModelCycleName(planObj.getSpeciesModelId());
ProductionPlanResp2VO.ModelInfo modelInfo = new ProductionPlanResp2VO.ModelInfo();
modelInfo.setId(modelManagementDo.getId());

View File

@@ -45,7 +45,6 @@ public class ModelCycleServiceImpl implements ModelCycleService {
modelCycle.setId(IdUtil.fastUUID());
modelCycle.setTenantId(String.valueOf(user.getTenantId()));
modelCycleMapper.insert(modelCycle);
// 返回
return modelCycle.getId();
}

View File

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.farm.controller.admin.modelmanagement.vo.ModelMan
import cn.iocoder.yudao.module.farm.controller.admin.modelmanagement.vo.ModelManagementSaveReqVO;
import cn.iocoder.yudao.module.farm.dal.dataobject.modelfarmingoperation.ModelFarmingOperationDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.modelmanagement.ModelManagementDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.productionplan.ProductionPlanDO;
import jakarta.validation.Valid;
import java.util.List;
@@ -26,7 +27,7 @@ public interface ModelManagementService {
* @return 编号
*/
String createModelManagement(@Valid ModelManagementSaveReqVO createReqVO);
String createModelManagementCase(String id,String taskId);
String createModelManagementCase(String id, ProductionPlanDO obj);
/**
* 更新模型管理

View File

@@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.farm.dal.dataobject.modelmanagement.ModelManageme
import cn.iocoder.yudao.module.farm.dal.dataobject.modelpestdiseaseweed.ModelPestDiseaseWeedDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.modelweatherdisaster.ModelWeatherDisasterDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.personnelmanagement.SystemUserDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.productionplan.ProductionPlanDO;
import cn.iocoder.yudao.module.farm.dal.dataobject.specieslibrary.SpeciesLibraryDO;
import cn.iocoder.yudao.module.farm.dal.mysql.farmingrecord.FarmingRecordMapper;
import cn.iocoder.yudao.module.farm.dal.mysql.modelcycle.ModelCycleMapper;
@@ -43,6 +44,7 @@ import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
@@ -86,29 +88,33 @@ public class ModelManagementServiceImpl implements ModelManagementService {
@Override
public String createModelManagement(ModelManagementSaveReqVO createReqVO) {
LoginUser user = getLoginUser();
SpeciesLibraryDO speciesLibrary = speciesLibraryService.getSpeciesLibrary(createReqVO.getSpeciesId());
// 物种类型拼接物种名称
String speciesName = speciesLibrary.getSpeciesName() + "/" + speciesLibrary.getCultivarName();
// 物种生命周期 -中文
String cycleName = LIFE_CYCLE_MAP.getOrDefault(speciesLibrary.getLifeCycle().toString(),"");
// 插入
ModelManagementDO modelManagement = BeanUtils.toBean(createReqVO, ModelManagementDO.class);
modelManagement.setId(IdUtil.fastUUID());
modelManagement.setTenantId(String.valueOf(user.getTenantId()));
modelManagement.setIsRelease(0);
modelManagement.setType(CommonConstants.MODEL_TYPE_1);
SpeciesLibraryDO speciesLibrary = speciesLibraryService.getSpeciesLibrary(createReqVO.getSpeciesId());
modelManagement.setSpeciesName(speciesLibrary.getSpeciesName() + "/" + speciesLibrary.getCultivarName());
modelManagement.setSpeciesName(speciesName);
modelManagement.setCycle(speciesLibrary.getLifeCycle().toString());
modelManagement.setCycleName(LIFE_CYCLE_MAP.getOrDefault(speciesLibrary.getLifeCycle().toString(),""));
modelManagement.setCycleName(cycleName);
// 处理suitablePlantingAreas字段,优先使用集合
if (createReqVO.getSuitablePlantingAreas() != null && !createReqVO.getSuitablePlantingAreas().isEmpty()) {
modelManagement.setSuitablePlantingAreasList(createReqVO.getSuitablePlantingAreas());
}
// 处理speciesImage字段,优先使用图片集合
SpeciesLibraryDO libraryDO = speciesLibraryService.getSpeciesLibrary(createReqVO.getSpeciesId());
if (libraryDO.getImageList() != null && !libraryDO.getImageList().isEmpty()) {
modelManagement.setSpeciesImageList(libraryDO.getImageList());
if (speciesLibrary.getImageList() != null && !speciesLibrary.getImageList().isEmpty()) {
modelManagement.setSpeciesImageList(speciesLibrary.getImageList());
} else if (createReqVO.getSpeciesImage() != null) {
modelManagement.setSpeciesImageList(libraryDO.getImageList());
modelManagement.setSpeciesImageList(speciesLibrary.getImageList());
}
modelManagement.setSpeciesImage(libraryDO.getImage());
modelManagement.setSpeciesImage(speciesLibrary.getImage());
modelManagementMapper.insert(modelManagement);
// 返回
@@ -116,16 +122,20 @@ public class ModelManagementServiceImpl implements ModelManagementService {
}
@Override
public String createModelManagementCase(String id, String taskId) {
public String createModelManagementCase(String id, ProductionPlanDO obj) {
String taskId = obj.getId();
LocalDate productionStartDate = obj.getProductionStartDate();
LocalDate productionEndDate = obj.getProductionEndDate();
// 1. 查询原模型(类型为模型模板)
ModelManagementDO modelManagementDO = modelManagementMapper.selectOne(
ModelManagementDO::getId, id,
ModelManagementDO::getType, CommonConstants.MODEL_TYPE_1
);
if (modelManagementDO == null) {
return "";
}
modelManagementDO.setQuoteCount(modelManagementDO.getQuoteCount() + 1);
modelManagementMapper.updateById(modelManagementDO);
// 2. 创建新的模型引用案例
@@ -141,13 +151,23 @@ public class ModelManagementServiceImpl implements ModelManagementService {
// 4. 复制模型周期及其子集
List<ModelCycleDO> originalCycles = modelCycleMapper.selectListByModelId(id);
long originalStageCount_start = 0;
long originalStageCount_end = 0;
for (ModelCycleDO originalCycle : originalCycles) {
// 复制周期
ModelCycleDO newCycle = BeanUtils.toBean(originalCycle, ModelCycleDO.class);
String newCycleId = IdUtil.fastUUID();
newCycle.setId(newCycleId);
newCycle.setModelId(newModelId);
// 设置周期范围-开始.结束日期
originalStageCount_end += originalCycle.getStage(); // 阶段天数
LocalDate cycleRangeStartDate = productionStartDate.plusDays(originalStageCount_start);
LocalDate cycleRangeEndDate = productionEndDate.plusDays(originalStageCount_end);
newCycle.setCycleRangeStartDate(cycleRangeStartDate);
newCycle.setCycleRangeEndDate(cycleRangeEndDate);
modelCycleMapper.insert(newCycle);
// 推进阶段天数
originalStageCount_start = originalStageCount_end;
// 复制环境指标
List<ModelEnvironmentIndexDO> environmentIndexes = modelEnvironmentIndexMapper.selectByCycleId(originalCycle.getId());
@@ -339,7 +359,7 @@ public class ModelManagementServiceImpl implements ModelManagementService {
);
if (modelManagementDO == null) {
return "";
throw exception(MODEL_MANAGEMENT_NOT_EXISTS);
}
// 2. 创建新的模型引用案例
@@ -454,9 +474,37 @@ public class ModelManagementServiceImpl implements ModelManagementService {
@Override
public String getModelCycleName(String modelId) {
// TODO: 2025/9/9 这里处理当前日期是否在 排查开始日期+持续天数的范围内
List<ModelCycleDO> cycleDOS = modelCycleMapper.selectList("model_id", modelId);
return cycleDOS.stream().findFirst().get().getName();
LambdaQueryWrapperX<ModelCycleDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(ModelCycleDO::getModelId, modelId);
wrapperX.orderByAsc(ModelCycleDO::getCreateTime);
List<ModelCycleDO> cycleDOS = modelCycleMapper.selectList(wrapperX);
String managementCycleName = "";
LocalDate currentDate = LocalDate.now();
for (ModelCycleDO c : cycleDOS) {
LocalDate cycleRangeStartDate = c.getCycleRangeStartDate();
LocalDate cycleRangeEndDate = c.getCycleRangeEndDate();
String name = c.getName();
if (cycleRangeStartDate == null ||cycleRangeEndDate == null) {
continue;
}
// 如果今天在 startDate 与 endDate 的范围内
if (!currentDate.isBefore(cycleRangeStartDate) && !currentDate.isAfter(cycleRangeEndDate)) {
managementCycleName = name;
break;
}
// 如果今天在 startDate 之前
else if (currentDate.isBefore(cycleRangeStartDate)) {
managementCycleName = name;
break;
}
// 如果今天在 endDate 之后
else if (currentDate.isAfter(cycleRangeEndDate)) {
managementCycleName = name;
}
}
return managementCycleName;
}
@Override

View File

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.farm.dal.dataobject.plotinfo.PlotInfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import jakarta.validation.Valid;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -14,7 +15,6 @@ import java.util.List;
* @author 芋道源码
*/
public interface PlotInfoService {
/**
* 创建地块
*
@@ -29,21 +29,18 @@ public interface PlotInfoService {
* @param updateReqVO 更新信息
*/
void updatePlotInfo(@Valid PlotInfoSaveReqVO updateReqVO);
/**
* 删除地块
*
* @param id 编号
*/
void deletePlotInfo(String id);
/**
* 批量删除地块
*
* @param ids 编号
*/
void deletePlotInfoListByIds(List<String> ids);
/**
* 获得地块
*
@@ -52,7 +49,17 @@ public interface PlotInfoService {
*/
PlotInfoDO getPlotInfo(String id);
/**
* 通过基地ID获取地块集合
* @param baseId
* @return
*/
List<PlotInfoDO> getPlotInfos(String baseId);
/**
* 地块集合
* @param baseId
* @return
*/
List<PlotInfoDO> getPlotInfos();
/**
* 获得地块分页
@@ -61,26 +68,29 @@ public interface PlotInfoService {
* @return 地块分页
*/
PageResult<PlotInfoDO> getPlotInfoPage(PlotInfoPageReqVO pageReqVO);
/**
* 通过基地ID获取地块
* @param plotId
* @return
*/
PlotInfoDO getPlotInfoByPlotId(String plotId);
/**
* 批量删除
* @param id
*/
void deleteByBaseId(String baseId);
/**
* 租户-地块总数
* @param tenantId
* @return
*/
Integer getPlotTotalCount(String tenantId);
/**
* 基地下存在地块,则不可删除
* @param id
* @return
*/
boolean validatePlotExistByBaseId(String id);
boolean validateTotalPlotArea(String baseId, BigDecimal baseArea);
}

View File

@@ -24,8 +24,7 @@ import java.util.List;
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.module.farm.enums.ErrorCodeConstants.PLOT_AREA_ERRORS;
import static cn.iocoder.yudao.module.farm.enums.ErrorCodeConstants.PLOT_INFO_NOT_EXISTS;
import static cn.iocoder.yudao.module.farm.enums.ErrorCodeConstants.*;
/**
* 地块 Service 实现类
@@ -46,12 +45,8 @@ public class PlotInfoServiceImpl implements PlotInfoService {
@Override
public String createPlotInfo(PlotInfoSaveReqVO createReqVO) {
LoginUser user = getLoginUser();
// 验证地块面积总和 需要小于或等于 基地面积
boolean flag = this.validateTotalPlotArea(createReqVO.getBaseId());
if (flag) {
throw exception(PLOT_AREA_ERRORS);
}
// 校验地块面积
validatePlotArea(createReqVO.getBaseId(),null,createReqVO.getPlotArea());
// 插入
PlotInfoDO plotInfo = BeanUtils.toBean(createReqVO, PlotInfoDO.class);
plotInfo.setId(IdUtil.fastUUID());
@@ -67,16 +62,11 @@ public class PlotInfoServiceImpl implements PlotInfoService {
public void updatePlotInfo(PlotInfoSaveReqVO updateReqVO) {
// 校验存在
validatePlotInfoExists(updateReqVO.getId());
// PlotInfoDO sourcePlotInfoDO = plotInfoMapper.selectById(updateReqVO.getId());
// String plotId = sourcePlotInfoDO.getPlotId();
// BigDecimal oldArea = sourcePlotInfoDO.getPlotArea();
// BigDecimal newArea = updateReqVO.getPlotArea();
// 校验地块面积
validatePlotArea(updateReqVO.getBaseId(), updateReqVO.getId(), updateReqVO.getPlotArea());
// 更新
PlotInfoDO updateObj = BeanUtils.toBean(updateReqVO, PlotInfoDO.class);
plotInfoMapper.updateById(updateObj);
// 同步
// productionPlanService.syncPlotAreaToAllPlansByBaseId(plotId,newArea,oldArea);
}
@Override
@@ -88,10 +78,11 @@ public class PlotInfoServiceImpl implements PlotInfoService {
}
// 校验: 存在在种地块时 不允许删除
boolean flag = productionPlanService.validateIncompletePlansBId("plot_id", obj.getPlotId());
// 删除
if (flag) {
plotInfoMapper.deleteById(id);
if (!flag) {
throw exception(BEGIN_AREA_ERROR);
}
// 删除
plotInfoMapper.deleteById(id);
}
@Override
@@ -106,6 +97,13 @@ public class PlotInfoServiceImpl implements PlotInfoService {
}
}
private void validatePlotArea(String baseId, String plotId, BigDecimal updatePlotArea) {
boolean flag = this.validateCurrentPlotArea(baseId, plotId, updatePlotArea);
if (flag) {
throw exception(PLOT_AREA_ERROR);
}
}
@Override
public PlotInfoDO getPlotInfo(String id) {
PlotInfoDO plotInfoDO = plotInfoMapper.selectById(id);
@@ -156,12 +154,7 @@ public class PlotInfoServiceImpl implements PlotInfoService {
return count <= 0;
}
/**
* 验证: 地块总面积不得大于基地面积
*
* @param baseId 基地面积
*/
private boolean validateTotalPlotArea(String baseId) {
private boolean validateCurrentPlotArea(String baseId, String plotId, BigDecimal currentPlotArea) {
BaseInfoDO baseObj = baseInfoMapper.selectOne("id", baseId);
BigDecimal baseArea = baseObj.getArea();
@@ -170,6 +163,29 @@ public class PlotInfoServiceImpl implements PlotInfoService {
return false;
}
BigDecimal plotTotalArea = plotInfoDOS.stream()
.map(PlotInfoDO::getPlotArea)
.filter(area -> area != null) // 重要:过滤null值
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal currentTotalArea = null;
if (plotId == null) {
currentTotalArea = plotTotalArea.add(currentPlotArea);
}else{
PlotInfoDO one = plotInfoMapper.selectOne("id", plotId);
BigDecimal sourcePlotArea = one.getPlotArea();
currentTotalArea = plotTotalArea.subtract(sourcePlotArea).add(currentPlotArea);
}
return currentTotalArea.compareTo(baseArea) > 0;
}
public boolean validateTotalPlotArea(String baseId, BigDecimal baseArea) {
List<PlotInfoDO> plotInfoDOS = plotInfoMapper.selectList("base_id", baseId);
if (plotInfoDOS == null || plotInfoDOS.isEmpty()) {
return false;
}
BigDecimal plotTotalArea = plotInfoDOS.stream()
.map(PlotInfoDO::getPlotArea)
.filter(area -> area != null) // 重要:过滤null值