feat (farm): 设备管理
- 本服务接口 - 对接第三方设备商
This commit is contained in:
@@ -109,11 +109,24 @@ public class BaseInfoController {
|
||||
BeanUtils.toBean(list, BaseInfoRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/base/plot/list")
|
||||
@Operation(summary = "基地-地块列表")
|
||||
public CommonResult<List<BaseInfoForWxRespVO>> getBasePlotList() {
|
||||
List<BaseInfoForWxRespVO> res = baseInfoService.getBaseListForWx();
|
||||
return success(res);
|
||||
}
|
||||
|
||||
@GetMapping("/base/list")
|
||||
@Operation(summary = "基地列表")
|
||||
public CommonResult<List<BaseNamesRespVO>> getBaseList() {
|
||||
List<BaseInfoDO> baseInfos = baseInfoService.getBaseInfos();
|
||||
List<BaseNamesRespVO> res = baseInfos.stream().map(e -> new BaseNamesRespVO(e.getId(),e.getName())).toList();
|
||||
return success(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* === === 微信端 === ===
|
||||
*/
|
||||
// @GetMapping("/wx/base/list")
|
||||
// @Operation(summary = "微信端-常用地块集合")
|
||||
@GetMapping("/wx/base/list")
|
||||
@Operation(summary = "基地列表(微信端)")
|
||||
public CommonResult<List<BaseInfoForWxRespVO>> getBaseListForWx() {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.baseinfo.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: binda
|
||||
* @date: 2025/9/19 16:08
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class BaseNamesRespVO {
|
||||
|
||||
private String baseId;
|
||||
private String baseName;
|
||||
|
||||
}
|
||||
@@ -6,9 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceClassifyPageReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceClassifyRespVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceClassifySaveReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.*;
|
||||
import cn.iocoder.yudao.module.farm.dal.dataobject.deviceclassify.DeviceClassifyDO;
|
||||
import cn.iocoder.yudao.module.farm.service.deviceclassify.DeviceClassifyService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -106,4 +104,18 @@ public class DeviceClassifyController {
|
||||
BeanUtils.toBean(list, DeviceClassifyRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/device-type/list")
|
||||
@Operation(summary = "设备类型列表")
|
||||
public CommonResult<List<DeviceTypeRespVO>> getDeviceTypeList() {
|
||||
List<DeviceTypeRespVO> res = deviceClassifyService.getDeviceTypeList();
|
||||
return success(res);
|
||||
}
|
||||
|
||||
@GetMapping("/device-model/list")
|
||||
@Operation(summary = "设备型号列表")
|
||||
public CommonResult<List<DeviceModelRespVO>> getDeviceModelList(String deviceTypeId) {
|
||||
List<DeviceModelRespVO> res = deviceClassifyService.getDeviceModelList(deviceTypeId);
|
||||
return success(res);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.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
|
||||
* @date: 2025/9/19 16:12
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceModelRespVO {
|
||||
|
||||
@Schema(description = "设备型号ID")
|
||||
private String deviceModelId;
|
||||
@Schema(description = "设备型号名称")
|
||||
private String deviceModelName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.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
|
||||
* @date: 2025/9/19 16:12
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceTypeRespVO {
|
||||
|
||||
@Schema(description = "设备类型ID")
|
||||
private String deviceTypeId;
|
||||
@Schema(description = "设备类型名称")
|
||||
private String deviceTypeName;
|
||||
@Schema(description = "设备类型图片")
|
||||
private String image;
|
||||
@Schema(description = "设备类型图片集合")
|
||||
private List<String> imageList;
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoRespVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2.*;
|
||||
import cn.iocoder.yudao.module.farm.dal.dataobject.deviceinfo.DeviceInfoDO;
|
||||
import cn.iocoder.yudao.module.farm.dal.dataobject.mqtt.MqttConnectionDO;
|
||||
import cn.iocoder.yudao.module.farm.service.deviceinfo.DeviceInfoService;
|
||||
@@ -43,14 +44,16 @@ public class DeviceInfoController {
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建设备信息")
|
||||
public CommonResult<String> createDeviceInfo(@Valid @RequestBody DeviceInfoSaveReqVO createReqVO) {
|
||||
return success(deviceInfoService.createDeviceInfo(createReqVO));
|
||||
public CommonResult<String> createDeviceInfo(@Valid @RequestBody DeviceInfoSaveV2ReqVO createReqVO) {
|
||||
DeviceInfoSaveReqVO targetObj = BeanUtils.toBean(createReqVO, DeviceInfoSaveReqVO.class);
|
||||
return success(deviceInfoService.createDeviceInfo(targetObj));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新设备信息")
|
||||
public CommonResult<Boolean> updateDeviceInfo(@Valid @RequestBody DeviceInfoSaveReqVO updateReqVO) {
|
||||
deviceInfoService.updateDeviceInfo(updateReqVO);
|
||||
public CommonResult<Boolean> updateDeviceInfo(@Valid @RequestBody DeviceInfoSaveV2ReqVO updateReqVO) {
|
||||
DeviceInfoSaveReqVO targetObj = BeanUtils.toBean(updateReqVO, DeviceInfoSaveReqVO.class);
|
||||
deviceInfoService.updateDeviceInfo(targetObj);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@@ -73,16 +76,24 @@ public class DeviceInfoController {
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得设备信息")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
public CommonResult<DeviceInfoRespVO> getDeviceInfo(@RequestParam("id") String id) {
|
||||
public CommonResult<DeviceInfoDetailV2RespVO> getDeviceInfo(@RequestParam("id") String id) {
|
||||
DeviceInfoDO deviceInfo = deviceInfoService.getDeviceInfo(id);
|
||||
return success(BeanUtils.toBean(deviceInfo, DeviceInfoRespVO.class));
|
||||
return success(BeanUtils.toBean(deviceInfo, DeviceInfoDetailV2RespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得设备信息分页")
|
||||
public CommonResult<PageResult<DeviceInfoRespVO>> getDeviceInfoPage(@Valid DeviceInfoPageReqVO pageReqVO) {
|
||||
PageResult<DeviceInfoDO> pageResult = deviceInfoService.getDeviceInfoPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, DeviceInfoRespVO.class));
|
||||
public CommonResult<PageResult<DeviceInfoPageV2RespVO>> getDeviceInfoPage(@Valid DeviceInfoPageV2ReqVO pageReqVO) {
|
||||
|
||||
DeviceInfoPageReqVO targetObj = new DeviceInfoPageReqVO();
|
||||
targetObj.setBaseId(pageReqVO.getBaseId());
|
||||
targetObj.setDeviceType(pageReqVO.getDeviceTypeId());
|
||||
targetObj.setDeviceName(pageReqVO.getDeviceName());
|
||||
PageResult<DeviceInfoDO> pageResult = deviceInfoService.getDeviceInfoPage(targetObj);
|
||||
|
||||
PageResult<DeviceInfoPageV2RespVO> deviceInfoRespVOPageResult = BeanUtils.toBean(pageResult, DeviceInfoPageV2RespVO.class);
|
||||
return success(deviceInfoRespVOPageResult);
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@@ -106,6 +117,14 @@ public class DeviceInfoController {
|
||||
} catch (MqttException e) {
|
||||
return error(500, "设备连接失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "订阅设备上报并持久化")
|
||||
@Parameter(name = "baseId", description = "基地编号")
|
||||
public CommonResult<DeviceStatisticsRespVO> getStatisticsInfo(@RequestParam("baseId") String baseId) {
|
||||
DeviceStatisticsRespVO res = deviceInfoService.getStatisticsInfo(baseId);
|
||||
return success(res);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,8 +40,10 @@ public class DeviceInfoPageReqVO extends PageParam {
|
||||
@Schema(description = "LP产品唯一ID", example = "10175")
|
||||
private String lpProductUniqueId;
|
||||
|
||||
@Schema(description = "设备状态", example = "2")
|
||||
private Integer status;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
@Schema(description = "绑定状态:1: 绑定; 0: 未绑定")
|
||||
private Integer bindingState;
|
||||
|
||||
@Schema(description = "设备类型", example = "2")
|
||||
private String deviceType;
|
||||
|
||||
@@ -52,9 +52,10 @@ public class DeviceInfoRespVO {
|
||||
@ExcelProperty("LP产品唯一ID")
|
||||
private String lpProductUniqueId;
|
||||
|
||||
@Schema(description = "设备状态", example = "2")
|
||||
@ExcelProperty("设备状态")
|
||||
private Integer status;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
@Schema(description = "绑定状态:1: 绑定; 0: 未绑定")
|
||||
private Integer bindingState;
|
||||
|
||||
@Schema(description = "设备类型", example = "2")
|
||||
@ExcelProperty("设备类型")
|
||||
|
||||
@@ -42,8 +42,10 @@ public class DeviceInfoSaveReqVO {
|
||||
@Schema(description = "LP产品唯一ID", example = "10175")
|
||||
private String lpProductUniqueId;
|
||||
|
||||
@Schema(description = "设备状态", example = "2")
|
||||
private Integer status;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
@Schema(description = "绑定状态:1: 绑定; 0: 未绑定")
|
||||
private Integer bindingState;
|
||||
|
||||
@Schema(description = "设备类型", example = "2")
|
||||
private String deviceType;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 设备详情 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/19 09:47
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceInfoDetailV2RespVO {
|
||||
|
||||
private String id;
|
||||
|
||||
@Schema(description = "设备全局唯一标识(设备厂商提供)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String deviceUniqueId;
|
||||
|
||||
@Schema(description = "设备类型 (设备类型ID)")
|
||||
private String deviceType;
|
||||
|
||||
@Schema(description = "设备分类名称 (设备型号,字符拼接)")
|
||||
private String deviceCategoryName;
|
||||
|
||||
@Schema(description = "设备名称(本服务自定义名称)")
|
||||
private String deviceName;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String managerId;
|
||||
@Schema(description = "是否存在物联网卡 (1:有;2:无;)")
|
||||
private Boolean isIotCardExisted;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
@Schema(description = "绑定状态:1: 绑定; 0: 未绑定")
|
||||
private Integer bindingState;
|
||||
@Schema(description = "设备坐标")
|
||||
private String point;
|
||||
|
||||
@Schema(description = "基地iD")
|
||||
private String baseId;
|
||||
@Schema(description = "基地坐标")
|
||||
private String basePoint;
|
||||
@Schema(description = "地块ID")
|
||||
private String plotId;
|
||||
@Schema(description = "地块坐标")
|
||||
private String plotPoint;
|
||||
|
||||
@Schema(description = "土壤墒情")
|
||||
private SoilInfo soilInfo;
|
||||
@Schema(description = "水肥机")
|
||||
private WfInfo wfInfo;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class SoilInfo{
|
||||
private String deviceUniqueId;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class WfInfo{
|
||||
private String deviceUniqueId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: binda
|
||||
* @date: 2025/9/19 15:35
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DeviceInfoPageV2ReqVO {
|
||||
|
||||
@Schema(description = "基地ID")
|
||||
private String baseId;
|
||||
@Schema(description = "设备类型ID")
|
||||
private String deviceTypeId;
|
||||
@Schema(description = "设备名称")
|
||||
private String deviceName;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description: 分页查询
|
||||
* @author: binda
|
||||
* @date: 2025/9/19 14:22
|
||||
*/
|
||||
@Data
|
||||
public class DeviceInfoPageV2RespVO extends PageParam {
|
||||
|
||||
private String id;
|
||||
|
||||
@Schema(description = "设备全局唯一标识(设备厂商提供)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String deviceUniqueId;
|
||||
|
||||
@Schema(description = "设备类型 (设备类型ID)")
|
||||
private String deviceType;
|
||||
|
||||
@Schema(description = "设备分类名称 (设备型号,字符拼接)")
|
||||
private String deviceCategoryName;
|
||||
|
||||
@Schema(description = "设备名称(本服务自定义名称)")
|
||||
private String deviceName;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String managerId;
|
||||
@Schema(description = "是否存在物联网卡 (1:有;2:无;)")
|
||||
private Boolean isIotCardExisted;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
@Schema(description = "绑定状态:1: 绑定; 0: 未绑定")
|
||||
private Integer bindingState;
|
||||
@Schema(description = "设备坐标")
|
||||
private String point;
|
||||
|
||||
@Schema(description = "基地iD")
|
||||
private String baseId;
|
||||
@Schema(description = "基地坐标")
|
||||
private String basePoint;
|
||||
@Schema(description = "地块ID")
|
||||
private String plotId;
|
||||
@Schema(description = "地块坐标")
|
||||
private String plotPoint;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 新增设备 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/19 09:47
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceInfoSaveV2ReqVO {
|
||||
|
||||
private String id;
|
||||
|
||||
@Schema(description = "设备全局唯一标识(设备厂商提供)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String deviceUniqueId;
|
||||
|
||||
@Schema(description = "设备类型 (设备类型ID)")
|
||||
private String deviceType;
|
||||
|
||||
@Schema(description = "设备分类名称 (设备型号,字符拼接)")
|
||||
private String deviceCategoryName;
|
||||
|
||||
@Schema(description = "设备名称(本服务自定义名称)")
|
||||
private String deviceName;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String managerId;
|
||||
@Schema(description = "是否存在物联网卡 (1:有;0:无;)")
|
||||
private Boolean isIotCardExisted;
|
||||
@Schema(description = "设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障")
|
||||
private Integer deviceState;
|
||||
@Schema(description = "绑定状态:1: 绑定; 0: 未绑定")
|
||||
private Integer bindingState;
|
||||
private String baseId;
|
||||
private String plotId;
|
||||
@Schema(description = "设备坐标")
|
||||
private String point;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 设备统计
|
||||
* @author: binda
|
||||
* @date: 2025/9/19 15:48
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceStatisticsRespVO {
|
||||
|
||||
@Schema(description = "设备总数")
|
||||
private Integer deviceTotal;
|
||||
@Schema(description = "在线总数")
|
||||
private Integer deviceOnline;
|
||||
@Schema(description = "离线总数")
|
||||
private Integer deviceOffline;
|
||||
@Schema(description = "异常总数")
|
||||
private Integer deviceWarning;
|
||||
|
||||
}
|
||||
@@ -54,11 +54,11 @@ public class DeviceStatusController {
|
||||
}
|
||||
|
||||
DeviceStatusRespVO respVO = BeanUtils.toBean(device, DeviceStatusRespVO.class);
|
||||
if (device.getState() != null) {
|
||||
DeviceStateEnum stateEnum = DeviceStateEnum.valueOf(device.getState());
|
||||
if (device.getDeviceState() != null) {
|
||||
DeviceStateEnum stateEnum = DeviceStateEnum.valueOf(device.getDeviceState());
|
||||
if (stateEnum != null) {
|
||||
respVO.setStateName(stateEnum.getName());
|
||||
respVO.setOnline(DeviceStateEnum.isOnline(device.getState()));
|
||||
respVO.setOnline(DeviceStateEnum.isOnline(device.getDeviceState()));
|
||||
}
|
||||
}
|
||||
return success(respVO);
|
||||
|
||||
@@ -23,12 +23,11 @@ import cn.iocoder.yudao.module.farm.framework.util.ImageCollectionUtil;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DeviceInfoDO extends BaseDO {
|
||||
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
/**
|
||||
* 设备全局唯一标识
|
||||
* 设备全局唯一标识 (设备商提供)
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String deviceUniqueId;
|
||||
/**
|
||||
* 设备名称
|
||||
@@ -166,7 +165,11 @@ public class DeviceInfoDO extends BaseDO {
|
||||
/**
|
||||
* 设备状态:0-未激活,1-在线,2-离线,3-维护中,4-故障
|
||||
*/
|
||||
private Integer state;
|
||||
private Integer deviceState;
|
||||
/**
|
||||
* 绑定状态:1: 绑定; 0: 未绑定
|
||||
*/
|
||||
private Integer bindingState;
|
||||
|
||||
/**
|
||||
* 设备激活时间
|
||||
@@ -212,6 +215,20 @@ public class DeviceInfoDO extends BaseDO {
|
||||
|
||||
private String tenantId;
|
||||
private String point;
|
||||
/**
|
||||
* 负责人
|
||||
*/
|
||||
private String managerId;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备图片集合
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.deviceinfo.vo.DeviceInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2.DeviceStatisticsRespVO;
|
||||
import cn.iocoder.yudao.module.farm.dal.dataobject.deviceinfo.DeviceInfoDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
@@ -30,7 +31,7 @@ public interface DeviceInfoMapper extends BaseMapperX<DeviceInfoDO> {
|
||||
.eqIfPresent(DeviceInfoDO::getLpProductKey, reqVO.getLpProductKey())
|
||||
.likeIfPresent(DeviceInfoDO::getLpProductName, reqVO.getLpProductName())
|
||||
.eqIfPresent(DeviceInfoDO::getLpProductUniqueId, reqVO.getLpProductUniqueId())
|
||||
.eqIfPresent(DeviceInfoDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(DeviceInfoDO::getStatus, reqVO.getDeviceState())
|
||||
.eqIfPresent(DeviceInfoDO::getDeviceType, reqVO.getDeviceType())
|
||||
.eqIfPresent(DeviceInfoDO::getType, reqVO.getType())
|
||||
.eqIfPresent(DeviceInfoDO::getVendor, reqVO.getVendor())
|
||||
@@ -62,7 +63,7 @@ public interface DeviceInfoMapper extends BaseMapperX<DeviceInfoDO> {
|
||||
*/
|
||||
default List<DeviceInfoDO> selectListByState(Integer state) {
|
||||
return selectList(new LambdaQueryWrapperX<DeviceInfoDO>()
|
||||
.eq(DeviceInfoDO::getState, state));
|
||||
.eq(DeviceInfoDO::getDeviceState, state));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +71,7 @@ public interface DeviceInfoMapper extends BaseMapperX<DeviceInfoDO> {
|
||||
*/
|
||||
default List<DeviceInfoDO> selectTimeoutDevices(@Param("timeoutTime") LocalDateTime timeoutTime) {
|
||||
return selectList(new LambdaQueryWrapperX<DeviceInfoDO>()
|
||||
.eq(DeviceInfoDO::getState, 1) // 只查询在线状态的设备
|
||||
.eq(DeviceInfoDO::getDeviceState, 1) // 只查询在线状态的设备
|
||||
.and(wrapper -> wrapper
|
||||
.isNull(DeviceInfoDO::getLastMessageTime) // 从未上报过消息
|
||||
.or()
|
||||
@@ -83,7 +84,14 @@ public interface DeviceInfoMapper extends BaseMapperX<DeviceInfoDO> {
|
||||
*/
|
||||
default Long selectCountByState(Integer state) {
|
||||
return selectCount(new LambdaQueryWrapperX<DeviceInfoDO>()
|
||||
.eq(DeviceInfoDO::getState, state));
|
||||
.eq(DeviceInfoDO::getDeviceState, state));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备统计信息
|
||||
* @param baseId
|
||||
* @return
|
||||
*/
|
||||
DeviceStatisticsRespVO getStatisticsInfo(@Param("baseId") String baseId,
|
||||
@Param("tenantId") String tenantId);
|
||||
}
|
||||
@@ -26,6 +26,12 @@ public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 设备信息 1-050-004-000 ==========
|
||||
ErrorCode DEVICE_INFO_NOT_EXISTS = new ErrorCode(1_050_004_000, "设备信息不存在");
|
||||
ErrorCode DEVICE_INFO_NOT_EXISTS_FOR_USER_LOGIN = new ErrorCode(1_050_004_001, "物连网农业四情采集,登录失败");
|
||||
ErrorCode DEVICE_INFO_NOT_EXISTS_FOR_USER_DEVICE = new ErrorCode(1_050_004_002, "获取用户设备失败");
|
||||
ErrorCode DEVICE_INFO_NOT_EXISTS_FOR_REAL_TIME_DATA= new ErrorCode(1_050_004_003, "获取设备实时数据失败");
|
||||
ErrorCode DEVICE_INFO_NOT_EXISTS_FOR_ALARM_NOTICE_RECORD= new ErrorCode(1_050_004_004, "获取设备报警通知记录失败");
|
||||
ErrorCode DEVICE_INFO_NOT_EXISTS_FOR_WF_LOGIN= new ErrorCode(1_050_004_005, "水肥机登录失败");
|
||||
ErrorCode DEVICE_TYPE_ERROR= new ErrorCode(1_050_004_006, "未找到对应的设备类型");
|
||||
|
||||
// ========== 设备地块关系 1-050-005-000 ==========
|
||||
ErrorCode DEVICE_PLOT_RELATION_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备地块关系不存在");
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @description: 设备管理
|
||||
* 参考文档: 物连网农业四情采集api协议.pdf 版本:v2.0.3 日期:2022.08.16
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 10:50
|
||||
*/
|
||||
@Service
|
||||
public class DeviceManager {
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.commons;
|
||||
|
||||
/**
|
||||
* @description: 设备常量
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 15:24
|
||||
*/
|
||||
public class DeviceConst {
|
||||
|
||||
/**
|
||||
* 四情 设备类型
|
||||
*/
|
||||
public final String DEVICE_TYPE_IRRIGATION = "irrigation"; // 灌溉
|
||||
public final String DEVICE_TYPE_WORM = "worm"; // 虫害
|
||||
public final String DEVICE_TYPE_WORM_FLAG_SHIP = "wormFlagship";
|
||||
public final String DEVICE_TYPE_SOIL = "soil"; // 土壤
|
||||
public final String DEVICE_TYPE_MET = "met"; // 重金属
|
||||
public final String DEVICE_TYPE_SPORE = "spore"; // 孢子
|
||||
public final String DEVICE_TYPE_CAMERA = "camera"; // 监控
|
||||
|
||||
/**
|
||||
* 四情 设备状态
|
||||
*/
|
||||
public final String DEVICE_STATUS_ONLINE = "online";
|
||||
public final String DEVICE_STATUS_OFFLINE = "offline";
|
||||
public final String DEVICE_STATUS_ALARM = "alarm";
|
||||
|
||||
/**
|
||||
* 四情 http 状态
|
||||
*/
|
||||
public final String HTTP_STATUS_1000 = "1000"; // 成功
|
||||
public final String HTTP_STATUS_1001 = "1001"; // 失败 (执行错误)
|
||||
public final String HTTP_STATUS_1002 = "1002"; // 失败 (验证失败)
|
||||
public final String HTTP_STATUS_1003 = "1003"; // 服务端异常
|
||||
public final String HTTP_STATUS_1004 = "1004"; // 登录过期
|
||||
public final String HTTP_STATUS_2000 = "2000"; // 登录超时
|
||||
public final String HTTP_STATUS_4000 = "4000"; // 未登录或登录失败
|
||||
public final String HTTP_STATUS_5000 = "5000"; // 操作失败
|
||||
|
||||
/**
|
||||
* 四情-墒情 设备实时数据 - 节点类型
|
||||
*/
|
||||
public final int NODE_TYPE_1 = 1; // 模拟量
|
||||
public final int NODE_TYPE_1_1 = 1; // 模拟量 使能模拟量
|
||||
public final int NODE_TYPE_1_2 = 2; // 模拟量 使能
|
||||
|
||||
public final int NODE_TYPE_2 = 2; // 模拟量
|
||||
public final int NODE_TYPE_2_1 = 1; // 模拟量 使能模拟量
|
||||
public final int NODE_TYPE_2_2 = 2; // 模拟量 禁用
|
||||
|
||||
public final int NODE_TYPE_3 = 3; // 模拟量
|
||||
public final int NODE_TYPE_3_1 = 1; // 模拟量 禁用模拟量
|
||||
public final int NODE_TYPE_3_2 = 2; // 模拟量 使能
|
||||
|
||||
public final int NODE_TYPE_4 = 4; // 浮点型设备
|
||||
public final int NODE_TYPE_5 = 5; // 开关量型设备
|
||||
public final int NODE_TYPE_6 = 6; // 32位有符号整型
|
||||
public final int NODE_TYPE_7 = 7; // 32位无符号整型
|
||||
public final int NODE_TYPE_8 = 8; // 遥调设备
|
||||
|
||||
/**
|
||||
* 四情-墒情 设备实时数据 - 节点报警等级
|
||||
*/
|
||||
public final int ALARM_STATUS_0 = 0; // 正常
|
||||
public final int ALARM_STATUS_1 = 1; // 超上限报警
|
||||
public final int ALARM_STATUS_2 = 2; // 超下限报警
|
||||
public final int ALARM_STATUS_3 = 3; // 开关闭合报警
|
||||
public final int ALARM_STATUS_4 = 4; // 开关断开报警
|
||||
public final int ALARM_STATUS_5 = 5; // 遥调报警
|
||||
|
||||
/**
|
||||
* 四情-设备报警通知记录 - 通知类型
|
||||
*/
|
||||
public final int NOTICE_TYPE_0 = 0; // 邮箱
|
||||
public final int NOTICE_TYPE_1 = 1; // 短信
|
||||
|
||||
// TODO: 2025/9/17 UpdateNodeInfoReqVO
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.conf;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @description: 设备配置
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 10:55
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "device-manager")
|
||||
public class DeviceConf {
|
||||
|
||||
private FourSituations fourSituations;
|
||||
private WaterAndFertilizer waterAndFertilizer;
|
||||
|
||||
@Data
|
||||
public static class FourSituations {
|
||||
private String hosts;
|
||||
private String username;
|
||||
private String password;
|
||||
private Global global;
|
||||
private Soil soil;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Global {
|
||||
private String url21; // 用户登录
|
||||
private String url22; // 获取登录用户信息
|
||||
private String url23; // 获取子用户信息
|
||||
private String url24; // 设备报警通知记录
|
||||
private String url25; // 获取用户区域
|
||||
private String url26; // 获取用户设备
|
||||
private String url27; // 批量获取摄像头设备信息
|
||||
private String url28; // 获取设备实时数据
|
||||
private String url29; // 区域绑定设备
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Soil {
|
||||
private String url41; // 墒情设备, 获取设备全部信息
|
||||
private String url42; // 墒情设备, 更新设备信息
|
||||
private String url43; // 墒情设备, 更新节点信息
|
||||
private String url44; // 墒情设备, 获取历史数据
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class WaterAndFertilizer {
|
||||
private String hosts;
|
||||
private String username;
|
||||
private String password;
|
||||
private String url1;
|
||||
private String url2;
|
||||
private String url3;
|
||||
private String url4;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.conf;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 14:42
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class SchedulingConfig {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil;
|
||||
|
||||
import cn.iocoder.yudao.module.farm.manager.devices.soil.dto.UserLoginRespVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* @description: 缓存token
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 14:33
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseTokenCacheService {
|
||||
|
||||
// 使用ConcurrentHashMap存储token信息,key: tenantId, value: token
|
||||
private final Map<String, String> tokenCache = new ConcurrentHashMap<>();
|
||||
// 存储token详细信息,key: token, value: UserLoginRespVO
|
||||
private final Map<String, UserLoginRespVO> tokenDetailCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 每30分钟清理一次过期token
|
||||
*/
|
||||
@Scheduled(fixedRate = 30 * 60 * 1000) // 30分钟,单位毫秒
|
||||
public void cleanExpiredTokens() {
|
||||
long currentTime = System.currentTimeMillis() / 1000;
|
||||
int initialTokenCount = tokenDetailCache.size();
|
||||
int initialTenantCount = tokenCache.size();
|
||||
|
||||
// 清理过期的token详情
|
||||
tokenDetailCache.entrySet().removeIf(entry ->
|
||||
entry.getValue() != null && entry.getValue().expDate <= currentTime
|
||||
);
|
||||
|
||||
// 同步清理租户ID映射
|
||||
tokenCache.entrySet().removeIf(entry -> {
|
||||
String token = entry.getValue();
|
||||
UserLoginRespVO tokenInfo = tokenDetailCache.get(token);
|
||||
return tokenInfo == null || tokenInfo.expDate <= currentTime;
|
||||
});
|
||||
|
||||
int finalTokenCount = tokenDetailCache.size();
|
||||
int finalTenantCount = tokenCache.size();
|
||||
int removedTokenCount = initialTokenCount - finalTokenCount;
|
||||
int removedTenantCount = initialTenantCount - finalTenantCount;
|
||||
|
||||
log.info("Token缓存清理完成: 清理前 {} 个token({} 租户), 清理后 {} 个token({} 租户), 移除 {} 个过期token({} 租户)",
|
||||
initialTokenCount, initialTenantCount,
|
||||
finalTokenCount, finalTenantCount,
|
||||
removedTokenCount, removedTenantCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存token(基于租户ID)
|
||||
*/
|
||||
protected void cacheToken(String tenantId, UserLoginRespVO loginRespVO) {
|
||||
if (tenantId != null && loginRespVO != null && loginRespVO.token != null) {
|
||||
// 存储租户ID到token的映射
|
||||
tokenCache.put(tenantId, loginRespVO.token);
|
||||
// 存储token详细信息
|
||||
tokenDetailCache.put(loginRespVO.token, loginRespVO);
|
||||
log.debug("租户 {} 的Token已缓存: {}", tenantId, loginRespVO.token);
|
||||
}
|
||||
}
|
||||
|
||||
protected void cacheToken(String tenantId, String token) {
|
||||
tokenCache.put(tenantId, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据租户ID获取token
|
||||
*/
|
||||
protected String getTokenByTenantId(String tenantId) {
|
||||
String token = tokenCache.get(tenantId);
|
||||
if (token != null && isTokenValid(token)) {
|
||||
log.debug("获取租户 {} 的Token: {}", tenantId, token);
|
||||
return token;
|
||||
}
|
||||
// 如果token过期,移除映射
|
||||
if (token != null) {
|
||||
tokenCache.remove(tenantId);
|
||||
tokenDetailCache.remove(token);
|
||||
log.debug("租户 {} 的Token已过期,已移除", tenantId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据租户ID获取token详细信息
|
||||
*/
|
||||
protected UserLoginRespVO getTokenInfoByTenantId(String tenantId) {
|
||||
String token = getTokenByTenantId(tenantId);
|
||||
if (token != null) {
|
||||
return tokenDetailCache.get(token);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据token获取详细信息
|
||||
*/
|
||||
protected UserLoginRespVO getTokenInfo(String token) {
|
||||
UserLoginRespVO tokenInfo = tokenDetailCache.get(token);
|
||||
if (tokenInfo != null && isTokenValid(tokenInfo)) {
|
||||
return tokenInfo;
|
||||
}
|
||||
// 如果token过期,移除
|
||||
if (tokenInfo != null) {
|
||||
removeTokenByValue(token);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查租户的token是否有效
|
||||
*/
|
||||
protected boolean isTokenValidByTenantId(String tenantId) {
|
||||
String token = tokenCache.get(tenantId);
|
||||
boolean isValid = token != null && isTokenValid(token);
|
||||
log.debug("租户 {} 的Token验证结果: {}", tenantId, isValid ? "有效" : "无效");
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查token是否有效
|
||||
*/
|
||||
protected boolean isTokenValid(String token) {
|
||||
UserLoginRespVO tokenInfo = tokenDetailCache.get(token);
|
||||
boolean isValid = tokenInfo != null && isTokenValid(tokenInfo);
|
||||
log.debug("Token验证结果: {} -> {}", token, isValid ? "有效" : "无效");
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查token是否有效(内部方法)
|
||||
*/
|
||||
private boolean isTokenValid(UserLoginRespVO tokenInfo) {
|
||||
long currentTime = System.currentTimeMillis() / 1000;
|
||||
return tokenInfo.expDate > currentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据租户ID移除token
|
||||
*/
|
||||
protected void removeTokenByTenantId(String tenantId) {
|
||||
String token = tokenCache.remove(tenantId);
|
||||
if (token != null) {
|
||||
tokenDetailCache.remove(token);
|
||||
log.debug("租户 {} 的Token已移除: {}", tenantId, token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据token值移除
|
||||
*/
|
||||
protected void removeTokenByValue(String token) {
|
||||
// 需要找到对应的租户ID
|
||||
tokenDetailCache.remove(token);
|
||||
tokenCache.entrySet().removeIf(entry -> entry.getValue().equals(token));
|
||||
log.debug("Token已移除: {}", token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有租户的token数量
|
||||
*/
|
||||
protected int getTenantTokenCount() {
|
||||
return tokenCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有token的数量
|
||||
*/
|
||||
protected int getTotalTokenCount() {
|
||||
return tokenDetailCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有token
|
||||
*/
|
||||
protected void clearAllTokens() {
|
||||
int tenantCount = tokenCache.size();
|
||||
int tokenCount = tokenDetailCache.size();
|
||||
tokenCache.clear();
|
||||
tokenDetailCache.clear();
|
||||
log.info("所有Token已清除,共清除 {} 个租户的 {} 个token", tenantCount, tokenCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户ID列表
|
||||
*/
|
||||
protected java.util.Set<String> getAllTenantIds() {
|
||||
return new java.util.HashSet<>(tokenCache.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有token信息(调试用)
|
||||
*/
|
||||
protected Map<String, String> getTokenCache() {
|
||||
return new ConcurrentHashMap<>(tokenCache);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil;
|
||||
|
||||
import cn.iocoder.yudao.module.farm.manager.devices.conf.DeviceConf;
|
||||
import cn.iocoder.yudao.module.farm.manager.devices.soil.dto.*;
|
||||
import cn.iocoder.yudao.module.farm.utils.HttpClientUtils;
|
||||
import cn.iocoder.yudao.module.farm.utils.HttpClientUtils.ApiResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* @description: 土壤墒情
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 10:57
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SoilService extends BaseTokenCacheService {
|
||||
|
||||
private final DeviceConf deviceConf;
|
||||
private final HttpClientUtils httpClientUtils;
|
||||
|
||||
public SoilService(DeviceConf deviceConf, HttpClientUtils httpClientUtils) {
|
||||
this.deviceConf = deviceConf;
|
||||
this.httpClientUtils = httpClientUtils;
|
||||
}
|
||||
|
||||
private final String SOIL = "soil_";
|
||||
|
||||
/**
|
||||
* 登录获取token
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
public UserLoginRespVO userLogin(UserLoginReqVO req) {
|
||||
String tenantId = getLoginUser().getTenantId().toString();
|
||||
String hosts = deviceConf.getFourSituations().getHosts();
|
||||
String userLoginURL = deviceConf.getFourSituations().getGlobal().getUrl21();
|
||||
|
||||
try {
|
||||
ApiResponse<UserLoginRespVO> response = httpClientUtils.postRequest(hosts, userLoginURL, req, null, UserLoginRespVO.class);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
UserLoginRespVO data = response.getData();
|
||||
// TODO: 2025/9/17 持久化
|
||||
cacheToken(SOIL+tenantId, data);
|
||||
}
|
||||
|
||||
return response.getData();
|
||||
} catch (Exception e) {
|
||||
log.error("租户 {} 登录失败: {}", tenantId, e.getMessage(), e);
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_USER_LOGIN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证租户token有效性
|
||||
*/
|
||||
public boolean validateUserToken(String tenantId) {
|
||||
log.debug("验证租户 {} 的Token", tenantId);
|
||||
return isTokenValidByTenantId(SOIL+tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户设备
|
||||
*/
|
||||
public List<SysUserDeviceRespVO> getSysUserDevice(SysUserDeviceReqVO req) {
|
||||
|
||||
String tenantId = getLoginUser().getTenantId().toString();
|
||||
String token = getTokenByTenantId(tenantId);
|
||||
String hosts = deviceConf.getFourSituations().getHosts();
|
||||
String getSysUserDeviceURL = deviceConf.getFourSituations().getGlobal().getUrl26();
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("token", token);
|
||||
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("groupId", StringUtils.defaultString(req.getGroupId()));
|
||||
params.put("deviceType", StringUtils.defaultString(req.getDeviceType()));
|
||||
|
||||
try {
|
||||
|
||||
ApiResponse<List<SysUserDeviceRespVO>> response = httpClientUtils.getRequestAsList(hosts, getSysUserDeviceURL, params, headers, SysUserDeviceRespVO.class);
|
||||
if (!response.isSuccess()) {
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_USER_DEVICE);
|
||||
}
|
||||
return response.getData();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_USER_DEVICE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备实时数据
|
||||
*/
|
||||
public List<RealTimeDataRespVO> getRealTimeData(RealTimeDataReqVO req) {
|
||||
String tenantId = getLoginUser().getTenantId().toString();
|
||||
String token = getTokenByTenantId(tenantId);
|
||||
String hosts = deviceConf.getFourSituations().getHosts();
|
||||
String getSysUserDeviceURL = deviceConf.getFourSituations().getGlobal().getUrl27();
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("token", token);
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("deviceAddrs", req.getDeviceAddrs());
|
||||
|
||||
try {
|
||||
ApiResponse<List<RealTimeDataRespVO>> response = httpClientUtils.getRequestAsList(hosts, getSysUserDeviceURL, params, headers, RealTimeDataRespVO.class);
|
||||
if (!response.isSuccess()) {
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_REAL_TIME_DATA);
|
||||
}
|
||||
return response.getData();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_REAL_TIME_DATA);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备报请通知记录
|
||||
*/
|
||||
public List<AlarmNoticeRecordRespVO> getAlarmNoticeRecord(AlarmNoticeRecordReqVO req) {
|
||||
|
||||
String tenantId = getLoginUser().getTenantId().toString();
|
||||
String token = getTokenByTenantId(tenantId);
|
||||
String hosts = deviceConf.getFourSituations().getHosts();
|
||||
String getAlarmNoticeRecord = deviceConf.getFourSituations().getGlobal().getUrl24();
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("token", token);
|
||||
Map<String, String> params = req.toFieldValueMap();
|
||||
|
||||
try {
|
||||
ApiResponse<List<AlarmNoticeRecordRespVO>> response = httpClientUtils.getRequestAsList(hosts, getAlarmNoticeRecord, params, headers, AlarmNoticeRecordRespVO.class);
|
||||
if (!response.isSuccess()) {
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_ALARM_NOTICE_RECORD);
|
||||
}
|
||||
return response.getData();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_ALARM_NOTICE_RECORD);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 墒情设备, 获取设备全部信息
|
||||
*/
|
||||
public List<DeviceAllInfoRespVO> getDeviceAllInfo(DeviceAllInfoReqVO req) {
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 墒情设备, 更新设备信息
|
||||
*/
|
||||
public Boolean updateDevice(UpdateDeviceReqVO req) {
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 墒情设备, 更新节点信息
|
||||
*/
|
||||
public Boolean updateNodeInfo(UpdateNodeInfoReqVO req) {
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 墒情设备, 获取历史数据
|
||||
*/
|
||||
public HistoryDataListPageRespVO getHistoryDataList(HistoryDataListPageReqVO req) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description: 设备报请通知记录 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 19:51
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AlarmNoticeRecordReqVO {
|
||||
/**
|
||||
* 开始时间(yyyy-MM-dd HH:mm:ss)
|
||||
* tips: 必选
|
||||
*/
|
||||
private String beginTime;
|
||||
/**
|
||||
* 结束时间(yyyy-MM-dd HH:mm:ss)
|
||||
* tips: 必选
|
||||
*/
|
||||
private String endTime;
|
||||
/**
|
||||
* 设备地址码
|
||||
* tips: 必选
|
||||
*/
|
||||
private String deviceAddr;
|
||||
/**
|
||||
* 节点ID (多个节点使用","分隔)
|
||||
* tips: 非必选
|
||||
*/
|
||||
private String factorIds;
|
||||
/**
|
||||
* 通知类型 0 邮件 1 短信
|
||||
*/
|
||||
private String noticeType;
|
||||
|
||||
public Map<String, String> toFieldValueMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("beginTime", this.getBeginTime());
|
||||
map.put("endTime", this.getEndTime());
|
||||
map.put("deviceAddr", this.getDeviceAddr());
|
||||
map.put("factorIds", this.getFactorIds());
|
||||
map.put("noticeType", this.getNoticeType());
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 设备报请通知记录 响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 19:51
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AlarmNoticeRecordRespVO {
|
||||
/**
|
||||
* 通知记录表 id
|
||||
*/
|
||||
private Integer noticeID;
|
||||
/**
|
||||
* 类型 0 邮件 1 短信
|
||||
*/
|
||||
private Integer noticeIType;
|
||||
/**
|
||||
* 设备地址
|
||||
*/
|
||||
private String deviceAddr;
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private String factorId;
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String factorName;
|
||||
/**
|
||||
* 联系人
|
||||
*/
|
||||
private String contact;
|
||||
/**
|
||||
* 通知内容
|
||||
*/
|
||||
private String noticeIContent;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String createTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 墒情设备, 设备全部信息 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 20:13
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceAllInfoReqVO {
|
||||
/**
|
||||
* 必选
|
||||
* 设备地址(多个用英文逗号分隔,最多同时获取 5 个设备信息)
|
||||
*/
|
||||
private String deviceStr;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 墒情设备, 设备全部信息 响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 20:13
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeviceAllInfoRespVO {
|
||||
/** 设备地址 */
|
||||
private String deviceAddr;
|
||||
|
||||
/** 设备名称 */
|
||||
private String deviceName;
|
||||
|
||||
/** 经度 */
|
||||
private double lng;
|
||||
|
||||
/** 纬度 */
|
||||
private double lat;
|
||||
|
||||
/** 数据保存间隔(分钟) */
|
||||
private Integer saveDataInterval;
|
||||
|
||||
/** 离线判断间隔(分钟) */
|
||||
private Integer offlineInterval;
|
||||
|
||||
/** 节点信息列表 */
|
||||
private List<NodeInfo> nodeInfoList;
|
||||
|
||||
/** 遥调信息列表 */
|
||||
private List<RegulatingInfo> regulatingInfoList;
|
||||
|
||||
/**
|
||||
* 节点信息内部类
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class NodeInfo {
|
||||
/** 节点编号 */
|
||||
private Integer nodeId;
|
||||
|
||||
/** 设备地址 */
|
||||
private String deviceAddr;
|
||||
|
||||
/** 节点名称 */
|
||||
private String nodeName;
|
||||
|
||||
/** 节点是否开启 0:关闭 1:开启 */
|
||||
private Integer enable;
|
||||
|
||||
/** 0 常规 ;1 雨量 ;2 蒸发量 */
|
||||
private Integer nodeMold;
|
||||
|
||||
/**
|
||||
* 节点类型:
|
||||
* 1: 模拟量 1 使能模拟量 2 使能
|
||||
* 2: 模拟量 1 使能模拟量 2 禁用
|
||||
* 3: 模拟量 1 禁用模拟量 2 使能
|
||||
* 4: 浮点型设备
|
||||
* 5: 开关量型设备
|
||||
* 6: 32 位有符号整形
|
||||
* 7: 32 位无符号整形
|
||||
* 8: 遥调设备
|
||||
*/
|
||||
private Integer nodeType;
|
||||
|
||||
/** 优先级 */
|
||||
private Integer priority;
|
||||
|
||||
/** 小数位 */
|
||||
private Integer digits;
|
||||
|
||||
/** 模拟量 1 名称 */
|
||||
private String temName;
|
||||
|
||||
/** 模拟量 1 单位 */
|
||||
private String temUnit;
|
||||
|
||||
/** 模拟量 1 系数 */
|
||||
private double temRatio;
|
||||
|
||||
/** 模拟量 1 偏差 */
|
||||
private double temOffset;
|
||||
|
||||
/** 模拟量 1 上限 */
|
||||
private double temUpperLimit;
|
||||
|
||||
/** 模拟量 1 下限 */
|
||||
private double temLowerLimit;
|
||||
|
||||
/** 模拟量 2 名称 */
|
||||
private String humName;
|
||||
|
||||
/** 模拟量 2 单位 */
|
||||
private String humUnit;
|
||||
|
||||
/** 模拟量 2 系数 */
|
||||
private double humRatio;
|
||||
|
||||
/** 模拟量 2 偏差 */
|
||||
private double humOffset;
|
||||
|
||||
/** 模拟量 2 上限 */
|
||||
private double humUpperLimit;
|
||||
|
||||
/** 模拟量 2 下限 */
|
||||
private double humLowerLimit;
|
||||
|
||||
/** 开关量闭合显示内容 */
|
||||
private String switchOnContent;
|
||||
|
||||
/** 开关量断开显示内容 */
|
||||
private String switchOffContent;
|
||||
|
||||
/** 开关量报警类型(0 不报警 1 闭合报警 2 断开报警) */
|
||||
private Integer switchAlarmType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遥调信息内部类
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class RegulatingInfo {
|
||||
/** 遥调编号(8 位或 15 位) */
|
||||
private String regularId;
|
||||
|
||||
/** 节点编号 */
|
||||
private Integer nodeId;
|
||||
|
||||
/** 设备地址 */
|
||||
private String deviceAddr;
|
||||
|
||||
/** 遥调档位值 */
|
||||
private double regularValue;
|
||||
|
||||
/** 遥调档位内容 */
|
||||
private String regularText;
|
||||
|
||||
/** 是否开启报警 0:不开启 1:开启 */
|
||||
private Integer alarmEnable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 墒情设备, 获取历史数据 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 20:53
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class HistoryDataListPageReqVO {
|
||||
/**
|
||||
* 必选
|
||||
* 开始时间 (时间戳)
|
||||
*/
|
||||
private String beginTime;
|
||||
/**
|
||||
* 必选
|
||||
* 结束时间 (时间戳)
|
||||
*/
|
||||
private String endTime;
|
||||
/**
|
||||
* 必选
|
||||
* 设备地址
|
||||
*/
|
||||
private String deviceAddr;
|
||||
/**
|
||||
* 非必选
|
||||
* 节点ID
|
||||
*/
|
||||
private String nodeId;
|
||||
/**
|
||||
* 必选
|
||||
* 页码
|
||||
*/
|
||||
private Integer pages;
|
||||
/**
|
||||
* 必选
|
||||
* 每页数量
|
||||
*/
|
||||
private Integer limit;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 墒情设备, 获取历史数据 响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 20:53
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class HistoryDataListPageRespVO {
|
||||
/**
|
||||
* 页码,每页数量,总页数,总条数
|
||||
*/
|
||||
private Integer pages;
|
||||
private Integer limit;
|
||||
private Integer totalPages;
|
||||
private Integer total;
|
||||
private List<Row> rows;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Row{
|
||||
/**
|
||||
* 记录表 id
|
||||
*/
|
||||
private Integer historyId;
|
||||
/**
|
||||
* 节点编号
|
||||
*/
|
||||
private Integer nodeId;
|
||||
/**
|
||||
* 设备地址
|
||||
*/
|
||||
private String deviceAddress;
|
||||
/**
|
||||
* 模拟量 1
|
||||
*/
|
||||
private String temStr;
|
||||
private Double temValue;
|
||||
/**
|
||||
* 模拟狼 2
|
||||
*/
|
||||
private String humStr;
|
||||
private Double humValue;
|
||||
/**
|
||||
* 是否是报警数据 0:正常 1:报警
|
||||
*/
|
||||
private Integer alarmStatus;
|
||||
/**
|
||||
* 保存时间(时间戳)
|
||||
*/
|
||||
private Long recordTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 设备实时数据 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 19:02
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RealTimeDataReqVO {
|
||||
/**
|
||||
* 设备地址 非必定
|
||||
* tips: 多个用英文逗号分隔,空为获取当前用户下的所有设备
|
||||
*/
|
||||
private String deviceAddrs;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 设备实时数据 相应试题
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 19:01
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RealTimeDataRespVO {
|
||||
/**
|
||||
* 设备地址
|
||||
*/
|
||||
private String deviceAddr;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceName;
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private Double lat;
|
||||
/**
|
||||
* 维度
|
||||
*/
|
||||
private Double lng;
|
||||
/**
|
||||
* 设备状态
|
||||
*/
|
||||
private String status;
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
private List<RealTimeData> data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class RealTimeData {
|
||||
// 节点编号
|
||||
private Integer nodeId;
|
||||
// 节点名称
|
||||
private String nodeName;
|
||||
// 节点类型
|
||||
private Integer nodeType;
|
||||
// 模拟量1 名称,单位,原值,显示值,报警等级
|
||||
private String temName;
|
||||
private String temUnit;
|
||||
private Double temValue;
|
||||
private String temValueStr;
|
||||
private Integer temAlarmStatus;
|
||||
// 模拟量2 名称,单位,原值,显示值,报警等级
|
||||
private String humName;
|
||||
private String humUnit;
|
||||
private String humValue;
|
||||
private String humValueStr;
|
||||
private String humAlarmStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 获取用户设备 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 15:28
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SysUserDeviceReqVO {
|
||||
/**
|
||||
* 区域ID 非必填
|
||||
*/
|
||||
private String groupId;
|
||||
/**
|
||||
* 设备类型 非必填
|
||||
*/
|
||||
private String deviceType;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 获取用户设备 响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 15:31
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SysUserDeviceRespVO {
|
||||
/**
|
||||
* 设备地址
|
||||
*/
|
||||
public String deviceAddr;
|
||||
/**
|
||||
* 区域ID
|
||||
*/
|
||||
public String groupId;
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
public String deviceType;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
public String deviceName;
|
||||
/**
|
||||
* 设备的经度
|
||||
*/
|
||||
public String deviceLng;
|
||||
/**
|
||||
* 设备的维度
|
||||
*/
|
||||
public String deviceLat;
|
||||
/**
|
||||
* 设备iccid
|
||||
*/
|
||||
public String deviceIccId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 墒情设备, 更新设备信息 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 20:29
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UpdateDeviceReqVO {
|
||||
/**
|
||||
* 设备地址
|
||||
*/
|
||||
private String deviceAddr;
|
||||
private String deviceName;
|
||||
private Double lat;
|
||||
private Double lng;
|
||||
/**
|
||||
* 离线判断间隔
|
||||
*/
|
||||
private Integer offlineInterval;
|
||||
/**
|
||||
* 数据保存间隔
|
||||
*/
|
||||
private Integer saveDataInterval;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 墒情设备, 更新节点信息 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 20:34
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UpdateNodeInfoReqVO {
|
||||
|
||||
/**
|
||||
* 必选
|
||||
* 设备地址
|
||||
*/
|
||||
private String deviceAddr;
|
||||
/**
|
||||
* 必选
|
||||
* 节点编号
|
||||
*/
|
||||
private String nodeId;
|
||||
private String nodeName;
|
||||
/**
|
||||
* 参考 cn.iocoder.yudao.module.farm.manager.devices.commons.DeviceConst: 四情-墒情 设备实时数据 - 节点类型
|
||||
*/
|
||||
private Integer nodeType;
|
||||
/**
|
||||
* 0 常规 ;1 雨量 ;2 蒸发量
|
||||
*/
|
||||
private Integer nodeMold;
|
||||
/**
|
||||
* 小数位数
|
||||
*/
|
||||
private Integer digits;
|
||||
/**
|
||||
* 节点是否开启 0:关闭 1:开启
|
||||
*/
|
||||
private Integer enable;
|
||||
/**
|
||||
* 优先级
|
||||
*/
|
||||
private Integer priority;
|
||||
/**
|
||||
* 开关量报警类型 (0 不报警 1 闭合报警 2 断开报警)
|
||||
*/
|
||||
private Integer switchAlarmType;
|
||||
/**
|
||||
* 开关量闭合显示内容
|
||||
*/
|
||||
private String switchOnContent;
|
||||
/**
|
||||
* 开关量断开显示内容
|
||||
*/
|
||||
private String switchOffContent;
|
||||
|
||||
/**
|
||||
* 模拟量2 : 名称,单位,上限,下限,偏差,系数
|
||||
*/
|
||||
private String humName;
|
||||
private String humUnit;
|
||||
private Double humUpperLimit;
|
||||
private Double humLowerLimit;
|
||||
private Double humOffset;
|
||||
private Double humRatio;
|
||||
|
||||
/**
|
||||
* 模拟量2 : 名称,单位,上限,下限,偏差,系数
|
||||
*/
|
||||
private String temName;
|
||||
private String temUnit;
|
||||
private Double temUpperLimit;
|
||||
private Double temLowerLimit;
|
||||
private Double temOffset;
|
||||
private Double temRatio;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 登录请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 13:46
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserLoginReqVO {
|
||||
public String loginName;
|
||||
public String loginPwd;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.soil.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 登录响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 13:46
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserLoginRespVO {
|
||||
/**
|
||||
* 登录标识
|
||||
*/
|
||||
public String loginSign;
|
||||
/**
|
||||
* token 生成时间
|
||||
*/
|
||||
public long currDate;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
public long expDate;
|
||||
/**
|
||||
* 令牌
|
||||
*/
|
||||
public String token;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.wf;
|
||||
|
||||
import cn.iocoder.yudao.module.farm.manager.devices.conf.DeviceConf;
|
||||
import cn.iocoder.yudao.module.farm.manager.devices.soil.BaseTokenCacheService;
|
||||
import cn.iocoder.yudao.module.farm.manager.devices.wf.dto.*;
|
||||
import cn.iocoder.yudao.module.farm.utils.HttpClientUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
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.DEVICE_INFO_NOT_EXISTS_FOR_WF_LOGIN;
|
||||
|
||||
/**
|
||||
* @description: 水肥机
|
||||
* @author: binda
|
||||
* @date: 2025/9/17 10:59
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WaterFertilizerService extends BaseTokenCacheService {
|
||||
|
||||
private final DeviceConf deviceConf;
|
||||
private final HttpClientUtils httpClientUtils;
|
||||
|
||||
public WaterFertilizerService(DeviceConf deviceConf, HttpClientUtils httpClientUtils) {
|
||||
this.deviceConf = deviceConf;
|
||||
this.httpClientUtils = httpClientUtils;
|
||||
}
|
||||
|
||||
private final String Authorization = "Authorization";
|
||||
private final String Bearer = "Bearer ";
|
||||
private final String WF = "wf_";
|
||||
|
||||
/**
|
||||
* 获取公钥
|
||||
*/
|
||||
public LoginRespVO login() {
|
||||
String tenantId = getLoginUser().getTenantId().toString();
|
||||
String hosts = deviceConf.getWaterAndFertilizer().getHosts();
|
||||
String loginURL = deviceConf.getWaterAndFertilizer().getUrl1();
|
||||
LoginReqVO req = new LoginReqVO(deviceConf.getWaterAndFertilizer().getUsername(),
|
||||
deviceConf.getWaterAndFertilizer().getPassword());
|
||||
|
||||
try {
|
||||
HttpClientUtils.ApiResponse<LoginRespVO> response = httpClientUtils.postRequest(hosts, loginURL, req, null, LoginRespVO.class);
|
||||
if (!response.isSuccess()) {
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_WF_LOGIN);
|
||||
}
|
||||
LoginRespVO data = response.getData();
|
||||
cacheToken(WF+tenantId, data.getToken());
|
||||
return response.getData();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw exception(DEVICE_INFO_NOT_EXISTS_FOR_WF_LOGIN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @param userCode 用户名
|
||||
* @return
|
||||
*/
|
||||
public DevicePageRespVO getDeviceList(String userCode) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备实时状态
|
||||
* @param deviceId 设备ID
|
||||
* @return
|
||||
*/
|
||||
public RealTimeDeviceDataRespVO getRealTimeDeviceData(String deviceId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改设备属性
|
||||
* @param deviceId 设备ID
|
||||
* @param paramStr 更改值: true: 打开,false: 关闭
|
||||
* @return
|
||||
*/
|
||||
public UpdateDeviceStatusRespVO updateDeviceStatus(String deviceId, String paramStr) {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.wf.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 获取设备列表 分页响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/18 14:30
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DevicePageRespVO {
|
||||
private Integer code;
|
||||
private String msg;
|
||||
private Integer total;
|
||||
private List<Row> rows;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Row{
|
||||
private Long deviceId;
|
||||
private Long deptId;
|
||||
private String productCode;
|
||||
private String productName;
|
||||
private String modelCode;
|
||||
private String deviceCode;
|
||||
private String code; // 设备编号
|
||||
private String name;
|
||||
private String farmland;
|
||||
private String modelName;
|
||||
private String userCode;
|
||||
private Double lng;
|
||||
private Double lat;
|
||||
private Long farmlandId;
|
||||
private String delFlag;
|
||||
private String loginIp;
|
||||
private Date loginDate;
|
||||
private String createBy;
|
||||
private Date createTime;
|
||||
private String updateBy;
|
||||
private Date updateTime;
|
||||
private String remark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.wf.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 获取公钥 请求实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/18 14:19
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class LoginReqVO {
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.wf.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 获取公钥 响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/18 14:26
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class LoginRespVO {
|
||||
private Integer code;
|
||||
private String msg;
|
||||
private String token;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.wf.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description: 设备实时数据
|
||||
* @author: binda
|
||||
* @date: 2025/9/18 14:38
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RealTimeDeviceDataRespVO {
|
||||
private String deviceId;
|
||||
private String name;
|
||||
private String stateText; // 是否在线 中文
|
||||
private String stateValue; // 是否在线 编码 online
|
||||
private List<ControlProperty> controlProperties;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class ControlProperty {
|
||||
private String id; // 开关标识
|
||||
private String name;
|
||||
private String operateType; // 设备类型
|
||||
private String operateValue;
|
||||
private String value;
|
||||
private String formatValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.farm.manager.devices.wf.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 更改设备属性 响应实体
|
||||
* @author: binda
|
||||
* @date: 2025/9/18 14:45
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UpdateDeviceStatusRespVO {
|
||||
private Integer code;
|
||||
private String msg;
|
||||
}
|
||||
@@ -352,6 +352,9 @@ public class BaseInfoServiceImpl implements BaseInfoService {
|
||||
@Override
|
||||
public Double getBaseTotalArea(String tenantId) {
|
||||
BigDecimal d = baseInfoMapper.getTotalBaseArea(null, Long.valueOf(tenantId));
|
||||
if (d == null) {
|
||||
return 0.00;
|
||||
}
|
||||
return d.doubleValue();
|
||||
}
|
||||
|
||||
|
||||
@@ -61,4 +61,7 @@ public interface DeviceClassifyService {
|
||||
|
||||
List<DeviceClassifyDO> getDeviceClassifyList();
|
||||
|
||||
List<DeviceTypeRespVO> getDeviceTypeList();
|
||||
|
||||
List<DeviceModelRespVO> getDeviceModelList(String deviceTypeId);
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceClassifyPageReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceClassifySaveReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceModelRespVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceclassify.vo.DeviceTypeRespVO;
|
||||
import cn.iocoder.yudao.module.farm.dal.dataobject.deviceclassify.DeviceClassifyDO;
|
||||
import cn.iocoder.yudao.module.farm.dal.mysql.deviceclassify.DeviceClassifyMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -15,6 +17,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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;
|
||||
@@ -108,6 +111,41 @@ public class DeviceClassifyServiceImpl implements DeviceClassifyService {
|
||||
return deviceClassifyDOS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceTypeRespVO> getDeviceTypeList() {
|
||||
List<DeviceClassifyDO> list = this.getDeviceClassifyList();
|
||||
return list.stream()
|
||||
.filter(device -> device.getParentId() != null && device.getParentId() == 0L)
|
||||
.map(device -> new DeviceTypeRespVO(
|
||||
String.valueOf(device.getId()),
|
||||
device.getName(),
|
||||
device.getImage(),
|
||||
device.getImageList()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceModelRespVO> getDeviceModelList(String deviceTypeId) {
|
||||
|
||||
List<DeviceClassifyDO> list = this.getDeviceClassifyList();
|
||||
Long parentId = Long.valueOf(deviceTypeId);
|
||||
|
||||
DeviceClassifyDO parentDevice = list.stream()
|
||||
.filter(device -> device.getId().equals(parentId))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> {throw exception(DEVICE_TYPE_ERROR);});
|
||||
|
||||
return list.stream()
|
||||
.filter(device -> device.getParentId() != null && device.getParentId().equals(parentId))
|
||||
.map(childDevice -> new DeviceModelRespVO(
|
||||
String.valueOf(childDevice.getId()),
|
||||
parentDevice.getName() + "/" + childDevice.getName()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理图片集合转换
|
||||
* 支持从 imageList 或 image 字段获取图片,优先使用 imageList
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.farm.service.deviceinfo;
|
||||
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2.DeviceStatisticsRespVO;
|
||||
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;
|
||||
@@ -61,4 +62,10 @@ public interface DeviceInfoService {
|
||||
*/
|
||||
PageResult<DeviceInfoDO> getDeviceInfoPage(DeviceInfoPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 设备统计
|
||||
* @param baseId
|
||||
* @return
|
||||
*/
|
||||
DeviceStatisticsRespVO getStatisticsInfo(String baseId);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.DeviceInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2.DeviceStatisticsRespVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -107,4 +108,9 @@ private void validateDeviceInfoExists(String id) {
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceStatisticsRespVO getStatisticsInfo(String baseId) {
|
||||
return deviceInfoMapper.getStatisticsInfo(baseId,String.valueOf(getLoginUser().getTenantId()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class DeviceStatusServiceImpl implements DeviceStatusService {
|
||||
// 构建更新对象
|
||||
DeviceInfoDO updateObj = new DeviceInfoDO();
|
||||
updateObj.setId(device.getId());
|
||||
updateObj.setState(state);
|
||||
updateObj.setDeviceState(state);
|
||||
updateObj.setLastMessageTime(messageTime);
|
||||
|
||||
// 根据状态设置相应的时间字段
|
||||
@@ -84,8 +84,8 @@ public class DeviceStatusServiceImpl implements DeviceStatusService {
|
||||
updateObj.setLastMessageTime(messageTime);
|
||||
|
||||
// 如果设备当前是离线状态,收到消息后自动上线
|
||||
if (DeviceStateEnum.isOffline(device.getState()) || device.getState() == null) {
|
||||
updateObj.setState(DeviceStateEnum.ONLINE.getState());
|
||||
if (DeviceStateEnum.isOffline(device.getDeviceState()) || device.getDeviceState() == null) {
|
||||
updateObj.setDeviceState(DeviceStateEnum.ONLINE.getState());
|
||||
updateObj.setOnlineTime(messageTime);
|
||||
// 如果是首次激活,设置激活时间
|
||||
if (device.getActiveTime() == null) {
|
||||
@@ -99,16 +99,16 @@ public class DeviceStatusServiceImpl implements DeviceStatusService {
|
||||
@Override
|
||||
public boolean isDeviceOnline(String deviceUniqueId) {
|
||||
DeviceInfoDO device = deviceInfoMapper.selectOne(DeviceInfoDO::getDeviceUniqueId, deviceUniqueId);
|
||||
return device != null && DeviceStateEnum.isOnline(device.getState());
|
||||
return device != null && DeviceStateEnum.isOnline(device.getDeviceState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceStateEnum getDeviceState(String deviceUniqueId) {
|
||||
DeviceInfoDO device = deviceInfoMapper.selectOne(DeviceInfoDO::getDeviceUniqueId, deviceUniqueId);
|
||||
if (device == null || device.getState() == null) {
|
||||
if (device == null || device.getDeviceState() == null) {
|
||||
return DeviceStateEnum.INACTIVE;
|
||||
}
|
||||
return DeviceStateEnum.valueOf(device.getState());
|
||||
return DeviceStateEnum.valueOf(device.getDeviceState());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,7 +140,7 @@ public class DeviceStatusServiceImpl implements DeviceStatusService {
|
||||
|
||||
for (DeviceInfoDO device : timeoutDevices) {
|
||||
// 只处理当前在线的设备
|
||||
if (DeviceStateEnum.isOnline(device.getState())) {
|
||||
if (DeviceStateEnum.isOnline(device.getDeviceState())) {
|
||||
deviceOffline(device.getDeviceUniqueId());
|
||||
count++;
|
||||
log.info("设备超时离线:{}", device.getDeviceUniqueId());
|
||||
|
||||
@@ -244,6 +244,9 @@ public class HomeServiceImpl implements HomeService {
|
||||
@Override
|
||||
public List<BaseNameVO> getBaseNameList() {
|
||||
List<BaseInfoDO> baseNameList = baseInfoService.getBaseInfos();
|
||||
if (ObjectUtils.isEmpty(baseNameList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return baseNameList.stream().map(e -> new BaseNameVO(e.getId(), e.getName())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package cn.iocoder.yudao.module.farm.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description: http client
|
||||
* @author: binda
|
||||
* @date: 2025/9/16 16:46
|
||||
*/
|
||||
@Component
|
||||
public class HttpClientUtils {
|
||||
private final HttpClient httpClient;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public HttpClientUtils(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
|
||||
}
|
||||
|
||||
private String getBaseUrl(String hosts) {
|
||||
if (hosts.startsWith("http")) {
|
||||
return hosts;
|
||||
}
|
||||
return "https://" + hosts;
|
||||
}
|
||||
|
||||
// 通用GET请求 - 返回原始JsonNode,让调用方自己解析
|
||||
public JsonNode getRequestRaw(String hosts,
|
||||
String path,
|
||||
Map<String, String> params,
|
||||
Map<String, String> headers) throws Exception {
|
||||
|
||||
String queryString = buildQueryString(params);
|
||||
HttpRequest request = buildRequest(hosts, path + queryString, "GET", null, headers);
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 200) {
|
||||
return objectMapper.readTree(response.body());
|
||||
} else {
|
||||
throw new RuntimeException("HTTP request failed with status: " + response.statusCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 通用GET请求 - 自动解析为指定类型
|
||||
public <T> ApiResponse<T> getRequest(String hosts,
|
||||
String path,
|
||||
Map<String, String> params,
|
||||
Map<String, String> headers,
|
||||
Class<T> dataType) throws Exception {
|
||||
|
||||
JsonNode responseJson = getRequestRaw(hosts, path, params, headers);
|
||||
return parseResponse(responseJson, dataType);
|
||||
|
||||
}
|
||||
|
||||
// 通用GET请求 - 自动解析为List类型
|
||||
public <T> ApiResponse<List<T>> getRequestAsList(String hosts,
|
||||
String path,
|
||||
Map<String, String> params,
|
||||
Map<String, String> headers,
|
||||
Class<T> itemType) throws Exception {
|
||||
|
||||
JsonNode responseJson = getRequestRaw(hosts, path, params, headers);
|
||||
return parseResponseAsList(responseJson, itemType);
|
||||
|
||||
}
|
||||
|
||||
// 通用POST请求 - 返回原始JsonNode
|
||||
public JsonNode postRequestRaw(String hosts,
|
||||
String path,
|
||||
Object requestBody,
|
||||
Map<String, String> headers) throws Exception {
|
||||
|
||||
String requestBodyJson = objectMapper.writeValueAsString(requestBody);
|
||||
|
||||
HttpRequest request = buildRequest(hosts, path, "POST", requestBodyJson, headers);
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 200) {
|
||||
return objectMapper.readTree(response.body());
|
||||
} else {
|
||||
throw new RuntimeException("HTTP request failed with status: " + response.statusCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 通用POST请求 - 自动解析为指定类型
|
||||
public <T> ApiResponse<T> postRequest(String hosts,
|
||||
String path,
|
||||
Object requestBody,
|
||||
Map<String, String> headers,
|
||||
Class<T> dataType) throws Exception {
|
||||
|
||||
JsonNode responseJson = postRequestRaw(hosts, path, requestBody, headers);
|
||||
return parseResponse(responseJson, dataType);
|
||||
|
||||
}
|
||||
|
||||
// 通用POST请求 - 自动解析为List类型
|
||||
public <T> ApiResponse<List<T>> postRequestAsList(String hosts,
|
||||
String path,
|
||||
Object requestBody,
|
||||
Map<String, String> headers,
|
||||
Class<T> itemType) throws Exception {
|
||||
|
||||
JsonNode responseJson = postRequestRaw(hosts, path, requestBody, headers);
|
||||
return parseResponseAsList(responseJson, itemType);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 构建查询字符串
|
||||
private String buildQueryString(Map<String, String> params) throws UnsupportedEncodingException {
|
||||
if (params == null || params.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder queryBuilder = new StringBuilder("?");
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
if (queryBuilder.length() > 1) {
|
||||
queryBuilder.append("&");
|
||||
}
|
||||
queryBuilder.append(entry.getKey())
|
||||
.append("=")
|
||||
.append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
|
||||
}
|
||||
return queryBuilder.toString();
|
||||
}
|
||||
|
||||
// 构建HTTP请求
|
||||
private HttpRequest buildRequest(String hosts,
|
||||
String path,
|
||||
String method,
|
||||
String requestBody,
|
||||
Map<String, String> headers) throws Exception {
|
||||
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(this.getBaseUrl(hosts) + "/" + path))
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(Duration.ofSeconds(10));
|
||||
|
||||
if ("GET".equals(method)) {
|
||||
builder.GET();
|
||||
} else if ("POST".equals(method)) {
|
||||
builder.POST(HttpRequest.BodyPublishers.ofString(requestBody));
|
||||
}
|
||||
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
builder.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
// 解析响应为单个对象
|
||||
private <T> ApiResponse<T> parseResponse(JsonNode responseJson, Class<T> dataType) {
|
||||
String code = responseJson.get("code").asText();
|
||||
String message = responseJson.get("message").asText();
|
||||
JsonNode dataNode = responseJson.get("data");
|
||||
|
||||
T data = null;
|
||||
if (dataNode != null && !dataNode.isNull()) {
|
||||
data = objectMapper.convertValue(dataNode, dataType);
|
||||
}
|
||||
|
||||
return new ApiResponse<>(code, message, data);
|
||||
}
|
||||
|
||||
// 解析响应为List
|
||||
private <T> ApiResponse<List<T>> parseResponseAsList(JsonNode responseJson, Class<T> itemType) {
|
||||
String code = responseJson.get("code").asText();
|
||||
String message = responseJson.get("message").asText();
|
||||
JsonNode dataNode = responseJson.get("data");
|
||||
|
||||
List<T> data = null;
|
||||
if (dataNode != null && !dataNode.isNull() && dataNode.isArray()) {
|
||||
JavaType listType = objectMapper.getTypeFactory().constructCollectionType(List.class, itemType);
|
||||
data = objectMapper.convertValue(dataNode, listType);
|
||||
}
|
||||
|
||||
return new ApiResponse<>(code, message, data);
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class ApiResponse<T> {
|
||||
private String code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return "1000".equals(code);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,4 +9,24 @@
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
<select id="getStatisticsInfo"
|
||||
resultType="cn.iocoder.yudao.module.farm.controller.admin.deviceinfo.vo.v2.DeviceStatisticsRespVO">
|
||||
|
||||
SELECT
|
||||
COALESCE(COUNT(*), 0) AS deviceTotal,
|
||||
COALESCE(SUM(CASE WHEN device_state = 1 THEN 1 ELSE 0 END), 0) AS deviceOnline,
|
||||
COALESCE(SUM(CASE WHEN device_state = 2 THEN 1 ELSE 0 END), 0) AS deviceOffline,
|
||||
COALESCE(SUM(CASE WHEN device_state = 4 THEN 1 ELSE 0 END), 0) AS deviceWarning
|
||||
FROM farm_device_info
|
||||
<where>
|
||||
deleted = b'0'
|
||||
<if test="baseId != null and baseId != ''">
|
||||
AND base_id = #{baseId}
|
||||
</if>
|
||||
<if test="tenantId != null and tenantId != ''">
|
||||
AND tenant_id = #{tenantId}
|
||||
</if>
|
||||
</where>
|
||||
|
||||
</select>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user