diff --git b/pom.xml a/pom.xml new file mode 100644 index 0000000..73b1973 --- /dev/null +++ a/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + com.objecteye + viid-collector + 1.0-SNAPSHOT + + + 8 + 8 + UTF-8 + 2.6.3 + 2021.0.0 + 2021.0.1.0 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + import + pom + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-configuration-processor + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-pool2 + + + \ No newline at end of file diff --git b/src/main/java/com/objecteye/VIIDCollectorApplication.java a/src/main/java/com/objecteye/VIIDCollectorApplication.java new file mode 100644 index 0000000..cfd5bdd --- /dev/null +++ a/src/main/java/com/objecteye/VIIDCollectorApplication.java @@ -0,0 +1,16 @@ +package com.objecteye; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@SpringBootApplication +public class VIIDCollectorApplication { + + public static void main(String[] args) { + SpringApplication.run(VIIDCollectorApplication.class, args); + } +} diff --git b/src/main/java/com/objecteye/config/ApplicationConfigurationProperties.java a/src/main/java/com/objecteye/config/ApplicationConfigurationProperties.java new file mode 100644 index 0000000..57913b9 --- /dev/null +++ a/src/main/java/com/objecteye/config/ApplicationConfigurationProperties.java @@ -0,0 +1,17 @@ +package com.objecteye.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +@Component +@ConfigurationProperties(prefix = "config.application") +public class ApplicationConfigurationProperties { + + private String platformCode; +} diff --git b/src/main/java/com/objecteye/config/ObjectMapperConfigure.java a/src/main/java/com/objecteye/config/ObjectMapperConfigure.java new file mode 100644 index 0000000..0bd64ac --- /dev/null +++ a/src/main/java/com/objecteye/config/ObjectMapperConfigure.java @@ -0,0 +1,18 @@ +package com.objecteye.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; +import org.springframework.context.annotation.Bean; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +public class ObjectMapperConfigure { + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } +} diff --git b/src/main/java/com/objecteye/config/RestConfigurationProperties.java a/src/main/java/com/objecteye/config/RestConfigurationProperties.java new file mode 100644 index 0000000..a05d178 --- /dev/null +++ a/src/main/java/com/objecteye/config/RestConfigurationProperties.java @@ -0,0 +1,23 @@ +package com.objecteye.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +@Component +@ConfigurationProperties(prefix = "config.rest") +public class RestConfigurationProperties { + /** + * 连接超时时间 + */ + private int connectionTimeOut = 5 * 1000; + /** + * 等待超时时间 + */ + private int readTimeOut = 30 * 1000; +} diff --git b/src/main/java/com/objecteye/config/RestTemplateConfigure.java a/src/main/java/com/objecteye/config/RestTemplateConfigure.java new file mode 100644 index 0000000..715530f --- /dev/null +++ a/src/main/java/com/objecteye/config/RestTemplateConfigure.java @@ -0,0 +1,22 @@ +package com.objecteye.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Configuration +public class RestTemplateConfigure { + + @Bean + public RestTemplate restTemplate(RestConfigurationProperties restConfigurationProperties) { + SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory(); + simpleClientHttpRequestFactory.setConnectTimeout(restConfigurationProperties.getConnectionTimeOut()); + simpleClientHttpRequestFactory.setReadTimeout(restConfigurationProperties.getReadTimeOut()); + return new RestTemplate(simpleClientHttpRequestFactory); + } +} diff --git b/src/main/java/com/objecteye/content/VIIDApi.java a/src/main/java/com/objecteye/content/VIIDApi.java new file mode 100644 index 0000000..8242342 --- /dev/null +++ a/src/main/java/com/objecteye/content/VIIDApi.java @@ -0,0 +1,25 @@ +package com.objecteye.content; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +public class VIIDApi { + + public static final String SYSTEM_PREFIX = "/VIID/System"; + /** + * 注册 + */ + public static final String REGISTER = SYSTEM_PREFIX + "/Register"; + /** + * 注销 + */ + public static final String UNREGISTER = SYSTEM_PREFIX + "/UnRegister"; + /** + * 保活 + */ + public static final String KEEPALIVE = SYSTEM_PREFIX + "/Keepalive"; + + private VIIDApi() { + } +} diff --git b/src/main/java/com/objecteye/content/ViidCollectorRedisKey.java a/src/main/java/com/objecteye/content/ViidCollectorRedisKey.java new file mode 100644 index 0000000..50ff7f5 --- /dev/null +++ a/src/main/java/com/objecteye/content/ViidCollectorRedisKey.java @@ -0,0 +1,15 @@ +package com.objecteye.content; + +/** + * @author: liuhaoyu + * @date: 2023/6/30 + */ +public class ViidCollectorRedisKey { + /** + * 设备连接信息缓存 + */ + public static final String DEVICE_CACHE = "vc:dc"; + + private ViidCollectorRedisKey() { + } +} diff --git b/src/main/java/com/objecteye/content/enums/VIIDConfirmStatusTypeEnum.java a/src/main/java/com/objecteye/content/enums/VIIDConfirmStatusTypeEnum.java new file mode 100644 index 0000000..fa8e96f --- /dev/null +++ a/src/main/java/com/objecteye/content/enums/VIIDConfirmStatusTypeEnum.java @@ -0,0 +1,65 @@ +package com.objecteye.content.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Getter +@AllArgsConstructor +public enum VIIDConfirmStatusTypeEnum { + /** + * 正常 + */ + OK("OK"), + /** + * 其他未知错误 + */ + OTHER_ERROR("OtherError"), + /** + * 设备忙 + */ + DEVICE_BUSY("Device Busy"), + /** + * 设备错 + */ + DEVICE_ERROR("Device Error"), + /** + * 无效操作 + */ + invalid_operation("Invalid Operation"), + /** + * XML 格式无效 + */ + INVALID_XML_FORMAT("Invalid XML Format"), + /** + * XML 内容无效 + */ + INVALID_XML_CONTENT("Invalid XML Content"), + /** + * JSON 格式无效 + */ + INVALID_JSON_FORMAT("Invalid JSON Format"), + /** + * JSON 内容无效 + */ + INVALID_JSON_CONTENT("Invalid JSON Content"), + /** + * 系统重启中 + */ + REBOOT("Reboot"), + ; + + private final String desc; + + public static String getDesc(int idx) { + for (VIIDConfirmStatusTypeEnum type : values()) { + if (type.ordinal() == idx) { + return type.desc; + } + } + return ""; + } +} diff --git b/src/main/java/com/objecteye/handle/StartUpAndShutdown.java a/src/main/java/com/objecteye/handle/StartUpAndShutdown.java new file mode 100644 index 0000000..846ecc4 --- /dev/null +++ a/src/main/java/com/objecteye/handle/StartUpAndShutdown.java @@ -0,0 +1,58 @@ +package com.objecteye.handle; + +import com.alibaba.nacos.common.utils.ExceptionUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.objecteye.content.ViidCollectorRedisKey; +import com.objecteye.handle.viid.VIIDCommonHandle; +import com.objecteye.pojo.platform.DeviceRequestParams; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.function.Consumer; + +/** + * @author: liuhaoyu + * @date: 2023/6/30 + */ +@Component +@RequiredArgsConstructor +public class StartUpAndShutdown implements DisposableBean, ApplicationRunner { + private final VIIDCommonHandle viidCommonHandle; + private final StringRedisTemplate stringRedisTemplate; + private final ObjectMapper objectMapper; + + private final Logger logger = LoggerFactory.getLogger(StartUpAndShutdown.class); + + @Override + public void destroy() throws Exception { + allCacheHandle(viidCommonHandle::unregister); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + allCacheHandle(viidCommonHandle::register); + } + + private void allCacheHandle(Consumer func) { + try (Cursor> cursor = stringRedisTemplate.boundHashOps(ViidCollectorRedisKey.DEVICE_CACHE) + .scan(ScanOptions.scanOptions().build())) { + while (cursor.hasNext()) { + Map.Entry entry = cursor.next(); + try { + func.accept(objectMapper.readValue(entry.getValue().toString(), DeviceRequestParams.class)); + } catch (Exception e) { + logger.error(ExceptionUtil.getAllExceptionMsg(e)); + } + } + } + } +} diff --git b/src/main/java/com/objecteye/handle/viid/VIIDCommonHandle.java a/src/main/java/com/objecteye/handle/viid/VIIDCommonHandle.java new file mode 100644 index 0000000..10887b4 --- /dev/null +++ a/src/main/java/com/objecteye/handle/viid/VIIDCommonHandle.java @@ -0,0 +1,108 @@ +package com.objecteye.handle.viid; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.objecteye.config.ApplicationConfigurationProperties; +import com.objecteye.content.VIIDApi; +import com.objecteye.content.enums.VIIDConfirmStatusTypeEnum; +import com.objecteye.pojo.platform.DeviceRequestParams; +import com.objecteye.pojo.viid.common.CommonDeviceIdRequestParams; +import com.objecteye.pojo.viid.common.ResponseStatusResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class VIIDCommonHandle { + private final ObjectMapper objectMapper; + private final RestTemplate restTemplate; + private final ApplicationConfigurationProperties applicationConfigurationProperties; + private static final String CONTENT_TYPE = "application/*+JSON"; + private static final String HTTP = "http://"; + + /** + * 注册 + * + * @param deviceRequestParams 注册目标设备 + * @return 注册结果 + */ + public boolean register(DeviceRequestParams deviceRequestParams) { + return commonDeviceIdPost(deviceRequestParams, VIIDApi.REGISTER); + } + + /** + * 保活 + * + * @param deviceRequestParams 心跳目标设备 + * @return 发送结果 + */ + public boolean keepalive(DeviceRequestParams deviceRequestParams) { + return commonDeviceIdPost(deviceRequestParams, VIIDApi.KEEPALIVE); + } + + /** + * 注销 + * + * @param deviceRequestParams 注销目标设备 + * @return 注销结果 + */ + public boolean unregister(DeviceRequestParams deviceRequestParams) { + return commonDeviceIdPost(deviceRequestParams, VIIDApi.UNREGISTER); + } + + private boolean commonDeviceIdPost(DeviceRequestParams deviceRequestParams, String uri) { + return checkResponse(restTemplate.postForEntity(getAddress(deviceRequestParams) + uri, + buildHttpEntity(buildCommonDeviceIdRequestParam(deviceRequestParams)), String.class)); + } + + private CommonDeviceIdRequestParams buildCommonDeviceIdRequestParam(DeviceRequestParams deviceRequestParams) { + CommonDeviceIdRequestParams commonDeviceIdRequestParams = new CommonDeviceIdRequestParams(); + commonDeviceIdRequestParams.setDeviceId(applicationConfigurationProperties.getPlatformCode()); + return commonDeviceIdRequestParams; + } + + private String getAddress(DeviceRequestParams deviceRequestParams) { + return HTTP + deviceRequestParams.getHost() + ":" + deviceRequestParams.getPort(); + } + + private boolean checkResponse(ResponseEntity responseEntity) { + String body = responseEntity.getBody(); + if (StringUtils.hasText(body)) { + try { + ResponseStatusResponse responseStatusResponse = objectMapper.readValue(body, ResponseStatusResponse.class); + if (null == responseStatusResponse || null == responseStatusResponse.getStatusCode() || + !responseStatusResponse.getStatusCode().equals(VIIDConfirmStatusTypeEnum.OK.ordinal())) { + if (log.isDebugEnabled()) { + log.debug("register failed, {}", body); + } + return false; + } + return true; + } catch (JsonProcessingException e) { + if (log.isDebugEnabled()) { + log.debug("convert failed, {}", body); + } + return false; + } + } + return false; + } + + private HttpEntity buildHttpEntity(T t) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE); + + return new HttpEntity<>(t, httpHeaders); + } +} diff --git b/src/main/java/com/objecteye/pojo/platform/DeviceRequestParams.java a/src/main/java/com/objecteye/pojo/platform/DeviceRequestParams.java new file mode 100644 index 0000000..036c7ef --- /dev/null +++ a/src/main/java/com/objecteye/pojo/platform/DeviceRequestParams.java @@ -0,0 +1,25 @@ +package com.objecteye.pojo.platform; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +public class DeviceRequestParams implements Serializable { + private static final long serialVersionUID = -7801549061937667804L; + + private String deviceId; + + private String host; + + private String port; + + private String accessKey; + + private String securityKey; + +} diff --git b/src/main/java/com/objecteye/pojo/viid/common/CommonDeviceIdRequestParams.java a/src/main/java/com/objecteye/pojo/viid/common/CommonDeviceIdRequestParams.java new file mode 100644 index 0000000..61dba35 --- /dev/null +++ a/src/main/java/com/objecteye/pojo/viid/common/CommonDeviceIdRequestParams.java @@ -0,0 +1,18 @@ +package com.objecteye.pojo.viid.common; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +public class CommonDeviceIdRequestParams implements Serializable { + private static final long serialVersionUID = 1L; + + @JsonProperty("DeviceID") + private String deviceId; +} diff --git b/src/main/java/com/objecteye/pojo/viid/common/ResponseStatusResponse.java a/src/main/java/com/objecteye/pojo/viid/common/ResponseStatusResponse.java new file mode 100644 index 0000000..ecc9f4a --- /dev/null +++ a/src/main/java/com/objecteye/pojo/viid/common/ResponseStatusResponse.java @@ -0,0 +1,31 @@ +package com.objecteye.pojo.viid.common; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +public class ResponseStatusResponse implements Serializable { + + private static final long serialVersionUID = 1L; + + @JsonProperty("RequestURL") + private String requestUrl; + + @JsonProperty("StatusCode") + private Integer statusCode; + + @JsonProperty("StatusString") + private String statusString; + + @JsonProperty("ID") + private String id; + + @JsonProperty("LocalTime") + private String localTime; +} diff --git b/src/main/java/com/objecteye/pojo/viid/subscribe/Subscribe.java a/src/main/java/com/objecteye/pojo/viid/subscribe/Subscribe.java new file mode 100644 index 0000000..47ed368 --- /dev/null +++ a/src/main/java/com/objecteye/pojo/viid/subscribe/Subscribe.java @@ -0,0 +1,98 @@ +package com.objecteye.pojo.viid.subscribe; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author: liuhaoyu + * @date: 2023/6/28 + */ +@Data +public class Subscribe implements Serializable { + private static final long serialVersionUID = 6385076997369719156L; + /** + * 订阅标识符 + * 数据共享接口调用时由VIID生成, 取消订阅时必选 + */ + @JsonProperty("SubscribeID") + private String subscribeId; + /** + * 订阅标题 + *

+ * 订阅时必填 + */ + @JsonProperty("Title") + private String title; + /** + * 订阅类别 + *

+ * 订阅时必填,可多个用英文逗号隔开 + */ + @JsonProperty("SubscribeDetail") + private String subscribeDetail; + /** + * 订阅资源路径 + *

+ * 资源路径URI(卡口ID, 设备ID, 采集内容ID, 案件ID, 目标视图库ID, 行政区编号2/4/6位等), 支持单个和批量, 订阅时必填 + */ + @JsonProperty("ResourceURI") + private String resourceUri; + /** + * 申请人 + *

+ * 必填 + */ + @JsonProperty("ApplicantName") + private String applicantName; + /** + * 申请单位 + *

+ * required + */ + @JsonProperty("ApplicantOrg") + private String applicantOrg; + /** + * 开始时间 + *

+ * required + */ + @JsonProperty("BeginTime") + private String beginTime; + /** + * 结束时间 + *

+ * required + */ + @JsonProperty("EndTime") + private String endTime; + /** + * 信息接收地址 string(256) + *

+ * e.g. http://ip:port/uri + */ + @JsonProperty("ReceiveAddr") + private String receiveAddr; + /** + * 信息上报间隔时间 + *

+ * required, 单位: s, <=0表示不限制 + */ + @JsonProperty("ReportInterval") + private Integer reportInterval; + /** + * 操作类型 + *

+ * 0: 订阅, 1: 取消订阅 + */ + @JsonProperty("OperateType") + private Integer operateType; + /** + * 订阅执行状态 + *

+ * 0: 订阅中, 1: 已取消, 2: 订阅到期, 9: 未订阅. 只读字段 + */ + @JsonProperty("SubscribeStatus") + private Integer subscribeStatus; +} diff --git b/src/main/resources/application.yml a/src/main/resources/application.yml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ a/src/main/resources/application.yml