提交 c216be5e 作者: 方治民

feat: 重构空间信息字段采用 4326 投影方式、围栏/区域进出标记、增加 @XxlJob 异常消息日志打印、增加围栏报警记录表创建

上级 8e6f4263
......@@ -4,10 +4,7 @@ package com.yiring.app.domain.broadcast;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
......@@ -52,6 +49,7 @@ public class Broadcast implements Serializable {
String broadcastName;
@Comment("坐标点信息")
@Column(columnDefinition = "geometry(Point,4326)")
Point point;
@Comment("播报设备地址")
......
......@@ -59,7 +59,7 @@ public class District implements Serializable {
@Comment("区域信息")
@Type(type = "jts_geometry")
@Column(columnDefinition = "geometry")
@Column(columnDefinition = "geometry(Geometry,4326)")
private Geometry geometry;
@Comment("创建时间")
......
......@@ -3,6 +3,7 @@ package com.yiring.app.domain.district;
import java.io.Serializable;
import java.util.List;
import org.locationtech.jts.geom.Geometry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
......@@ -36,4 +37,12 @@ public interface DistrictRepository extends JpaRepository<District, Serializable
*/
@Query("SELECT d FROM District d WHERE d.name like %?1%")
List<District> findLikeName(String name);
/**
* 查询空间信息在区域内的区域信息
* @param geometry 空间信息
* @return 区域信息
*/
@Query(value = "select d.* from bs_district d where st_contains(d.geometry, :geometry)", nativeQuery = true)
List<District> findByGeometryContains(Geometry geometry);
}
......@@ -80,6 +80,7 @@ public class LocationBeacon extends BasicEntity implements Serializable {
Double distance;
@Comment("坐标点信息")
@Column(columnDefinition = "geometry(Point,4326)")
Point point;
@FieldMapping
......
......@@ -51,7 +51,7 @@ public class LocationFence extends BasicEntity implements Serializable {
private String mapName;
@Comment("摄像头")
@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "video_id")
private Video video;
......@@ -60,7 +60,7 @@ public class LocationFence extends BasicEntity implements Serializable {
@Comment("空间信息")
@Type(type = "jts_geometry")
@Column(columnDefinition = "geometry")
@Column(columnDefinition = "geometry(Geometry,4326)")
private Geometry geometry;
@Comment("滞留时间(秒)")
......@@ -87,6 +87,12 @@ public class LocationFence extends BasicEntity implements Serializable {
@OneToMany(mappedBy = "fence")
@ToString.Exclude
private Set<LocationFenceRule> rules = new HashSet<>(0);
@ToString.Exclude
@Comment("围栏中的标签集合")
@Builder.Default
@ManyToMany(fetch = FetchType.LAZY)
Set<LocationTag> tags = new HashSet<>(0);
/*@SuppressWarnings({ "unused" })
public enum Mode {
NORMAL("常规区域"),
......
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.location;
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
import com.yiring.app.domain.alarm.AlarmType;
import com.yiring.auth.domain.user.User;
import com.yiring.common.domain.BasicEntity;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.TypeDef;
import org.locationtech.jts.geom.Point;
/**
* 围栏报警记录
*
* @author Jim
* @version 0.1
* 2022/5/12 21:33
*/
@Getter
@Setter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@FieldDefaults(level = AccessLevel.PRIVATE)
@Entity
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
@Table(name = "BS_LOCATION_FENCE_ALARM")
@Comment("围栏报警记录")
public class LocationFenceAlarm extends BasicEntity implements Serializable {
@Serial
private static final long serialVersionUID = 2984248537199016912L;
@Comment("围栏")
@ManyToOne(fetch = FetchType.LAZY)
LocationFence fence;
@Comment("触警位置")
@Column(columnDefinition = "geometry(Point,4326)")
Point point;
@Comment("地图编号")
Long areaId;
@Comment("触警人员")
@ManyToOne(fetch = FetchType.LAZY)
User user;
@Comment("触警标签")
@ManyToOne(fetch = FetchType.LAZY)
LocationTag tag;
@Comment("报警开始时间")
LocalDateTime startTime;
@Comment("报警结束时间")
LocalDateTime endTime;
@Comment("报警类型")
@ManyToOne(fetch = FetchType.LAZY)
AlarmType type;
@Comment("状态")
@Enumerated(EnumType.STRING)
Status status;
// 推送记录集合(含接收状态)
// TODO
@SuppressWarnings({ "unused" })
public enum Status {
ING("进行中"),
STOP("停止");
final String text;
Status(String text) {
this.text = text;
}
public String text() {
return this.text;
}
}
}
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.location;
import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
/**
* @author Jim
* @version 0.1
* 2022/5/12 21:34
*/
@Repository
public interface LocationFenceAlarmRepository
extends JpaRepository<LocationFenceAlarm, Serializable>, JpaSpecificationExecutor<LocationFenceAlarm> {}
......@@ -3,6 +3,7 @@ package com.yiring.app.domain.location;
import java.io.Serializable;
import java.util.List;
import org.locationtech.jts.geom.Geometry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
......@@ -37,4 +38,12 @@ public interface LocationFenceRepository
*/
@Query("SELECT f FROM LocationFence f WHERE name like %?1% AND deleted = false")
List<LocationFence> findLikeName(String name);
/**
* 查询空间信息在围栏内的围栏信息
* @param geometry 空间信息
* @return 围栏信息
*/
@Query(value = "select f.* from bs_location_fence f where st_contains(f.geometry, :geometry)", nativeQuery = true)
List<LocationFence> findByGeometryContains(Geometry geometry);
}
......@@ -9,7 +9,7 @@ import org.springframework.stereotype.Repository;
/**
* @author tml
* @version 1.0
* @date 2022/4/29 11:40
* 2022/4/29 11:40
*/
@Repository
public interface LocationFenceRuleRepository
......
......@@ -71,6 +71,7 @@ public class LocationLog implements Serializable {
User.Status status;
@Comment("坐标点信息")
@Column(columnDefinition = "geometry(Point,4326)")
Point point;
@Comment("信标集合")
......@@ -83,6 +84,11 @@ public class LocationLog implements Serializable {
@Column(columnDefinition = "jsonb")
JSONArray fences;
@Comment("区域集合")
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
JSONArray districts;
@Comment("静止/运动")
Boolean silent;
......
......@@ -103,6 +103,7 @@ public class LocationTag extends BasicEntity implements Serializable {
Integer category;
@Comment("最后定位坐标")
@Column(columnDefinition = "geometry(Point,4326)")
Point point;
@SuppressWarnings({ "unused" })
......
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.location;
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
import com.yiring.common.domain.BasicEntity;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.TypeDef;
/**
* 定位进出记录
*
* @author Jim
* @version 0.1
* 2022/5/12 17:54
*/
@Getter
@Setter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@FieldDefaults(level = AccessLevel.PRIVATE)
@Entity
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
@Table(name = "BS_LOCATION_TURNOVER", indexes = { @Index(columnList = "type"), @Index(columnList = "sourceId") })
@Comment("定位进出记录")
public class LocationTurnover extends BasicEntity implements Serializable {
@Serial
private static final long serialVersionUID = 887764448464587364L;
@Comment("进出区域/围栏表主键")
Long sourceId;
@Comment("类型")
@Enumerated(EnumType.STRING)
Type type;
@Comment("定位标签")
@ManyToOne(fetch = FetchType.LAZY)
LocationTag tag;
@Comment("定位时间")
LocalDateTime time;
@Comment("进入")
Boolean enter;
@Comment("是否为最新状态")
Boolean isLatest;
@SuppressWarnings({ "unused" })
public enum Type {
FENCE("围栏"),
DISTRICT("区域");
final String text;
Type(String text) {
this.text = text;
}
public String text() {
return this.text;
}
}
}
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.location;
import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
/**
* @author Jim
* @version 0.1
* 2022/5/12 18:13
*/
@Repository
public interface LocationTurnoverRepository
extends JpaRepository<LocationTurnover, Serializable>, JpaSpecificationExecutor<LocationTurnover> {}
......@@ -48,7 +48,7 @@ public class AccidentSpot extends BasicEntity implements Serializable {
@Comment("空间信息")
@Type(type = "jts_geometry")
@Column(columnDefinition = "geometry")
@Column(columnDefinition = "geometry(Geometry,4326)")
private Geometry geometry;
@Comment("摄像头")
......
......@@ -48,7 +48,7 @@ public class EvacuationZone extends BasicEntity implements Serializable {
@Comment("空间信息")
@Type(type = "jts_geometry")
@Column(columnDefinition = "geometry")
@Column(columnDefinition = "geometry(Geometry,4326)")
private Geometry geometry;
@Comment("摄像头")
......
......@@ -44,6 +44,7 @@ public class Video implements Serializable {
Long id;
@Comment("坐标点信息")
@Column(columnDefinition = "geometry(Point,4326)")
Point point;
@Comment("标识")
......
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.job;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.yiring.app.domain.location.LocationLog;
import com.yiring.app.domain.location.LocationLogRepository;
......@@ -29,7 +31,7 @@ import org.springframework.stereotype.Component;
@SuppressWarnings("unused")
@Slf4j
@Component
public class MockZyMessageJob {
public class MockPositionMessageJob {
@Resource
RabbitTemplate rabbitTemplate;
......@@ -40,19 +42,33 @@ public class MockZyMessageJob {
@Resource
ZyConfigProperties.ZyConfigRabbitmq rabbitmq;
@XxlJob("MockZyMessageHandler")
public void mockMessageHandler() {
log.info("MockZyMessageHandler: {}", LocalDateTime.now().format(DateFormatter.DATE_TIME));
@XxlJob("MockPositionHandler")
public void MockPositionHandler() {
JSONObject extra = toJSON(XxlJobHelper.getJobParam());
log.info("[Mock] Position: {}, {}", mockPositionMessage(extra), extra);
}
@XxlJob("MockLowPowerHandler")
public void MockLowPowerHandler() {
JSONObject extra = toJSON(XxlJobHelper.getJobParam());
log.info("[Mock] LowPower: {}, {}", mockLowPowerMessage(extra), extra);
}
log.info("[Mock] Position: {}", mockPositionMessage());
log.info("[Mock] LowPower: {}", mockLowPowerMessage());
log.info("[Mock] DeviceStatus: {}", mockDeviceStatusMessage());
log.info("[Mock] KeyWarning: {}", mockKeyWarningMessage());
@XxlJob("MockDeviceStatusHandler")
public void MockDeviceStatusHandler() {
JSONObject extra = toJSON(XxlJobHelper.getJobParam());
log.info("[Mock] DeviceStatus: {}, {}", mockDeviceStatusMessage(extra), extra);
}
@XxlJob("MockKeyWarningHandler")
public void MockKeyWarningHandler() {
JSONObject extra = toJSON(XxlJobHelper.getJobParam());
log.info("[Mock] KeyWarning: {}, {}", mockKeyWarningMessage(extra), extra);
}
@XxlJob("QueryMessageHandler")
public void queryMessageHandler() {
log.info("QueryZyMessageHandler: {}", LocalDateTime.now().format(DateFormatter.DATE_TIME));
log.info("QueryMessageHandler: {}", LocalDateTime.now().format(DateFormatter.DATE_TIME));
try {
Specification<LocationLog> spec = (root, query, cb) -> {
......@@ -62,17 +78,31 @@ public class MockZyMessageJob {
};
List<LocationLog> logs = locationLogRepository.findAll(spec);
log.info("QueryZyMessageHandler: {}", logs.size());
log.info("log size: {}", logs.size());
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
public JSONObject send(JSONObject body) {
if (rabbitmq.isMock()) {
rabbitTemplate.convertAndSend(rabbitmq.getQueueName(), body.toJSONString());
}
return body;
}
public JSONObject toJSON(String params) {
JSONObject extra = new JSONObject();
try {
extra = JSON.parseObject(params);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return extra == null ? new JSONObject() : extra;
}
private String mockTag() {
return "BTT33333331";
}
......@@ -81,7 +111,7 @@ public class MockZyMessageJob {
return 10019L;
}
private JSONObject mockPositionMessage() {
private JSONObject mockPositionMessage(JSONObject extra) {
// 随机生成一个坐标点
Point point = GeoUtils.randomPoint(GeoUtils.defaultBounds(), 0);
......@@ -98,6 +128,7 @@ public class MockZyMessageJob {
params.put("volt", 3650);
params.put("voltUnit", "mV");
params.put("floor", 1);
params.putAll(extra);
JSONObject body = new JSONObject();
body.put("method", "position");
......@@ -105,11 +136,12 @@ public class MockZyMessageJob {
return send(body);
}
private JSONObject mockLowPowerMessage() {
private JSONObject mockLowPowerMessage(JSONObject extra) {
JSONObject params = new JSONObject();
params.put("tagId", mockTag());
params.put("volt", 3650);
params.put("voltUnit", "mV");
params.putAll(extra);
JSONObject body = new JSONObject();
body.put("method", "lowPower");
......@@ -117,7 +149,7 @@ public class MockZyMessageJob {
return send(body);
}
private JSONObject mockDeviceStatusMessage() {
private JSONObject mockDeviceStatusMessage(JSONObject extra) {
JSONObject params = new JSONObject();
params.put("deviceId", mockTag());
params.put("areaId", mockAreaId());
......@@ -125,6 +157,7 @@ public class MockZyMessageJob {
params.put("volt", 3650);
params.put("field_21", "mV");
params.put("updateTime", System.currentTimeMillis());
params.putAll(extra);
JSONObject body = new JSONObject();
body.put("method", "deviceStatus");
......@@ -132,7 +165,7 @@ public class MockZyMessageJob {
return send(body);
}
private JSONObject mockKeyWarningMessage() {
private JSONObject mockKeyWarningMessage(JSONObject extra) {
JSONObject params = new JSONObject();
params.put("tagId", mockTag());
params.put("entityId", "1522770547178475520");
......@@ -142,6 +175,7 @@ public class MockZyMessageJob {
params.put("y", 100);
params.put("z", 0);
params.put("floor", 1);
params.putAll(extra);
JSONObject body = new JSONObject();
body.put("method", "keyWarning");
......
......@@ -34,13 +34,13 @@ public class PositionMessageHandler implements ChannelAwareMessageListener {
@Resource
PositionMessageService positionMessageService;
@Times
@Times("Position System Message Handler")
@Override
public void onMessage(Message message, Channel channel) throws IOException {
// 消费消息
positionMessageService.consume(new String(message.getBody(), StandardCharsets.UTF_8));
// 手动确认消息已收到
assert channel != null;
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// 消费消息
positionMessageService.consume(new String(message.getBody(), StandardCharsets.UTF_8));
}
}
......@@ -4,6 +4,8 @@ package com.yiring.app.service.message.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yiring.app.domain.district.District;
import com.yiring.app.domain.district.DistrictRepository;
import com.yiring.app.domain.location.*;
import com.yiring.app.domain.log.ZyRealtimeLog;
import com.yiring.app.domain.log.ZyRealtimeLogRepository;
......@@ -14,14 +16,15 @@ import com.yiring.common.annotation.Times;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Resource;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Point;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.*;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -46,12 +49,24 @@ public class PositionMessageServiceImpl implements PositionMessageService {
LocationLogRepository locationLogRepository;
@Resource
LocationFenceRepository locationFenceRepository;
@Resource
DistrictRepository districtRepository;
@Resource
LocationFenceAlarmRepository locationFenceAlarmRepository;
@Resource
LocationTurnoverRepository locationTurnoverRepository;
@Resource
SimpMessagingTemplate simpMessagingTemplate;
@Resource
ZyRealtimeLogRepository zyRealtimeLogRepository;
@Times
@Times("Message Consume")
@Override
public void consume(String message) {
// 将消息转换成 JSON 格式
......@@ -118,13 +133,13 @@ public class PositionMessageServiceImpl implements PositionMessageService {
LocationLog locationLog = LocationLog
.builder()
.id(id)
.raw(data)
.locationTime(locationTime)
.areaId(data.getLong("areaId"))
.floor(data.getString("floor"))
.silent(data.getBoolean("silent"))
.volt(data.getInteger("volt"))
.voltUnit(data.getString("voltUnit"))
.raw(data)
.build();
// 获取定位卡当前绑定的用户
......@@ -145,16 +160,112 @@ public class PositionMessageServiceImpl implements PositionMessageService {
locationLog.setPoint(point);
// 定位信标
Set<String> codes = Arrays
Set<String> beaconCodes = Arrays
.stream(data.getString("beacons").split(","))
.map(beacon -> beacon.replaceAll("\\(.*\\)", ""))
.collect(Collectors.toSet());
locationLog.setBeacons(new JSONArray().fluentAddAll(codes));
locationLog.setBeacons(new JSONArray().fluentAddAll(beaconCodes));
// TODO
// 并计算出入标记(围栏、区域)
// 计算出入标记(围栏、区域)
List<LocationTurnover> turnovers = new ArrayList<>();
// 查询定位在围栏内的围栏信息
List<LocationFence> fences = locationFenceRepository.findByGeometryContains(point);
Set<Long> fenceIds = fences.stream().map(LocationFence::getId).collect(Collectors.toSet());
locationLog.setFences(new JSONArray().fluentAddAll(fenceIds));
// 计算围栏进出
for (LocationFence fence : fences) {
// 查询当前围栏的防抖时间内的所有定位记录
List<LocationLog> logs = findByTagTimeIdAndThreshold(id, fence.getThreshold());
List<JSONArray> list = logs.stream().map(LocationLog::getFences).toList();
if (list.isEmpty()) {
continue;
}
// 检查是否进入区域
Boolean isEnter = checkEnter(list, fence.getId());
if (isEnter != null) {
// 检查是否为重复进入/退出
Optional<LocationTurnover> enter = findRepeatTurnoverRecord(
fence.getId(),
LocationTurnover.Type.FENCE,
id.getTag(),
isEnter
);
if (enter.isEmpty()) {
// 尝试将上一次记录标记为非最新记录
trySetPrevTurnoverExpired(fence.getId(), LocationTurnover.Type.FENCE, id.getTag());
// 写入数据
// 当前标签进入/离开围栏的动作标记为最新记录
LocationTurnover turnover = LocationTurnover
.builder()
.enter(isEnter)
.type(LocationTurnover.Type.FENCE)
.sourceId(fence.getId())
.tag(id.getTag())
.time(id.getTime())
.isLatest(true)
.build();
turnovers.add(turnover);
// 更新围栏内的标签
Set<LocationTag> tags = fence.getTags();
if (Boolean.TRUE.equals(isEnter)) {
tags.add(id.getTag());
} else {
tags.remove(id.getTag());
}
fence.setTags(tags);
// 有人员进出围栏,需要检查是否触发围栏报警规则
// TODO: 通过定时任务调度异步实现,提高定位消息消费能力
// 1. 判断是否触发围栏报警规则,触发则记录报警记录,同时记录报警记录触发所需推送的消息
// 2. 同时进行 WebSocket 消息推送
}
}
}
// 查询定位在区域内的区域信息
List<District> districts = districtRepository.findByGeometryContains(point);
Set<Long> districtIds = districts.stream().map(District::getId).collect(Collectors.toSet());
locationLog.setDistricts(new JSONArray().fluentAddAll(districtIds));
// 计算区域进出
for (District district : districts) {
// 查询当前区域的防抖时间内的所有定位记录
List<LocationLog> logs = findByTagTimeIdAndThreshold(id, district.getDebouncingDuration());
List<JSONArray> list = logs.stream().map(LocationLog::getDistricts).toList();
if (list.isEmpty()) {
continue;
}
// 检查是否进入区域
Boolean isEnter = checkEnter(list, district.getId());
if (isEnter != null) {
// 检查是否为重复进入/退出
Optional<LocationTurnover> enter = findRepeatTurnoverRecord(
district.getId(),
LocationTurnover.Type.DISTRICT,
id.getTag(),
isEnter
);
if (enter.isEmpty()) {
// 尝试将上一次记录标记为非最新记录
trySetPrevTurnoverExpired(district.getId(), LocationTurnover.Type.DISTRICT, id.getTag());
// 当前标签进入/离开围栏的动作标记为最新记录
LocationTurnover turnover = LocationTurnover
.builder()
.enter(isEnter)
.type(LocationTurnover.Type.DISTRICT)
.sourceId(district.getId())
.tag(id.getTag())
.time(id.getTime())
.build();
turnovers.add(turnover);
}
}
}
// 写入定位数据
locationLogRepository.saveAndFlush(locationLog);
// 更新定位标签卡状态信息
......@@ -164,11 +275,109 @@ public class PositionMessageServiceImpl implements PositionMessageService {
tag.setSilent(locationLog.getSilent());
locationTagRepository.save(tag);
// 更新围栏记录的标签数据
locationFenceRepository.saveAll(fences);
// 写入围栏/区域进出记录
locationTurnoverRepository.saveAll(turnovers);
// WebSocket 推送定位消息
// 消息内容需要确定 TODO
simpMessagingTemplate.convertAndSend("/topic/position", "{}");
// TODO
// 判断围栏告警是否触发,触发写入告警记录,并推送消息
JSONObject message = new JSONObject();
message.put("type", "location");
message.put("time", id.getTime());
message.put("tagId", id.getTag().getId());
message.put("tagCode", id.getTag().getCode());
message.put("entityId", locationLog.getPoint());
message.put("point", locationLog.getPoint());
simpMessagingTemplate.convertAndSend("/topic/position", message.toJSONString());
}
/**
* 根据定位日志 ID 和防抖时间查询定位日志
* @param id 定位日志 ID
* @param threshold 防抖时间
* @return 定位日志集合
*/
public List<LocationLog> findByTagTimeIdAndThreshold(TagTimeId id, int threshold) {
return locationLogRepository.findAll((root, query, cb) -> {
Predicate predicate = cb.conjunction();
List<Expression<Boolean>> expressions = predicate.getExpressions();
expressions.add(cb.equal(root.get("id").get("tag"), id.getTag()));
expressions.add(cb.greaterThanOrEqualTo(root.get("id").get("time"), id.getTime().minusSeconds(threshold)));
expressions.add(cb.lessThanOrEqualTo(root.get("id").get("time"), id.getTime()));
return predicate;
});
}
/**
* 根据防抖期间进入的区域/围栏以及新产生的围栏/区域记录,判断是否进入
* @param array 区域/围栏集合
* @param id 区域/围栏 ID
* @return true 进入,false 退出,null 未发生变化
*/
public Boolean checkEnter(List<JSONArray> array, Long id) {
long count = array.stream().filter(ids -> ids != null && ids.contains(id)).count();
Boolean isEnter = null;
if (count == array.size()) {
isEnter = true;
} else if (count == 0) {
isEnter = false;
}
return isEnter;
}
/**
* 查找
* @param sourceId 区域/围栏 ID
* @param type 类型(区域/围栏)
* @param tag 定位标签
* @param enter true 进入,false 退出
* @return true 重复进入,false 非重复进入
*/
public Optional<LocationTurnover> findRepeatTurnoverRecord(
Long sourceId,
LocationTurnover.Type type,
LocationTag tag,
Boolean enter
) {
Pageable pageable = PageRequest.of(0, 1, Sort.by(Sort.Order.desc(LocationTurnover.Fields.time)));
Page<LocationTurnover> page = locationTurnoverRepository.findAll(
(root, query, cb) -> {
Predicate predicate = cb.conjunction();
List<Expression<Boolean>> expressions = predicate.getExpressions();
expressions.add(cb.equal(root.get(LocationTurnover.Fields.sourceId), sourceId));
expressions.add(cb.equal(root.get(LocationTurnover.Fields.type), type));
expressions.add(cb.equal(root.get(LocationTurnover.Fields.tag), tag));
return predicate;
},
pageable
);
if (page.getTotalElements() > 0) {
// 检查是否重复进入
Stream<LocationTurnover> stream = page.get();
if (enter == null || stream.anyMatch(turnover -> turnover.getEnter() == enter)) {
return stream.findFirst();
}
}
return Optional.empty();
}
/**
* 尝试设置上一个进出记录为过期标识
* @param sourceId 区域/围栏 ID
* @param type 类型(区域/围栏)
* @param tag 定位标签
*/
public void trySetPrevTurnoverExpired(Long sourceId, LocationTurnover.Type type, LocationTag tag) {
Optional<LocationTurnover> record = findRepeatTurnoverRecord(sourceId, type, tag, null);
if (record.isPresent()) {
LocationTurnover turnoverRecord = record.get();
turnoverRecord.setIsLatest(false);
locationTurnoverRepository.save(turnoverRecord);
}
}
/**
......
......@@ -22,6 +22,8 @@ public class GeoUtils {
public final GeometryFactory factory = new GeometryFactory();
public final int DEFAULT_SRID = 4326;
/**
* 创建点
*
......@@ -30,7 +32,7 @@ public class GeoUtils {
* @return 点
*/
public Point createPoint(double lon, double lat) {
return factory.createPoint(new Coordinate(lon, lat));
return createPoint(lon, lat, 0);
}
/**
......@@ -41,7 +43,9 @@ public class GeoUtils {
* @return 点
*/
public Point createPoint(double lon, double lat, double alt) {
return factory.createPoint(new Coordinate(lon, lat, alt));
Point point = factory.createPoint(new Coordinate(lon, lat, alt));
point.setSRID(DEFAULT_SRID);
return point;
}
/**
......@@ -70,13 +74,12 @@ public class GeoUtils {
y
);
// 构建经纬度坐标信息
Coordinate coordinate = new Coordinate(
// 构建一个坐标点
return createPoint(
result.getDoubleValue("lon"),
result.getDoubleValue("lat"),
root.getDoubleValue("altitude") + z
result.getDoubleValue("altitude") + z
);
return factory.createPoint(coordinate);
}
/**
......@@ -110,6 +113,6 @@ public class GeoUtils {
public Point randomPoint(double minX, double minY, double maxX, double maxY, double z) {
double x = minX + (maxX - minX) * Math.random();
double y = minY + (maxY - minY) * Math.random();
return factory.createPoint(new Coordinate(x, y, z));
return createPoint(x, y, z);
}
}
......@@ -83,22 +83,22 @@ xxl:
logging:
level:
# sql bind parameter
# org.hibernate.type.descriptor.sql.BasicBinder: trace
org.hibernate.type.descriptor.sql.BasicBinder: error
org.hibernate.type.descriptor.sql.BasicBinder: trace
# org.hibernate.type.descriptor.sql.BasicBinder: error
# 真源定位系统相关配置
zy-config:
host: project.yz-online.com
# RabbitMQ 订阅配置
rabbitmq:
mock: false
mock: true
enabled: true
host: ${zy-config.host}
port: 672
username: admin
password: admin
virtual-host: /
queue-name: tenant_msg_${zy-config.open.client-secret}_${zy-config.open.client-id}
queue-name: tenant_msg_${zy-config.open.client-secret}_${zy-config.open.client-id}_mock
# 开放接口信息配置
open:
api: http://${zy-config.host}:789/positionApi
......
/* (C) 2021 YiRing, Inc. */
package com.yiring.common.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* XxlJob 注解切面
*
* @author ifzm
* @version 0.1
*/
@Slf4j
@Aspect
@Component
public class XxlJobAspect {
@Pointcut("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")
public void log() {}
@Around("log()")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
return point.proceed();
} catch (Exception e) {
log.error("XxlJob Execute Error: " + e.getMessage(), e);
throw e;
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论