feat (farm): 设备商接口重构

- 设备厂接口 - 吉水两山 代码临时性解决现场问题
This commit is contained in:
达富斌
2025-09-29 16:06:51 +08:00
parent b28b1463f7
commit e7cf08ff2e
28 changed files with 456 additions and 187 deletions

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.farm.controller.admin.api.vo.*;
import cn.iocoder.yudao.module.farm.dal.dataobject.deviceinfo.DeviceInfoDO;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.RealTimeDataResponse;
import cn.iocoder.yudao.module.farm.service.baseinfo.BaseInfoService;
import cn.iocoder.yudao.module.farm.service.deviceinfo.DeviceInfoService;
import cn.iocoder.yudao.module.farm.service.farmingrecord.FarmingRecordService;
@@ -137,8 +138,8 @@ public class ApiController {
@PostMapping("/digital-cockpit/chart/device-monitoring")
@Operation(summary = "数字驾驶舱-设备监测")
public CommonResult<DeviceMonitoringRespVO> getDeviceMonitoring(@Valid @RequestBody DeviceMonitoringReqVO req) {
return deviceInfoService.getDeviceMonitoring(req);// TODO: 2025/9/25
public CommonResult<RealTimeDataResponse> getDeviceMonitoring(@Valid @RequestBody DeviceMonitoringReqVO req) {
return deviceInfoService.getDeviceMonitoring(req);
}
@PostMapping("/digital-cockpit/chart/disaster-statistics")

View File

@@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.farm.controller.admin.api.vo;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.RealTimeDataResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @description:
* @author: binda
@@ -14,42 +17,5 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class DeviceMonitoringRespVO {
private String baseName;
private DeviceMonitoringInfo deviceMonitoringInfo ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class DeviceMonitoringInfo{
@Schema(description = "节点编号")
private Integer nodeId;
@Schema(description = "节点名称")
private String nodeName;
@Schema(description = "节点类型")
private Integer nodeType;
// 模拟量1 名称,单位,原值,显示值,报警等级
@Schema(description = "temName" , example = "温度")
private String temName;
@Schema(description = "temUnit" , example = "`C")
private String temUnit;
@Schema(description = "temValue" , example = "28.100000381469727")
private Double temValue;
@Schema(description = "temValueStr" , example = "30.2")
private String temValueStr;
@Schema(description = "temAlarmStatus" , example = "0")
private Integer temAlarmStatus;
// 模拟量2 名称,单位,原值,显示值,报警等级
@Schema(description = "humName" , example = "湿度")
private String humName;
@Schema(description = "humUnit" , example = "%RH")
private String humUnit;
@Schema(description = "humValue" , example = "13.699999809265137")
private String humValue;
@Schema(description = "humValueStr" , example = "13.7")
private String humValueStr;
@Schema(description = "humAlarmStatus" , example = "0")
private String humAlarmStatus;
}
private RealTimeDataResponse realTimeDataList ;
}

View File

@@ -1,9 +1,13 @@
package cn.iocoder.yudao.module.farm.controller.admin.api.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
/**
* @description: 数字驾驶舱-统计图-灾害分析 请求实体
* @author: binda
@@ -13,6 +17,10 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class DisasterAnalysisReqVO {
private String id;
@Schema(description = "基地D")
@NotNull(message = "基地D: 不能为空")
private String baseId;
@Schema(description = "租户ID")
@NotNull(message = "租户ID: 不能为空")
private String tenantId;
}

View File

@@ -1,9 +1,12 @@
package cn.iocoder.yudao.module.farm.controller.admin.api.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @description: 数字驾驶舱-统计图-灾害分析 响应实体
* @author: binda
@@ -13,6 +16,19 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class DisasterAnalysisRespVO {
private String id;
private Integer year;
private List<DisasterReports> disasterReports;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class DisasterReports{
@Schema(description = "月份")
private Integer month;
@Schema(description = "气象类发生次数")
private Integer meteorologyCount;
@Schema(description = "虫草病害类发生次数")
private Integer pestCount;
}
}

View File

@@ -168,7 +168,6 @@ public class CropFarmingCenterController {
@Operation(summary = "生产计划-结束按钮")
@PutMapping("/production-plan/finish")
public CommonResult<Boolean> finishPlanning(@RequestBody ProductionPlanUpdateReqVO updateReqVO) {
updateReqVO.setPlanStatus(CommonConstants.PLAN_STATUS_3);
cropFarmingCenterService.updateProductionPlan(updateReqVO);
return success(true);
}

View File

@@ -114,9 +114,10 @@ public class DeviceInfoController {
@GetMapping("/get")
@Operation(summary = "获得设备信息")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@Parameter(name = "id", description = "编号", required = true)
// @Parameter(name = "dateType", description = "日期类型", required = false)
public CommonResult<DeviceInfoDetailV2RespVO> getDeviceInfo(@RequestParam("id") String id) {
DeviceInfoDetailV2RespVO res = deviceInfoService.getDeviceRealTimeInfo(id);
DeviceInfoDetailV2RespVO res = deviceInfoService.getDeviceRealTimeInfo(id,"dateType");
return success(res);
}

View File

@@ -1,5 +1,8 @@
package cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.HistoryDataResponse;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.RealTimeDataResponse;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.soil.realtime.RealTimeDataRespVO;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
@@ -73,85 +76,8 @@ public class DeviceInfoDetailV2RespVO {
@NoArgsConstructor
public static class SoilInfo{
private String deviceUniqueId;
private RealTimeData realTimeData;
private List<HistoryDataList> historyDataLis;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RealTimeData{
@Schema(description = "设备地址")
private String deviceAddr;
@Schema(description = "设备名称")
private String deviceName;
@Schema(description = "设备状态", example = "online")
private String status;
@Schema(description = "设备类型", example = "soil(土壤墒情)")
private String deviceType;
@Schema(description = "数据集合")
private List<RealData> realDataList;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RealData{
@Schema(description = "节点编号")
private Integer nodeId;
@Schema(description = "节点名称")
private String nodeName;
@Schema(description = "节点类型")
private Integer nodeType;
// 模拟量1 名称,单位,原值,显示值,报警等级
@Schema(description = "temName" , example = "温度")
private String temName;
@Schema(description = "temUnit" , example = "`C")
private String temUnit;
@Schema(description = "temValue" , example = "28.100000381469727")
private Double temValue;
@Schema(description = "temValueStr" , example = "30.2")
private String temValueStr;
@Schema(description = "temAlarmStatus" , example = "0")
private Integer temAlarmStatus;
// 模拟量2 名称,单位,原值,显示值,报警等级
@Schema(description = "humName" , example = "湿度")
private String humName;
@Schema(description = "humUnit" , example = "%RH")
private String humUnit;
@Schema(description = "humValue" , example = "13.699999809265137")
private String humValue;
@Schema(description = "humValueStr" , example = "13.7")
private String humValueStr;
@Schema(description = "humAlarmStatus" , example = "0")
private String humAlarmStatus;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class HistoryDataList {
@Schema(description = "历史记录ID")
private Integer historyId;
@Schema(description = "历史记录时间")
@JsonFormat(pattern = "yyyy-MM-dd hh:MM:ss")
private LocalDateTime recordTime;
@Schema(description = "设备地址")
private String deviceAddress;
@Schema(description = "警告状态")
private Integer alarmStatus;
@Schema(description = "警告状态 (0:正常 1:报警)")
private String alarmStatusName;
@Schema(description = "模拟量1 (字符)")
private String temStr;
@Schema(description = "模拟量1 (值)")
private Double temValue;
@Schema(description = "模拟量2 (字符)")
private String humStr;
@Schema(description = "模拟量2 (值)")
private Double humValue;
private RealTimeDataResponse realTimeData;
private HistoryDataResponse historyData;
}
@Data

View File

@@ -39,13 +39,6 @@ public class HomeController {
return success(homeService.getHomeInfo(baseId));
}
// @Operation(summary = "农场首页")
// @GetMapping("")
// @Parameter(name = "baseId", description = "基地ID")
// public CommonResult<List<HomeInfo>> getBaseInfo(@RequestParam("baseId") String baseId) {
// return success(homeService.getHomeInfoList(baseId));
// }
@Operation(summary = "农场首页-基地名称列表")
@GetMapping("/baseNames")
public CommonResult<List<BaseNameVO>> getBaseNameList() {

View File

@@ -3,10 +3,14 @@ package cn.iocoder.yudao.module.farm.dal.mysql.screendisasterdata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.farm.controller.admin.api.vo.DisasterAnalysisReqVO;
import cn.iocoder.yudao.module.farm.controller.admin.api.vo.DisasterAnalysisRespVO;
import cn.iocoder.yudao.module.farm.controller.admin.managementcenter.largescreen.screendisasterdata.vo.ScreenDisasterDataPageReqVO;
import cn.iocoder.yudao.module.farm.dal.dataobject.screendisasterdata.ScreenDisasterDataDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 大屏数据-灾害数据 Mapper
*
@@ -29,4 +33,5 @@ public interface ScreenDisasterDataMapper extends BaseMapperX<ScreenDisasterData
.orderByDesc(ScreenDisasterDataDO::getId));
}
List<DisasterAnalysisRespVO> countDisasterAnalysis(DisasterAnalysisReqVO req);
}

View File

@@ -16,15 +16,15 @@ import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
public class DeviceListResponse {
private String code;
private Integer code;
private String message;
private List<TokenCacheService.DeviceInfo> data;
public static DeviceListResponse success(List<TokenCacheService.DeviceInfo> data) {
return new DeviceListResponse("200", "成功", data);
return new DeviceListResponse(200, "成功", data);
}
public static DeviceListResponse error(String message) {
return new DeviceListResponse("500", message, null);
return new DeviceListResponse(500, message, null);
}
}

View File

@@ -15,7 +15,7 @@ import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
public class SysUserDeviceRespVO {
private String code;
private Integer code;
private String message;
private List<DeviceInfo> data;

View File

@@ -14,7 +14,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class UserLoginRespVO {
private String code;
private Integer code;
private String message;
private VendorData data;

View File

@@ -150,8 +150,9 @@ public abstract class AbstractDeviceService extends TokenCacheService implements
// 遍历token缓存,查找匹配的token
for (Map.Entry<String, TokenCacheService.TokenInfo> entry : tokenCacheMap.entrySet()) {
if (entry.getKey().startsWith(getDeviceType() + ":" + tenantId + ":") &&
token.equals(entry.getValue().getToken()) &&
isTokenValid(entry.getKey())) {
token.equals(entry.getValue().getToken())
// && isTokenValid(entry.getKey())
) {
return true;
}
}

View File

@@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.farm.manager.equipmentproducer.service.AbstractDe
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.service.HistoryDataStorageService;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.service.TokenCacheService;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.utils.HttpClientUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
@@ -27,6 +28,7 @@ import java.util.stream.Collectors;
* @author: binda
* @date: 2025/9/25 20:40
*/
@Slf4j
@Service
public class SoilService extends AbstractDeviceService {
@@ -58,6 +60,7 @@ public class SoilService extends AbstractDeviceService {
return convertToUnifiedResponse(vendorResponse);
} catch (Exception e) {
e.printStackTrace();
return UnifiedLoginResponse.error("土壤设备登录失败: " + e.getMessage());
}
}
@@ -102,11 +105,11 @@ public class SoilService extends AbstractDeviceService {
// 转换请求参数
HistoryDataListPageReqVO soilRequest = (HistoryDataListPageReqVO) historyRequest;
// T调用土壤设备厂商API获取历史数据 GET 请求方式
// 调用土壤设备厂商API获取历史数据 GET 请求方式
HistoryDataListPageRespVO vendorResponse = callSoilHistoryDataApi(token, soilRequest);
// TODO: 2025/9/26 模拟保存到MySQL
saveHistoryDataToDatabase(vendorResponse, soilRequest);
// saveHistoryDataToDatabase(vendorResponse, soilRequest);
return HistoryDataResponse.success(vendorResponse);
@@ -116,12 +119,40 @@ public class SoilService extends AbstractDeviceService {
}
private UserLoginRespVO callSoilVendorApi(UserLoginReqVO request) {
String url = deviceConf.getFourSituations().getHosts() + deviceConf.getFourSituations().getGlobal().getUrl21();
return httpClientUtils.doPost(url, null, request, UserLoginRespVO.class);
try {
String url = deviceConf.getFourSituations().getHosts() +
deviceConf.getFourSituations().getGlobal().getUrl21();
log.info("调用土壤登录API: {}", url);
log.info("请求参数: {}", request);
Map<String, String> headers = new HashMap<>();
// headers.put("User-Agent", "FarmDeviceManager/1.0");
headers.put("Content-Type", "application/json");
// 先尝试获取原始响应进行调试
String rawResponse = httpClientUtils.doGetRaw(
url,
headers,
Collections.singletonMap("loginName", request.getLoginName())
);
log.info("土壤登录原始响应: {}", rawResponse);
// 然后进行正式的POST请求
UserLoginRespVO response = httpClientUtils.doPost(url, headers, request, UserLoginRespVO.class);
log.info("土壤登录解析响应: {}", response);
return response;
} catch (Exception e) {
log.error("土壤登录API调用失败: {}", e.getMessage(), e);
throw new RuntimeException("土壤设备登录接口调用失败: " + e.getMessage());
}
}
private UnifiedLoginResponse convertToUnifiedResponse(UserLoginRespVO vendorResponse) {
if (!"1000".equals(vendorResponse.getCode())) {
if (1000 != vendorResponse.getCode()) {
return UnifiedLoginResponse.error(vendorResponse.getMessage());
}
@@ -154,7 +185,7 @@ public class SoilService extends AbstractDeviceService {
}
private DeviceListResponse convertToUnifiedDeviceResponse(SysUserDeviceRespVO vendorResponse) {
if (!"1000".equals(vendorResponse.getCode())) {
if (1000 != vendorResponse.getCode()) {
return DeviceListResponse.error(vendorResponse.getMessage());
}
@@ -200,7 +231,6 @@ public class SoilService extends AbstractDeviceService {
Map<String, String> headers = new HashMap<>();
headers.put("token", token);
// headers.put("Authorization", "Bearer " + token);
Map<String, Object> params = new HashMap<>();
params.put("beginTime", request.getBeginTime());

View File

@@ -19,9 +19,11 @@ import java.util.Map;
public class HttpClientUtils {
private final RestTemplate restTemplate;
private final HttpResponseUtils httpResponseUtils;
public HttpClientUtils(RestTemplateBuilder restTemplateBuilder) {
public HttpClientUtils(RestTemplateBuilder restTemplateBuilder, HttpResponseUtils httpResponseUtils) {
this.restTemplate = restTemplateBuilder.build();
this.httpResponseUtils = httpResponseUtils;
}
/**
@@ -46,15 +48,20 @@ public class HttpClientUtils {
}
HttpEntity<String> entity = new HttpEntity<>(httpHeaders);
ResponseEntity<T> response = restTemplate.exchange(
// 先以String类型接收响应
ResponseEntity<String> response = restTemplate.exchange(
builder.build().toUri(),
HttpMethod.GET,
entity,
responseType
String.class
);
return response.getBody();
// 使用增强的响应处理
return httpResponseUtils.handleResponse(response, responseType);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("HTTP GET请求失败: " + e.getMessage(), e);
}
}
@@ -72,16 +79,50 @@ public class HttpClientUtils {
}
HttpEntity<Object> entity = new HttpEntity<>(requestBody, httpHeaders);
ResponseEntity<T> response = restTemplate.exchange(
// 先以String类型接收响应
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.POST,
entity,
responseType
String.class
);
// 使用增强的响应处理
return httpResponseUtils.handleResponse(response, responseType);
} catch (Exception e) {
throw new RuntimeException("HTTP POST请求失败: " + e.getMessage(), e);
}
}
public String doGetRaw(String url, Map<String, String> headers, Map<String, Object> params) {
try {
HttpHeaders httpHeaders = new HttpHeaders();
if (headers != null) {
headers.forEach(httpHeaders::set);
}
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
if (params != null) {
params.forEach((key, value) -> {
if (value != null) {
builder.queryParam(key, value.toString());
}
});
}
HttpEntity<String> entity = new HttpEntity<>(httpHeaders);
ResponseEntity<String> response = restTemplate.exchange(
builder.build().toUri(),
HttpMethod.GET,
entity,
String.class
);
return response.getBody();
} catch (Exception e) {
throw new RuntimeException("HTTP POST请求失败: " + e.getMessage(), e);
throw new RuntimeException("HTTP GET原始请求失败: " + e.getMessage(), e);
}
}

View File

@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.farm.manager.equipmentproducer.utils;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
/**
* @description: HTTP响应处理工具类
* @author: binda
* @date: 2025/9/28 21:52
*/
@Component
public class HttpResponseUtils {
private final ObjectMapper objectMapper;
public HttpResponseUtils() {
this.objectMapper = new ObjectMapper();
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
/**
* 处理HTTP响应,支持更灵活的反序列化
*/
public <T> T handleResponse(ResponseEntity<String> response, Class<T> responseType) {
try {
String responseBody = response.getBody();
if (responseBody == null) {
throw new RuntimeException("响应体为空");
}
// 记录原始响应用于调试
System.out.println("原始响应: " + responseBody);
return objectMapper.readValue(responseBody, responseType);
} catch (Exception e) {
throw new RuntimeException("响应解析失败: " + e.getMessage(), e);
}
}
/**
* 处理通用响应,支持不同结构的响应
*/
public <T> T handleGenericResponse(ResponseEntity<String> response, Class<T> responseType, String... rootPaths) {
try {
String responseBody = response.getBody();
if (responseBody == null) {
throw new RuntimeException("响应体为空");
}
JsonNode rootNode = objectMapper.readTree(responseBody);
JsonNode targetNode = rootNode;
// 支持从指定路径提取数据
for (String path : rootPaths) {
if (targetNode.has(path)) {
targetNode = targetNode.get(path);
}
}
return objectMapper.treeToValue(targetNode, responseType);
} catch (Exception e) {
throw new RuntimeException("响应解析失败: " + e.getMessage(), e);
}
}
}

View File

@@ -24,6 +24,7 @@ import cn.iocoder.yudao.module.farm.dal.mysql.personnelmanagement.PersonnelManag
import cn.iocoder.yudao.module.farm.dal.mysql.specieslibrary.SpeciesLibraryMapper;
import cn.iocoder.yudao.module.farm.enums.CommonConstants;
import cn.iocoder.yudao.module.farm.service.companyinfo.CompanyInfoService;
import cn.iocoder.yudao.module.farm.service.deviceinfo.DeviceInfoService;
import cn.iocoder.yudao.module.farm.service.personnelmanagement.PersonnelManagementService;
import cn.iocoder.yudao.module.farm.service.plotinfo.PlotInfoService;
import cn.iocoder.yudao.module.farm.service.productionplan.ProductionPlanService;
@@ -77,6 +78,8 @@ public class BaseInfoServiceImpl implements BaseInfoService {
private PlotInfoService plotInfoService;
@Resource
private CompanyInfoService companyInfoService;
@Resource
private DeviceInfoService deviceInfoService;
@SneakyThrows
@Override
@@ -322,6 +325,8 @@ public class BaseInfoServiceImpl implements BaseInfoService {
String tenantId = req.getTenantId();
String baseId = req.getBaseId();
BaseInfoDO bDO = baseInfoMapper.selectOne("id", baseId, "tenant_id", tenantId);
int countByTenant = deviceInfoService.countDevice("",tenantId);
int countByBase = deviceInfoService.countDevice(baseId,tenantId);
FarmBaseOverviewRespVO.TenantInfo tenantInfo = new FarmBaseOverviewRespVO.TenantInfo();
tenantInfo.setTenantId(bDO.getTenantId());
@@ -329,7 +334,7 @@ public class BaseInfoServiceImpl implements BaseInfoService {
tenantInfo.setBaseTotalCount(this.getBaseTotalCount(tenantId));
tenantInfo.setPlotTotalCount(plotInfoService.getPlotTotalCount(tenantId));
tenantInfo.setCropTotalCount(speciesLibraryService.getCropTotalCount(tenantId));
tenantInfo.setIotDeviceTotalCount(0); // TODO: 2025/9/4
tenantInfo.setIotDeviceTotalCount(countByTenant);
FarmBaseOverviewRespVO.BaseInfo baseInfo = new FarmBaseOverviewRespVO.BaseInfo();
baseInfo.setBaseId(bDO.getId());
@@ -339,7 +344,7 @@ public class BaseInfoServiceImpl implements BaseInfoService {
baseInfo.setBaseCentrePoint(bDO.getCentrePoint());
baseInfo.setPlantingCropCount(productionPlanService.getGrowingCropCount(baseId));
baseInfo.setPlantingPlotCount(productionPlanService.getGrowingPlotCount(baseId));
baseInfo.setIotDeviceCount(0); // TODO: 2025/9/4
baseInfo.setIotDeviceCount(countByBase);
baseInfo.setPlantingTaskCount(productionPlanService.getGrowingPlanCount(baseId));
baseInfo.setAdministratorName(getAdministratorName(tenantId, bDO.getManager()));
return CommonResult.success(new FarmBaseOverviewRespVO(tenantInfo, baseInfo));

View File

@@ -237,8 +237,15 @@ public class CropFarmingCenterServiceImpl implements CropFarmingCenterService {
@Override
public void updateProductionPlan(ProductionPlanUpdateReqVO updateReqVO) {
validateProductionPlanExists(updateReqVO.getId());
productionPlanMapper.updateById(BeanUtils.toBean(updateReqVO, ProductionPlanDO.class));
ProductionPlanDO one = productionPlanMapper.selectById(updateReqVO.getId());
if (one == null) {
throw exception(PRODUCTION_PLAN_NOT_EXISTS);
}
ProductionPlanDO planDO = BeanUtils.toBean(updateReqVO, ProductionPlanDO.class);
planDO.setPlanStatus(CommonConstants.PLAN_STATUS_3);
planDO.setPracticalYields(one.getPredictedYields());
productionPlanMapper.updateById(planDO);
}
@Override

View File

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2.DeviceStat
import cn.iocoder.yudao.module.farm.dal.dataobject.deviceinfo.DeviceInfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.RealTimeDataResponse;
import jakarta.validation.Valid;
import java.util.List;
@@ -63,7 +64,7 @@ public interface DeviceInfoService {
* @param id
* @return
*/
DeviceInfoDetailV2RespVO getDeviceRealTimeInfo(String id);
DeviceInfoDetailV2RespVO getDeviceRealTimeInfo(String id,String dateType);
/**
* 获得设备信息分页
@@ -93,7 +94,7 @@ public interface DeviceInfoService {
* @param req
* @return
*/
CommonResult<DeviceMonitoringRespVO> getDeviceMonitoring(DeviceMonitoringReqVO req);
CommonResult<RealTimeDataResponse> getDeviceMonitoring(DeviceMonitoringReqVO req);
/**
* api接口
@@ -110,4 +111,12 @@ public interface DeviceInfoService {
List<String> getImage(String deviceType);
DeviceStateRespVO countDeviceState(String baseId, String tenantId);
/**
* 设备数量统计
* @param baseId
* @param tenantId
* @return
*/
int countDevice(String baseId, String tenantId);
}

View File

@@ -15,6 +15,14 @@ import cn.iocoder.yudao.module.farm.dal.mysql.baseinfo.BaseInfoMapper;
import cn.iocoder.yudao.module.farm.dal.mysql.deviceclassify.DeviceClassifyMapper;
import cn.iocoder.yudao.module.farm.dal.mysql.plotinfo.PlotInfoMapper;
import cn.iocoder.yudao.module.farm.enums.CommonConstants;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.EquipmentProducerManager;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.conf.DeviceConf;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.HistoryDataResponse;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.RealTimeDataResponse;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.UnifiedLoginResponse;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.soil.history.HistoryDataListPageReqVO;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.soil.login.UserLoginReqVO;
import cn.iocoder.yudao.module.farm.manager.equipmentproducer.dto.soil.realtime.RealTimeDataReqVO;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@@ -26,12 +34,16 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.module.farm.dal.mysql.deviceinfo.DeviceInfoMapper;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static cn.iocoder.yudao.module.farm.enums.ErrorCodeConstants.DEVICE_CLASS_TYPE_ERROR;
@@ -54,6 +66,10 @@ public class DeviceInfoServiceImpl implements DeviceInfoService {
private BaseInfoMapper baseInfoMapper;
@Resource
private PlotInfoMapper plotInfoMapper;
@Resource
private EquipmentProducerManager equipmentProducerManager;
@Resource
private DeviceConf deviceConf;
@Override
public String createDeviceInfo(DeviceInfoSaveReqVO createReqVO) {
@@ -121,7 +137,7 @@ public class DeviceInfoServiceImpl implements DeviceInfoService {
}
@Override
public DeviceInfoDetailV2RespVO getDeviceRealTimeInfo(String id) {
public DeviceInfoDetailV2RespVO getDeviceRealTimeInfo(String id,String dateType) {
List<DeviceClassifyDO> deviceClassifyDOS = deviceClassifyMapper.selectList();
Map<String, String> map = deviceClassifyDOS.stream()
@@ -133,8 +149,58 @@ public class DeviceInfoServiceImpl implements DeviceInfoService {
DeviceInfoDetailV2RespVO target = BeanUtils.toBean(deviceInfo, DeviceInfoDetailV2RespVO.class);
// 设备类型名称
target.setDeviceTypeName(map.getOrDefault(target.getDeviceType(), ""));
String deviceUniqueId = deviceInfo.getDeviceUniqueId();
String soil = "190"; // soil
String wf = "189"; // water_fertilizer
String type = "";
if (target.getDeviceType().equals(soil)) {
type = "soil";
// soil
UserLoginReqVO userLoginReqVO = new UserLoginReqVO(deviceConf.getFourSituations().getUsername(), deviceConf.getFourSituations().getPassword());
UnifiedLoginResponse login = equipmentProducerManager.login(type, getLoginUser().getTenantId(), deviceUniqueId, userLoginReqVO);
String token = login.getData().getToken();
RealTimeDataReqVO soilRequest = new RealTimeDataReqVO();
soilRequest.setDeviceAddrs(deviceUniqueId);
RealTimeDataResponse realTimeData = equipmentProducerManager.getRealTimeData(type, getLoginUser().getTenantId(), token, soilRequest);
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
LocalDateTime startLocalDateTime = now.minusDays(3);
LocalDateTime endLocalDateTime = now;
String startStr = startLocalDateTime.format(formatter1);
String endStr=endLocalDateTime.format(formatter1);
System.out.println("标准格式-结束: " + startStr);
System.out.println("标准格式-开始: " + endStr);
long startTime = startLocalDateTime.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
long endTime = endLocalDateTime.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
HistoryDataListPageReqVO req = new HistoryDataListPageReqVO(String.valueOf(startTime),String.valueOf(endTime));
req.setDeviceAddr(deviceUniqueId);
req.setPages(1);
req.setLimit(500);
HistoryDataResponse historyData = equipmentProducerManager.getHistoryData(type, getLoginUser().getTenantId(), token, req);
DeviceInfoDetailV2RespVO.SoilInfo soilInfo = new DeviceInfoDetailV2RespVO.SoilInfo(deviceUniqueId, realTimeData, historyData);
target.setSoilInfo(soilInfo);
} else {
// type = "water_fertilizer";
// LoginReqVO loginReqVO = new LoginReqVO();
// UnifiedLoginResponse login = equipmentProducerManager.login(type, getLoginUser().getTenantId(), deviceUniqueId, loginReqVO);
// String token = login.getData().getToken();
}
// TODO: 2025/9/26 从设备商服务 获取设备实时/历史数据
return target;
}
@@ -181,8 +247,27 @@ public class DeviceInfoServiceImpl implements DeviceInfoService {
}
@Override
public CommonResult<DeviceMonitoringRespVO> getDeviceMonitoring(DeviceMonitoringReqVO req) {
return null;
public CommonResult<RealTimeDataResponse> getDeviceMonitoring(DeviceMonitoringReqVO req) {
try {
LambdaQueryWrapperX<DeviceInfoDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(DeviceInfoDO::getBaseId, req.getBaseId());
wrapperX.eq(DeviceInfoDO::getTenantId, req.getTenantId());
List<DeviceInfoDO> list = deviceInfoMapper.selectList(wrapperX);
DeviceInfoDO d = list.get(0);
String type = "soil";
UserLoginReqVO userLoginReqVO = new UserLoginReqVO(deviceConf.getFourSituations().getUsername(), deviceConf.getFourSituations().getPassword());
UnifiedLoginResponse login = equipmentProducerManager.login(type, Long.valueOf(req.getTenantId()), req.getTenantId(), userLoginReqVO);
String token = login.getData().getToken();
RealTimeDataReqVO soilRequest = new RealTimeDataReqVO();
soilRequest.setDeviceAddrs("");
RealTimeDataResponse realTimeData = equipmentProducerManager.getRealTimeData(type, Long.valueOf(req.getTenantId()), token, soilRequest);
return success(realTimeData);
} catch (Exception e) {
return error(500, "");
}
}
@Override
@@ -219,4 +304,14 @@ public class DeviceInfoServiceImpl implements DeviceInfoService {
public DeviceStateRespVO countDeviceState(String baseId,String tenantId) {
return deviceInfoMapper.countDeviceState(baseId,tenantId);
}
@Override
public int countDevice(String baseId, String tenantId) {
LambdaQueryWrapperX<DeviceInfoDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(DeviceInfoDO::getTenantId, tenantId);
wrapperX.eq(DeviceInfoDO::getDeleted, CommonConstants.NO_CODE);
wrapperX.eqIfPresent(DeviceInfoDO::getBaseId, baseId);
return deviceInfoMapper.selectCount(wrapperX).intValue();
}
}

View File

@@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.farm.dal.mysql.farmingrecord.FarmingRecordMapper;
import cn.iocoder.yudao.module.farm.dal.mysql.farmingrecordinput.FarmingRecordInputMapper;
import cn.iocoder.yudao.module.farm.dal.mysql.farmingtypemaintenance.FarmingTypeMaintenanceMapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@@ -60,11 +61,13 @@ public class FarmingRecordServiceImpl implements FarmingRecordService {
// 处理图片集合转换
handleImageConversion(farmingRecord, createReqVO);
//投入品集合单条保存
createReqVO.getInvestGoodsList().forEach(e -> {
e.setId(IdUtil.fastUUID());
e.setFarmRecordId(farmingRecord.getId());
farmingRecordInputMapper.insert(e);
});
if (ObjectUtils.isNotEmpty(createReqVO.getInvestGoodsList())) {
createReqVO.getInvestGoodsList().forEach(e -> {
e.setId(IdUtil.fastUUID());
e.setFarmRecordId(farmingRecord.getId());
farmingRecordInputMapper.insert(e);
});
}
farmingRecordMapper.insert(farmingRecord);
// 返回

View File

@@ -187,7 +187,7 @@ public class HomeServiceImpl implements HomeService {
t.setTaskCount(taskCount);
t.setLotCount(0); // TODO: 2025/8/31 物联设备数量
t.setManager(employeeName);
t.setQrCodeUrl(""); // TODO: 2025/8/31 暂无
t.setQrCodeUrl(e.getBaseId());
t.setPlanInfos(this.getPlanInfoList(e.getBaseId(), e.getPlotId(), employeeName));
return t;
}

View File

@@ -101,7 +101,8 @@ public class ScreenDisasterDataServiceImpl implements ScreenDisasterDataService
@Override
public CommonResult<List<DisasterAnalysisRespVO>> getDisasterStatisticsChart(DisasterAnalysisReqVO req) {
return null;
List<DisasterAnalysisRespVO> res = screenDisasterDataMapper.countDisasterAnalysis(req);
return CommonResult.success(res);
}
}

View File

@@ -52,7 +52,6 @@
COALESCE(SUM(practical_yields), 0) AS practicalYields
FROM farm_production_plan
<where>
plan_status = 3
AND deleted = 0
AND production_start_date IS NOT NULL
<if test="tenantId != null and tenantId != ''">AND tenant_id = #{tenantId}</if>

View File

@@ -9,4 +9,63 @@
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-->
<resultMap id="DisasterAnalysisResultMap"
type="cn.iocoder.yudao.module.farm.controller.admin.api.vo.DisasterAnalysisRespVO">
<result property="year" column="year"/>
<collection property="disasterReports"
ofType="cn.iocoder.yudao.module.farm.controller.admin.api.vo.DisasterAnalysisRespVO$DisasterReports">
<result property="month" column="month"/>
<result property="meteorologyCount" column="meteorology_count"/>
<result property="pestCount" column="pest_count"/>
</collection>
</resultMap>
<select id="countDisasterAnalysis"
parameterType="cn.iocoder.yudao.module.farm.controller.admin.api.vo.DisasterAnalysisReqVO"
resultMap="DisasterAnalysisResultMap">
SELECT y.year,
m.month,
COALESCE(SUM(CASE WHEN f.category = '0' THEN COALESCE(f.disaster_count, 0) ELSE 0 END),0) as meteorology_count,
COALESCE(SUM(CASE WHEN f.category != '0' THEN COALESCE(f.disaster_count, 0) ELSE 0 END), 0) as pest_count
FROM (SELECT YEAR (CURDATE()) as year UNION SELECT YEAR (CURDATE()) - 1) y
CROSS JOIN
(SELECT 1 as month
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6
UNION
SELECT 7
UNION
SELECT 8
UNION
SELECT 9
UNION
SELECT 10
UNION
SELECT 11
UNION
SELECT 12) m
LEFT JOIN farm_screen_disaster_data f ON y.year = YEAR (STR_TO_DATE(f.disaster_time, '%Y-%m'))
AND m.month = MONTH (STR_TO_DATE(f.disaster_time, '%Y-%m'))
AND f.deleted = 0
<if test="baseId != null and baseId != ''">
AND f.base_id = #{baseId}
</if>
<if test="tenantId != null and tenantId != ''">
AND f.tenant_id = #{tenantId}
</if>
AND f.disaster_time IS NOT NULL
GROUP BY y.year, m.month
ORDER BY y.year DESC, m.month
</select>
</mapper>

View File

@@ -47,22 +47,22 @@ spring:
primary: master
datasource:
master:
url: jdbc:mysql://223.83.129.121:3360/farming-platform?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
username: farming-platform
password: wCM7fBMhFP3zNdJ3
url: ${mysql.url}
username: ${mysql.username}
password: ${mysql.password}
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
lazy: true # 开启懒加载,保证启动速度
url: jdbc:mysql://223.83.129.121:3360/farming-platform?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
username: farming-platform
password: wCM7fBMhFP3zNdJ3
url: ${mysql.url}
username: ${mysql.username}
password: ${mysql.password}
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 223.83.152.93 # 地址
port: 65532 # 端口
database: 5 # 数据库索引
password: luxifa970219 # 密码,建议生产环境开启
host: ${redis.host}
port: ${redis.port}
database: ${redis.database}
password: ${redis.password}
mongodb:
uri: mongodb://mongo_BiKBdR:mongo_KaShrX@223.83.129.121:27017/farming-platform-iot?authSource=farming-platform-iot
auto-index-creation: true
@@ -154,16 +154,16 @@ logging:
wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
mp:
# 公众号配置(必填)
app-id: wxde87f69bb4a2cf5c
secret: a5e114df50a86c2578af255adba1c3e7
app-id: ${wm.wx.mp.app-id}
secret: ${wm.wx.mp.secret}
# 存储配置,解决 AccessToken 的跨节点的共享
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
appid: wxde87f69bb4a2cf5c
secret: a5e114df50a86c2578af255adba1c3e7
appid: ${wm.wx.miniapp.app-id}
secret: ${wm.wx.miniapp.secret}
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀

View File

@@ -4,7 +4,9 @@ spring:
profiles:
active: dev
config:
import:
- classpath:db.properties
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
@@ -349,7 +351,7 @@ wms:
device-manager:
# 四情 (文档来源: 物连网农业四情采集api协议.pdf 版本:v2.0.3 日期:2022.08.16)
four-situations:
hosts: http://farm.0531yun.cn/#/ # http://farm.0531yun.cn/#/
hosts: http://api.farm.0531yun.cn # http://farm.0531yun.cn/#/
username: cq250904jxtr
password: cq250904jxtr
global:

View File

@@ -0,0 +1,31 @@
# local
#mysql.url=jdbc:mysql://223.83.129.121:3360/farming-platform?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
#mysql.username=farming-platform
#mysql.password=wCM7fBMhFP3zNdJ3
#
#redis.host=223.83.152.93
#redis.port=65532
#redis.database=5
#redis.password=luxifa970219
#
#wm.wx.mp.app-id=wxde87f69bb4a2cf5c
#wm.wx.mp.secret=a5e114df50a86c2578af255adba1c3e7
#wm.wx.miniapp.app-id=wxde87f69bb4a2cf5c
#wm.wx.miniapp.secret=a5e114df50a86c2578af255adba1c3e7
# ????
mysql.url=jdbc:mysql://rm-bp10c7f0fou5efc95fo.mysql.rds.aliyuncs.com:3306/farming-platform?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
mysql.username=root
mysql.password=Jsnm2025
redis.host=r-bp14zubs85fmfuhnw7pd.redis.rds.aliyuncs.com
redis.port=6379
redis.database=1
redis.password=m&wRPVcyy7Gl
wm.wx.mp.app-id=wxde87f69bb4a2cf5c
wm.wx.mp.secret=a5e114df50a86c2578af255adba1c3e7
wm.wx.miniapp.app-id=wxde87f69bb4a2cf5c
wm.wx.miniapp.secret=a5e114df50a86c2578af255adba1c3e7