提交 3c00542a 作者: 方治民

feat: 简化信标、围栏存储表字段、新增真源实时消息日志存储、README 新增 JTS 引用

上级 f360f80d
......@@ -33,3 +33,9 @@
- [ ] XXL-JOB 定时任务模块
- [x] @Convert 处理 Raw JSON 数据格式转换
- [ ] 扩展 PostgresDialect 实现时序查询函数
---
> 引用
1. [JTS](https://github.com/locationtech/jts)
2. [GeoTools](http://docs.geotools.org/)
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.alarm;
/**
* 标签报警
*
* @author Jim
* @version 0.1
* 2022/4/25 15:38
*/
public class TagAlarm {}
......@@ -2,20 +2,21 @@
package com.yiring.app.domain.location;
import com.yiring.common.annotation.FieldMapping;
import com.yiring.common.domain.BasicEntity;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
import org.hibernate.snowflake.SnowflakeId;
import org.locationtech.jts.geom.Point;
/**
......@@ -32,7 +33,7 @@ import org.locationtech.jts.geom.Point;
@Getter
@Setter
@ToString
@Builder
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
......@@ -43,17 +44,11 @@ import org.locationtech.jts.geom.Point;
indexes = { @Index(columnList = "linkId"), @Index(columnList = "code", unique = true) }
)
@Comment("定位信标")
public class LocationBeacon implements Serializable {
public class LocationBeacon extends BasicEntity implements Serializable {
@Serial
private static final long serialVersionUID = 5419734189897829250L;
@Comment("主键")
@Id
@GeneratedValue(generator = SnowflakeId.GENERATOR)
@GenericGenerator(name = SnowflakeId.GENERATOR, strategy = SnowflakeId.Strategy.LONG)
Long id;
/**
* 数据来源于【真源人员定位系统 - 定位信标】
* 作用: 用于双向联动进行数据同步
......@@ -99,19 +94,18 @@ public class LocationBeacon implements Serializable {
@Comment("电量单位")
String voltUnit;
@FieldMapping(value = "time", desc = "更新时间戳", type = Long.class)
@Comment("更新时间")
LocalDateTime updateTime;
@Comment("创建时间")
LocalDateTime createTime;
@Comment("围栏集合")
@Builder.Default
@ManyToMany(mappedBy = "beacons")
@ToString.Exclude
Set<LocationFence> fences = new HashSet<>(0);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
LocationBeacon that = (LocationBeacon) o;
return id != null && Objects.equals(id, that.id);
return getId() != null && Objects.equals(getId(), that.getId());
}
@Override
......
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.location;
import java.io.Serializable;
import java.util.Set;
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/4/24 14:19
*/
@Repository
public interface LocationBeaconRepository
extends JpaRepository<LocationBeacon, Serializable>, JpaSpecificationExecutor<LocationBeacon> {
/**
* 根据编号集合查询
* @param codes 编号集合
* @return 查询结果
*/
Set<LocationBeacon> findByCodeIn(Set<String> codes);
}
......@@ -2,19 +2,18 @@
package com.yiring.app.domain.location;
import com.yiring.common.annotation.FieldMapping;
import com.yiring.common.domain.BasicEntity;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
import org.hibernate.snowflake.SnowflakeId;
import org.locationtech.jts.geom.Geometry;
/**
......@@ -29,7 +28,7 @@ import org.locationtech.jts.geom.Geometry;
@Getter
@Setter
@ToString
@Builder
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
......@@ -37,25 +36,11 @@ import org.locationtech.jts.geom.Geometry;
@Entity
@Table(name = "BS_LOCATION_FENCE", indexes = { @Index(columnList = "linkId"), @Index(columnList = "mode") })
@Comment("围栏")
public class LocationFence implements Serializable {
public class LocationFence extends BasicEntity implements Serializable {
@Serial
private static final long serialVersionUID = 4155868702188991300L;
@Comment("主键")
@Id
@GeneratedValue(generator = SnowflakeId.GENERATOR)
@GenericGenerator(name = SnowflakeId.GENERATOR, strategy = SnowflakeId.Strategy.LONG)
Long id;
/**
* 数据来源于【真源人员定位系统 - 电子围栏】
* 作用: 用于双向联动进行数据同步
*/
@FieldMapping("id")
@Comment("外链主键")
Long linkId;
@Comment("地图 ID")
Long areaId;
......@@ -64,21 +49,11 @@ public class LocationFence implements Serializable {
@Enumerated(EnumType.STRING)
Mode mode;
@Comment("坐标信息(x,y,r)")
@Column(columnDefinition = "JSON")
String shape;
@Comment("形状信息(circle: 圆形, polygon: 多边形)")
String shapeType;
@Comment("空间信息")
@Type(type = "jts_geometry")
@Column(columnDefinition = "geometry")
Geometry geometry;
@Comment("半径,米/单位(圆形围栏)")
Double radius;
@Comment("超时时间(秒)")
Integer overtime;
......@@ -87,17 +62,20 @@ public class LocationFence implements Serializable {
@Comment("信标集合")
@Builder.Default
@ManyToMany
@ManyToMany(mappedBy = "fences")
@ToString.Exclude
Set<LocationBeacon> beacons = new HashSet<>(0);
@Comment("创建时间")
LocalDateTime createTime;
@Comment("围栏规则集合")
@Builder.Default
@OneToMany(mappedBy = "fence")
@ToString.Exclude
Set<LocationFenceRule> rules = new HashSet<>(0);
@SuppressWarnings({ "unused" })
public enum Mode {
NORMAL("标准"),
DANGER("危险区域");
NORMAL("常规区域"),
DANGER("危险区域");
@Getter
final String text;
......
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.location;
import com.yiring.auth.domain.user.User;
import com.yiring.common.annotation.FieldMapping;
import com.yiring.common.domain.BasicEntity;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalTime;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.snowflake.SnowflakeId;
/**
* 围栏规则
......@@ -36,67 +33,18 @@ import org.hibernate.snowflake.SnowflakeId;
@Entity
@Table(name = "BS_LOCATION_FENCE_RULE")
@Comment("围栏规则")
public class LocationFenceRule implements Serializable {
public class LocationFenceRule extends BasicEntity implements Serializable {
@Serial
private static final long serialVersionUID = -6683465582430417205L;
@Comment("主键")
@Id
@GeneratedValue(generator = SnowflakeId.GENERATOR)
@GenericGenerator(name = SnowflakeId.GENERATOR, strategy = SnowflakeId.Strategy.LONG)
Long id;
@Comment("围栏")
@ManyToOne
@JoinColumn(name = "fence_id")
LocationFence fence;
// TODO
// 字段补充
// 关联表补充(报警规则)
// 关联表配置值补充
@FieldMapping("entityTypes")
@Comment("规则模型")
@Enumerated(EnumType.STRING)
Mode mode;
@Comment("规则名称")
String name;
@Comment("规则描述")
String describe;
@Comment("最小值(人数)")
Integer minValue;
@Comment("最大值(人数)")
Integer maxValue;
@Comment("规则生效开始时间")
LocalTime minTime;
@Comment("规则生效结束时间")
LocalTime maxTime;
@Comment("允许的用户(人员)")
@OneToMany
@Builder.Default
@ToString.Exclude
Set<User> includes = new HashSet<>(0);
@Comment("不允许的用户(人员)")
@OneToMany
@Builder.Default
@ToString.Exclude
Set<User> excludes = new HashSet<>(0);
@SuppressWarnings({ "unused" })
public enum Mode {
STAFF("人员"),
NUMBER("数量");
@Getter
final String text;
Mode(String text) {
this.text = text;
}
}
}
......@@ -6,26 +6,25 @@ import com.vladmihalcea.hibernate.type.json.JsonType;
import com.yiring.auth.domain.user.User;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.snowflake.SnowflakeId;
import org.locationtech.jts.geom.Point;
/**
* 定位数据
* 引用: 定位平台接口规范V3.0.1 #6
* TODO:
* 1. 实时数据日志的存储结构 TimeScale: 时间粒度,每天一个分区
* eg: SELECT create_hypertable('BS_LOCATION_LOG', 'time', chunk_time_interval => INTERVAL '1 day')
*
* @author Jim
* @version 0.1
......@@ -42,18 +41,24 @@ import org.locationtech.jts.geom.Point;
@FieldDefaults(level = AccessLevel.PRIVATE)
@Entity
@TypeDef(name = "json", typeClass = JsonType.class)
@Table(name = "BS_LOCATION_LOG", indexes = { @Index(columnList = "time"), @Index(columnList = "silent") })
@Table(name = "BS_LOCATION_LOG")
@Comment("定位数据")
public class LocationLog implements Serializable {
@Serial
private static final long serialVersionUID = 3467455881020691989L;
@Comment("主键")
@Id
@GeneratedValue(generator = SnowflakeId.GENERATOR)
@GenericGenerator(name = SnowflakeId.GENERATOR, strategy = SnowflakeId.Strategy.LONG)
Long id;
@Comment("时间")
@Column(nullable = false)
@CreationTimestamp
LocalDateTime time;
@Comment("地图 ID")
Long areaId;
@Comment("楼层")
String floor;
/**
* 来源于定位数据产生时刻标签所属的人员
......@@ -74,19 +79,6 @@ public class LocationLog implements Serializable {
@JoinColumn(name = "tag_id")
LocationTag tag;
@Comment("时间")
@Column(nullable = false)
LocalDateTime time;
@Comment("经度")
BigDecimal lon;
@Comment("纬度")
BigDecimal lat;
@Comment("海拔高度(m)")
BigDecimal altitude;
@Comment("坐标点信息")
@Type(type = "jts_geometry")
@Column(columnDefinition = "point")
......@@ -94,13 +86,13 @@ public class LocationLog implements Serializable {
@Comment("信标集合")
@Builder.Default
@ManyToMany
@OneToMany
@ToString.Exclude
Set<LocationBeacon> beacons = new HashSet<>(0);
@Comment("围栏集合")
@Builder.Default
@ManyToMany
@OneToMany
@ToString.Exclude
Set<LocationFence> fences = new HashSet<>(0);
......@@ -118,19 +110,6 @@ public class LocationLog implements Serializable {
@Column(columnDefinition = "json")
JSONObject raw;
@Comment("创建时间")
LocalDateTime createTime;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
LocationLog locationLog = (LocationLog) o;
return id != null && Objects.equals(id, locationLog.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Comment("定位时间")
LocalDateTime locationTime;
}
/* (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/4/24 18:03
*/
@Repository
public interface LocationLogRepository
extends JpaRepository<LocationLog, Serializable>, JpaSpecificationExecutor<LocationLog> {}
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.log;
import com.alibaba.fastjson.JSONObject;
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
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.CreationTimestamp;
import org.hibernate.annotations.TypeDef;
/**
* 真源定位系统实时数据日志
* TODO:
* 1. 实时数据日志的存储结构 TimeScale: 时间粒度,每小时一个分区
* eg: SELECT create_hypertable('zy_realtime_log', 'time', chunk_time_interval => INTERVAL '1 hour')
* 2. 数据分区压缩
* 3. 定时删除过期分区数据
* 参考:<a href="https://blog.csdn.net/yang_z_1/article/details/111560747">文档</a>
*
* @author Jim
* @version 0.1
* 2022/4/25 15:52
*/
@Getter
@Setter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@FieldDefaults(level = AccessLevel.PRIVATE)
@Entity
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
@Table(name = "ZY_REALTIME_LOG", indexes = { @Index(columnList = "method") })
@Comment("真源定位系统实时数据日志")
public class ZyRealtimeLog implements Serializable {
@Serial
private static final long serialVersionUID = 5545864821082386L;
@Id
@Comment("时间")
@Column(nullable = false)
@CreationTimestamp
LocalDateTime time;
@Comment("类型")
@Column(nullable = false)
String method;
@Comment("内容")
@org.hibernate.annotations.Type(type = "jsonb")
@Column(nullable = false, columnDefinition = "jsonb")
JSONObject raw;
}
/* (C) 2022 YiRing, Inc. */
package com.yiring.app.domain.log;
import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author Jim
* @version 0.1
* 2022/4/25 16:02
*/
public interface ZyRealtimeLogRepository extends JpaRepository<ZyRealtimeLog, Serializable> {}
......@@ -4,16 +4,26 @@ package com.yiring.app.rabbit.receiver;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import com.yiring.app.push.service.PushService;
import com.yiring.app.domain.location.*;
import com.yiring.app.domain.log.ZyRealtimeLog;
import com.yiring.app.domain.log.ZyRealtimeLogRepository;
import com.yiring.app.rabbit.config.ZyRabbitConfig;
import com.yiring.app.util.GeoUtils;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Point;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Example;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
......@@ -38,11 +48,20 @@ public class ZyRabbitReceiver {
// 3. 订阅 position(定位数据)、lowPower(低电量报警)、deviceStatus(设备状态)、keyWarning(按键报警)
@Resource
PushService pushService;
LocationTagRepository locationTagRepository;
@Resource
LocationBeaconRepository locationBeaconRepository;
@Resource
LocationLogRepository locationLogRepository;
@Resource
SimpMessagingTemplate simpMessagingTemplate;
@Resource
ZyRealtimeLogRepository zyRealtimeLogRepository;
/**
* 订阅真源定位系统 RabbitMQ 推送过来的消息(主动订阅的一些消息类别)
* 参见: 定位平台接口规范V3.0.1 - 通用版.pdf #6
......@@ -68,6 +87,12 @@ public class ZyRabbitReceiver {
// 解构消息内容
JSONObject data = info.getJSONObject("params");
String method = info.getString("method");
// 记录日志
ZyRealtimeLog realtimeLog = ZyRealtimeLog.builder().method(method).raw(info).build();
zyRealtimeLogRepository.save(realtimeLog);
// 业务处理
switch (method) {
// 实时定位
case "position" -> processPositionMessage(data);
......@@ -100,10 +125,74 @@ public class ZyRabbitReceiver {
// 1. 解析消息内容,进行围栏、出入标识判断等处理,将定位记录录入数据库
// 2. 创建一条需要进行消息推送的记录
// 3. 将记录推送的消息推送模块
// 4. 坚持是否触发围栏告警,记录告警数据,并推送消息
// 4. 检查是否触发围栏告警,记录告警数据,并推送消息
// 定位时间
Instant instant = Instant.ofEpochMilli(data.getLongValue("locationTime"));
LocalDateTime time = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// 定位的基本信息
LocationLog locationLog = LocationLog
.builder()
.raw(data)
.locationTime(time)
.silent(data.getBoolean("silent"))
.volt(data.getInteger("volt"))
.voltUnit(data.getString("voltUnit"))
.build();
// 设置空间点位信息
Point point = GeoUtils.createPoint(
data.getDoubleValue("longitude"),
data.getDoubleValue("latitude"),
data.getDoubleValue("altitude")
);
locationLog.setPoint(point);
// WebSocket 消息推送
simpMessagingTemplate.convertAndSend("/topic/position", data);
// 查询定位标签
Example<LocationTag> example = Example.of(LocationTag.builder().code(data.getString("tagId")).build());
Optional<LocationTag> optional = locationTagRepository.findOne(example);
if (optional.isPresent()) {
LocationTag tag = optional.get();
// 设置定位标签
locationLog.setTag(tag);
// 定位标签当时所属的用户
locationLog.setUser(tag.getUser());
// 查询当前用户的状态
// TODO
}
// 查询定位信标
Set<String> codes = Arrays
.stream(data.getString("beacons").split(","))
.map(beacon -> beacon.replaceAll("\\(.*\\)", ""))
.collect(Collectors.toSet());
Set<LocationBeacon> beacons = locationBeaconRepository.findByCodeIn(codes);
locationLog.setBeacons(beacons);
// 查询定位所在围栏信息
Set<LocationFence> fences = locationLog
.getBeacons()
.stream()
.map(LocationBeacon::getFences)
.flatMap(Set::stream)
.collect(Collectors.toSet());
locationLog.setFences(fences);
// TODO
// 并计算出入标记(围栏、区域)
// 写入数据
locationLogRepository.saveAndFlush(locationLog);
// 更新定位标签卡电量信息
// TODO
// WebSocket 推送定位消息
// 消息内容需要确定 TODO
simpMessagingTemplate.convertAndSend("/topic/position", "{}");
// TODO
// 判断围栏告警是否触发,触发写入告警记录,并推送消息
}
/**
......
......@@ -18,7 +18,7 @@ import org.locationtech.jts.geom.Point;
@UtilityClass
public class GeoUtils {
private final GeometryFactory factory = new GeometryFactory();
public final GeometryFactory factory = new GeometryFactory();
/**
* 创建点
......@@ -30,4 +30,29 @@ public class GeoUtils {
public Point createPoint(double lon, double lat) {
return factory.createPoint(new Coordinate(lon, lat));
}
/**
* 创建一个三维点
* @param lon 经度
* @param lat 纬度
* @param alt 高度
* @return 点
*/
public Point createPoint(double lon, double lat, double alt) {
return factory.createPoint(new Coordinate(lon, lat, alt));
}
/**
* 相对坐标转换为经纬度
* @param x x 坐标值
* @param y y 坐标值
* @param z z 坐标值
* @return 经纬度点
*/
public Point xyzToPoint(double x, double y, double z) {
// TODO
// 根据真源定位系统设置的坐标系,转换为经纬度
// 根据北向的地图左下角坐标点,矩形长宽距离,结合二维图片的像素比,计算经纬度
return factory.createPoint(new Coordinate(x, y, z));
}
}
......@@ -2,29 +2,15 @@
package com.yiring.app.web;
import com.yiring.app.constant.Code;
import com.yiring.app.domain.location.LocationTag;
import com.yiring.app.exception.CodeException;
import com.yiring.app.util.GeoUtils;
import com.yiring.auth.domain.user.User;
import com.yiring.common.core.Result;
import com.yiring.common.domain.BasicEntity;
import com.yiring.common.param.PageParam;
import com.yiring.common.vo.PageVo;
import io.swagger.annotations.Api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.criteria.*;
import javax.validation.Valid;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Point;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -60,56 +46,4 @@ public class HelloController {
PageVo<String> vo = PageVo.build(data, data.size());
return Result.ok(vo);
}
@Resource
SimpMessagingTemplate simpMessagingTemplate;
@GetMapping("test")
public Result<Point> test() {
Point point = GeoUtils.createPoint(112.1, 23.56);
simpMessagingTemplate.convertAndSend("/topic/position", point);
return Result.ok(point);
}
@Resource
EntityManager em;
@Data
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class UserVo {
Long id;
String avatar;
String code;
}
@GetMapping("test2")
public Result<ArrayList<UserVo>> query() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserVo> cq = cb.createQuery(UserVo.class);
Root<User> root = cq.from(User.class);
Expression<Long> id = root.get(BasicEntity.Fields.id);
Expression<String> avatar = root.get(User.Fields.avatar);
// 子查询
Subquery<String> query = cq.subquery(String.class);
Root<LocationTag> tagRoot = query.from(LocationTag.class);
query.select(tagRoot.get(LocationTag.Fields.code));
query.where(cb.equal(tagRoot.get(LocationTag.Fields.user), root));
// 构建查询字段
cq.multiselect(id, avatar, query);
// 查询条件
List<Predicate> predicates = new ArrayList<>();
// 可根据入参判断是否需要查询指定字段
predicates.add(cb.isNotNull(root.get(User.Fields.avatar)));
predicates.add(cb.equal(query, "BTT22222222"));
cq.where(predicates.toArray(new Predicate[0]));
List<UserVo> users = em.createQuery(cq).getResultList();
return Result.ok(new ArrayList<>(users));
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论