feat(iot): 增加设备和产品状态统计接口

- 在 DeviceController 和 ProductController 中添加状态统计接口
- 在 DeviceService 和 ProductService 中实现状态统计方法- 新增 DeviceStatusVo 和 ProductStatusVo 类用于返回统计结果- 在 RecordDataRepository 中添加警告数据统计相关的查询方法
- 在 RecordDataService 中实现数据统计方法- 新增 RecordDataStatusVo 和 DataWarningVo 类用于返回记录数据和警告数据的统计结果
This commit is contained in:
zhuangtianxiang 2025-03-02 15:24:41 +08:00
parent bf846e95a4
commit 4ddf88b799
25 changed files with 320 additions and 19 deletions

View File

@ -11,8 +11,11 @@ import com.zsc.edu.gateway.modules.iot.device.dto.DeviceServeDto;
import com.zsc.edu.gateway.modules.iot.device.entity.Device;
import com.zsc.edu.gateway.modules.iot.device.query.DeviceQuery;
import com.zsc.edu.gateway.modules.iot.device.service.DeviceService;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceStatusVo;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceVo;
import com.zsc.edu.gateway.modules.iot.record.entity.DataWarningVo;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordData;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordDataStatusVo;
import com.zsc.edu.gateway.modules.iot.record.service.RecordDataService;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
@ -115,4 +118,34 @@ public class DeviceController {
public Page<RecordData> recordData(Page<RecordData> page, String clientId) {
return recordService.page(page, new LambdaQueryWrapper<RecordData>().eq(RecordData::getClientId, clientId));
}
/**
* 查询设备信息数量
*/
@GetMapping("status")
@PreAuthorize("hasAuthority('iot:device:query')")
@DataPermission
public DeviceStatusVo status() {
return service.status();
}
/**
* 查询设备消息信息
*/
@GetMapping("/record/status")
@PreAuthorize("hasAuthority('iot:device:query')")
@DataPermission
public RecordDataStatusVo recordDataStatus() {
return recordService.recordDataStatus();
}
/**
*
*/
@GetMapping("/data/status")
@PreAuthorize("hasAuthority('iot:device:query')")
@DataPermission
public DataWarningVo dataWarning() {
return recordService.dataWarning();
}
}

View File

@ -57,4 +57,9 @@ public class BatchDeviceDto {
*/
private String remark;
/**
* 图标
*/
public String icon;
}

View File

@ -54,4 +54,9 @@ public class DeviceDto {
*/
private String remark;
/**
* 图标
*/
public String icon;
}

View File

@ -35,6 +35,11 @@ public class DeviceServeDto {
*/
public String serveName;
/**
* 图标
*/
public String icon;
/**
* 参数
*/

View File

@ -8,6 +8,7 @@ import com.zsc.edu.gateway.modules.iot.device.dto.DeviceDto;
import com.zsc.edu.gateway.modules.iot.device.dto.DeviceServeDto;
import com.zsc.edu.gateway.modules.iot.device.entity.Device;
import com.zsc.edu.gateway.modules.iot.device.query.DeviceQuery;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceStatusVo;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceVo;
import java.util.List;
@ -27,4 +28,6 @@ public interface DeviceService extends IService<Device> {
Boolean serve(DeviceServeDto dto);
IPage<Device> query(Page<Device> page, DeviceQuery query);
DeviceStatusVo status();
}

View File

@ -15,9 +15,13 @@ import com.zsc.edu.gateway.modules.iot.device.mapper.DeviceMapper;
import com.zsc.edu.gateway.modules.iot.device.query.DeviceQuery;
import com.zsc.edu.gateway.modules.iot.device.repo.DeviceRepository;
import com.zsc.edu.gateway.modules.iot.device.service.DeviceService;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceStatusVo;
import com.zsc.edu.gateway.modules.iot.device.vo.DeviceVo;
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
import com.zsc.edu.gateway.modules.iot.product.repo.ProductRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.EventRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.PropertyRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.ServeRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -39,6 +43,12 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceRepository, Device> imp
private ProductRepository productRepo;
@Resource
private RedisUtils redisUtils;
@Resource
private PropertyRepository propertyRepo;
@Resource
private EventRepository eventRepo;
@Resource
private ServeRepository serveRepo;
/**
@ -48,7 +58,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceRepository, Device> imp
@Transactional
public Device create(DeviceDto dto) {
if (baseMapper.findByName(dto.getName()) != null) {
throw new ConstraintException("该设备已存在!");
throw new ConstraintException("该设备名称已存在!");
}
Device device = mapper.toEntity(dto);
save(device);
@ -183,5 +193,13 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceRepository, Device> imp
return devicePage;
}
@Override
public DeviceStatusVo status() {
DeviceStatusVo vo = new DeviceStatusVo();
vo.deviceCount = baseMapper.selectCount(null);
vo.propertyCount = propertyRepo.selectCount(null);
vo.serveCount = serveRepo.selectCount(null);
vo.eventCount = eventRepo.selectCount(null);
return vo;
}
}

View File

@ -0,0 +1,30 @@
package com.zsc.edu.gateway.modules.iot.device.vo;
import lombok.Data;
/**
* @author zhuang
*/
@Data
public class DeviceStatusVo {
/**
* 设备总数
*/
public Long deviceCount;
/**
* 属性总数
*/
public Long propertyCount;
/**
* 服务总数
*/
public Long serveCount;
/**
* 事件总数
*/
public Long eventCount;
}

View File

@ -166,6 +166,11 @@ public class DeviceVo {
*/
public String updateBy;
/**
* 是否启用/停用
*/
private Boolean enabled = true;
/**
* 设备包含的扩展属性产品不同设备所包含的属性也略有不同
*/

View File

@ -8,6 +8,7 @@ import com.zsc.edu.gateway.modules.iot.product.entity.Product;
import com.zsc.edu.gateway.modules.iot.product.query.ProductQuery;
import com.zsc.edu.gateway.modules.iot.product.service.ProductService;
import com.zsc.edu.gateway.modules.iot.product.service.impl.ProductServiceImpl;
import com.zsc.edu.gateway.modules.iot.product.vo.ProductStatusVo;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@ -103,4 +104,13 @@ public class ProductController {
public Product detail(@PathVariable("id") Long id) {
return service.detail(id);
}
/**
* 查询产品各类信息
*/
@GetMapping("status")
@DataPermission
public ProductStatusVo status() {
return service.status();
}
}

View File

@ -56,6 +56,11 @@ public class Product extends BaseEntity {
@TableField(exist = false)
private Set<Param> params;
/**
* 是否启用/停用
*/
private Boolean enabled = true;
/**
* 接入方式

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.zsc.edu.gateway.modules.iot.product.dto.ProductDto;
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
import com.zsc.edu.gateway.modules.iot.product.query.ProductQuery;
import com.zsc.edu.gateway.modules.iot.product.vo.ProductStatusVo;
import java.util.List;
@ -22,4 +23,6 @@ public interface ProductService extends IService<Product> {
boolean delete(Long id);
List<Product> list(ProductQuery query);
ProductStatusVo status();
}

View File

@ -1,5 +1,6 @@
package com.zsc.edu.gateway.modules.iot.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.edu.gateway.exception.ConstraintException;
import com.zsc.edu.gateway.modules.iot.product.dto.ProductDto;
@ -8,6 +9,7 @@ import com.zsc.edu.gateway.modules.iot.product.mapper.ProductMapper;
import com.zsc.edu.gateway.modules.iot.product.query.ProductQuery;
import com.zsc.edu.gateway.modules.iot.product.repo.ProductRepository;
import com.zsc.edu.gateway.modules.iot.product.service.ProductService;
import com.zsc.edu.gateway.modules.iot.product.vo.ProductStatusVo;
import com.zsc.edu.gateway.modules.iot.tsl.entity.Param;
import com.zsc.edu.gateway.modules.iot.tsl.service.ParamService;
import lombok.AllArgsConstructor;
@ -90,4 +92,12 @@ public class ProductServiceImpl extends ServiceImpl<ProductRepository, Product>
return products.subList(0, Math.min(5, products.size()));
}
@Override
public ProductStatusVo status() {
ProductStatusVo vo = new ProductStatusVo();
vo.productCount = baseMapper.selectCount(null);
vo.enabledCount = baseMapper.selectCount(new LambdaQueryWrapper<Product>().eq(Product::getEnabled, true));
vo.disabledCount = baseMapper.selectCount(new LambdaQueryWrapper<Product>().eq(Product::getEnabled, false));
return vo;
}
}

View File

@ -0,0 +1,24 @@
package com.zsc.edu.gateway.modules.iot.product.vo;
import lombok.Data;
/**
* @author zhuang
*/
@Data
public class ProductStatusVo {
/**
* 产品总数
*/
public Long productCount;
/**
* 启用数
*/
public Long enabledCount;
/**
* 停用数
*/
public Long disabledCount;
}

View File

@ -0,0 +1,19 @@
package com.zsc.edu.gateway.modules.iot.record.entity;
import lombok.Data;
/**
* @author zhuang
*/
@Data
public class DataWarningVo {
/**
* 告警总数
*/
private Long warningCount;
/**
* 今日新增
*/
private Long todayWarningCount;
}

View File

@ -1,5 +1,6 @@
package com.zsc.edu.gateway.modules.iot.record.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -32,4 +33,8 @@ public class RecordData {
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime recordTime;
@TableField(value = "dept_id", fill = FieldFill.INSERT)
private Long deptId;
}

View File

@ -0,0 +1,19 @@
package com.zsc.edu.gateway.modules.iot.record.entity;
import lombok.Data;
/**
* @author zhuang
*/
@Data
public class RecordDataStatusVo {
/**
* 消息总数
*/
public Long recordCount;
/**
* 数据点数
*/
public Long dataCount;
}

View File

@ -2,6 +2,7 @@ package com.zsc.edu.gateway.modules.iot.record.repo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordData;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
@ -11,8 +12,9 @@ import java.util.List;
* @author zhuang
*/
public interface RecordDataRepository extends BaseMapper<RecordData> {
@Select("select * from iot_record_data where record_time_str = #{recordTimeStr}")
List<RecordData> findByRecordTimeStr(String recordTimeStr);
@Select("SELECT COUNT(*) FROM iot_record_data WHERE content->>'warning' IS NOT NULL AND (content->>'warning')::int & 1 = 1")
long countWarnings();
@Select("SELECT COUNT(*) FROM iot_record_data WHERE content->>'warning' IS NOT NULL AND (content->>'warning')::int & 1 = 1 AND record_time >= #{todayStart}")
long countTodayWarnings(@Param("todayStart") LocalDateTime todayStart);
}

View File

@ -3,7 +3,9 @@ package com.zsc.edu.gateway.modules.iot.record.service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zsc.edu.gateway.modules.iot.record.entity.DataWarningVo;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordData;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordDataStatusVo;
import java.util.List;
@ -13,4 +15,8 @@ import java.util.List;
public interface RecordDataService extends IService<RecordData> {
RecordData recordData(String clientId, JSONObject data);
RecordDataStatusVo recordDataStatus();
DataWarningVo dataWarning();
}

View File

@ -2,10 +2,10 @@ package com.zsc.edu.gateway.modules.iot.record.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.edu.gateway.framework.redis.RedisUtils;
import com.zsc.edu.gateway.modules.iot.record.entity.DataWarningVo;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordData;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordDataStatusVo;
import com.zsc.edu.gateway.modules.iot.record.repo.RecordDataRepository;
import com.zsc.edu.gateway.modules.iot.record.service.RecordDataService;
import lombok.AllArgsConstructor;
@ -31,4 +31,22 @@ public class RecordDataServiceImpl extends ServiceImpl<RecordDataRepository, Rec
return recordData;
}
@Override
public RecordDataStatusVo recordDataStatus() {
RecordDataStatusVo recordDataStatusVo = new RecordDataStatusVo();
recordDataStatusVo.setRecordCount(baseMapper.selectCount(null));
recordDataStatusVo.setDataCount(baseMapper.selectCount(new LambdaQueryWrapper<RecordData>().isNotNull(RecordData::getContent)));
return recordDataStatusVo;
}
@Override
public DataWarningVo dataWarning() {
DataWarningVo dataWarningVo = new DataWarningVo();
LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
long warningCount = baseMapper.countWarnings();
long todayWarningCount = baseMapper.countTodayWarnings(todayStart);
dataWarningVo.setWarningCount(warningCount);
dataWarningVo.setTodayWarningCount(todayWarningCount);
return dataWarningVo;
}
}

View File

@ -26,9 +26,6 @@ public class PropertyServiceImpl extends ServiceImpl<PropertyRepository, Propert
@Override
@Transactional
public Property create(PropertyDto dto) {
if (baseMapper.findByName(dto.getName()) != null) {
throw new ApiException("该属性已存在!");
}
Property property = mapper.toEntity(dto);
save(property);
return property;

View File

@ -863,4 +863,70 @@ VALUES ('Device1', TRUE, 2, 'HW1.0', 'FW1.0', 'FAC12345', 'CLI12345', 1, '{
'icon10.png');
ALTER TABLE iot_device
ADD CONSTRAINT uc_client_id UNIQUE (client_id);
ADD CONSTRAINT uc_client_id UNIQUE (client_id);
INSERT INTO iot_product (name, product_type, model, link, create_by, create_time, update_by, update_time, remark,
dept_id, create_id, enabled)
VALUES ('Product1', 'TypeA', 'ModelX', 1, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product1',
1, 1, TRUE),
('Product2', 'TypeB', 'ModelY', 2, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product2',
2, 2, FALSE),
('Product3', 'TypeA', 'ModelZ', 1, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product3',
1, 3, TRUE),
('Product4', 'TypeC', 'ModelW', 3, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product4',
3, 4, TRUE),
('Product5', 'TypeB', 'ModelV', 2, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product5',
2, 5, FALSE),
('Product6', 'TypeA', 'ModelU', 1, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product6',
1, 6, TRUE),
('Product7', 'TypeD', 'ModelT', 4, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product7',
4, 7, FALSE),
('Product8', 'TypeC', 'ModelS', 3, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product8',
3, 8, TRUE),
('Product9', 'TypeA', 'ModelR', 1, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, 'Remark for Product9',
1, 9, FALSE),
('Product10', 'TypeB', 'ModelQ', 2, 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP,
'Remark for Product10', 2, 10, TRUE);
INSERT INTO iot_record_data (id, client_id, attachment_id, content, record_time, dept_id, create_id)
VALUES ('1', 'client1', 'attach1', '{
"key1": "value1",
"key2": 123
}', CURRENT_TIMESTAMP - INTERVAL '1 day', 1, 1),
('2', 'client2', 'attach2', '{
"key1": "value2",
"key2": 456
}', CURRENT_TIMESTAMP - INTERVAL '2 days', 2, 2),
('3', 'client3', 'attach3', '{
"key1": "value3",
"key2": 789
}', CURRENT_TIMESTAMP - INTERVAL '3 days', 1, 3),
('4', 'client4', 'attach4', '{
"key1": "value4",
"key2": 101
}', CURRENT_TIMESTAMP - INTERVAL '4 days', 3, 4),
('5', 'client5', 'attach5', '{
"key1": "value5",
"key2": 202
}', CURRENT_TIMESTAMP - INTERVAL '5 days', 2, 5),
('6', 'client6', 'attach6', '{
"key1": "value6",
"key2": 303
}', CURRENT_TIMESTAMP - INTERVAL '6 days', 1, 6),
('7', 'client7', 'attach7', '{
"key1": "value7",
"key2": 404
}', CURRENT_TIMESTAMP - INTERVAL '7 days', 4, 7),
('8', 'client8', 'attach8', '{
"key1": "value8",
"key2": 505
}', CURRENT_TIMESTAMP - INTERVAL '8 days', 3, 8),
('9', 'client9', 'attach9', '{
"key1": "value9",
"key2": 606
}', CURRENT_TIMESTAMP - INTERVAL '9 days', 2, 9),
('10', 'client10', 'attach10', '{
"key1": "value10",
"key2": 707
}', CURRENT_TIMESTAMP - INTERVAL '10 days', 1, 10);

View File

@ -1,6 +1,6 @@
create table iot_product
(
id bigint not null
id bigint generated by default as identity
constraint iot_product_pk
primary key,
name varchar,
@ -12,7 +12,9 @@ create table iot_product
update_by varchar,
update_time timestamp,
remark varchar,
dept_id bigint
dept_id bigint,
create_id bigint,
enabled boolean
);
comment on table iot_product is '产品表';
@ -39,9 +41,10 @@ comment on column iot_product.remark is '备注';
comment on column iot_product.dept_id is '所属部门';
comment on column iot_product.create_id is '创建者id';
comment on column iot_product.enabled is '是否启用/停用';
alter table iot_product
owner to gitea;
t_product
owner to gitea;

View File

@ -1,12 +1,14 @@
create table iot_record_data
(
id serial not null
id varchar not null
constraint iot_record_data_pk
primary key,
client_id varchar,
attachment_id varchar,
content varchar,
record_time timestamp
content jsonb,
record_time timestamp,
dept_id bigint,
create_id bigint
);
comment on table iot_record_data is '上报数据表';
@ -21,6 +23,12 @@ comment on column iot_record_data.content is '内容';
comment on column iot_record_data.record_time is '记录时间';
comment
on column iot_record_data.dept_id is '所属部门id';
comment
on column iot_record_data.create_id is '创建者id';
alter table iot_record_data
owner to gitea;

View File

@ -34,6 +34,7 @@
<result column="create_time" jdbcType="DATE" property="createTime"/>
<result column="update_time" jdbcType="DATE" property="updateTime"/>
<result column="remark" jdbcType="VARCHAR" property="remark"/>
<result column="enabled" jdbcType="BOOLEAN" property="enabled"/>
<collection property="params" ofType="com.zsc.edu.gateway.modules.iot.tsl.entity.Param" autoMapping="true"
columnPrefix="param_">
<id column="id" property="id"/>

View File

@ -16,6 +16,7 @@
<result column="update_time" jdbcType="DATE" property="updateTime"/>
<result column="remark" jdbcType="VARCHAR" property="remark"/>
<result column="create_id" jdbcType="BIGINT" property="createId"/>
<result column="enabled" jdbcType="BOOLEAN" property="enabled"/>
<collection property="params" ofType="com.zsc.edu.gateway.modules.iot.tsl.entity.Param" autoMapping="true"
columnPrefix="param_">
<id column="id" property="id"/>