Эх сурвалжийг харах

feat:更新青萍设备报警配置

陈长荣 5 сар өмнө
parent
commit
1b8fe0e298

+ 4 - 0
snowy-plugin/snowy-plugin-coldchain/pom.xml

@@ -78,5 +78,9 @@
             <artifactId>weixin-java-mp</artifactId>
             <version>4.6.0</version>
         </dependency>
+      <dependency>
+        <groupId>vip.xiaonuo</groupId>
+        <artifactId>snowy-plugin-dev</artifactId>
+      </dependency>
     </dependencies>
 </project>

+ 9 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/enums/MonitorTargetRegionEvent.java

@@ -0,0 +1,9 @@
+package vip.xiaonuo.coldchain.modular.monitortargetregion.enums;
+
+public class MonitorTargetRegionEvent {
+
+    /**
+     * 监测点配置变化事件
+     */
+    public static final String MONITOR_TARGET_REGION_EDIT_EVENT = "MONITOR_TARGET_REGION_EDIT_EVENT";
+}

+ 3 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/service/impl/MonitorTargetRegionServiceImpl.java

@@ -35,6 +35,7 @@ import vip.xiaonuo.coldchain.modular.monitordevice.service.MonitorDeviceService;
 import vip.xiaonuo.coldchain.modular.monitortarget.entity.MonitorTarget;
 import vip.xiaonuo.coldchain.modular.monitortarget.service.MonitorTargetService;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
+import vip.xiaonuo.coldchain.modular.monitortargetregion.enums.MonitorTargetRegionEvent;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.mapper.MonitorTargetRegionMapper;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.param.MonitorTargetRegionAddParam;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.param.MonitorTargetRegionEditParam;
@@ -44,6 +45,7 @@ import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRe
 import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
 import vip.xiaonuo.common.enums.CommonSortOrderEnum;
 import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
 import vip.xiaonuo.common.page.CommonPageRequest;
 
 import java.util.ArrayList;
@@ -123,6 +125,7 @@ public class MonitorTargetRegionServiceImpl extends ServiceImpl<MonitorTargetReg
         MonitorTargetRegion monitorTargetRegion = this.queryEntity(monitorTargetRegionEditParam.getId());
         BeanUtil.copyProperties(monitorTargetRegionEditParam, monitorTargetRegion);
         this.updateById(monitorTargetRegion);
+        CommonDataChangeEventCenter.doUpdateWithData(MonitorTargetRegionEvent.MONITOR_TARGET_REGION_EDIT_EVENT, JSONUtil.createArray().put(monitorTargetRegion));
     }
 
     @Transactional(rollbackFor = Exception.class)

+ 16 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/qp/config/QpConfig.java

@@ -0,0 +1,16 @@
+package vip.xiaonuo.coldchain.modular.qp.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "qp")
+public class QpConfig {
+
+    private String appKey;
+
+    private String appSecret;
+
+}

+ 199 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/qp/config/QpPush.java

@@ -0,0 +1,199 @@
+package vip.xiaonuo.coldchain.modular.qp.config;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.Method;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.coldchain.modular.qp.vo.QpAlertConfigVo;
+import vip.xiaonuo.common.cache.CommonCacheOperator;
+
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class QpPush {
+
+    private final CommonCacheOperator cacheOperator;
+    private final QpConfig qpConfig;
+
+    /**
+     * redis accessToken key
+     */
+    public static final String QP_ACCESS_TOKEN_KEY = "qp:accessToken";
+
+    /**
+     * 采集指标名称
+     */
+    public static final List<String> QP_METRIC_NAMES = Arrays.asList("temperature", "humidity", "co2");
+
+    /**
+     * 获取accessToken
+     */
+    public String getAccessToken() {
+        Object token = cacheOperator.get(QP_ACCESS_TOKEN_KEY);
+        JSONObject tokenObj;
+        if (token == null) {
+            // 获取accessToken
+            try (HttpResponse response = HttpUtil.createPost("https://oauth.cleargrass.com/oauth2/token")
+                    .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+                    .auth("Basic " + Base64.getEncoder().encodeToString((qpConfig.getAppKey() + ":" + qpConfig.getAppSecret()).getBytes()))
+                    .form("grant_type", "client_credentials")
+                    .form("scope", "device_full_access")
+                    .execute()) {
+                String body = response.body();
+                log.info("获取青萍accessToken: {}", body);
+                tokenObj = JSON.parseObject(body);
+                cacheOperator.put(QP_ACCESS_TOKEN_KEY, body, tokenObj.getLong("expires_in"));
+            }
+        } else {
+            tokenObj = JSON.parseObject(token.toString());
+        }
+
+        return tokenObj.getString("access_token");
+    }
+
+
+    /**
+     * 查询报警配置
+     *
+     * @param mac 设置mac地址
+     */
+    public List<QpAlertConfigVo> getAlertConfig(String mac) {
+        Assert.notBlank(mac, "mac不能为空");
+
+        String accessToken = getAccessToken();
+        try (HttpResponse response = HttpUtil.createGet("https://apis.cleargrass.com/v1/apis/devices/settings/alert?mac=" + mac)
+                .bearerAuth(accessToken)
+                .execute()) {
+            String body = response.body();
+            log.info("查询青萍报警配置: {}", body);
+            JSONArray configs = JSON.parseObject(body).getJSONArray("alert_configs");
+            if (CollUtil.isEmpty(configs)) {
+                return Collections.emptyList();
+            }
+            return configs.toJavaList(QpAlertConfigVo.class);
+        }
+    }
+
+    /**
+     * 修改报警配置
+     *
+     * @param mac      mac地址
+     * @param configVo 报警配置
+     */
+    public void updateAlertConfig(String mac, QpAlertConfigVo configVo) {
+        Assert.notBlank(mac, "mac不能为空");
+        Assert.notNull(configVo, "配置不能为空");
+
+        JSONObject param = new JSONObject()
+                .fluentPut("mac", mac)
+                .fluentPut("alert_config", configVo)
+                .fluentPut("timestamp", System.currentTimeMillis());
+
+        String accessToken = getAccessToken();
+        try (HttpResponse response = HttpUtil.createRequest(Method.PUT, "https://apis.cleargrass.com/v1/apis/devices/settings/alert")
+                .bearerAuth(accessToken)
+                .contentType(MediaType.APPLICATION_JSON_VALUE)
+                .body(param.toJSONString())
+                .execute()) {
+            boolean ok = response.isOk();
+            log.info("修改青萍报警配置 {}", ok ? "成功" : "失败");
+        }
+    }
+
+    /**
+     * 添加单个报警配置
+     *
+     * @param mac      mac地址
+     * @param configVo 报警配置
+     */
+    public void addAlertConfig(String mac, QpAlertConfigVo configVo) {
+        Assert.notBlank(mac, "mac不能为空");
+        Assert.notNull(configVo, "配置不能为空");
+        log.info("添加青萍报警配置 mac={}, config={}", mac, configVo);
+
+        //添加配置移除id
+        JSONObject configObj = JSON.parseObject(JSON.toJSONString(configVo));
+        configObj.remove("id");
+        JSONObject param = new JSONObject()
+                .fluentPut("mac", mac)
+                .fluentPut("alert_config", configObj)
+                .fluentPut("timestamp", System.currentTimeMillis());
+
+        String accessToken = getAccessToken();
+        try (HttpResponse response = HttpUtil.createPost("https://apis.cleargrass.com/v1/apis/devices/settings/alert")
+                .bearerAuth(accessToken)
+                .contentType(MediaType.APPLICATION_JSON_VALUE)
+                .body(param.toJSONString())
+                .execute()) {
+            boolean ok = response.isOk();
+            log.info("添加青萍报警配置 {}", ok ? "成功" : "失败");
+        }
+    }
+
+    /**
+     * 删除报警配置
+     *
+     * @param mac       mac地址
+     * @param configIds 配置id列表
+     */
+    public void deleteAlertConfig(String mac, List<Long> configIds) {
+        Assert.notBlank(mac, "mac不能为空");
+        if (CollUtil.isEmpty(configIds)) {
+            log.info("青萍mac={} 删除配置为空", mac);
+            return;
+        }
+        log.info("删除青萍mac={} 配置 configIds={}", mac, configIds);
+        JSONObject param = new JSONObject()
+                .fluentPut("mac", mac)
+                .fluentPut("config_id", configIds)
+                .fluentPut("timestamp", System.currentTimeMillis());
+
+        String accessToken = getAccessToken();
+        try (HttpResponse response = HttpUtil.createRequest(Method.DELETE, "https://apis.cleargrass.com/v1/apis/devices/settings/alert")
+                .bearerAuth(accessToken)
+                .contentType(MediaType.APPLICATION_JSON_VALUE)
+                .body(param.toJSONString())
+                .execute()) {
+            boolean ok = response.isOk();
+            log.info("删除青萍报警配置 {}", ok ? "成功" : "失败");
+        }
+    }
+
+    /**
+     * 保存或更新报警配置
+     *
+     * @param mac      mac地址
+     * @param configVos 报警配置
+     */
+    public void saveConfig(String mac, List<QpAlertConfigVo> configVos) {
+        List<QpAlertConfigVo> configList = getAlertConfig(mac);
+        log.info("更新青萍报警配置 mac={}, configs={}", mac, configList);
+        if (CollUtil.isNotEmpty(configList)) {
+            //删除温度、湿度、CO2报警配置
+            List<Long> configIds = configList.stream().filter(x -> QP_METRIC_NAMES.contains(x.getMetric_name())).map(QpAlertConfigVo::getId).toList();
+            deleteAlertConfig(mac, configIds);
+        }
+
+        for (QpAlertConfigVo configVo : configVos) {
+            addAlertConfig(mac, configVo);
+
+            //调用接口暂停一会
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException ignored) {}
+        }
+    }
+}

+ 149 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/qp/listener/QpListener.java

@@ -0,0 +1,149 @@
+package vip.xiaonuo.coldchain.modular.qp.listener;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONArray;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
+import vip.xiaonuo.coldchain.modular.monitordevice.service.MonitorDeviceService;
+import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
+import vip.xiaonuo.coldchain.modular.monitortargetregion.enums.MonitorTargetRegionEvent;
+import vip.xiaonuo.coldchain.modular.qp.config.QpPush;
+import vip.xiaonuo.coldchain.modular.qp.vo.QpAlertConfigVo;
+import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
+import vip.xiaonuo.common.listener.CommonDataChangeListener;
+import vip.xiaonuo.dev.modular.config.service.DevConfigService;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class QpListener implements CommonDataChangeListener {
+
+    private final QpPush qpPush;
+    private final DevConfigService devConfigService;
+    private final MonitorDeviceService monitorDeviceService;
+
+    @Override
+    public void doAddWithDataIdList(String dataType, List<String> dataIdList) {
+
+    }
+
+    @Override
+    public void doAddWithDataList(String dataType, JSONArray jsonArray) {
+
+    }
+
+    @Override
+    public void doUpdateWithDataIdList(String dataType, List<String> dataIdList) {
+
+    }
+
+    @Override
+    public void doUpdateWithDataList(String dataType, JSONArray jsonArray) {
+        //监测点配置更新
+        if(!dataType.equals(MonitorTargetRegionEvent.MONITOR_TARGET_REGION_EDIT_EVENT)) {
+            return;
+        }
+
+        List<MonitorTargetRegion> monitorTargetRegionList = jsonArray.toList(MonitorTargetRegion.class);
+        if (monitorTargetRegionList.isEmpty()) {
+            return;
+        }
+
+        Set<String> deviceIdSet = monitorTargetRegionList.stream()
+                .map(MonitorTargetRegion::getMonitorDeviceId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (deviceIdSet.isEmpty()) {
+            return;
+        }
+
+        //获取青萍的设备型号
+        String qingpingType = devConfigService.getValueByKey("qingping_type");
+        if (qingpingType == null) {
+            return;
+        }
+        Set<String> qpDeviceType = Stream.of(qingpingType.split(",")).filter(StrUtil::isNotBlank).collect(Collectors.toSet());
+
+        //获取设备型号
+        List<MonitorDevice> qpDeviceList = monitorDeviceService.list(new LambdaQueryWrapper<>(MonitorDevice.class)
+                .in(MonitorDevice::getId, deviceIdSet)
+                .in(MonitorDevice::getModelName, qpDeviceType)
+                .eq(MonitorDevice::getDeleteFlag, CommonDeleteFlagEnum.NOT_DELETE));
+        if (qpDeviceList.isEmpty()) {
+            return;
+        }
+
+        //监测点配置更新中的青萍设备
+        Map<String, List<QpAlertConfigVo>> qpAlertConfigMap = new LinkedHashMap<>();
+        Map<String, String> qpDeviceMac = qpDeviceList.stream().collect(Collectors.toMap(MonitorDevice::getId, MonitorDevice::getDeviceCode));
+        monitorTargetRegionList = monitorTargetRegionList.stream().filter(x -> qpDeviceMac.containsKey(x.getMonitorDeviceId())).toList();
+        for (MonitorTargetRegion region : monitorTargetRegionList) {
+            String mac = qpDeviceMac.get(region.getMonitorDeviceId());
+            if (!qpAlertConfigMap.containsKey(mac)) {
+                qpAlertConfigMap.put(mac, new ArrayList<>());
+            }
+            //温度下限
+            if (region.getTemperatureDown() != null) {
+                QpAlertConfigVo configVo = new QpAlertConfigVo()
+                        .setMetric_name("temperature")
+                        .setOperator("lt")
+                        .setThreshold(region.getTemperatureDown().intValue());
+                qpAlertConfigMap.get(mac).add(configVo);
+            }
+            //温度上限
+            if (region.getTemperatureUp() != null) {
+                QpAlertConfigVo configVo = new QpAlertConfigVo()
+                        .setMetric_name("temperature")
+                        .setOperator("gt")
+                        .setThreshold(region.getTemperatureUp().intValue());
+                qpAlertConfigMap.get(mac).add(configVo);
+            }
+            //湿度下限
+            if (region.getHumidityDown() != null) {
+                QpAlertConfigVo configVo = new QpAlertConfigVo()
+                        .setMetric_name("humidity")
+                        .setOperator("lt")
+                        .setThreshold(region.getHumidityDown().intValue());
+                qpAlertConfigMap.get(mac).add(configVo);
+            }
+            //湿度上限
+            if (region.getHumidityUp() != null) {
+                QpAlertConfigVo configVo = new QpAlertConfigVo()
+                        .setMetric_name("humidity")
+                        .setOperator("gt")
+                        .setThreshold(region.getHumidityUp().intValue());
+                qpAlertConfigMap.get(mac).add(configVo);
+            }
+            //Co2下限
+            if (region.getCo2Down() != null) {
+                QpAlertConfigVo configVo = new QpAlertConfigVo()
+                        .setMetric_name("co2")
+                        .setOperator("lt")
+                        .setThreshold(region.getCo2Down().intValue());
+                qpAlertConfigMap.get(mac).add(configVo);
+            }
+            //Co2上限
+            if (region.getCo2Up() != null) {
+                QpAlertConfigVo configVo = new QpAlertConfigVo()
+                        .setMetric_name("co2")
+                        .setOperator("gt")
+                        .setThreshold(region.getCo2Up().intValue());
+                qpAlertConfigMap.get(mac).add(configVo);
+            }
+        }
+
+        qpAlertConfigMap.forEach(qpPush::saveConfig);
+    }
+
+    @Override
+    public void doDeleteWithDataIdList(String dataType, List<String> dataIdList) {
+
+    }
+}

+ 30 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/qp/vo/QpAlertConfigVo.java

@@ -0,0 +1,30 @@
+package vip.xiaonuo.coldchain.modular.qp.vo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Accessors(chain = true)
+@Data
+public class QpAlertConfigVo {
+
+    /**
+     * 配置id
+     */
+    private Long id;
+
+    /**
+     * 属性名(温度:temperature,湿度:humidity,二氧化碳:co2)
+     */
+    private String metric_name;
+
+    /**
+     * 运算符(lt小于,gt大于)
+     */
+    private String operator;
+
+    /**
+     * 阈值
+     */
+    private int threshold;
+
+}