Przeglądaj źródła

更新! 取消GateWay信息

TRX 1 rok temu
rodzic
commit
6df556d6a3
23 zmienionych plików z 347 dodań i 73 usunięć
  1. 11 1
      OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/artemis/OperationMessageSearchParam.java
  2. 3 0
      OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/baseParam/SeriesModel.java
  3. 6 0
      OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/iot/ping/DeviceOnLineRecordSearch.java
  4. 33 0
      OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/iot/statistics/DeviceStatisticsModel.java
  5. 5 0
      OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/iot/statistics/LineModel.java
  6. 19 0
      OneCardIotClient/src/main/java/com/zhongshu/iot/client/type/OperationBusType.java
  7. 8 1
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/devices/DeviceStatisticsController.java
  8. 1 1
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/iot/DeviceOnLineRecordDao.java
  9. 4 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/iot/extend/DeviceOnLineRecordDaoExtend.java
  10. 45 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/iot/impl/DeviceOnLineRecordDaoImpl.java
  11. 2 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/DeviceInfoDao.java
  12. 1 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/extend/DeviceInfoDaoExtend.java
  13. 3 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/extend/OperationMessageDaoExtend.java
  14. 17 7
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/impl/DeviceInfoDaoImpl.java
  15. 64 53
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/impl/OperationMessageDaoImpl.java
  16. 8 2
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/domain/iot/device/DeviceOnLineRecord.java
  17. 5 1
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/domain/iot/device/OperationMessage.java
  18. 4 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/domain/iot/device/OperationMessageResult.java
  19. 6 1
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/artemis/OperationMessageService.java
  20. 31 4
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/device/DeviceOnLineRecordService.java
  21. 50 1
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/device/DeviceStatisticsService.java
  22. 4 1
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotSendMessageService.java
  23. 17 0
      OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/util/CommonUtil.java

+ 11 - 1
OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/artemis/OperationMessageSearchParam.java

@@ -1,10 +1,14 @@
 package com.zhongshu.iot.client.model.artemis;
 
 import com.zhongshu.iot.client.model.baseParam.SuperSearchParam;
+import com.zhongshu.iot.client.type.OperationBusType;
 import com.zhongshu.iot.client.type.OperationType;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * @author TRX
  * @date 2024/5/21
@@ -37,7 +41,13 @@ public class OperationMessageSearchParam extends SuperSearchParam {
     private Boolean isTimeOut;
 
     @Schema(description = "处理标记,判断用那个业务方法处理")
-    private String event;
+    private List<String> events = new ArrayList<>();
+
+    @Schema(description = "不在的处理标记")
+    private List<String> nevents = new ArrayList<>();
+
+    @Schema(description = "消息业务类型")
+    private OperationBusType operationBusType;
 
     //------------------------关联的消息
 

+ 3 - 0
OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/baseParam/SeriesModel.java

@@ -14,4 +14,7 @@ public class SeriesModel {
 
     private List<Object> series = new ArrayList<Object>();
 
+    public void addSeries(Object series) {
+        this.series.add(series);
+    }
 }

+ 6 - 0
OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/iot/ping/DeviceOnLineRecordSearch.java

@@ -29,4 +29,10 @@ public class DeviceOnLineRecordSearch extends SuperSearchParam {
     @Schema(description = "设备code,产品code")
     private String productCode;
 
+    @Schema(description = "开始记录时间")
+    private Long startRecordTime;
+
+    @Schema(description = "结束记录时间")
+    private Long endRecordTime;
+
 }

+ 33 - 0
OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/iot/statistics/DeviceStatisticsModel.java

@@ -0,0 +1,33 @@
+package com.zhongshu.iot.client.model.iot.statistics;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 设备、网关、消息数统计
+ *
+ * @author TRX
+ * @date 2025/2/27
+ */
+@Data
+public class DeviceStatisticsModel {
+
+    @Schema(description = "设备数量")
+    private Long deviceNumber = 0L;
+
+    @Schema(description = "今日薪增")
+    private Long toDayDeviceNumber = 0L;
+
+    @Schema(description = "网关数量")
+    private Long gateWayNumber = 0L;
+
+    @Schema(description = "网关今日新增")
+    private Long toDayGateWayNumber = 0L;
+
+    @Schema(description = "消息数")
+    private Long messageNumber = 0L;
+
+    @Schema(description = "消息今日新增")
+    private Long toDayMessageNumber = 0L;
+
+}

+ 5 - 0
OneCardIotClient/src/main/java/com/zhongshu/iot/client/model/iot/statistics/LineModel.java

@@ -2,6 +2,7 @@ package com.zhongshu.iot.client.model.iot.statistics;
 
 import com.zhongshu.iot.client.model.baseParam.SeriesModel;
 import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -12,6 +13,7 @@ import java.util.List;
  * @author TRX
  * @date 2025/2/27
  */
+@Data
 public class LineModel {
 
     @Schema(description = "X轴数据,横向方向")
@@ -20,4 +22,7 @@ public class LineModel {
     @Schema(description = "Y轴的数据")
     private List<SeriesModel> series = new ArrayList<>();
 
+    public void addSeries(SeriesModel series) {
+        this.series.add(series);
+    }
 }

+ 19 - 0
OneCardIotClient/src/main/java/com/zhongshu/iot/client/type/OperationBusType.java

@@ -0,0 +1,19 @@
+package com.zhongshu.iot.client.type;
+
+import lombok.Getter;
+
+/**
+ * 消息业务类型
+ */
+public enum OperationBusType {
+    IotThing("物模型相关"),
+    Other("其他"),
+    ;
+
+    @Getter
+    private String remark;
+
+    OperationBusType(String remark) {
+        this.remark = remark;
+    }
+}

+ 8 - 1
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/controller/devices/DeviceStatisticsController.java

@@ -34,10 +34,17 @@ public class DeviceStatisticsController {
     private DeviceOnLineRecordService deviceOnLineRecordService;
 
     @ResourceAuth(value = "user", type = AuthType.User)
-    @Operation(summary = "设备在线状态")
+    @Operation(summary = "设备在线状态统计")
     @RequestMapping(value = "statisticsDeviceOnLine", method = {RequestMethod.POST})
     public ResultContent statisticsDeviceOnLine(@RequestBody DeviceOnLineRecordSearch param) {
         return deviceOnLineRecordService.statisticsDeviceOnLine(param);
     }
 
+    @ResourceAuth(value = "user", type = AuthType.User)
+    @Operation(summary = "设备等统计数量")
+    @RequestMapping(value = "deviceStatistics", method = {RequestMethod.GET})
+    public ResultContent deviceStatistics() {
+        return deviceStatisticsService.deviceStatistics();
+    }
+
 }

+ 1 - 1
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/iot/DeviceOnLineRecordDao.java

@@ -12,6 +12,6 @@ public interface DeviceOnLineRecordDao extends DeviceOnLineRecordDaoExtend, Mong
 
     DeviceOnLineRecord findTopById(String id);
 
-    DeviceOnLineRecord findTopByDeviceIdAndStatisticsKey(String deviceId, String statisticsKey);
+    DeviceOnLineRecord findTopByDeviceIdAndMinuteKey(String deviceId, String minuteKey);
 
 }

+ 4 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/iot/extend/DeviceOnLineRecordDaoExtend.java

@@ -5,6 +5,8 @@ import com.zhongshu.iot.server.core.domain.iot.device.DeviceOnLineRecord;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
+import java.util.Map;
+
 /**
  * @Author TRX
  * @CreateDate: 2023/7/7
@@ -14,4 +16,6 @@ public interface DeviceOnLineRecordDaoExtend {
 
     Page<DeviceOnLineRecord> page(Pageable pageable, DeviceOnLineRecordSearch param);
 
+    Map<String, Long> statisticsMinNumber(DeviceOnLineRecordSearch param);
+
 }

+ 45 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/iot/impl/DeviceOnLineRecordDaoImpl.java

@@ -5,6 +5,7 @@ import com.zhongshu.iot.client.model.iot.ping.DeviceOnLineRecordSearch;
 import com.zhongshu.iot.server.core.dao.base.BaseImpl;
 import com.zhongshu.iot.server.core.dao.iot.extend.DeviceOnLineRecordDaoExtend;
 import com.zhongshu.iot.server.core.domain.iot.device.DeviceOnLineRecord;
+import com.zhongshu.iot.server.core.util.CommonUtil;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -12,12 +13,16 @@ import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.util.CollectionUtils;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Pattern;
 
 /**
@@ -42,6 +47,42 @@ public class DeviceOnLineRecordDaoImpl extends BaseImpl implements DeviceOnLineR
         return dbHelper.pages(query, pageable, DeviceOnLineRecord.class);
     }
 
+    @Override
+    public Map<String, Long> statisticsMinNumber(DeviceOnLineRecordSearch param) {
+        Criteria criteria = buildFilterCriteria(param);
+
+        String fieldName = "minuteKey";
+        Map<String, Long> map = new HashMap<>();
+
+        List<AggregationOperation> operations = new ArrayList<>(3);
+        // 条件
+        AggregationOperation match = Aggregation.match(criteria);
+        operations.add(match);
+
+        // 分组
+        AggregationOperation group = Aggregation.group("$" + fieldName).count().as("count");
+        operations.add(group);
+
+        // 排序
+        AggregationOperation sort = Aggregation.sort(Sort.Direction.ASC, fieldName);
+        operations.add(sort);
+
+        Aggregation groupAggregation = Aggregation.newAggregation(operations);
+        List<Map> countObjs = mongoTemplate.aggregate(groupAggregation, DeviceOnLineRecord.class, Map.class).getMappedResults();
+        if (countObjs.size() > 0) {
+            countObjs.stream().forEach(it -> {
+                String name = (String) it.get(fieldName);
+                if (StringUtils.isEmpty(name)) {
+                    Object _id = it.get("_id");
+                    name = String.valueOf(_id);
+                }
+                Long number = CommonUtil.getLongByObj(it.get("count"));
+                map.put(name, number);
+            });
+        }
+        return map;
+    }
+
     private Criteria buildFilterCriteria(DeviceOnLineRecordSearch param) {
         Criteria criteria = buildCriteriaAboutTime(param);
 
@@ -63,6 +104,10 @@ public class DeviceOnLineRecordDaoImpl extends BaseImpl implements DeviceOnLineR
             criteria.and("productCode").is(param.getProductCode());
         }
 
+        if (!CommonUtil.longIsEmpty(param.getStartRecordTime()) && !CommonUtil.longIsEmpty(param.getEndRecordTime())) {
+            criteria.and("recordTime").gte(param.getStartRecordTime()).lte(param.getEndRecordTime());
+        }
+
         // 模糊搜索
         List<Criteria> criterias = new ArrayList<>();
         if (StringUtils.isNotEmpty(param.getDeviceName())) {

+ 2 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/DeviceInfoDao.java

@@ -29,4 +29,6 @@ public interface DeviceInfoDao extends DeviceInfoDaoExtend, org.springframework.
 
     List<DeviceInfo> findByDeviceCategory(DeviceCategory deviceCategory);
 
+    Long countByDeviceCategory(DeviceCategory deviceCategory);
+
 }

+ 1 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/extend/DeviceInfoDaoExtend.java

@@ -14,5 +14,6 @@ public interface DeviceInfoDaoExtend {
 
     Page<DeviceInfo> page(Pageable pageable, DeviceInfoSearchParam param);
 
+    Long count(DeviceInfoSearchParam param);
 
 }

+ 3 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/extend/OperationMessageDaoExtend.java

@@ -11,6 +11,7 @@ import org.springframework.data.domain.Pageable;
  * @Version: 1.0
  */
 public interface OperationMessageDaoExtend {
+
     Page<OperationMessage> page(Pageable pageable, OperationMessageSearchParam param);
 
     OperationMessage init(String dataId, String token);
@@ -19,4 +20,6 @@ public interface OperationMessageDaoExtend {
 
     boolean release(String dataId);
 
+    Long count(OperationMessageSearchParam param);
+
 }

+ 17 - 7
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/impl/DeviceInfoDaoImpl.java

@@ -35,7 +35,22 @@ public class DeviceInfoDaoImpl extends BaseImpl implements DeviceInfoDaoExtend {
 
     @Override
     public Page<DeviceInfo> page(Pageable pageable, DeviceInfoSearchParam param) {
-        Criteria criteria = new Criteria();
+        Criteria criteria = buildFilterCriteria(param);
+        Sort sort = buildSort(param);
+        Query query = Query.query(criteria);
+        query.with(sort);
+        return dbHelper.pages(query, pageable, DeviceInfo.class);
+    }
+
+    @Override
+    public Long count(DeviceInfoSearchParam param) {
+        Criteria criteria = buildFilterCriteria(param);
+        Query query = Query.query(criteria);
+        return mongoTemplate.count(query, DeviceInfo.class);
+    }
+
+    public Criteria buildFilterCriteria(DeviceInfoSearchParam param) {
+        Criteria criteria = buildCriteriaAboutTime(param);
 
         if (ObjectUtils.isNotEmpty(param.getDeviceCategorys())) {
             criteria.and("deviceCategory").in(param.getDeviceCategorys());
@@ -70,12 +85,7 @@ public class DeviceInfoDaoImpl extends BaseImpl implements DeviceInfoDaoExtend {
         if (!CollectionUtils.isEmpty(criterias)) {
             criteria.andOperator(criterias.toArray(new Criteria[]{}));
         }
-        criteria.and("isDelete").is(Boolean.FALSE);
-
-        Sort sort = buildSort(param);
-        Query query = Query.query(criteria);
-        query.with(sort);
-        return dbHelper.pages(query, pageable, DeviceInfo.class);
+        return criteria;
     }
 
 }

+ 64 - 53
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/dao/mqtt/impl/OperationMessageDaoImpl.java

@@ -5,8 +5,8 @@ import com.zhongshu.iot.client.model.artemis.OperationMessageSearchParam;
 import com.zhongshu.iot.server.core.dao.base.BaseImpl;
 import com.zhongshu.iot.server.core.dao.mqtt.extend.OperationMessageDaoExtend;
 import com.zhongshu.iot.server.core.domain.iot.device.OperationMessage;
-import com.zhongshu.iot.server.core.util.CommonUtil;
 import com.zhongshu.iot.server.core.util.TokenUtil;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,6 +33,7 @@ import java.util.regex.Pattern;
 public class OperationMessageDaoImpl extends BaseImpl implements OperationMessageDaoExtend {
 
     private static final Logger log = LoggerFactory.getLogger(OperationMessageDaoImpl.class);
+
     @Autowired
     private MongoTemplate mongoTemplate;
 
@@ -41,57 +42,27 @@ public class OperationMessageDaoImpl extends BaseImpl implements OperationMessag
 
     @Override
     public Page<OperationMessage> page(Pageable pageable, OperationMessageSearchParam param) {
-        Criteria criteria = new Criteria();
-
-        if (StringUtils.isNotEmpty(param.getDeviceId())) {
-            criteria.and("deviceId").is(param.getDeviceId());
-        }
-
-        if (StringUtils.isNotEmpty(param.getEpId())) {
-            criteria.and("epId").is(param.getEpId());
-        }
-
-        if (StringUtils.isNotEmpty(param.getGateWayId())) {
-            criteria.and("gateWayId").is(param.getGateWayId());
-        }
-
-        if (param.getIsReceive() != null && param.getIsReceive()) {
-            criteria.and("isReceive").is(param.getIsReceive());
-        }
-
-        // 时间范围
-        if (!CommonUtil.longIsEmpty(param.getStartTime()) && !CommonUtil.longIsEmpty(param.getEndTime())) {
-            criteria.and("createTime").gte(param.getStartTime()).lte(param.getEndTime());
-        }
-
-        // 模糊搜索
-        List<Criteria> criterias = new ArrayList<>();
-        if (StringUtils.isNotEmpty(param.getTopic())) {
-            Pattern pattern = Pattern.compile("^.*" + param.getTopic() + ".*$");
-            criterias.add(Criteria.where("topic").is(pattern));
-        }
-        if (!CollectionUtils.isEmpty(criterias)) {
-            criteria.andOperator(criterias.toArray(new Criteria[]{}));
-        }
-        criteria.and("isDelete").is(Boolean.FALSE);
-
+        Criteria criteria = buildFilterCriteria(param);
         Sort sort = buildSort(param);
         Query query = Query.query(criteria);
         query.with(sort);
         return dbHelper.pages(query, pageable, OperationMessage.class);
     }
 
+    @Override
+    public Long count(OperationMessageSearchParam param) {
+        Criteria criteria = buildFilterCriteria(param);
+        Query query = Query.query(criteria);
+        return mongoTemplate.count(query, OperationMessage.class);
+    }
+
     public OperationMessage init(String dataId, String token) {
         OperationMessage doc = null;
         try {
             Query query = Query.query(Criteria.where("dataId").is(dataId).and("token").isNull());
-            Update update = new Update()
-                    .set("token", token)
-                    .set("createTime", System.currentTimeMillis());
-            FindAndModifyOptions options = new FindAndModifyOptions().upsert(true)
-                    .returnNew(true);
-            doc = mongoTemplate.findAndModify(query, update, options,
-                    OperationMessage.class);
+            Update update = new Update().set("token", token).set("createTime", System.currentTimeMillis());
+            FindAndModifyOptions options = new FindAndModifyOptions().upsert(true).returnNew(true);
+            doc = mongoTemplate.findAndModify(query, update, options, OperationMessage.class);
         } catch (Exception e) {
             log.error("init {}", e.getMessage());
         }
@@ -101,15 +72,11 @@ public class OperationMessageDaoImpl extends BaseImpl implements OperationMessag
     public String acquire(String dataId, Long expiration) {
         Query query = Query.query(Criteria.where("dataId").is(dataId).and("token").isNull());
         String token = TokenUtil.create();
-        Update update = new Update()
-                .set("expireAt", System.currentTimeMillis() + expiration)
-                .set("token", token);
+        Update update = new Update().set("expireAt", System.currentTimeMillis() + expiration).set("token", token);
 
-        FindAndModifyOptions options = new FindAndModifyOptions().upsert(false)
-                .returnNew(true);
+        FindAndModifyOptions options = new FindAndModifyOptions().upsert(false).returnNew(true);
 
-        OperationMessage doc = mongoTemplate.findAndModify(query, update, options,
-                OperationMessage.class);
+        OperationMessage doc = mongoTemplate.findAndModify(query, update, options, OperationMessage.class);
 
         if (doc == null) {
             OperationMessage lockObj = mongoTemplate.findOne(Query.query(Criteria.where("dataId").is(dataId)), OperationMessage.class);
@@ -129,11 +96,55 @@ public class OperationMessageDaoImpl extends BaseImpl implements OperationMessag
         Query releaseQuery = Query.query(Criteria.where("dataId").is(dataId));
         Update releaseUpdate = new Update().set("expireAt", null).set("token", null);
 
-        FindAndModifyOptions releaseOptions = new FindAndModifyOptions().upsert(true)
-                .returnNew(true);
-        OperationMessage flowDisposition = mongoTemplate.findAndModify(releaseQuery, releaseUpdate, releaseOptions,
-                OperationMessage.class);
+        FindAndModifyOptions releaseOptions = new FindAndModifyOptions().upsert(true).returnNew(true);
+        OperationMessage flowDisposition = mongoTemplate.findAndModify(releaseQuery, releaseUpdate, releaseOptions, OperationMessage.class);
         return StringUtils.isEmpty(flowDisposition.getToken());
     }
 
+    public Criteria buildFilterCriteria(OperationMessageSearchParam param) {
+        Criteria criteria = buildCriteria(param);
+
+        if (StringUtils.isNotEmpty(param.getDeviceId())) {
+            criteria.and("deviceId").is(param.getDeviceId());
+        }
+
+        if (StringUtils.isNotEmpty(param.getEpId())) {
+            criteria.and("epId").is(param.getEpId());
+        }
+
+        if (StringUtils.isNotEmpty(param.getGateWayId())) {
+            criteria.and("gateWayId").is(param.getGateWayId());
+        }
+
+        if (param.getIsReceive() != null && param.getIsReceive()) {
+            criteria.and("isReceive").is(param.getIsReceive());
+        }
+
+        if (ObjectUtils.isNotEmpty(param.getEvents())) {
+            if (ObjectUtils.isNotEmpty(param.getNevents())) {
+                criteria.and("event").in(param.getEvents()).nin(param.getNevents());
+            } else {
+                criteria.and("event").in(param.getEvents());
+            }
+        } else if (ObjectUtils.isNotEmpty(param.getNevents())) {
+            criteria.and("event").nin(param.getNevents());
+        }
+
+        // 消息业务类型
+        if (param.getOperationBusType() != null) {
+            criteria.and("operationBusType").is(param.getOperationBusType());
+        }
+
+        // 模糊搜索
+        List<Criteria> criterias = new ArrayList<>();
+        if (StringUtils.isNotEmpty(param.getTopic())) {
+            Pattern pattern = Pattern.compile("^.*" + param.getTopic() + ".*$");
+            criterias.add(Criteria.where("topic").is(pattern));
+        }
+        if (!CollectionUtils.isEmpty(criterias)) {
+            criteria.andOperator(criterias.toArray(new Criteria[]{}));
+        }
+        return criteria;
+    }
+
 }

+ 8 - 2
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/domain/iot/device/DeviceOnLineRecord.java

@@ -45,6 +45,9 @@ public class DeviceOnLineRecord extends SuperEntity {
     @Indexed(expireAfterSeconds = 0)
     private Date TTL;
 
+    @Schema(description = "记录时间")
+    private Long recordTime;
+
     //------------------------------时间信息 start--------------------
     @Schema(description = "年份,如:2024")
     private Integer year;
@@ -64,8 +67,11 @@ public class DeviceOnLineRecord extends SuperEntity {
     @Schema(description = "当前的第几小时")
     private Integer hourOfDay;
 
-    @Schema(description = "")
-    private String statisticsKey;
+    @Schema(description = "分钟key")
+    private String minuteKey;
+
+    @Schema(description = "小时key")
+    private String hourKey;
 
     public void setTimes() {
         this.year = DateUtils.getCurrentYear();

+ 5 - 1
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/domain/iot/device/OperationMessage.java

@@ -1,6 +1,7 @@
 package com.zhongshu.iot.server.core.domain.iot.device;
 
 import cn.hutool.json.JSONObject;
+import com.zhongshu.iot.client.type.OperationBusType;
 import com.zhongshu.iot.client.type.OperationType;
 import com.zhongshu.iot.server.core.domain.base.SuperEntity;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -14,7 +15,7 @@ import org.springframework.data.mongodb.core.mapping.Document;
 import java.util.Date;
 
 /**
- * 指令数据
+ * 指令数据 (mqtt消息)
  *
  * @author TRX
  * @date 2024/5/21
@@ -59,6 +60,9 @@ public class OperationMessage extends SuperEntity {
     @Schema(description = "处理标记,判断用那个业务方法处理")
     private String event;
 
+    @Schema(description = "消息业务类型")
+    private OperationBusType operationBusType;
+
     //------------------------关联的消息
 
     @Schema(description = "消息的类型")

+ 4 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/domain/iot/device/OperationMessageResult.java

@@ -1,6 +1,7 @@
 package com.zhongshu.iot.server.core.domain.iot.device;
 
 import cn.hutool.json.JSONObject;
+import com.zhongshu.iot.client.type.OperationBusType;
 import com.zhongshu.iot.client.type.OperationType;
 import com.zhongshu.iot.server.core.domain.base.SuperEntity;
 import com.zhongshu.iot.server.core.domain.iot.IotMain;
@@ -31,6 +32,9 @@ public class OperationMessageResult extends SuperEntity {
     @DBRef(lazy = true)
     private OperationMessage operationMessage;
 
+    @Schema(description = "消息业务类型")
+    private OperationBusType operationBusType;
+
     @Schema(description = "主数据dataId")
     private String dataId;
 

+ 6 - 1
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/artemis/OperationMessageService.java

@@ -13,6 +13,7 @@ import com.zhongshu.iot.client.model.artemis.OperationMessageResultModel;
 import com.zhongshu.iot.client.model.artemis.OperationMessageResultSearch;
 import com.zhongshu.iot.client.model.artemis.OperationMessageSearchParam;
 import com.zhongshu.iot.client.model.mqtt.SendMessageModel;
+import com.zhongshu.iot.client.type.OperationBusType;
 import com.zhongshu.iot.client.type.OperationType;
 import com.zhongshu.iot.server.core.dao.iot.IotMainDao;
 import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
@@ -354,8 +355,10 @@ public class OperationMessageService {
             // 查询有对应事件的设备
             List<IotMain> events = iotMainDao.findByRealIotTopicAndFunctionTypeOrderByCreateTimeAsc(entity.getTopic(), FunctionType.Event);
             if (ObjectUtils.isNotEmpty(events)) {
+                entity.setOperationBusType(OperationBusType.IotThing);
                 // 去重
                 events = events.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(IotMain::getRealIotTopic))), ArrayList::new));
+                // 物模型执行
                 for (IotMain iotMain : events) {
                     executeOperationMessage(entity, requestData, iotMain);
                 }
@@ -367,10 +370,12 @@ public class OperationMessageService {
 
             // 本地处理的事件
             if (event != null && MqttConfig.localHandelEvent.contains(event)) {
+                if (entity.getOperationBusType() == null) {
+                    entity.setOperationBusType(OperationBusType.Other);
+                }
                 operationMessageDao.save(entity);
                 localHandler(entity, requestData);
             } else {
-                // 业务处理失败
                 entity.setIsHandleSuccess(isHandleSuccess);
                 entity.setHandleMsg(handleMsg);
                 entity.setReTime(System.currentTimeMillis());

+ 31 - 4
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/device/DeviceOnLineRecordService.java

@@ -3,6 +3,7 @@ package com.zhongshu.iot.server.core.service.device;
 import com.github.microservice.net.ResultContent;
 import com.github.microservice.types.deviceUse.OnLineState;
 import com.zhongshu.card.client.utils.DateUtils;
+import com.zhongshu.iot.client.model.baseParam.SeriesModel;
 import com.zhongshu.iot.client.model.iot.ping.DeviceOnLineRecordModel;
 import com.zhongshu.iot.client.model.iot.ping.DeviceOnLineRecordSearch;
 import com.zhongshu.iot.client.model.iot.statistics.LineModel;
@@ -11,6 +12,7 @@ import com.zhongshu.iot.server.core.dataConfig.CommonTTLTimeConfig;
 import com.zhongshu.iot.server.core.domain.iot.device.DeviceInfo;
 import com.zhongshu.iot.server.core.domain.iot.device.DeviceOnLineRecord;
 import com.zhongshu.iot.server.core.service.base.SuperService;
+import com.zhongshu.iot.server.core.util.CommonUtil;
 import com.zhongshu.iot.server.core.util.page.PageEntityUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
@@ -20,7 +22,10 @@ import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author TRX
@@ -40,14 +45,18 @@ public class DeviceOnLineRecordService extends SuperService {
         if (ObjectUtils.isEmpty(deviceInfo) || onLineState != OnLineState.OnLine) {
             return;
         }
-        String statisticsKey = DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyyMMddHHmm);
-        DeviceOnLineRecord entity = deviceOnLineRecordDao.findTopByDeviceIdAndStatisticsKey(deviceInfo.getDeviceId(), statisticsKey);
+        // 具体到分钟
+        String minuteKey = DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyyMMddHHmmKey);
+        String hourKey = DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyyMMddHHKey);
+        DeviceOnLineRecord entity = deviceOnLineRecordDao.findTopByDeviceIdAndMinuteKey(deviceInfo.getDeviceId(), minuteKey);
         if (ObjectUtils.isEmpty(entity)) {
             entity = new DeviceOnLineRecord();
             entity.setTimes();
+            entity.setRecordTime(System.currentTimeMillis());
         }
         entity.setDeviceId(deviceInfo.getDeviceId());
-        entity.setStatisticsKey(statisticsKey);
+        entity.setMinuteKey(minuteKey);
+        entity.setHourKey(hourKey);
         entity.setDeviceName(deviceInfo.getDeviceName());
         entity.setDeviceCategory(deviceInfo.getDeviceCategory());
         entity.setProductCode(deviceInfo.getProductCode());
@@ -69,9 +78,27 @@ public class DeviceOnLineRecordService extends SuperService {
      * @return
      */
     public ResultContent<LineModel> statisticsDeviceOnLine(DeviceOnLineRecordSearch search) {
+        if (CommonUtil.longIsEmpty(search.getStartRecordTime()) || CommonUtil.longIsEmpty(search.getEndRecordTime())) {
+            return ResultContent.buildFail("搜索时间不能为空");
+        }
         LineModel lineModel = new LineModel();
+        List<String> xAxis = new ArrayList<>();
+        SeriesModel seriesModel = new SeriesModel();
 
-
+        Map<String, Long> map = deviceOnLineRecordDao.statisticsMinNumber(search);
+        Long startRecordTime = search.getStartRecordTime();
+        Long endRecordTime = search.getEndRecordTime();
+        // 已分钟一条数据
+        while (startRecordTime <= endRecordTime) {
+            String minuteKey = DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternyyyyMMddHHmmKey);
+            String xAxi = DateUtils.paresTime(System.currentTimeMillis(), DateUtils.patternHHmm);
+            xAxis.add(xAxi);
+            Long number = map.containsKey(minuteKey) ? map.get(minuteKey) : 0L;
+            seriesModel.addSeries(number);
+            startRecordTime += 60 * 1000L;
+        }
+        lineModel.setXAxis(xAxis);
+        lineModel.addSeries(seriesModel);
         return ResultContent.buildSuccess(lineModel);
     }
 

+ 50 - 1
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/device/DeviceStatisticsService.java

@@ -1,10 +1,20 @@
 package com.zhongshu.iot.server.core.service.device;
 
 import com.github.microservice.net.ResultContent;
-import com.zhongshu.iot.client.model.iot.statistics.LineModel;
+import com.github.microservice.types.deviceUse.DeviceCategory;
+import com.zhongshu.iot.client.model.artemis.OperationMessageSearchParam;
+import com.zhongshu.iot.client.model.iot.statistics.DeviceStatisticsModel;
+import com.zhongshu.iot.client.model.mqtt.DeviceInfoSearchParam;
+import com.zhongshu.iot.client.type.OperationBusType;
+import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
+import com.zhongshu.iot.server.core.dao.mqtt.OperationMessageDao;
+import com.zhongshu.iot.server.core.util.DateUtils;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * 设备统计服务
  *
@@ -15,6 +25,45 @@ import org.springframework.stereotype.Service;
 @Service
 public class DeviceStatisticsService {
 
+    @Autowired
+    private DeviceInfoDao deviceInfoDao;
+
+    @Autowired
+    private OperationMessageDao operationMessageDao;
+
+    /**
+     * 设备等的统计
+     *
+     * @return
+     */
+    public ResultContent deviceStatistics() {
+        DeviceStatisticsModel model = new DeviceStatisticsModel();
+        Long deviceNumber = deviceInfoDao.countByDeviceCategory(DeviceCategory.DE);
+        model.setDeviceNumber(deviceNumber);
+
+        Long startTime = DateUtils.getDayStartTime(System.currentTimeMillis());
+        DeviceInfoSearchParam param = new DeviceInfoSearchParam();
+        param.setStartTime(startTime);
+        param.setDeviceCategorys(List.of(DeviceCategory.DE));
+        Long toDayDeviceNumber = deviceInfoDao.count(param);
+        model.setToDayDeviceNumber(toDayDeviceNumber);
+
+        Long gateWayNumber = deviceInfoDao.countByDeviceCategory(DeviceCategory.GW);
+        model.setGateWayNumber(gateWayNumber);
+        param.setDeviceCategorys(List.of(DeviceCategory.GW));
+        Long toDayGateWayNumber = deviceInfoDao.count(param);
+        model.setToDayGateWayNumber(toDayGateWayNumber);
+
+        OperationMessageSearchParam messageSearchParam = new OperationMessageSearchParam();
+        messageSearchParam.setOperationBusType(OperationBusType.IotThing);
+        Long messageNumber = operationMessageDao.count(messageSearchParam);
+        model.setMessageNumber(messageNumber);
+
+        messageSearchParam.setStartTime(startTime);
+        Long toDayMessageNumber = operationMessageDao.count(messageSearchParam);
+        model.setToDayMessageNumber(toDayMessageNumber);
 
+        return ResultContent.buildSuccess(model);
+    }
 
 }

+ 4 - 1
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/service/iot/IotSendMessageService.java

@@ -9,6 +9,7 @@ import com.github.microservice.net.ResultContent;
 import com.github.microservice.net.ResultMessage;
 import com.zhongshu.iot.client.model.iot.IotMainSearch;
 import com.zhongshu.iot.client.type.IotDataType;
+import com.zhongshu.iot.client.type.OperationBusType;
 import com.zhongshu.iot.client.type.OperationType;
 import com.zhongshu.iot.server.core.dao.iot.IotMainDao;
 import com.zhongshu.iot.server.core.dao.mqtt.DeviceInfoDao;
@@ -135,6 +136,7 @@ public class IotSendMessageService extends SuperService {
         OperationMessage entity = operationMessageDao.init(dataId, token);
         if (ObjectUtils.isNotEmpty(entity)) {
             entity.setMessageId(CommonUtil.UUID()); // 消息ID
+            entity.setOperationBusType(OperationBusType.IotThing);
             entity.setClientId(""); // 终端ID
             entity.setTopic(""); // topic名称
             entity.setOperationType(OperationType.Push);
@@ -144,7 +146,8 @@ public class IotSendMessageService extends SuperService {
             entity.setSendTime(System.currentTimeMillis());
             entity.setDataId(dataId);
             entity.setIsTimeOut(false);
-            entity.setEvent("");
+            entity.setEvent(param.getEvent());
+
             if (ObjectUtils.isNotEmpty(gateWayInfo)) {
                 entity.setGateWayInfo(gateWayInfo);
                 entity.setGateWayId(gateWayInfo.getDeviceId());

+ 17 - 0
OneCardIotServer/src/main/java/com/zhongshu/iot/server/core/util/CommonUtil.java

@@ -190,6 +190,23 @@ public class CommonUtil {
         return count;
     }
 
+    public static Long getLongByObj(Object obj) {
+        BigDecimal count = BigDecimal.ZERO;
+        if (obj != null) {
+            if (obj instanceof Integer) {
+                count = new BigDecimal((Integer) obj);
+            } else if (obj instanceof Decimal128) {
+                Decimal128 countDecimal = (Decimal128) obj;
+                count = countDecimal.bigDecimalValue();
+            } else if (obj instanceof BigDecimal) {
+                count = (BigDecimal) obj;
+            } else if (obj instanceof Long) {
+                count = new BigDecimal((Long) obj);
+            }
+        }
+        return count.longValue();
+    }
+
     /**
      * BigDecimal 保留四位小数
      *