更新代码
This commit is contained in:
parent
cabc747f2c
commit
3622a611f4
@ -100,14 +100,15 @@ spring:
|
||||
timeout: 30 # 超时时间
|
||||
keepalive: 60 # 保持连接
|
||||
clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)
|
||||
topics:
|
||||
- /zxwl/vehicle/+/info # 清扫车信息上报
|
||||
- /zxwl/vehicle/+/gps # 定位信息上报
|
||||
- /zxwl/vehicle/+/fault # 故障信息上报
|
||||
- /zxwl/vehicle/+/task # 清扫任务推送、任务停止
|
||||
- /zxwl/vehicle/+/task/status # 清扫任务状态上报
|
||||
- /zxwl/vehicle/+/ctrl # 驾驶舱远程控制、路径采集
|
||||
- /zxwl/cockpit/+/heartbeat # 网关心跳
|
||||
topics: # 订阅主题
|
||||
- /zxwl/vehicle/+/test
|
||||
# - /zxwl/vehicle/+/info # 清扫车信息上报
|
||||
# - /zxwl/vehicle/+/gps # 定位信息上报
|
||||
# - /zxwl/vehicle/+/fault # 故障信息上报
|
||||
# - /zxwl/vehicle/+/task # 清扫任务推送、任务停止
|
||||
# - /zxwl/vehicle/+/task/status # 清扫任务状态上报
|
||||
# - /zxwl/vehicle/+/ctrl # 驾驶舱远程控制、路径采集
|
||||
# - /zxwl/cockpit/+/heartbeat # 网关心跳
|
||||
|
||||
--- # redis配置
|
||||
spring:
|
||||
|
||||
@ -28,9 +28,9 @@ spring:
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
max-file-size: 50MB
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
max-request-size: 50MB
|
||||
mvc:
|
||||
# 设置静态资源路径 防止所有请求都去查静态资源
|
||||
static-path-pattern: /static/**
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package org.zxwl.common.core.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对象存储管理
|
||||
*
|
||||
* @author zy
|
||||
*/
|
||||
public interface OssStorageService {
|
||||
|
||||
boolean updateByBusinessId(Long businessId, List<Long> ids);
|
||||
}
|
||||
@ -1,282 +0,0 @@
|
||||
package org.zxwl.common.core.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* stream 流工具类
|
||||
*
|
||||
* @author zxwl
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class StreamUtils {
|
||||
|
||||
/**
|
||||
* 将collection过滤
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function 过滤方法
|
||||
* @return 过滤后的list
|
||||
*/
|
||||
public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||
return collection.stream().filter(function).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到流中满足条件的第一个元素
|
||||
*
|
||||
* @param collection 需要查询的集合
|
||||
* @param function 过滤方法
|
||||
* @return 找到符合条件的第一个元素,没有则返回null
|
||||
*/
|
||||
public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return null;
|
||||
}
|
||||
return collection.stream().filter(function).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到流中任意一个满足条件的元素
|
||||
*
|
||||
* @param collection 需要查询的集合
|
||||
* @param function 过滤方法
|
||||
* @return 找到符合条件的任意一个元素,没有则返回null
|
||||
*/
|
||||
public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return collection.stream().filter(function).findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection拼接
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function 拼接方法
|
||||
* @return 拼接后的list
|
||||
*/
|
||||
public static <E> String join(Collection<E> collection, Function<E, String> function) {
|
||||
return join(collection, function, StringUtil.SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection拼接
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function 拼接方法
|
||||
* @param delimiter 拼接符
|
||||
* @return 拼接后的list
|
||||
*/
|
||||
public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return StringUtil.EMPTY;
|
||||
}
|
||||
return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection排序
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param comparing 排序方法
|
||||
* @return 排序后的list
|
||||
*/
|
||||
public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||
return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection转化为类型不变的map<br>
|
||||
* <B>{@code Collection<V> ----> Map<K,V>}</B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param key V类型转化为K类型的lambda方法
|
||||
* @param <V> collection中的泛型
|
||||
* @param <K> map中的key类型
|
||||
* @return 转化后的map
|
||||
*/
|
||||
public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Collection转化为map(value类型与collection的泛型不同)<br>
|
||||
* <B>{@code Collection<E> -----> Map<K,V> }</B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param key E类型转化为K类型的lambda方法
|
||||
* @param value E类型转化为V类型的lambda方法
|
||||
* @param <E> collection中的泛型
|
||||
* @param <K> map中的key类型
|
||||
* @param <V> map中的value类型
|
||||
* @return 转化后的map
|
||||
*/
|
||||
public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection按照规则(比如有相同的班级id)分类成map<br>
|
||||
* <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
|
||||
*
|
||||
* @param collection 需要分类的集合
|
||||
* @param key 分类的规则
|
||||
* @param <E> collection中的泛型
|
||||
* @param <K> map中的key类型
|
||||
* @return 分类后的map
|
||||
*/
|
||||
public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection
|
||||
.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
|
||||
* <B>{@code Collection<E> ---> Map<T,Map<U,List<E>>> } </B>
|
||||
*
|
||||
* @param collection 需要分类的集合
|
||||
* @param key1 第一个分类的规则
|
||||
* @param key2 第二个分类的规则
|
||||
* @param <E> 集合元素类型
|
||||
* @param <K> 第一个map中的key类型
|
||||
* @param <U> 第二个map中的key类型
|
||||
* @return 分类后的map
|
||||
*/
|
||||
public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection
|
||||
.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
|
||||
* <B>{@code Collection<E> ---> Map<T,Map<U,E>> } </B>
|
||||
*
|
||||
* @param collection 需要分类的集合
|
||||
* @param key1 第一个分类的规则
|
||||
* @param key2 第二个分类的规则
|
||||
* @param <T> 第一个map中的key类型
|
||||
* @param <U> 第二个map中的key类型
|
||||
* @param <E> collection中的泛型
|
||||
* @return 分类后的map
|
||||
*/
|
||||
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
|
||||
if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection
|
||||
.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection转化为List集合,但是两者的泛型不同<br>
|
||||
* <B>{@code Collection<E> ------> List<T> } </B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function collection中的泛型转化为list泛型的lambda表达式
|
||||
* @param <E> collection中的泛型
|
||||
* @param <T> List中的泛型
|
||||
* @return 转化后的list
|
||||
*/
|
||||
public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return collection
|
||||
.stream()
|
||||
.map(function)
|
||||
.filter(Objects::nonNull)
|
||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection转化为Set集合,但是两者的泛型不同<br>
|
||||
* <B>{@code Collection<E> ------> Set<T> } </B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function collection中的泛型转化为set泛型的lambda表达式
|
||||
* @param <E> collection中的泛型
|
||||
* @param <T> Set中的泛型
|
||||
* @return 转化后的Set
|
||||
*/
|
||||
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
|
||||
if (CollUtil.isEmpty(collection) || function == null) {
|
||||
return CollUtil.newHashSet();
|
||||
}
|
||||
return collection
|
||||
.stream()
|
||||
.map(function)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 合并两个相同key类型的map
|
||||
*
|
||||
* @param map1 第一个需要合并的 map
|
||||
* @param map2 第二个需要合并的 map
|
||||
* @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况
|
||||
* @param <K> map中的key类型
|
||||
* @param <X> 第一个 map的value类型
|
||||
* @param <Y> 第二个 map的value类型
|
||||
* @param <V> 最终map的value类型
|
||||
* @return 合并后的map
|
||||
*/
|
||||
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
|
||||
if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
|
||||
return MapUtil.newHashMap();
|
||||
} else if (MapUtil.isEmpty(map1)) {
|
||||
map1 = MapUtil.newHashMap();
|
||||
} else if (MapUtil.isEmpty(map2)) {
|
||||
map2 = MapUtil.newHashMap();
|
||||
}
|
||||
Set<K> key = new HashSet<>();
|
||||
key.addAll(map1.keySet());
|
||||
key.addAll(map2.keySet());
|
||||
Map<K, V> map = new HashMap<>();
|
||||
for (K t : key) {
|
||||
X x = map1.get(t);
|
||||
Y y = map2.get(t);
|
||||
V z = merge.apply(x, y);
|
||||
if (z != null) {
|
||||
map.put(t, z);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@ -13,6 +13,7 @@ import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.zxwl.common.core.utils.IpUtils;
|
||||
import org.zxwl.common.core.utils.SpringUtil;
|
||||
import org.zxwl.common.core.utils.StringUtil;
|
||||
import org.zxwl.common.log.annotation.Log;
|
||||
import org.zxwl.common.log.event.OperLogEvent;
|
||||
|
||||
@ -60,7 +61,6 @@ public class LogAspect {
|
||||
return object;
|
||||
}
|
||||
|
||||
// @Async
|
||||
public void saveLog(JoinPoint joinPoint, long time, Exception e) {
|
||||
OperLogEvent systemLog = new OperLogEvent();
|
||||
systemLog.setExecuteTime(time);
|
||||
@ -80,7 +80,7 @@ public class LogAspect {
|
||||
if (log.operateType() != null) {
|
||||
systemLog.setOperateType(log.operateType().toString());
|
||||
}
|
||||
if (!"".equals(log.operateExplain())) {
|
||||
if (StringUtil.isNotBlank(log.operateExplain())) {
|
||||
systemLog.setOperateExplain(log.operateExplain());
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,6 @@ public class MqttInboundConfig {
|
||||
@Resource
|
||||
private MqttConfig mqttConfig;
|
||||
|
||||
|
||||
private MqttMessageReceiver mqttMessageReceiver;
|
||||
|
||||
/**
|
||||
|
||||
@ -5,14 +5,18 @@ import cn.hutool.core.map.MapUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ -56,7 +60,6 @@ public class RedisUtil {
|
||||
}
|
||||
|
||||
public static Boolean exist(String key) {
|
||||
System.out.println("key = " + key);
|
||||
try {
|
||||
return Objects.equals(redisTemplate.hasKey(key), Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
@ -124,7 +127,7 @@ public class RedisUtil {
|
||||
public static void putHashAll(String key, Map<String, Object> map) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
log.error("Error putting hashMap: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
@ -133,7 +136,7 @@ public class RedisUtil {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
redisTemplate.expire(key, time, timeUnit);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
log.error("Error putting hashMap: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
@ -232,4 +235,118 @@ public class RedisUtil {
|
||||
return count;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前缀匹配的所有 key 名称
|
||||
*
|
||||
* @param keyPrefix key 前缀,如 "user:profile:"
|
||||
* @return key集合
|
||||
*/
|
||||
public static Set<String> getKeysByPrefix(String keyPrefix) {
|
||||
Set<String> keys = new HashSet<>();
|
||||
|
||||
redisTemplate.execute((RedisConnection connection) -> {
|
||||
ScanOptions options = ScanOptions.scanOptions()
|
||||
.match(keyPrefix + "*")
|
||||
.count(100)
|
||||
.build();
|
||||
|
||||
try (Cursor<byte[]> cursor = connection.scan(options)) {
|
||||
while (cursor.hasNext()) {
|
||||
byte[] rawKey = cursor.next();
|
||||
keys.add(new String(rawKey));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定前缀的所有 key 的“后缀”
|
||||
*
|
||||
* @param keyPrefix 前缀,如 "ZXWL:COCKPIT:ONLINE"
|
||||
* @return 后缀集合(去除前缀后的部分)
|
||||
*/
|
||||
public static Set<String> getSuffixesByPrefix(String keyPrefix) {
|
||||
Set<String> suffixes = new HashSet<>();
|
||||
|
||||
redisTemplate.execute((RedisConnection connection) -> {
|
||||
// 构造 SCAN 模式:前缀 + "*"
|
||||
ScanOptions options = ScanOptions.scanOptions()
|
||||
.match(keyPrefix + "*") // 匹配所有以前缀开头的 key
|
||||
.count(100)
|
||||
.build();
|
||||
|
||||
try (Cursor<byte[]> cursor = connection.scan(options)) {
|
||||
while (cursor.hasNext()) {
|
||||
String fullKey = new String(cursor.next());
|
||||
// 判断是否以前缀开头(双重保险)
|
||||
if (fullKey.startsWith(keyPrefix)) {
|
||||
String suffix = fullKey.substring(keyPrefix.length());
|
||||
if (!suffix.isEmpty()) {
|
||||
suffixes.add(suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error scanning Redis keys", e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return suffixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用方法:根据前缀扫描 Hash Key,检查指定 field 是否满足条件,返回 Key 的后缀集合
|
||||
*
|
||||
* @param keyPrefix Hash Key 前缀
|
||||
* @param fieldName 要检查的 field 名
|
||||
* @param condition 条件判断器(Predicate<String>),接收 field 的 value(字符串),返回是否匹配
|
||||
* @return 满足条件的 Key 后缀集合
|
||||
*/
|
||||
public static Set<String> getKeySuffixesByHashField(String keyPrefix, String fieldName, Predicate<String> condition) {
|
||||
Set<String> suffixes = new HashSet<>();
|
||||
|
||||
redisTemplate.execute((RedisConnection connection) -> {
|
||||
ScanOptions options = ScanOptions.scanOptions()
|
||||
.match(keyPrefix + "*")
|
||||
.count(100)
|
||||
.build();
|
||||
|
||||
try (Cursor<byte[]> cursor = connection.scan(options)) {
|
||||
while (cursor.hasNext()) {
|
||||
byte[] rawKey = cursor.next();
|
||||
String fullKey = new String(rawKey, StandardCharsets.UTF_8);
|
||||
|
||||
// 判断键是否为哈希类型
|
||||
DataType type = connection.type(rawKey);
|
||||
if (type == null || !type.equals(DataType.HASH)) {
|
||||
continue; // 如果不是哈希类型,则跳过
|
||||
}
|
||||
|
||||
// 获取指定 field 的值
|
||||
byte[] rawValue = connection.hGet(rawKey, fieldName.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
if (rawValue != null) {
|
||||
String value = new String(rawValue, StandardCharsets.UTF_8).trim();
|
||||
// 使用传入的条件判断器进行匹配
|
||||
if (condition.test(value)) {
|
||||
if (fullKey.startsWith(keyPrefix)) {
|
||||
String suffix = fullKey.substring(keyPrefix.length());
|
||||
suffixes.add(suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error scanning Redis hash keys", e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return suffixes;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ public class ScheduleConfig {
|
||||
@Scheduled(cron = "0/10 * * * * ?")
|
||||
public void checkCockpitStatus() {
|
||||
long threshold = Instant.now().minus(30, ChronoUnit.SECONDS).getEpochSecond();
|
||||
RedisUtil.removeRangeByScore(RedisKeyConst.COCKPIT_ONLINE_KEY, 0, threshold);
|
||||
|
||||
RedisUtil.removeRangeByScore(RedisKeyConst.COCKPIT_ONLINE, 0, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,29 +2,22 @@ package org.zxwl.sweeper.constant;
|
||||
|
||||
public interface RedisKeyConst {
|
||||
|
||||
String HEARTBEAT_PREFIX = "ZXWL:GATEWAY:HEARTBEAT:"; //网关心跳
|
||||
String DELAY_PREFIX = "ZXWL:GATEWAY:DELAY:"; //网关时延
|
||||
String DISPATCH_UNDO_VID_LIST = "ZXWL:DISPATCH:UNDO"; //调度记录未完成的VID列表
|
||||
String GATEWAY_INFO_IP = "ZXWL:GATEWAY:INFO:IP"; //记录网关IP信息
|
||||
String GATEWAY_INFO_URL = "ZXWL:GATEWAY:INFO:URL"; //记录网关摄像头地址信息
|
||||
String HEARTBEAT_PREFIX = "ZXWL:GATEWAY:HEARTBEAT:"; // 网关心跳
|
||||
String DELAY_PREFIX = "ZXWL:GATEWAY:DELAY:"; // 网关时延
|
||||
String DISPATCH_UNDO_VID_LIST = "ZXWL:DISPATCH:UNDO"; // 调度记录未完成的VID列表
|
||||
String GATEWAY_INFO_IP = "ZXWL:GATEWAY:INFO:IP"; // 记录网关IP信息
|
||||
String GATEWAY_INFO_URL = "ZXWL:GATEWAY:INFO:URL"; // 记录网关摄像头地址信息
|
||||
|
||||
// vehicle车俩
|
||||
String VEHICLE_CTRL_RESPONSE = "ZXWL:VEHICLE:CTRL:RESPONSE:"; //车辆控制响应
|
||||
String VEHICLE_STATUS = "ZXWL:VEHICLE:ONLINE:"; //车辆在线状态
|
||||
String VEHICLE_FAULT_STATUS = "ZXWL:VEHICLE:FAULT:"; //车辆故障状态
|
||||
String VEHICLE_POWER = "ZXWL:VEHICLE:POWER:"; //车辆电量
|
||||
String VEHICLE_STATUS_INFO = "ZXWL:VEHICLE:STATUS:INFO:"; //车俩状态
|
||||
String VEHICLE_CTRL_RESPONSE = "ZXWL:VEHICLE:CTRL:RESPONSE:"; // 车辆控制响应
|
||||
String VEHICLE_ONLINE = "ZXWL:VEHICLE:ONLINE:"; // 车辆实时状态
|
||||
String VEHICLE_FAULT = "ZXWL:VEHICLE:FAULT:"; // 车辆故障状态
|
||||
String VEHICLE_POWER = "ZXWL:VEHICLE:POWER:"; // 车辆剩余电量
|
||||
|
||||
// route路径
|
||||
String ROUTE_KEY_PREFIX = "routeName:";
|
||||
String COCKPIT_ONLINE = "ZXWL:COCKPIT:ONLINE:"; //驾驶实时状态
|
||||
|
||||
// task任务
|
||||
String TASK_RESPONSE = "ZXWL:VEHICLE:TASK:RESPONSE:"; //任务响应
|
||||
|
||||
// cockpit驾驶舱
|
||||
String COCKPIT_ONLINE_PREFIX = "ZXWL:COCKPIT:ONLINE:";
|
||||
String COCKPIT_ONLINE_KEY = "ZXWL:COCKPIT:ONLINE";
|
||||
String ROUTE_KEY_PREFIX = "routeName:"; // 路径采集
|
||||
|
||||
String DEVICE_STATUS_KEY_PREFIX = "ZXWL:DEVICE_STATUS:";
|
||||
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import org.zxwl.common.core.validate.UpdateGroup;
|
||||
import org.zxwl.common.log.annotation.Log;
|
||||
import org.zxwl.common.log.enums.ModuleType;
|
||||
import org.zxwl.common.log.enums.OperateType;
|
||||
import org.zxwl.common.satoken.utils.LoginHelper;
|
||||
import org.zxwl.common.web.base.BaseController;
|
||||
import org.zxwl.sweeper.enums.RepairStatusEnum;
|
||||
import org.zxwl.sweeper.model.faultRepair.*;
|
||||
@ -32,14 +33,36 @@ public class FaultRepairController extends BaseController {
|
||||
|
||||
private final FaultRepairService faultRepairService;
|
||||
|
||||
/**
|
||||
* 分页列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
*/
|
||||
@GetMapping
|
||||
public Result<Page<FaultRepairVo>> queryPage(FaultRepairQuery query) {
|
||||
return Result.success(faultRepairService.queryPage(query));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Result<FaultRepairDataVo> data() {
|
||||
return Result.success(faultRepairService.data());
|
||||
/**
|
||||
* 我提交的列表 (报修人员查询)
|
||||
*
|
||||
* @param query 查询条件
|
||||
*/
|
||||
@GetMapping("/mySubmitList")
|
||||
public Result<Page<FaultRepairVo>> mySubmitList(FaultRepairQuery query) {
|
||||
query.setReportUserId(LoginHelper.getUserId());
|
||||
return Result.success(faultRepairService.queryPage(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* 代办列表(维修人员查询)
|
||||
*
|
||||
* @param query 查询条件
|
||||
*/
|
||||
@GetMapping("/pendList")
|
||||
public Result<Page<FaultRepairVo>> pendList(FaultRepairQuery query) {
|
||||
query.setAssignToUserId(LoginHelper.getUserId());
|
||||
return Result.success(faultRepairService.queryPage(query));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ -90,4 +113,14 @@ public class FaultRepairController extends BaseController {
|
||||
public Result<Void> cancel(@PathVariable("id") Long id) {
|
||||
return toResult(faultRepairService.updateByStatus(id, RepairStatusEnum.CANCEL.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计数据(待办数量、处理中数量)
|
||||
*
|
||||
* @return Result
|
||||
*/
|
||||
@GetMapping("/data")
|
||||
public Result<FaultRepairDataVo> data() {
|
||||
return Result.success(faultRepairService.data());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
package org.zxwl.sweeper.controller;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.zxwl.common.mqtt.handler.MqttMessageSender;
|
||||
import org.zxwl.common.utils.RedisUtil;
|
||||
import org.zxwl.sweeper.model.taskInfo.TaskStatusReport;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@ -15,10 +24,34 @@ public class TestController {
|
||||
|
||||
private final MqttMessageSender mqttMessageSender;
|
||||
|
||||
private final KafkaTemplate<String, Object> kafkaTemplate;
|
||||
|
||||
@PostMapping("/mqtt")
|
||||
public void mqtt(String topic, Integer qos, String message) {
|
||||
log.info("测试mqtt发送消息-topic: {}, qos: {}, message: {}", topic, qos, message);
|
||||
mqttMessageSender.send(topic, qos, message);
|
||||
}
|
||||
|
||||
@PostMapping("/kafka")
|
||||
public void kafka(@RequestBody TaskStatusReport taskStatusReport) {
|
||||
kafkaTemplate.send("zxwl.vehicle.123456.task.status", JSONUtil.toJsonStr(taskStatusReport));
|
||||
}
|
||||
|
||||
@PostMapping("/redis")
|
||||
public void redis() {
|
||||
Map<String,Object> map = new HashMap<>();
|
||||
map.put("name","zhangyu");
|
||||
map.put("age",25);
|
||||
RedisUtil.putHashAll("user", map);
|
||||
|
||||
RedisUtil.setValue("name", "zhangyu");
|
||||
|
||||
RedisUtil.addZSet("status", "1", Instant.now().getEpochSecond());
|
||||
RedisUtil.addZSet("status", "2", Instant.now().getEpochSecond());
|
||||
|
||||
System.out.println(RedisUtil.exist("name"));
|
||||
System.out.println(RedisUtil.exist("user"));
|
||||
System.out.println(RedisUtil.exist("status"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.zxwl.sweeper.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -7,15 +8,28 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.zxwl.common.core.domain.Result;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleDetail;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleQuery;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleVO;
|
||||
import org.zxwl.sweeper.service.VehicleAppInfoService;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/vehicleApp")
|
||||
public class VehicleAppInfoController {
|
||||
public class VehicleAppController {
|
||||
|
||||
private final VehicleAppInfoService vehicleAppInfoService;
|
||||
|
||||
/**
|
||||
* 小程序首页-车辆列表(实时展示任务执行状态、故障、电量、充电状态等)
|
||||
*
|
||||
* @param query 查询
|
||||
* @return Result
|
||||
*/
|
||||
@GetMapping()
|
||||
public Result<Page<VehicleVO>> pageList(VehicleQuery query) {
|
||||
return Result.success(vehicleAppInfoService.pageList(query));
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{vid}")
|
||||
public Result<VehicleDetail> vehicleDetail(@PathVariable("vid") Long vid) {
|
||||
return Result.success(vehicleAppInfoService.getVehicleDetail(vid));
|
||||
@ -12,7 +12,7 @@ import org.zxwl.common.log.annotation.Log;
|
||||
import org.zxwl.common.log.enums.ModuleType;
|
||||
import org.zxwl.common.log.enums.OperateType;
|
||||
import org.zxwl.common.web.base.BaseController;
|
||||
import org.zxwl.sweeper.model.VehicleInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.app.vehicleCard.VehicleCard;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.*;
|
||||
import org.zxwl.sweeper.service.VehicleInfoService;
|
||||
@ -107,6 +107,13 @@ public class VehicleInfoController extends BaseController {
|
||||
return toResult(vehicleInfoService.bindCockpit(id, cid));
|
||||
}
|
||||
|
||||
@PutMapping("unbinding")
|
||||
//@SaCheckPermission("vehicle:info:unbinding")
|
||||
@Log(module = ModuleType.VEHICLE, operateType = OperateType.OTHER, operateExplain = "车辆解绑")
|
||||
public Result<Void> unbindCockpit(@RequestParam("id") Long id) {
|
||||
return toResult(vehicleInfoService.unbindCockpit(id));
|
||||
}
|
||||
|
||||
@GetMapping("exist")
|
||||
public Result<Void> existVehicleInfo(@RequestParam("vid") String vid) {
|
||||
return toResult(vehicleInfoService.exitVehicleInfo(vid));
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.zxwl.sweeper.listener;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -14,7 +15,6 @@ import org.zxwl.sweeper.constant.KafkaTopicConst;
|
||||
import org.zxwl.sweeper.constant.RedisKeyConst;
|
||||
import org.zxwl.sweeper.utils.KafkaUtil;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@ -29,8 +29,8 @@ public class CockpitKafkaConsumer {
|
||||
if (message.isPresent()) {
|
||||
String cid = KafkaUtil.getVIdByTopic(topic, 3);
|
||||
if (CharSequenceUtil.isEmpty(cid)) return;
|
||||
// RedisUtil.setValueWithExpire(RedisKeyConst.COCKPIT_ONLINE_PREFIX + cid, LocalDateTimeUtil.now(), 30);
|
||||
RedisUtil.addZSet(RedisKeyConst.COCKPIT_ONLINE_KEY, cid, Instant.now().getEpochSecond());
|
||||
RedisUtil.setValueWithExpire(RedisKeyConst.COCKPIT_ONLINE + cid, LocalDateTimeUtil.now(), 30);
|
||||
// RedisUtil.addZSet(RedisKeyConst.COCKPIT_ONLINE, cid, Instant.now().getEpochSecond());
|
||||
ack.acknowledge();
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ import org.zxwl.common.utils.RedisUtil;
|
||||
import org.zxwl.sweeper.constant.KafkaTopicConst;
|
||||
import org.zxwl.sweeper.constant.RedisKeyConst;
|
||||
import org.zxwl.sweeper.model.reply.ReplyHeader;
|
||||
import org.zxwl.sweeper.model.taskInfo.TaskStatusReport;
|
||||
import org.zxwl.sweeper.service.TaskInfoService;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@ -22,6 +24,8 @@ import java.util.Optional;
|
||||
@RequiredArgsConstructor
|
||||
public class TaskKafkaConsumer {
|
||||
|
||||
private final TaskInfoService taskInfoService;
|
||||
|
||||
@KafkaListener(id = "task_push", topicPattern = KafkaTopicConst.VEHICLE_TASK)
|
||||
public void handleVehicleTask(ConsumerRecord<String, String> consumerRecord, Acknowledgment ack,
|
||||
@Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
|
||||
@ -46,6 +50,9 @@ public class TaskKafkaConsumer {
|
||||
if (message.isPresent()) {
|
||||
String msg = message.get();
|
||||
log.info("task_status_push <==> {}", msg);
|
||||
// 同步更新任务状态
|
||||
TaskStatusReport taskStatusReport = JSONUtil.toBean(msg, TaskStatusReport.class);
|
||||
taskInfoService.updateStatus(taskStatusReport.getId(), taskStatusReport.getStatus());
|
||||
ack.acknowledge();
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,25 +57,21 @@ public class VehicleKafkaConsumer {
|
||||
|
||||
@KafkaListener(id = "vehicle_base_info", topicPattern = KafkaTopicConst.VEHICLE_INFO)
|
||||
public void handleVehicleInfo(ConsumerRecord<String, String> consumerRecord, Acknowledgment ack,
|
||||
@Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
|
||||
@Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
|
||||
Optional<String> message = Optional.ofNullable(consumerRecord.value());
|
||||
if (message.isPresent()) {
|
||||
String msg = message.get();
|
||||
String vid = KafkaUtil.getVIdByTopic(topic, 3);
|
||||
if (CharSequenceUtil.isEmpty(vid)) return;
|
||||
|
||||
// 车俩在线状态
|
||||
RedisUtil.setValueWithExpire(RedisKeyConst.VEHICLE_STATUS + vid,
|
||||
System.currentTimeMillis(), 30);
|
||||
|
||||
JSONObject jsonObject = new JSONObject(msg);
|
||||
Optional<Object> optional = RedisUtil.getValue(RedisKeyConst.DELAY_PREFIX + vid);
|
||||
if (optional.isPresent()) {
|
||||
jsonObject.set("delay", optional.get());
|
||||
}else {
|
||||
} else {
|
||||
jsonObject.set("delay", 999);
|
||||
}
|
||||
// log.info("[kafka] ==> Topic: {}, Message: {}", topic, jsonObject);
|
||||
|
||||
VehicleInfoWebSocket.sendBroadcast(vid, 1, jsonObject.toString());
|
||||
|
||||
Map<String, Object> map = BeanUtil.beanToMap(jsonObject);
|
||||
@ -83,18 +79,18 @@ public class VehicleKafkaConsumer {
|
||||
vehicleInfoReport.setVid(vid);
|
||||
vehicleInfoReport.setCreatedAt(LocalDateTime.now());
|
||||
// 车俩电池容量
|
||||
RedisUtil.setValue(RedisKeyConst.VEHICLE_POWER + vid ,vehicleInfoReport.getPower());
|
||||
RedisUtil.setValue(RedisKeyConst.VEHICLE_POWER + vid, vehicleInfoReport.getPower());
|
||||
// 车俩实时状态信息(档位、水位、充电状态、速度、电机温度、垃圾豆容量等)
|
||||
RedisUtil.putHashAll(RedisKeyConst.VEHICLE_STATUS_INFO + vid, map, 30, TimeUnit.SECONDS);
|
||||
mongoTemplate.save(vehicleInfoReport, "v_vehicle_info");
|
||||
RedisUtil.putHashAll(RedisKeyConst.VEHICLE_ONLINE + vid, map, 30, TimeUnit.SECONDS);
|
||||
|
||||
mongoTemplate.save(vehicleInfoReport, "v_vehicle_info");
|
||||
ack.acknowledge();
|
||||
}
|
||||
}
|
||||
|
||||
@KafkaListener(id = "vehicle_gps_info", topicPattern = KafkaTopicConst.VEHICLE_GPS)
|
||||
public void handleGPSInfo(ConsumerRecord<String, String> consumerRecord, Acknowledgment ack,
|
||||
@Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
|
||||
@Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
|
||||
Optional<String> message = Optional.ofNullable(consumerRecord.value());
|
||||
if (message.isPresent()) {
|
||||
String msg = message.get();
|
||||
@ -133,7 +129,7 @@ public class VehicleKafkaConsumer {
|
||||
VehicleFaultInfo vehicleFaultInfo = JSONUtil.toBean(msg, VehicleFaultInfo.class);
|
||||
vehicleFaultInfo.setVin(vid);
|
||||
vehicleFaultInfo.setCreatedAt(LocalDateTime.now());
|
||||
RedisUtil.setValueWithExpire(RedisKeyConst.VEHICLE_FAULT_STATUS + vid ,
|
||||
RedisUtil.setValueWithExpire(RedisKeyConst.VEHICLE_FAULT + vid,
|
||||
System.currentTimeMillis(), 30);
|
||||
mongoTemplate.save(vehicleFaultInfo, "v_fault_info");
|
||||
ack.acknowledge();
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
package org.zxwl.sweeper.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.zxwl.sweeper.entity.VehicleInfo;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.zxwl.sweeper.entity.VehicleInfo;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleVO;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -15,4 +20,8 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper
|
||||
public interface VehicleInfoMapper extends BaseMapper<VehicleInfo> {
|
||||
|
||||
Page<VehicleVO> queryPage(@Param("page") Page<VehicleInfo> page,
|
||||
@Param("key") String key,
|
||||
@Param("status") Integer status,
|
||||
@Param("vids") Set<String> vids);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ public class CockpitInfoVO {
|
||||
this.picture = EnvUtil.getProperty("minio.pathPrefix") + cockpitInfo.getPicture();
|
||||
}
|
||||
|
||||
if (RedisUtil.exist(RedisKeyConst.COCKPIT_ONLINE_PREFIX + cockpitInfo.getCid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.COCKPIT_ONLINE + cockpitInfo.getCid())) {
|
||||
this.status = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.status = OnlineEnum.OFFLINE.getValue();
|
||||
|
||||
@ -30,7 +30,7 @@ public class CockpitStatusInfoVO {
|
||||
if (cockpitInfo != null) {
|
||||
this.name = cockpitInfo.getName();
|
||||
this.faultStatus = cockpitInfo.getFaultStatus();
|
||||
if (RedisUtil.exist(RedisKeyConst.COCKPIT_ONLINE_PREFIX + cockpitInfo.getCid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.COCKPIT_ONLINE + cockpitInfo.getCid())) {
|
||||
this.status = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.status = OnlineEnum.OFFLINE.getValue();
|
||||
|
||||
@ -17,10 +17,29 @@ import org.zxwl.sweeper.entity.FaultRepair;
|
||||
@Setter
|
||||
public class FaultRepairQuery extends BaseQuery<FaultRepair> {
|
||||
|
||||
private String title;
|
||||
/**
|
||||
* 搜索
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 状态 0-待处理 1-处理中 2-已处理中 3-已关闭 4-已取消
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 优先级 0-低 1-中 2-高 3-紧急
|
||||
*/
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 报修人id
|
||||
*/
|
||||
private Long reportUserId;
|
||||
|
||||
/**
|
||||
* 维修人id
|
||||
*/
|
||||
private Long assignToUserId;
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,6 @@ public class FaultRepairVo {
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
@ -34,21 +33,11 @@ public class FaultRepairVo {
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 报修人id
|
||||
*/
|
||||
private Long reportUserId;
|
||||
|
||||
/**
|
||||
* 报修人姓名
|
||||
*/
|
||||
private String reportName;
|
||||
|
||||
/**
|
||||
* 报修人手机号
|
||||
*/
|
||||
private String reportPhone;
|
||||
|
||||
/**
|
||||
* 优先级
|
||||
*/
|
||||
@ -69,21 +58,11 @@ public class FaultRepairVo {
|
||||
*/
|
||||
private String statusCn;
|
||||
|
||||
/**
|
||||
* 指派给维修人员ID
|
||||
*/
|
||||
private Long assignToUserId;
|
||||
|
||||
/**
|
||||
* 指派给维修人员姓名
|
||||
*/
|
||||
private String assignName;
|
||||
|
||||
/**
|
||||
* 维修人员手机号
|
||||
*/
|
||||
private String assignPhone;
|
||||
|
||||
/**
|
||||
* 维修结果描述
|
||||
*/
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package org.zxwl.sweeper.model.taskInfo;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
@ -59,8 +57,4 @@ public class TaskInfoDTO {
|
||||
BeanUtil.copyProperties(this, taskInfo);
|
||||
return taskInfo;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(JSONUtil.toJsonStr(new TaskInfoDTO(), JSONConfig.create().setIgnoreNullValue(false)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,6 +60,9 @@ public class TaskInfoVO {
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime created;
|
||||
|
||||
/**
|
||||
* 执行状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
public TaskInfoVO(TaskInfo taskInfo) {
|
||||
|
||||
@ -24,6 +24,10 @@ public class TaskPushInfo {
|
||||
* 清扫次数
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
/**
|
||||
* 路径信息
|
||||
*/
|
||||
private RoutePushInfo routeInfo;
|
||||
|
||||
public TaskPushInfo(TaskInfo taskInfo, RoutePushInfo routeInfo) {
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package org.zxwl.sweeper.model.taskInfo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class TaskStatusReport {
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
private Integer status;
|
||||
}
|
||||
@ -15,7 +15,7 @@ public class TaskRecordQuery extends BaseQuery<TaskRecord> {
|
||||
private Long vehicleId;
|
||||
|
||||
/**
|
||||
* 执行状态 0 待执行 1 执行中 2 已完成
|
||||
* 执行状态 0 待执行 1 执行中 2 已完成 3-执行异常
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
|
||||
@ -12,6 +12,9 @@ import java.time.LocalDateTime;
|
||||
@Setter
|
||||
public class TaskRecordVO {
|
||||
|
||||
/**
|
||||
* 任务记录主键id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
@ -19,10 +22,13 @@ public class TaskRecordVO {
|
||||
*/
|
||||
private String taskName;
|
||||
|
||||
/**
|
||||
* 车辆名称
|
||||
*/
|
||||
private String vehicleName;
|
||||
|
||||
/**
|
||||
* 执行状态 0 待执行 1 执行中 2 已完成
|
||||
* 执行状态 0 待执行 1 执行中 2 已完成 3 执行异常
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
|
||||
@ -19,7 +19,15 @@ public class VehicleInfoQuery extends BaseQuery<VehicleInfo> {
|
||||
* 车辆名称/车牌号
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 车辆编号
|
||||
*/
|
||||
private String vid;
|
||||
|
||||
/**
|
||||
* 驾驶舱编号
|
||||
*/
|
||||
private String cid;
|
||||
|
||||
/**
|
||||
|
||||
@ -94,7 +94,7 @@ public class VehicleInfoVO {
|
||||
if (CharSequenceUtil.isNotEmpty(vehicleInfo.getPicture())) {
|
||||
this.picture = EnvUtil.getProperty("minio.pathPrefix") + vehicleInfo.getPicture();
|
||||
}
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_ONLINE + vehicleInfo.getVid())) {
|
||||
this.status = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.status = OnlineEnum.OFFLINE.getValue();
|
||||
|
||||
@ -29,8 +29,14 @@ public class VehicleStatusVO {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 路径id
|
||||
*/
|
||||
private Long currentRouteId;
|
||||
|
||||
/**
|
||||
* 经纬度
|
||||
*/
|
||||
private Coordinate coordinate;
|
||||
|
||||
public VehicleStatusVO(VehicleInfo vehicleInfo, Long currentRouteId) {
|
||||
|
||||
@ -8,7 +8,9 @@ import org.zxwl.sweeper.model.taskInfo.app.TaskInfoAPPVO;
|
||||
@Setter
|
||||
public class VehicleDetail {
|
||||
|
||||
private VehicleSimpleInfoVO vehicleSimpleInfoVO; //车辆信息
|
||||
private VehicleSimpleInfoVO vehicleSimpleInfoVO; //基础信息
|
||||
|
||||
private VehicleOperatingStatusVO vehicleOperatingStatusVO; //实时状态
|
||||
|
||||
private TaskInfoAPPVO taskInfoVO; //当前任务
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package org.zxwl.sweeper.model;
|
||||
package org.zxwl.sweeper.model.vehicleInfo.app;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
@ -55,13 +55,13 @@ public class VehicleInfoAPPVO {
|
||||
if (CharSequenceUtil.isNotEmpty(vehicleInfo.getPicture())) {
|
||||
this.picture = EnvUtil.getProperty("minio.pathPrefix") + vehicleInfo.getPicture();
|
||||
}
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_ONLINE + vehicleInfo.getVid())) {
|
||||
this.status = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.status = OnlineEnum.OFFLINE.getValue();
|
||||
}
|
||||
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT + vehicleInfo.getVid())) {
|
||||
this.faultStatus = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.faultStatus = OnlineEnum.OFFLINE.getValue();
|
||||
@ -0,0 +1,16 @@
|
||||
package org.zxwl.sweeper.model.vehicleInfo.app;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class VehicleOperatingStatusVO {
|
||||
|
||||
private Float gear; //档位 0:空档 1:前进档 2:倒档 3:驻车档
|
||||
private Float waterLevel; //水位 %
|
||||
private Boolean chargeStatus; //充电状态
|
||||
private Float speed; //车辆速度 km/h
|
||||
private Float motorTemp; //电机温度 ℃
|
||||
private Float power; //剩余电量 %
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package org.zxwl.sweeper.model.vehicleInfo.app;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.zxwl.common.mybatis.page.BaseQuery;
|
||||
import org.zxwl.sweeper.entity.VehicleInfo;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class VehicleQuery extends BaseQuery<VehicleInfo> {
|
||||
|
||||
/**
|
||||
* 车辆名称/车牌号
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 状态 0-任务执行中 1-待执行 2-故障 3-充电
|
||||
*/
|
||||
private Integer status;
|
||||
}
|
||||
@ -52,12 +52,12 @@ public class VehicleSimpleInfoVO {
|
||||
if (CharSequenceUtil.isNotEmpty(vehicleInfo.getPicture())) {
|
||||
this.picture = EnvUtil.getProperty("minio.pathPrefix") + vehicleInfo.getPicture();
|
||||
}
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_ONLINE + vehicleInfo.getVid())) {
|
||||
this.status = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.status = OnlineEnum.OFFLINE.getValue();
|
||||
}
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT + vehicleInfo.getVid())) {
|
||||
this.faultStatus = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.faultStatus = OnlineEnum.OFFLINE.getValue();
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package org.zxwl.sweeper.model.vehicleInfo.app;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class VehicleVO {
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 车辆编号
|
||||
*/
|
||||
private String vid;
|
||||
|
||||
/**
|
||||
* 车辆名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 车牌号
|
||||
*/
|
||||
private String plateNumber;
|
||||
|
||||
/**
|
||||
* 车辆图片
|
||||
*/
|
||||
private String picture;
|
||||
|
||||
/**
|
||||
* 状态 0 离线 1 在线
|
||||
*/
|
||||
private int status;
|
||||
|
||||
/**
|
||||
* 故障状态
|
||||
*/
|
||||
private int faultStatus;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
private int taskStatus;
|
||||
|
||||
/**
|
||||
* 剩余电量
|
||||
*/
|
||||
private double power;
|
||||
|
||||
/**
|
||||
* 充电状态
|
||||
*/
|
||||
private boolean chargeStatus;
|
||||
}
|
||||
@ -30,13 +30,13 @@ public class VehicleStatusInfoVO {
|
||||
if (vehicleInfo != null) {
|
||||
this.name = vehicleInfo.getName();
|
||||
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_ONLINE + vehicleInfo.getVid())) {
|
||||
this.status = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.status = OnlineEnum.OFFLINE.getValue();
|
||||
}
|
||||
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT_STATUS + vehicleInfo.getVid())) {
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT + vehicleInfo.getVid())) {
|
||||
this.faultStatus = OnlineEnum.ONLINE.getValue();
|
||||
} else {
|
||||
this.faultStatus = OnlineEnum.OFFLINE.getValue();
|
||||
|
||||
@ -14,6 +14,7 @@ import org.zxwl.sweeper.model.faultRepair.*;
|
||||
* @since 2025-09-19
|
||||
*/
|
||||
public interface FaultRepairService extends IService<FaultRepair> {
|
||||
|
||||
Page<FaultRepairVo> queryPage(FaultRepairQuery query); // 分页查询
|
||||
|
||||
FaultRepairDataVo data(); // 统计代办和处理中总数量
|
||||
|
||||
@ -35,4 +35,6 @@ public interface TaskInfoService extends IService<TaskInfo> {
|
||||
boolean start(Long id); //开始
|
||||
|
||||
boolean stop(Long id); //停止
|
||||
|
||||
void updateStatus(Long id, Integer status); //车端任务状态上报更新
|
||||
}
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
package org.zxwl.sweeper.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleDetail;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleQuery;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleVO;
|
||||
|
||||
public interface VehicleAppInfoService {
|
||||
|
||||
Page<VehicleVO> pageList(VehicleQuery query);
|
||||
|
||||
VehicleDetail getVehicleDetail(Long vehicleId);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ package org.zxwl.sweeper.service;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.zxwl.sweeper.entity.VehicleInfo;
|
||||
import org.zxwl.sweeper.model.VehicleInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.app.vehicleCard.VehicleCard;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.*;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleSimpleInfoVO;
|
||||
@ -47,6 +47,8 @@ public interface VehicleInfoService extends IService<VehicleInfo> {
|
||||
|
||||
boolean bindCockpit(Long id, String cid); //绑定驾驶舱
|
||||
|
||||
boolean unbindCockpit(Long id); //解绑驾驶舱
|
||||
|
||||
boolean hasGateway(String vid); //判断是否具有网关
|
||||
|
||||
Long onlineCount(); //在线数
|
||||
|
||||
@ -175,7 +175,7 @@ public class CockpitInfoServiceImpl extends ServiceImpl<CockpitInfoMapper, Cockp
|
||||
long remoteCtrlCount = dispatchRecordMapper.selectCount(Wrappers.query(DispatchRecord.class).lambda()
|
||||
.ne(DispatchRecord::getStatus, DispatchStatusEnum.DISPATCH_END.getValue()));
|
||||
|
||||
long onlineCount = RedisUtil.countKeysByPrefix(RedisKeyConst.COCKPIT_ONLINE_PREFIX);
|
||||
long onlineCount = RedisUtil.countKeysByPrefix(RedisKeyConst.COCKPIT_ONLINE);
|
||||
|
||||
CockpitStatisticsVO statisticsVO = new CockpitStatisticsVO();
|
||||
statisticsVO.setOnlineCount(onlineCount);
|
||||
|
||||
@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.zxwl.common.core.service.OssStorageService;
|
||||
import org.zxwl.common.satoken.utils.LoginHelper;
|
||||
import org.zxwl.sweeper.entity.FaultRepair;
|
||||
import org.zxwl.sweeper.enums.RepairStatusEnum;
|
||||
import org.zxwl.sweeper.mapper.FaultRepairMapper;
|
||||
@ -29,6 +32,8 @@ public class FaultRepairServiceImpl extends ServiceImpl<FaultRepairMapper, Fault
|
||||
|
||||
private final FaultRepairMapper faultRepairMapper;
|
||||
|
||||
private final OssStorageService ossStorageService;
|
||||
|
||||
@Override
|
||||
public Page<FaultRepairVo> queryPage(FaultRepairQuery query) {
|
||||
return faultRepairMapper.queryPage(query.toPage(), query);
|
||||
@ -46,19 +51,28 @@ public class FaultRepairServiceImpl extends ServiceImpl<FaultRepairMapper, Fault
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean add(FaultRepairDTO faultRepairDTO) {
|
||||
return save(faultRepairDTO.toFaultRepair());
|
||||
FaultRepair faultRepair = faultRepairDTO.toFaultRepair();
|
||||
faultRepair.setReportUserId(LoginHelper.getUserId());
|
||||
save(faultRepair);
|
||||
return ossStorageService.updateByBusinessId(faultRepair.getId(), faultRepairDTO.getFileIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean edit(FaultRepairDTO faultRepairDTO) {
|
||||
return updateById(faultRepairDTO.toFaultRepair());
|
||||
FaultRepair faultRepair = faultRepairDTO.toFaultRepair();
|
||||
updateById(faultRepair);
|
||||
|
||||
return ossStorageService.updateByBusinessId(faultRepair.getId(), faultRepairDTO.getFileIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean assign(FaultRepairAllocateDTO dto) {
|
||||
return update(new LambdaUpdateWrapper<FaultRepair>()
|
||||
.eq(FaultRepair::getId, dto.getId())
|
||||
.set(FaultRepair::getStatus, RepairStatusEnum.PROCESS.getValue())
|
||||
.set(FaultRepair::getAssignToUserId, dto.getAssignToUserId()));
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ import org.zxwl.sweeper.constant.MqttTopicConst;
|
||||
import org.zxwl.sweeper.constant.RedisKeyConst;
|
||||
import org.zxwl.sweeper.entity.RouteInfo;
|
||||
import org.zxwl.sweeper.entity.TaskInfo;
|
||||
import org.zxwl.sweeper.enums.EnableEnum;
|
||||
import org.zxwl.sweeper.enums.TaskStatusEnum;
|
||||
import org.zxwl.sweeper.mapper.RouteInfoMapper;
|
||||
import org.zxwl.sweeper.mapper.TaskInfoMapper;
|
||||
@ -284,7 +285,7 @@ public class RouteInfoServiceImpl extends ServiceImpl<RouteInfoMapper, RouteInfo
|
||||
// 判断路径信息是否存在已启用
|
||||
LambdaQueryWrapper<RouteInfo> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(RouteInfo::getId, ids);
|
||||
wrapper.eq(RouteInfo::getStatus, 1);
|
||||
wrapper.eq(RouteInfo::getStatus, EnableEnum.ENABLE.getValue());
|
||||
if (exists(wrapper)) {
|
||||
throw new BusinessException(SystemErrorCode.ROUTE_STATUS_EXIST);
|
||||
}
|
||||
@ -295,7 +296,7 @@ public class RouteInfoServiceImpl extends ServiceImpl<RouteInfoMapper, RouteInfo
|
||||
public boolean enable(Long id) {
|
||||
LambdaUpdateWrapper<RouteInfo> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(Objects.nonNull(id), RouteInfo::getId, id);
|
||||
updateWrapper.set(RouteInfo::getStatus, 1);
|
||||
updateWrapper.set(RouteInfo::getStatus, EnableEnum.ENABLE.getValue());
|
||||
return routeInfoMapper.update(updateWrapper) > 0;
|
||||
}
|
||||
|
||||
|
||||
@ -15,10 +15,11 @@ import org.zxwl.common.json.utils.JacksonUtil;
|
||||
import org.zxwl.common.mqtt.handler.MqttMessageSender;
|
||||
import org.zxwl.common.satoken.utils.LoginHelper;
|
||||
import org.zxwl.common.utils.RedisUtil;
|
||||
import org.zxwl.sweeper.constant.RedisKeyConst;
|
||||
import org.zxwl.sweeper.constant.MqttTopicConst;
|
||||
import org.zxwl.sweeper.constant.RedisKeyConst;
|
||||
import org.zxwl.sweeper.entity.RouteInfo;
|
||||
import org.zxwl.sweeper.entity.TaskInfo;
|
||||
import org.zxwl.sweeper.entity.TaskRecord;
|
||||
import org.zxwl.sweeper.entity.VehicleInfo;
|
||||
import org.zxwl.sweeper.enums.TaskStatusEnum;
|
||||
import org.zxwl.sweeper.mapper.TaskInfoMapper;
|
||||
@ -36,6 +37,7 @@ import org.zxwl.sweeper.service.TaskInfoService;
|
||||
import org.zxwl.sweeper.service.TaskRecordService;
|
||||
import org.zxwl.sweeper.service.VehicleInfoService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -152,13 +154,22 @@ public class TaskInfoServiceImpl extends ServiceImpl<TaskInfoMapper, TaskInfo> i
|
||||
|
||||
@Override
|
||||
public boolean start(Long id) {
|
||||
// 获取任务详情
|
||||
TaskInfo taskInfo = taskInfoMapper.selectById(id);
|
||||
if (taskInfo == null) {
|
||||
throw new BusinessException(SystemErrorCode.TASK_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 获取路径详情
|
||||
RouteInfo routeInfo = routeInfoService.getById(taskInfo.getRouteId());
|
||||
|
||||
// 判断是否存在未完成调度,避免同一车辆被分配多个任务
|
||||
boolean result = taskInfoMapper.exists(new LambdaQueryWrapper<TaskInfo>()
|
||||
.eq(TaskInfo::getVehicleId, taskInfo.getVehicleId())
|
||||
.eq(TaskInfo::getStatus, TaskStatusEnum.EXECUTING.getValue()));
|
||||
if (result) {
|
||||
throw new BusinessException(SystemErrorCode.TASK_EXECUTING);
|
||||
}
|
||||
|
||||
TaskPushInfo taskPushInfo = new TaskPushInfo(taskInfo, new RoutePushInfo(routeInfo));
|
||||
|
||||
String vid = getVidByVehicleId(taskInfo.getVehicleId());
|
||||
@ -208,14 +219,39 @@ public class TaskInfoServiceImpl extends ServiceImpl<TaskInfoMapper, TaskInfo> i
|
||||
ResponseMessage responseMessage = JacksonUtil.parseObject((String) o, ResponseMessage.class);
|
||||
if (responseMessage != null && responseMessage.getCode().equals(200)) {
|
||||
taskRecordService.updateRecordCompleted(taskInfo.getId(), taskInfo.getVehicleId());
|
||||
updateTaskStatus(taskInfo.getId(), TaskStatusEnum.COMPLETED.getValue());
|
||||
updateTaskStatus(taskInfo.getId(), TaskStatusEnum.UN_EXECUTED.getValue());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 车端任务状态上报更新
|
||||
*
|
||||
* @param id 任务id
|
||||
* @param status 任务状态
|
||||
*/
|
||||
@Override
|
||||
public void updateStatus(Long id, Integer status) {
|
||||
// 判断车端上报的任务状态是否已完成或执行异常,是则同步更新
|
||||
if (TaskStatusEnum.COMPLETED.getValue().equals(status) || TaskStatusEnum.EXCEPTION.getValue().equals(status)) {
|
||||
LambdaUpdateWrapper<TaskInfo> task = new LambdaUpdateWrapper<>();
|
||||
task.set(TaskInfo::getStatus, TaskStatusEnum.UN_EXECUTED.getValue());
|
||||
task.eq(TaskInfo::getId, id);
|
||||
task.eq(TaskInfo::getStatus, TaskStatusEnum.EXECUTING.getValue());
|
||||
taskInfoMapper.update(task);
|
||||
|
||||
LambdaUpdateWrapper<TaskRecord> record = new LambdaUpdateWrapper<>();
|
||||
record.set(TaskRecord::getStatus, status);
|
||||
record.set(TaskRecord::getEndTime, LocalDateTime.now());
|
||||
record.eq(TaskRecord::getTaskId, id);
|
||||
record.eq(TaskRecord::getStatus, TaskStatusEnum.EXECUTING.getValue());
|
||||
record.ne(TaskRecord::getStatus, status);
|
||||
taskRecordService.update(record);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean updateTaskStatus(Long taskId, int status) {
|
||||
LambdaUpdateWrapper<TaskInfo> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.set(TaskInfo::getStatus, status);
|
||||
|
||||
@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.zxwl.common.core.exception.BusinessException;
|
||||
import org.zxwl.common.core.exception.SystemErrorCode;
|
||||
import org.zxwl.sweeper.entity.TaskRecord;
|
||||
import org.zxwl.sweeper.enums.TaskStatusEnum;
|
||||
import org.zxwl.sweeper.mapper.TaskRecordMapper;
|
||||
@ -78,7 +80,14 @@ public class TaskRecordServiceImpl extends ServiceImpl<TaskRecordMapper, TaskRec
|
||||
|
||||
@Override
|
||||
public boolean delete(Set<Long> ids) {
|
||||
return taskRecordMapper.deleteByIds(ids) > 0;
|
||||
// 检查是否存在未完成调度的任务
|
||||
boolean result = taskRecordMapper.exists(new LambdaUpdateWrapper<TaskRecord>()
|
||||
.in(TaskRecord::getId, ids)
|
||||
.eq(TaskRecord::getStatus, TaskStatusEnum.EXECUTING.getValue()));
|
||||
if (result) {
|
||||
throw new BusinessException(SystemErrorCode.DELETE_FAILURE_EXIST_UNDO_RECORD);
|
||||
}
|
||||
return removeByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,28 +1,105 @@
|
||||
package org.zxwl.sweeper.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.zxwl.common.core.utils.EnvUtil;
|
||||
import org.zxwl.common.utils.RedisUtil;
|
||||
import org.zxwl.sweeper.constant.RedisKeyConst;
|
||||
import org.zxwl.sweeper.mapper.VehicleInfoMapper;
|
||||
import org.zxwl.sweeper.model.taskInfo.app.TaskInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleDetail;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleSimpleInfoVO;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.*;
|
||||
import org.zxwl.sweeper.service.TaskInfoService;
|
||||
import org.zxwl.sweeper.service.VehicleAppInfoService;
|
||||
import org.zxwl.sweeper.service.VehicleInfoService;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VehicleAppInfoServiceImpl implements VehicleAppInfoService {
|
||||
|
||||
private final VehicleInfoMapper vehicleInfoMapper;
|
||||
private final VehicleInfoService vehicleInfoService;
|
||||
private final TaskInfoService taskInfoService;
|
||||
|
||||
@Override
|
||||
public Page<VehicleVO> pageList(VehicleQuery query) {
|
||||
// 根据查询状态查询对应车辆实际运行状态(如故障、剩余电量和充电状态等)
|
||||
// 从Redis字符串或哈希缓存中获取所有的车辆vid集合,关联sql和封装分页返回
|
||||
Set<String> faultAll = RedisUtil.getSuffixesByPrefix(RedisKeyConst.VEHICLE_FAULT); // 车辆故障状态
|
||||
|
||||
Set<String> charge = RedisUtil.getKeySuffixesByHashField( // 车辆实时状态
|
||||
RedisKeyConst.VEHICLE_ONLINE,
|
||||
"chargeStatus",
|
||||
"true"::equalsIgnoreCase //支持多种匹配模式(如值为true(布尔)、值为某个数字(如1,100)、值满足某种条件(如>0))
|
||||
);
|
||||
|
||||
// 根据 status 过滤 vids
|
||||
Set<String> vids = new HashSet<>();
|
||||
Integer status = query.getStatus();
|
||||
if (status != null) {
|
||||
switch (status) {
|
||||
case 2: // 故障
|
||||
vids.addAll(faultAll);
|
||||
break;
|
||||
case 3: // 充电
|
||||
vids.addAll(charge);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if ((status == 2 || status == 3) && CollUtil.isEmpty(vids)) {
|
||||
return new Page<>(query.getPageNum(), query.getPageSize());
|
||||
}
|
||||
}
|
||||
|
||||
// 查询数据库分页
|
||||
Page<VehicleVO> page = vehicleInfoMapper.queryPage(query.toPage(), query.getKey(), status, vids);
|
||||
for (VehicleVO infoVO : page.getRecords()) {
|
||||
if (CharSequenceUtil.isNotEmpty(infoVO.getPicture())) {
|
||||
infoVO.setPicture(EnvUtil.getProperty("minio.pathPrefix") + infoVO.getPicture());
|
||||
}
|
||||
// 在线状态
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_ONLINE + infoVO.getVid())) {
|
||||
infoVO.setStatus(1);
|
||||
}
|
||||
// 故障状态
|
||||
if (RedisUtil.exist(RedisKeyConst.VEHICLE_FAULT + infoVO.getVid())) {
|
||||
infoVO.setFaultStatus(1);
|
||||
}
|
||||
// 剩余电量
|
||||
Optional<Object> power = RedisUtil.getValue(RedisKeyConst.VEHICLE_POWER + infoVO.getVid());
|
||||
if (power.isPresent()) {
|
||||
infoVO.setPower((double) power.orElse(0d));
|
||||
}
|
||||
// 充电状态
|
||||
infoVO.setChargeStatus(charge.contains(infoVO.getVid()));
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleDetail getVehicleDetail(Long vehicleId) {
|
||||
VehicleDetail vehicleDetail = new VehicleDetail();
|
||||
VehicleSimpleInfoVO vehicleSimpleInfo = vehicleInfoService.getVehicleSimpleInfo(vehicleId);
|
||||
vehicleDetail.setVehicleSimpleInfoVO(vehicleSimpleInfo);
|
||||
TaskInfoAPPVO taskInfoAPPVO = taskInfoService.currentTaskInfo(vehicleId);
|
||||
vehicleDetail.setTaskInfoVO(taskInfoAPPVO);
|
||||
// 车辆信息
|
||||
VehicleSimpleInfoVO vehicleInfo = vehicleInfoService.getVehicleSimpleInfo(vehicleId);
|
||||
// 任务信息
|
||||
TaskInfoAPPVO taskInfo = taskInfoService.currentTaskInfo(vehicleId);
|
||||
// 实时状态
|
||||
if (vehicleInfo != null) {
|
||||
Map<Object, Object> map = RedisUtil.getAllHashValue(RedisKeyConst.VEHICLE_ONLINE + vehicleInfo.getVid());
|
||||
VehicleOperatingStatusVO operatingStatusVO = BeanUtil.toBean(map, VehicleOperatingStatusVO.class);
|
||||
vehicleDetail.setVehicleOperatingStatusVO(operatingStatusVO);
|
||||
}
|
||||
vehicleDetail.setVehicleSimpleInfoVO(vehicleInfo);
|
||||
vehicleDetail.setTaskInfoVO(taskInfo);
|
||||
return vehicleDetail;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import org.zxwl.sweeper.mapper.DispatchRecordMapper;
|
||||
import org.zxwl.sweeper.mapper.RouteInfoMapper;
|
||||
import org.zxwl.sweeper.mapper.TaskInfoMapper;
|
||||
import org.zxwl.sweeper.mapper.VehicleInfoMapper;
|
||||
import org.zxwl.sweeper.model.VehicleInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.app.VehicleInfoAPPVO;
|
||||
import org.zxwl.sweeper.model.app.vehicleCard.VehicleCard;
|
||||
import org.zxwl.sweeper.model.cache.DeviceStatusCache;
|
||||
import org.zxwl.sweeper.model.vehicleInfo.*;
|
||||
@ -237,6 +237,14 @@ public class VehicleInfoServiceImpl extends ServiceImpl<VehicleInfoMapper, Vehic
|
||||
return update(updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unbindCockpit(Long id) {
|
||||
LambdaUpdateWrapper<VehicleInfo> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.set(VehicleInfo::getCid, null);
|
||||
updateWrapper.eq(VehicleInfo::getId, id);
|
||||
return update(updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGateway(String vid) {
|
||||
VehicleInfo vehicleInfo = getOne(Wrappers.lambdaQuery(VehicleInfo.class).eq(VehicleInfo::getVid, vid));
|
||||
@ -284,9 +292,9 @@ public class VehicleInfoServiceImpl extends ServiceImpl<VehicleInfoMapper, Vehic
|
||||
long count = vehicleInfoMapper.selectCount(Wrappers.query(VehicleInfo.class).lambda()
|
||||
.eq(VehicleInfo::getEnableStatus, EnableEnum.ENABLE.getValue()));
|
||||
|
||||
long onlineCount = RedisUtil.countKeysByPrefix(RedisKeyConst.VEHICLE_STATUS);
|
||||
long onlineCount = RedisUtil.countKeysByPrefix(RedisKeyConst.VEHICLE_ONLINE);
|
||||
|
||||
long faultCount = RedisUtil.countKeysByPrefix(RedisKeyConst.VEHICLE_FAULT_STATUS);
|
||||
long faultCount = RedisUtil.countKeysByPrefix(RedisKeyConst.VEHICLE_FAULT);
|
||||
|
||||
VehicleStatisticsVo statisticsVo = new VehicleStatisticsVo();
|
||||
statisticsVo.setCount(count);
|
||||
|
||||
@ -7,16 +7,12 @@
|
||||
a.id,
|
||||
a.title,
|
||||
a.description,
|
||||
a.report_user_id,
|
||||
d.real_name reportName,
|
||||
d.phone reportPhone,
|
||||
a.priority,
|
||||
b.dict_label priorityCn,
|
||||
a.status,
|
||||
c.dict_label statusCn,
|
||||
a.assign_to_user_id,
|
||||
e.real_name assignName,
|
||||
e.phone assignPhone,
|
||||
a.repair_result,
|
||||
a.remark,
|
||||
a.created,
|
||||
@ -27,8 +23,14 @@
|
||||
left join sys_user_info d on d.id = a.report_user_id
|
||||
left join sys_user_info e on e.id = a.assign_to_user_id
|
||||
where a.deleted = 0
|
||||
<if test="query.title != null and query.title != ''">
|
||||
AND a.title LIKE CONCAT('%', #{query.title}, '%')
|
||||
<if test="query.key != null and query.key != ''">
|
||||
AND a.title LIKE CONCAT('%', #{query.key}, '%')
|
||||
</if>
|
||||
<if test="query.reportUserId != null">
|
||||
AND a.report_user_id = #{query.reportUserId}
|
||||
</if>
|
||||
<if test="query.assignToUserId != null">
|
||||
AND a.assign_to_user_id = #{query.assignToUserId}
|
||||
</if>
|
||||
<if test="query.status != null">
|
||||
and a.title = #{query.status}
|
||||
|
||||
@ -26,4 +26,41 @@
|
||||
id, vid, name, picture, status, enable_status, cid, plate_number, has_gateway, model, manufacture_date, organize_id, created, last, deleted
|
||||
</sql>
|
||||
|
||||
<select id="queryPage" resultType="org.zxwl.sweeper.model.vehicleInfo.app.VehicleVO">
|
||||
SELECT
|
||||
s1.id,
|
||||
s1.vid,
|
||||
s1.name,
|
||||
s1.picture,
|
||||
s1.plate_number,
|
||||
s1.enable_status,
|
||||
IF(s2.count > 0, 1, 0) taskStatus
|
||||
FROM info_vehicle_info s1
|
||||
LEFT JOIN (
|
||||
SELECT vehicle_id, count(*) count
|
||||
FROM info_task_info
|
||||
WHERE deleted = 0 AND status = 1
|
||||
GROUP BY vehicle_id
|
||||
) s2 ON s1.id = s2.vehicle_id
|
||||
WHERE s1.deleted = 0 AND s1.enable_status = 1
|
||||
<if test="key != null and key != ''">
|
||||
AND (s1.vid LIKE CONCAT('%', #{key}, '%') OR s1.cid LIKE CONCAT('%', #{key}, '%'))
|
||||
</if>
|
||||
<if test="status != null">
|
||||
<if test="status == 0">
|
||||
AND s2.count > 0
|
||||
</if>
|
||||
<if test="status == 1">
|
||||
AND (s2.count IS NULL OR s2.count = 0)
|
||||
</if>
|
||||
</if>
|
||||
<if test="vids != null and vids.size() > 0">
|
||||
AND s1.vid IN
|
||||
<foreach item="item" index="index" collection="vids" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
ORDER BY s1.created DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@ -3,6 +3,7 @@ package org.zxwl.system.controller;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -45,6 +46,17 @@ public class OssController extends BaseController {
|
||||
return Result.success(ossService.queryPageList(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据业务id获取附件集合
|
||||
*
|
||||
* @param businessId 业务主键id
|
||||
*/
|
||||
@GetMapping("/listByBusinessId/{businessId}")
|
||||
public Result<List<OssInfoVO>> listByBusinessId(@PathVariable Long businessId) {
|
||||
List<OssInfoVO> list = ossService.listByBusinessId(businessId);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传OSS对象存储
|
||||
*
|
||||
@ -87,7 +99,7 @@ public class OssController extends BaseController {
|
||||
@DeleteMapping("{ids}")
|
||||
//@SaCheckPermission("sys:oss:delete")
|
||||
@Log(module = ModuleType.SYSTEM, operateType = OperateType.DELETE, operateExplain = "OSS对象存储删除")
|
||||
public Result<Void> delete(@PathVariable("ids") List<Long> ids) {
|
||||
public Result<Void> delete(@NotEmpty(message = "主键不能为空") @PathVariable("ids") List<Long> ids) {
|
||||
return toResult(ossService.removeByIds(ValidatorUtil.checkIds(ids)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import org.zxwl.system.entity.OssInfo;
|
||||
import org.zxwl.system.model.oss.OssInfoQuery;
|
||||
import org.zxwl.system.model.oss.OssInfoVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 接口
|
||||
@ -17,4 +19,6 @@ import org.zxwl.system.model.oss.OssInfoVO;
|
||||
public interface OssService extends IService<OssInfo> {
|
||||
|
||||
Page<OssInfoVO> queryPageList(OssInfoQuery query); //分页
|
||||
|
||||
List<OssInfoVO> listByBusinessId(Long businessId);
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import org.zxwl.common.core.domain.dto.DictTypeInfoDTO;
|
||||
import org.zxwl.common.core.exception.BusinessException;
|
||||
import org.zxwl.common.core.exception.SystemErrorCode;
|
||||
import org.zxwl.common.core.service.DictService;
|
||||
import org.zxwl.common.core.utils.StreamUtils;
|
||||
import org.zxwl.common.core.utils.StreamUtil;
|
||||
import org.zxwl.system.entity.DictType;
|
||||
import org.zxwl.system.enums.EnableEnum;
|
||||
import org.zxwl.system.mapper.DictTypeMapper;
|
||||
@ -130,7 +130,7 @@ public class DictTypeServiceImpl extends ServiceImpl<DictTypeMapper, DictType> i
|
||||
@Override
|
||||
public String getDictLabel(String dictCode, String dictValue, String separator) {
|
||||
List<DictItemSimpleVO> datas = listByDictCode(dictCode);
|
||||
Map<String, String> map = StreamUtils.toMap(datas, DictItemSimpleVO::getDictValue, DictItemSimpleVO::getDictLabel);
|
||||
Map<String, String> map = StreamUtil.toMap(datas, DictItemSimpleVO::getDictValue, DictItemSimpleVO::getDictLabel);
|
||||
if (StringUtils.containsAny(dictValue, separator)) {
|
||||
return Arrays.stream(dictValue.split(separator))
|
||||
.map(v -> map.getOrDefault(v, StringUtils.EMPTY))
|
||||
@ -151,7 +151,7 @@ public class DictTypeServiceImpl extends ServiceImpl<DictTypeMapper, DictType> i
|
||||
@Override
|
||||
public String getDictValue(String dictCode, String dictLabel, String separator) {
|
||||
List<DictItemSimpleVO> datas = listByDictCode(dictCode);
|
||||
Map<String, String> map = StreamUtils.toMap(datas, DictItemSimpleVO::getDictLabel, DictItemSimpleVO::getDictValue);
|
||||
Map<String, String> map = StreamUtil.toMap(datas, DictItemSimpleVO::getDictLabel, DictItemSimpleVO::getDictValue);
|
||||
if (StringUtils.containsAny(dictLabel, separator)) {
|
||||
return Arrays.stream(dictLabel.split(separator))
|
||||
.map(l -> map.getOrDefault(l, StringUtils.EMPTY))
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
package org.zxwl.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.zxwl.common.core.service.OssStorageService;
|
||||
import org.zxwl.system.entity.OssInfo;
|
||||
import org.zxwl.system.mapper.OssMapper;
|
||||
import org.zxwl.system.model.oss.OssInfoQuery;
|
||||
@ -23,7 +26,7 @@ import java.util.List;
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OssServiceImpl extends ServiceImpl<OssMapper, OssInfo> implements OssService {
|
||||
public class OssServiceImpl extends ServiceImpl<OssMapper, OssInfo> implements OssService, OssStorageService {
|
||||
|
||||
private final OssMapper ossMapper;
|
||||
|
||||
@ -33,4 +36,18 @@ public class OssServiceImpl extends ServiceImpl<OssMapper, OssInfo> implements O
|
||||
List<OssInfoVO> list = page.getRecords().stream().map(OssInfoVO::new).toList();
|
||||
return new Page<OssInfoVO>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OssInfoVO> listByBusinessId(Long businessId) {
|
||||
List<OssInfo> list = ossMapper.selectList(new LambdaQueryWrapper<OssInfo>().eq(OssInfo::getBusinessId, businessId));
|
||||
return list.stream().map(OssInfoVO::new).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateByBusinessId(Long businessId, List<Long> ids) {
|
||||
LambdaUpdateWrapper<OssInfo> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.set(OssInfo::getBusinessId, businessId);
|
||||
updateWrapper.in(OssInfo::getId, ids);
|
||||
return update(updateWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.zxwl.common.core.exception.BusinessException;
|
||||
import org.zxwl.common.core.exception.SystemErrorCode;
|
||||
import org.zxwl.common.core.utils.StreamUtils;
|
||||
import org.zxwl.common.core.utils.StreamUtil;
|
||||
import org.zxwl.common.satoken.utils.SecureUtil;
|
||||
import org.zxwl.system.entity.RoleInfo;
|
||||
import org.zxwl.system.entity.UserInfo;
|
||||
@ -90,7 +90,7 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
|
||||
// 删除旧数据
|
||||
userRoleMapper.delete(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, userId));
|
||||
// 插入新数据
|
||||
List<UserRole> list = StreamUtils.toList(roleIds, roleId -> {
|
||||
List<UserRole> list = StreamUtil.toList(roleIds, roleId -> {
|
||||
UserRole ur = new UserRole();
|
||||
ur.setUserId(userId);
|
||||
ur.setRoleId(roleId);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user