设备功能新增

This commit is contained in:
zkzh_admin committed 2026-10-09 11:25:20 +08:00
1 parent 89bb6d9e83
commit 0280cf3f0b
10 files changed
+372 -33

No files matched your search

@@ -0,0 +1,147 @@
# 项目相关配置
hzvtc:
# 名称
name: AiBird
# 版本
version: 1.0.0
# 版权年份
copyrightYear: 2023
# 实例演示开关
demoEnabled: false
profile: /storage2/file
birdMediaPath: /storage2/file/
birdImagePath: /storage2/file/
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数组计算 char 字符验证
captchaType: math
dailyReportFilePath: /storage2/file/template/
dailyReportFileOutPath: /storage2/file/excel/
cameraSnapshopFilePath: /storage2/file/images/snapshot/
observationFilePath: /storage2/file/images/observation/
observationTokenUrl: http://semi-ai.tech:19101/v1/getAccessToken
observationImgCheckUrl: http://semi-ai.tech:19101/v2/detImg
fileServerUrl: http://semi-ai.tech:9099
videoBasePath: /storage2/file/xuebao
videoInferBasePath: /storage2/file/xuebao/labeled
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/ai-xuebao-db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: l/e9anIszcsjpFyUvBjbX6t5tOT2lNGm5dXNrfHVAL+2P1F3dZYTcFihaJBXMnplAJ8dhp94K94RUXMpD2i4dw==
filters: config
connection-properties: 'config.decrypt=true;config.decrypt.key=${publickey}'
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: root
login-password: root123
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
elastic:
#username: elastic
#password: OjNq8y*ck337qcWXmrg2
username: aibird
password: Semi_Aibird*123$
hosts: 127.0.0.1:9200
# port: 9200
http: https
apiKey: R3RkY0I1RUJaYTMtRWdUdEJZM1A6OFg4M1FlMFdRdHFsdjZkQXlMalJJdw==
certName: es_ca_server.crt
# redis 配置
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
mail:
host: smtp.163.com
username: aisemibird@163.com
password: VDRSLZUDDXGQMGVJ
kafka:
bootstrapServers: 127.0.0.1:8190
topic:
FileProcessRequest: file_process_request_topic
FileProcessVideoResponse: file_process_video_response_topic
FileProcessImageResponse: file_process_image_response_topic
CameraUploadRequest: camera_upload_request_topic
CameraProcessRequest: camera_process_request_topic
CameraProcessFinishResponse: camera_process_finish_response_topic
consumer:
groupId: VideoInferMessage
keyDeserializer: org.apache.kafka.common.serialization.StringDeserializer
valueDserializer: org.apache.kafka.common.serialization.StringDeserializer
producer:
groupId: VideoInferMessage
keyDeserializer: org.apache.kafka.common.serialization.StringSerializer
valueDserializer: org.apache.kafka.common.serialization.StringSerializer
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKOmv7xNg5aqMTeQAW48cLTv0B9/IGAGkfMG/cZU27rW9SMNGmDgllAWB50xAaYlkSYJXq1LqWBSWpScpoiHoDkCAwEAAQ==
# 日志配置
logging:
level:
net.hzvtc: debug
org.springframework: warn
@@ -1,31 +1,27 @@
Bag Attributes
friendlyName: ca
2.16.840.1.113894.746875.1.1: <Unsupported tag 6>
subject=CN = Elastic Certificate Tool Autogenerated CA
issuer=CN = Elastic Certificate Tool Autogenerated CA
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIUSIljr8sHsOSVpGEnulyHwIU1au0wDQYJKoZIhvcNAQEL MIIDSTCCAjGgAwIBAgIUSbDiVEgvbavhbDbU6xLz5SZ6SvgwDQYJKoZIhvcNAQEL
BQAwPDE6MDgGA1UEAxMxRWxhc3RpY3NlYXJjaCBzZWN1cml0eSBhdXRvLWNvbmZp BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l
Z3VyYXRpb24gSFRUUCBDQTAeFw0yNDA3MzEwMjQ4MjZaFw0yNzA3MzEwMjQ4MjZa cmF0ZWQgQ0EwHhcNMjYwODI5MTEyMDI0WhcNMjkwODI4MTEyMDI0WjA0MTIwMAYD
MDwxOjA4BgNVBAMTMUVsYXN0aWNzZWFyY2ggc2VjdXJpdHkgYXV0by1jb25maWd1 VQQDEylFbGFzdGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBDQTCC
cmF0aW9uIEhUVFAgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCN ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVFHDG7u2dlCJ5UGV4vhUL9
/nnBG0Ljres5Y9b6gjNKauEYZWTAzJCGG7E7no7siwjYnwlnNqiD0kwAcdtICkjv GI3A0YIymGiDSsDBKTIbgXxcGSHtpxxD3Cnm3Y3seq6D8CgFCApAA65fCA1iL6pN
9uOHXGdfeExh1BQFtaWqbvCA3k8h/lilmXa+fF3xYxvetBUC1clwQl/YtGO02pPg pyWNlMT6hXREKixxwgw5EIha8+VdRwo5HPdPo6Q89qRhhsyvQaDl7RyEwdoiiDxX
9vKHIq5q24xzVrMfHyY2xdITvv7fbSXUvQKFJmaGN3+5V8vKQK6KT+YY9ic9LJi1 PNY5wInRbjCp638XqSUHm4V5aHflsbvx8f6bfjEEnqPNGcVtb1+RlIEJ891zPlDM
MD8e4CPG654XJEDMysogU2MXoW9NFTqCS7K2u49P0VfBnfqeJRQGpKMZrLRGo1zu ceUHQDq0lqTHSSZKBQxnIX5iF8Y+e6vWrUr2h7B8Oe+j4/et17HwMDKqr/sMhFml
bZgOZdWsY1TQ/IOwbsmH2GXw995/u0R+kKpcl3ViWm+2ErUckktVUzh2f4LCPhwZ +z+UpgjqGNSsBH2UcX5HcPJF4MQDoODXTmwqCnu8ucQbD/D01FY5Hdj0TSMlwHEC
C5ds8yhMP/WfDnrWSDwPTTptLLczgxsuYk36M6RFBF+jUd23yZJvJxGXde6zzplc AwEAAaNTMFEwHQYDVR0OBBYEFCSkKXt/9nbGPxZCpMuQxUyYnnP1MB8GA1UdIwQY
OQLNtHByUKMCq8EoS691ufJqWMMCHb1H2OQHEPRGxcgtQ1hpjz13GEelnUF3MGlD MBaAFCSkKXt/9nbGPxZCpMuQxUyYnnP1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
DPiNfmRvD4IBojmh21SfuDGPZxIoIrhMt2XaC0eaq+soKJGReer5l6Q9Wh+b5s4U hvcNAQELBQADggEBAKI6vTv2L5aZaMRtNMkNqbsgjs63wSBUV+otKHgWm3T50Qp7
HJ2yEg57VjW5Dwshad1RnD8WhV4UD1fEthGNGfMQZvNZyezA4dwHp5SthbOYgbHF KLjJBgw+tf5Om/tAajcpt8X9Awc1+hBMiPLWANhdloKVw4eCBxiKeL5IJgv3MsOQ
QU8ByTRcxNPsx64IIXYa0RqjyBVaD2OL7YJEYsO+PbAsCyVXtr/4h/q7fmkKNX4N GnVMcJ4idz5L0LKZf5caUcOmjAEXodyfwXlEjY6aVJRyYEQZUv6iJ76FCuWt3S5U
ArAtyvrDVqtYjwmGFix7l8G1GXbJAf9abci4aPGiOQIDAQABo1MwUTAdBgNVHQ4E N7Aqj+s56EEU09/Npt8rJ4NVP7/IkJBlMZnEgN/A8VeKwWLsZ1vPjTxDNhLAyx5o
FgQUnqBxAJLZymZOICjUmjWXcKfk+v0wHwYDVR0jBBgwFoAUnqBxAJLZymZOICjU /yGkG8fFVXClR8S8fj67WQ+6DyFi8I9X+IDfTE7J7dKCvFA/3ep52c4pYhteNfLf
mjWXcKfk+v0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATy/M DsMJCoDjZE4+oguSedQQmav0bYl8sfPPfu4MYIw=
7kgdZrg61PgeUrzsqTLwFRV559j7deZ8K+bRofwL5vQJ14w6fuoqipLXxTq3obS3
dQvO1xx1p2AHn8nu4d29CW8PUzZFD52ZHdU8gxVF5dUCnbF3b15d2CKoivSVSHpl
0R6SwuyhnWeaV2ue0/0H+KBlbB1pl94wNw+eSLCL/m8k68DnI+qY5skLZpfxjcoc
mEG8EckHoO3+JKshj6darBS+I5QmmbBrFzB/AJN9YbrM0OjqEC3u1SYWEPwinv4G
H9rSZC0wP8xjKVGjVub8C4Fkz+StWucotBrD3aadao09Sovrs7GmLFgmObrzuP/N
HPD+tq6sFXfDV0tnKu4EMM+GNsp9wHyASBpasbbYWSy9gjK080kzq7WjfmcBYPU0
8/nsGH7vBrQvh5XU2zRiuRmHZnpaXpjbmD9LG69yoYI1vNlK6/qrBXVFm2zZ2bRa
UW4B1kY3avj0DvsA2ggYZDeln9pIvt6364tbqZZa9w0KRXzRcKW3zUjmjqTQgtuy
C2KvRF+vQk6BzHG8Szxi8sRLzoNaUsketbB526mjyq0I9+Q/Yjp07iobfZBCcydw
KLfmTthicyQAruDdUXltXSnuEXP8fJXHm1qSERRfOnKWy3tDJCF1u6C/di5HeOKq
dILlQH26K7yRlUcDpQZelnaHdruDmXBREClOMns=
-----END CERTIFICATE----- -----END CERTIFICATE-----
@@ -45,6 +45,11 @@ public class EsResultRecordDetailsController {
return esResultRecordDetailsService.getResultMediaListByParam(params); return esResultRecordDetailsService.getResultMediaListByParam(params);
} }
@PostMapping("/messageInfolist")
public TableDataInfo getSnowLeopardMessageListByParam(@RequestBody JSONObject params) {
return esResultRecordDetailsService.getSnowLeopardMessageListByParam(params);
}
@DeleteMapping("/delDocument/{ids}") @DeleteMapping("/delDocument/{ids}")
public AjaxResult deleteDocumentById(@PathVariable String ids) { public AjaxResult deleteDocumentById(@PathVariable String ids) {
return AjaxResult.success(esResultRecordDetailsService.deleteDocumentById(ids)); return AjaxResult.success(esResultRecordDetailsService.deleteDocumentById(ids));
@@ -0,0 +1,22 @@
package net.hzvtc.es.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.Id;
import java.time.ZonedDateTime;
@Data
public class MessageInfoVo {
@Id
private Long id;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private ZonedDateTime recordTime;
private Long deviceId;
private String deviceName;
private Integer count;
}
@@ -15,6 +15,8 @@ public interface EsResultRecordDetailsService {
TableDataInfo getResultMediaListByParam(JSONObject jsonObject); TableDataInfo getResultMediaListByParam(JSONObject jsonObject);
TableDataInfo getSnowLeopardMessageListByParam(JSONObject param);
int deleteDocumentById(String id); int deleteDocumentById(String id);
void bulkAddResultRecordDocument(); void bulkAddResultRecordDocument();
@@ -37,6 +37,7 @@ import net.hzvtc.device.service.DeviceInfoService;
import net.hzvtc.echart.domain.vo.BirdStatDataVo; import net.hzvtc.echart.domain.vo.BirdStatDataVo;
import net.hzvtc.es.domain.EsResultRecordDetails; import net.hzvtc.es.domain.EsResultRecordDetails;
import net.hzvtc.es.domain.EsResultRecordDetailsVo; import net.hzvtc.es.domain.EsResultRecordDetailsVo;
import net.hzvtc.es.domain.MessageInfoVo;
import net.hzvtc.es.mapper.EsResultRecordDetailsMapper; import net.hzvtc.es.mapper.EsResultRecordDetailsMapper;
import net.hzvtc.service.EsResultRecordDetailsService; import net.hzvtc.service.EsResultRecordDetailsService;
import net.hzvtc.system.mapper.SysDeptMapper; import net.hzvtc.system.mapper.SysDeptMapper;
@@ -48,6 +49,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -310,6 +312,77 @@ public class EsResultRecordDetailsServiceImpl implements EsResultRecordDetailsSe
return getTableDataInfo(new ArrayList<>(), 0); return getTableDataInfo(new ArrayList<>(), 0);
} }
@Override
public TableDataInfo getSnowLeopardMessageListByParam(JSONObject param) {
// 使用有效记录索引 animal_valid_record(无 is_deleted 字段,不能附加该过滤条件)
String validIndexName = "animal_valid_record";
// 雪豹 speciesId 固定为 1513,按 species_id 过滤
param.put("species_id", 1513L);
param.remove("isDeleted");
Integer pageNum = param.getInteger("pageNum");
if (pageNum == null) {
pageNum = 1;
}
Integer pageSize = param.getInteger("pageSize");
if (pageSize == null) {
pageSize = 6;
}
int pageFrom = (pageNum - 1) * pageSize;
try {
// 按天聚合雪豹出现日期(一天只出现一次)
BoolQuery.Builder boolQueryBuilder = CommonEsUtils.createQuery(param);
BoolQuery boolQuery = boolQueryBuilder.build();
SearchResponse<Void> dayResponse = elasticsearchClient.search(s -> s.index(validIndexName).size(0)
.query(q -> q.bool(boolQuery))
.aggregations("by_day", a -> a.dateHistogram(dh -> dh
.field("record_time")
.calendarInterval(CalendarInterval.Day)
.minDocCount(1)
.format("yyyy-MM-dd")
.timeZone("+08:00"))), Void.class);
List<DateHistogramBucket> dayBuckets = dayResponse.aggregations().get("by_day").dateHistogram().buckets().array();
Collections.reverse(dayBuckets);
long total = dayBuckets.size();
List<JSONObject> rows = new ArrayList<>();
if (pageFrom < total) {
int end = (int) Math.min(pageFrom + pageSize, total);
for (DateHistogramBucket bucket : dayBuckets.subList(pageFrom, end)) {
String day = bucket.keyAsString();
// 取当天最早的一条雪豹记录
BoolQuery.Builder dayQueryBuilder = CommonEsUtils.createQuery(param);
dayQueryBuilder.must(m -> m.range(v -> v.field("record_time").format("yyyy-MM-dd").gte(JsonData.of(day)).lte(JsonData.of(day)).timeZone("+08:00")));
SearchResponse<MessageInfoVo> response = elasticsearchClient.search(s -> s
.trackTotalHits(new TrackHits.Builder().enabled(true).build()).index(validIndexName)
.query(q -> q.bool(dayQueryBuilder.build()))
.sort(sort -> sort.field(f -> f.field("record_time").order(SortOrder.Asc)))
.source(sc -> sc.filter(f -> f.includes(Arrays.asList("id", "device_id", "device_name","count" ,"record_time"))))
.from(0).size(1), MessageInfoVo.class);
for (Hit<MessageInfoVo> mapHit : response.hits().hits()) {
MessageInfoVo data = mapHit.source();
JSONObject item = new JSONObject();
item.put("documentId", mapHit.id());
item.put("deviceId", data.getDeviceId());
item.put("count", data.getCount());
item.put("deviceName", data.getDeviceName());
if (data.getRecordTime() != null) {
item.put("time", data.getRecordTime().withZoneSameInstant(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
item.put("date", day);
rows.add(item);
}
}
}
return getTableDataInfo(rows, total);
} catch (IOException ex) {
logger.error(ex.getMessage());
}
return getTableDataInfo(new ArrayList<>(), 0);
}
@Override @Override
public int deleteDocumentById(String ids) { public int deleteDocumentById(String ids) {
try { try {
@@ -0,0 +1,43 @@
package net.hzvtc.device.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import net.hzvtc.common.core.domain.BaseEntity;
import org.apache.ibatis.type.Alias;
import java.util.Date;
/**
* 设备日志对象 device_log
*
* @author admin
* @date 2026-10-07
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Alias("DeviceLog")
public class DeviceLog extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 时间
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date time;
/**
* 状态:1:未上线 2:未上传文件 3:相机故障 4:其他
*/
private Integer state;
/**
* 设备ID
*/
private Long deviceId;
}
@@ -0,0 +1,48 @@
package net.hzvtc.device.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import net.hzvtc.common.core.domain.BaseEntity;
import org.apache.ibatis.type.Alias;
import java.util.Date;
/**
* 设备维护对象 device_maintaince
*
* @author admin
* @date 2026-10-07
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Alias("DeviceMaintaince")
public class DeviceMaintaince extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 维护时间
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date time;
/**
* 维护记录
*/
private String content;
/**
* 维护人姓名
*/
private String person;
/**
* 设备
*/
private Long deviceId;
}
@@ -285,8 +285,8 @@ public class DeviceInfoServiceImpl implements DeviceInfoService {
if (jsonObject != null) { if (jsonObject != null) {
Long deptId = jsonObject.getLong("deptId"); Long deptId = jsonObject.getLong("deptId");
if (deptId != null) { if (deptId != null) {
param.put("ancestors", getAncestorsById(deptId)); List<Long> deptIds = sysDeptService.getDeptSubIdsById(deptId);
param.put("deptId", deptId); param.put("deptIds", deptIds);
} }
} }
@@ -200,8 +200,11 @@
<if test="userId != null"> <if test="userId != null">
and id in (select post_id from sys_user_post where user_id=#{userId}) and id in (select post_id from sys_user_post where user_id=#{userId})
</if> </if>
<if test="ancestors != null and ancestors !=''"> <if test="deptIds != null and deptIds.size() > 0">
and (dept_id in (select dept_id from sys_dept where ancestors like concat(#{ancestors},'%')) or dept_id=#{deptId}) and dept_id in
<foreach collection="deptIds" item="dId" open="(" separator="," close=")">
#{dId}
</foreach>
</if> </if>
order by id order by id
</select> </select>