优化在线逻辑

This commit is contained in:
磷叶 2025-05-17 12:41:41 +08:00
parent 33b469cdbc
commit 6be8a70cf4
19 changed files with 248 additions and 74 deletions

View File

@ -127,4 +127,9 @@ public class CacheConstants
* 设备更新物联网状态队列
*/
public static final String DEVICE_UPDATE_IOT_QUEUE = "device_update_iot_queue";
/**
* 设备在线状态
*/
public static final String MAC_ONLINE_STATUS = "mac_online_status:";
}

View File

@ -341,4 +341,19 @@ public class RedisCache
// 执行 Lua 脚本
return (List<T>) redisTemplate.<T>execute(redisScript, Collections.singletonList(key));
}
/**
* 获取并清空Hash缓存中的所有数据原子性操作
*
* @param key 缓存的键值
* @return Hash中的所有数据
*/
public <T> List<T> getAndClearHashValues(final String key) {
String scriptText =
"local values = redis.call('HVALS', KEYS[1]) " +
"redis.call('DEL', KEYS[1]) " +
"return values";
RedisScript<List<T>> redisScript = new DefaultRedisScript<>(scriptText, (Class<List<T>>) (Class<?>) List.class);
return (List<T>) redisTemplate.execute(redisScript, Collections.singletonList(key));
}
}

View File

@ -18,7 +18,8 @@ public enum RedisLockKey {
DEVICE_MAC_UNIQUE("device_mac_unique", "设备MAC唯一"),
USER_NAME_UNIQUE("user_name_unique", "用户账号唯一"),
DEVICE_IOT_REFRESH("device_iot_refresh", "设备物联网信息刷新"),
DEVICE_UPDATE_IOT_LOCK("device_update_iot_lock", "设备更新物联网信息锁");
DEVICE_UPDATE_IOT_LOCK("device_update_iot_lock", "设备更新物联网信息锁"),
DEVICE_IOT_OPERATION("device_iot_operation", "设备物联网操作锁");
private final String key;
private final String name;

View File

@ -64,4 +64,7 @@ public class DeviceQuery extends DeviceVO {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private List<LocalDate> createDateRange;
@ApiModelProperty("订单设备状态列表")
private List<String> orderDeviceStatusList;
}

View File

@ -131,6 +131,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.lastTimeEnd != null">and bd.last_time &lt;= #{query.lastTimeEnd}</if>
<if test="query.lastTimeStart != null">and bd.last_time &gt;= #{query.lastTimeStart}</if>
<if test="query.locationType != null and query.locationType != ''">and bd.location_type = #{query.locationType}</if>
<if test="query.orderDeviceId != null">and bd.order_device_id = #{query.orderDeviceId}</if>
<if test="query.keyword != null and query.keyword != ''">
and (
bd.sn like concat('%', #{query.keyword}, '%')
@ -155,6 +156,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{item}
</foreach>
</if>
<if test="query.orderDeviceStatusList != null and query.orderDeviceStatusList.size() > 0">
and bod.status in
<foreach item="item" collection="query.orderDeviceStatusList" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="query.center != null and query.center.size() == 2 and query.radius != null">
and #{query.radius} >= round(st_distance_sphere(point(#{query.center[0]}, #{query.center[1]}), point(bd.longitude, bd.latitude)))
</if>

View File

@ -1,5 +1,6 @@
package com.ruoyi.bst.device.service;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@ -8,6 +9,7 @@ import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.dto.DeviceBltUploadDTO;
import com.ruoyi.bst.device.domain.enums.DeviceUnLockType;
import com.ruoyi.bst.device.domain.vo.DeviceIotVO;
import com.ruoyi.bst.device.domain.vo.DeviceOnlineInfo;
public interface DeviceIotService {
@ -154,4 +156,19 @@ public interface DeviceIotService {
*/
int bltUpload(DeviceBltUploadDTO dto);
/**
* 更新设备在线状态
* @param mac 设备mac地址
* @param status 在线状态
* @param at 时间
*/
int updateDeviceOnlineStatus(String mac, String status, LocalDateTime at);
/**
* 获取设备在线状态
* @param mac 设备mac地址
* @return 设备在线状态
*/
DeviceOnlineInfo getDeviceOnlineStatus(String mac);
}

View File

@ -152,11 +152,6 @@ public interface DeviceService
*/
public int clearCurrentOrderDevice(Long id, Long orderDeviceId);
/**
* 根据mac更新在线状态
*/
int updateOnlineStatusByMac(String mac, String onlineStatus);
/**
* 划拨运营区
*/

View File

@ -19,6 +19,7 @@ import com.ruoyi.bst.device.domain.enums.DeviceQuality;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.device.domain.enums.DeviceUnLockType;
import com.ruoyi.bst.device.domain.vo.DeviceIotVO;
import com.ruoyi.bst.device.domain.vo.DeviceOnlineInfo;
import com.ruoyi.bst.device.mapper.DeviceMapper;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
@ -79,6 +80,7 @@ public class DeviceIotServiceImpl implements DeviceIotService {
private final static Integer SUB_FAST = 5; // 上报频率
private final static Integer SUB_SLOW = 300; // 上报频率
private final static Integer SUB_X_SLOW = 50; // 上报频率X开头硬件
@Override
public DeviceIotVO unlock(DeviceVO device, DeviceUnLockType type, String reason, boolean requiredIot) {
@ -154,21 +156,27 @@ public class DeviceIotServiceImpl implements DeviceIotService {
ServiceUtil.assertion(!DeviceStatus.canUserLock().contains(device.getStatus()), "设备%s当前状态不允许用户锁车", device.getSn());
query.setStatusList(DeviceStatus.canUserLock());
}
if (DeviceStatus.TEMP_LOCKED.getCode().equals(data.getStatus())) {
query.setOrderDeviceId(device.getOrderDeviceId());
query.setOrderDeviceStatusList(OrderDeviceStatus.inUse());
}
transactionTemplate.execute(status -> {
// 更新设备状态
int rows = deviceMapper.updateByQuerySimple(data, query);
int rows = deviceMapper.updateByQuery(data, query);
vo.setDb(rows);
if (rows > 0) {
// 发送命令锁车
CommandResponse res = null;
int sub = getDeviceSubSlow(device);
if (DeviceStatus.TEMP_LOCKED.getCode().equals(data.getStatus())) {
// 临时锁车
res = iotService.tempLock(device, SUB_SLOW, reason, 3);
res = iotService.tempLock(device, sub, reason, 3);
} else {
// 锁车
res = iotService.lock(device, SUB_SLOW, reason, 3);
res = iotService.lock(device, sub, reason, 3);
}
boolean iot = IotUtil.isSuccess(res);
ServiceUtil.assertion(requiredIot && !iot, IotUtil.getMsg(res), ServiceCode.IOT_FAILED);
@ -181,6 +189,13 @@ public class DeviceIotServiceImpl implements DeviceIotService {
return vo;
}
public int getDeviceSubSlow(DeviceVO device) {
if (device != null && device.getHardwareVersion() != null && device.getHardwareVersion().startsWith("X")) {
return SUB_X_SLOW;
}
return SUB_SLOW;
}
@Override
public DeviceIotVO qLock(DeviceVO device, String reason, boolean requiredIot) {
if (device == null || device.getId() == null) {
@ -297,12 +312,10 @@ public class DeviceIotServiceImpl implements DeviceIotService {
}
public int updateIot(Device device) {
if (device == null) {
return 0;
}
if (StringUtils.isBlank(device.getMac()) && device.getId() == null) {
if (device == null || StringUtils.isBlank(device.getMac())) {
return 0;
}
Device data = new Device();
data.setMac(device.getMac());
data.setVoltage(device.getVoltage());
@ -320,7 +333,7 @@ public class DeviceIotServiceImpl implements DeviceIotService {
data.setSoftwareVersion(device.getSoftwareVersion());
// 添加到缓存队列
redisCache.rightPush(CacheConstants.DEVICE_UPDATE_IOT_QUEUE, data);
redisCache.setCacheMapValue(CacheConstants.DEVICE_UPDATE_IOT_QUEUE, data.getMac(), data);
return 1;
}
@ -385,7 +398,7 @@ public class DeviceIotServiceImpl implements DeviceIotService {
}
}
if (DeviceLockStatus.CLOSE.getCode().equals(device.getLockStatus()) && !DeviceQuality.CLOSE.getCode().equals(device.getQuality())) {
CommandResponse res = iotService.lock(device, SUB_SLOW, "重新尝试锁车", 1);
CommandResponse res = iotService.lock(device, getDeviceSubSlow(device), "重新尝试锁车", 1);
if (device.getOrderId() != null) {
operLogService.operSysLog("【设备监控】发现未关闭的车辆", IotUtil.isSuccess(res), LogBizType.ORDER, device.getOrderId(), device);
} else {
@ -459,4 +472,37 @@ public class DeviceIotServiceImpl implements DeviceIotService {
return 1;
}
@Override
public int updateDeviceOnlineStatus(String mac, String status, LocalDateTime at) {
if (StringUtils.isBlank(mac) || StringUtils.isBlank(status)) {
return 0;
}
DeviceOnlineInfo info = this.getDeviceOnlineStatus(mac);
if (info == null) {
info = new DeviceOnlineInfo();
}
info.setMac(mac);
info.setStatus(status);
info.setAt(at);
if (DeviceOnlineStatus.ONLINE.getStatus().equals(status)) {
info.setLastOnlineTime(at);
}
String key = CacheConstants.MAC_ONLINE_STATUS + mac;
redisCache.setCacheObject(key, info);
return 1;
}
@Override
public DeviceOnlineInfo getDeviceOnlineStatus(String mac) {
if (StringUtils.isBlank(mac)) {
return null;
}
String key = CacheConstants.MAC_ONLINE_STATUS + mac;
return redisCache.getCacheObject(key);
}
}

View File

@ -1,6 +1,5 @@
package com.ruoyi.bst.device.service.impl;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -34,7 +33,6 @@ import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.service.impl.DeviceOnlineStatus;
import lombok.extern.slf4j.Slf4j;
@ -480,20 +478,6 @@ public class DeviceServiceImpl implements DeviceService
return deviceMapper.clearCurrentOrderDevice(id, orderDeviceId);
}
@Override
public int updateOnlineStatusByMac(String mac, String onlineStatus) {
if (StringUtils.isBlank(mac) || StringUtils.isBlank(onlineStatus)) {
return 0;
}
Device data = new Device();
data.setOnlineStatus(onlineStatus);
if (DeviceOnlineStatus.ONLINE.getStatus().equals(onlineStatus)) {
data.setLastTime(LocalDateTime.now());
}
data.setMac(mac);
return deviceIotService.updateIot(data);
}
@Override
public int transfer(List<DeviceVO> deviceList, Long areaId) {
if (CollectionUtils.isEmptyElement(deviceList) || areaId == null) {

View File

@ -93,13 +93,15 @@ public class OrderPayHandlerImpl implements PayHandler {
int start = orderDeviceService.start(orderDevice);
ServiceUtil.assertion(start != 1, "开始使用ID为%s的订单设备失败", orderDevice.getId());
return rows;
});
if (result != null && result == 1) {
// 解锁设备
DeviceIotVO unlock = deviceIotService.unlock(orderDevice.getDeviceId(), DeviceUnLockType.USER, "订单支付成功开锁:" + order.getNo(), false);
operLogService.operSysLog("订单支付成功开锁", unlock.isIotSuccess(), LogBizType.ORDER, order.getId(), orderDevice.getDeviceId(), orderDevice.getDeviceLongitude(), orderDevice.getDeviceLatitude());
ServiceUtil.assertion(unlock.getDb() <= 0, "ID为%s的设备解锁失败", orderDevice.getDeviceId());
return rows;
});
}
return result != null && result == 1;
}

View File

@ -112,6 +112,9 @@ public class IotReceiveServiceImpl implements IotReceiveService {
// 增加更新频率控制
this.updateDeviceIot(device);
// 更新设备在线状态
deviceIotService.updateDeviceOnlineStatus(device.getMac(), DeviceOnlineStatus.ONLINE.getStatus(), at);
// 定位无效不处理
if (device.getLongitude() == null || device.getLatitude() == null) {
log.info("设备{}定位无效,不处理", device.getMac());

View File

@ -1,5 +1,6 @@
package com.ruoyi.iot.service.impl;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -17,7 +18,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.bst.commandLog.domain.CommandLog;
import com.ruoyi.bst.commandLog.domain.enums.CommandLogType;
import com.ruoyi.bst.commandLog.service.CommandLogService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.model.LoginUser;
@ -87,7 +88,7 @@ public class IotServiceImpl implements IotService {
private RedisLock redisLock;
@Autowired
private DeviceService deviceService;
private DeviceIotService deviceIotService;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
@ -306,18 +307,8 @@ public class IotServiceImpl implements IotService {
String status = this.parseToOnlineStatus(res, deviceName, true);
redisCache.setCacheObject(this.getOnlineCacheKey(deviceName), status, 10, TimeUnit.SECONDS);
// 异步更新设备在线状态
boolean lock = redisLock.lock(RedisLockKey.DEVICE_UPDATE_IOT_LOCK, deviceName, 60L);
if (lock ) {
scheduledExecutorService.execute(() -> {
int update = deviceService.updateOnlineStatusByMac(deviceName, status);
if (update != 1) {
log.error("异步更新设备在线状态失败,MAC={},status={}", deviceName, status);
}
});
} else {
log.info("设备{}更新太频繁,跳过本次更新", deviceName);
}
// 更新设备在线状态
deviceIotService.updateDeviceOnlineStatus(deviceName, status, LocalDateTime.now());
return res;
} catch (Exception e) {

View File

@ -4,7 +4,6 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import com.ruoyi.common.utils.collection.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -14,6 +13,7 @@ import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.service.impl.DeviceOnlineStatus;
import lombok.extern.slf4j.Slf4j;
@ -53,7 +53,7 @@ public class DeviceTask {
public void refreshIot() {
// 从队列中获取数据
List<Device> deviceList = redisCache.getAndClearCacheList(CacheConstants.DEVICE_UPDATE_IOT_QUEUE);
List<Device> deviceList = redisCache.getAndClearHashValues(CacheConstants.DEVICE_UPDATE_IOT_QUEUE);
if (CollectionUtils.isEmptyElement(deviceList)) {
return ;
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.web.app;
import java.math.BigDecimal;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@ -9,7 +10,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.device.service.DeviceAssembler;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
@ -24,6 +27,9 @@ public class AppDeviceController extends BaseController {
@Autowired
private DeviceService deviceService;
@Autowired
private DeviceAssembler deviceAssembler;
@ApiOperation("获取附近可用车辆列表")
@GetMapping("/listNearBy")
@Anonymous
@ -38,14 +44,18 @@ public class AppDeviceController extends BaseController {
return error("中心坐标格式错误,必须包含经度和纬度");
}
query.setStatus(DeviceStatus.AVAILABLE.getCode());
return success(deviceService.selectDeviceList(query));
List<DeviceVO> list = deviceService.selectDeviceList(query);
deviceAssembler.assembleOnlineStatus(list);
return success(list);
}
@ApiOperation("获取可用车辆详情")
@GetMapping("/availableDetail")
@Anonymous
public AjaxResult getAvailableDetail(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
return success(deviceService.selectAvaliableDevice(id, sn));
DeviceVO device = deviceService.selectAvaliableDevice(id, sn);
deviceAssembler.assembleOnlineStatus(device);
return success(device);
}
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.web.app;
import java.math.BigDecimal;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@ -17,6 +18,8 @@ import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.LogBizType;
import com.ruoyi.common.utils.MathUtils;
@ -36,6 +39,36 @@ public class AppDeviceIotController extends BaseController {
@Autowired
private DeviceService deviceService;
@Autowired
private RedisLock redisLock;
/**
* 执行带设备锁的操作
* @param device 设备信息
* @param operation 具体操作
* @return 操作结果
*/
private AjaxResult executeWithLock(Long deviceId, Supplier<AjaxResult> operation) {
ServiceUtil.assertion(deviceId == null, "设备ID不能为空");
// 获取设备锁
boolean lock = redisLock.lock(RedisLockKey.DEVICE_IOT_OPERATION, deviceId);
if (!lock) {
return AjaxResult.error("设备操作过于频繁,请稍后再试");
}
try {
return operation.get();
} finally {
redisLock.unlock(RedisLockKey.DEVICE_IOT_OPERATION, deviceId);
}
}
private DeviceVO getDevice(Long id, String sn) {
if (id != null) {
return deviceService.selectDeviceById(id);
} else {
return deviceService.selectDeviceBySn(sn);
}
}
@ApiOperation("用户响铃寻车")
@PutMapping("/ring")
@ -44,12 +77,7 @@ public class AppDeviceIotController extends BaseController {
@RequestParam(required = false) String sn,
@RequestParam(required = false) BigDecimal lon,
@RequestParam(required = false) BigDecimal lat) {
DeviceVO device = null;
if (id != null) {
device = deviceService.selectDeviceById(id);
} else {
device = deviceService.selectDeviceBySn(sn);
}
DeviceVO device = getDevice(id, sn);
ServiceUtil.assertion(device == null, "当前车辆不存在,无法响铃寻车");
if (device.getAreaRequiredRingRadius() != null && device.getAreaRequiredRingRadius()) {
@ -59,7 +87,9 @@ public class AppDeviceIotController extends BaseController {
"您当前距离车辆%s米无法响铃寻车。请距离车辆%s米以内再试", distance, device.getAreaRingRadius());
}
return toAjax(deviceIotService.play(device, IotConstants.PLAY_WARNING, "用户响铃寻车", true));
return executeWithLock(device.getId(), () -> {
return toAjax(deviceIotService.play(device, IotConstants.PLAY_WARNING, "用户响铃寻车", true));
});
}
@ApiOperation("蓝牙上传设备信息")

View File

@ -1,6 +1,7 @@
package com.ruoyi.web.app;
import java.util.List;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@ -33,6 +34,8 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.LogBizType;
import com.ruoyi.common.utils.LogParamHolder;
@ -53,6 +56,27 @@ public class AppOrderController extends BaseController {
@Autowired
private OrderValidator orderValidator;
@Autowired
private RedisLock redisLock;
/**
* 执行带设备锁的操作
* @param device 设备信息
* @param operation 具体操作
* @return 操作结果
*/
private AjaxResult executeWithLock(Long deviceId, Supplier<AjaxResult> operation) {
ServiceUtil.assertion(deviceId == null, "设备ID不能为空");
Long lockKey = deviceId;
boolean lock = redisLock.lock(RedisLockKey.DEVICE_IOT_OPERATION, lockKey);
ServiceUtil.assertion(!lock, "设备操作过于频繁,请稍后再试");
try {
return operation.get();
} finally {
redisLock.unlock(RedisLockKey.DEVICE_IOT_OPERATION, lockKey);
}
}
@ApiOperation("获取我的订单列表")
@GetMapping("/mineList")
public TableDataInfo getMineList(OrderQuery query) {
@ -149,7 +173,9 @@ public class AppOrderController extends BaseController {
if (dto.getRequiredIot() == null) {
dto.setRequiredIot(true);
}
return success(orderService.openDevice(dto));
return executeWithLock(order.getDeviceId(), () -> {
return success(orderService.openDevice(dto));
});
}
@ApiOperation("临时锁车")
@ -167,7 +193,9 @@ public class AppOrderController extends BaseController {
if (dto.getRequiredIot() == null) {
dto.setRequiredIot(true);
}
return success(orderService.closeDevice(dto));
return executeWithLock(order.getDeviceId(), () -> {
return success(orderService.closeDevice(dto));
});
}
@ApiOperation("换车")

View File

@ -1,12 +1,9 @@
package com.ruoyi.web.bst;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.bst.areaJoin.domain.enums.AreaJoinPermission;
import com.ruoyi.common.utils.collection.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
@ -21,10 +18,12 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.bst.area.service.AreaValidator;
import com.ruoyi.bst.areaJoin.domain.enums.AreaJoinPermission;
import com.ruoyi.bst.device.domain.Device;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.dto.DeviceTransferDTO;
import com.ruoyi.bst.device.service.DeviceAssembler;
import com.ruoyi.bst.device.service.DeviceConverter;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
@ -68,6 +67,8 @@ public class DeviceController extends BaseController
@Autowired
private DeviceIotService deviceIotService;
@Autowired
private DeviceAssembler deviceAssembler;
/**
* 查询设备列表
@ -84,6 +85,7 @@ public class DeviceController extends BaseController
if (query.getRefresh() != null && query.getRefresh()) {
deviceIotService.refresh(list, IotConstants.ONLINE_TYPE_COMMAND);
}
deviceAssembler.assembleOnlineStatus(list);
return getDataTable(list);
}
@ -102,6 +104,7 @@ public class DeviceController extends BaseController
if (query.getRefresh() != null && query.getRefresh()) {
deviceIotService.refresh(list, IotConstants.ONLINE_TYPE_COMMAND);
}
deviceAssembler.assembleOnlineStatus(list);
return success(list);
}
@ -134,6 +137,7 @@ public class DeviceController extends BaseController
if (refresh) {
deviceIotService.refresh(device, IotConstants.ONLINE_TYPE_COMMAND);
}
deviceAssembler.assembleOnlineStatus(device);
return success(device);
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.web.bst;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PutMapping;
@ -16,12 +18,18 @@ import com.ruoyi.bst.device.service.DeviceValidator;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.LogBizType;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.iot.constants.IotConstants;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping("/bst/device/iot")
@Slf4j
public class DeviceIotController extends BaseController {
@Autowired
@ -33,16 +41,39 @@ public class DeviceIotController extends BaseController {
@Autowired
private DeviceValidator deviceValidator;
@Autowired
private RedisLock redisLock;
private DeviceVO getDevice(Long id, String sn) {
return deviceService.selectDevice(id, sn, true, AreaJoinPermission.DEVICE_EDIT);
}
/**
* 执行带设备锁的操作
* @param device 设备信息
* @param operation 具体操作
* @return 操作结果
*/
private AjaxResult executeWithLock(DeviceVO device, Supplier<AjaxResult> operation) {
if (device == null) {
return toAjax(0);
}
Long lockKey = device.getId();
boolean lock = redisLock.lock(RedisLockKey.DEVICE_IOT_OPERATION, lockKey);
ServiceUtil.assertion(!lock, "设备操作过于频繁,请稍后再试");
try {
return operation.get();
} finally {
redisLock.unlock(RedisLockKey.DEVICE_IOT_OPERATION, lockKey);
}
}
@PreAuthorize("@ss.hasPermi('bst:device:unlock')")
@Log(title = "管理员开锁", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
@PutMapping("/unlock")
public AjaxResult unlock(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
DeviceVO device = this.getDevice(id, sn);
return success(deviceIotService.unlock(device, DeviceUnLockType.ADMIN, "管理员开锁", true));
return executeWithLock(device, () -> success(deviceIotService.unlock(device, DeviceUnLockType.ADMIN, "管理员开锁", true)));
}
@PreAuthorize("@ss.hasPermi('bst:device:lock')")
@ -50,7 +81,7 @@ public class DeviceIotController extends BaseController {
@PutMapping("/lock")
public AjaxResult lock(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
DeviceVO device = this.getDevice(id, sn);
return success(deviceIotService.lock(device, true, "管理员锁车", true));
return executeWithLock(device, () -> success(deviceIotService.lock(device, true, "管理员锁车", true)));
}
@PreAuthorize("@ss.hasPermi('bst:device:ring')")
@ -58,7 +89,7 @@ public class DeviceIotController extends BaseController {
@PutMapping("/ring")
public AjaxResult ring(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
DeviceVO device = this.getDevice(id, sn);
return toAjax(deviceIotService.play(device, IotConstants.PLAY_WARNING, "管理员响铃寻车", true));
return executeWithLock(device, () -> success(deviceIotService.play(device, IotConstants.PLAY_WARNING, "管理员响铃寻车", true)));
}
@PreAuthorize("@ss.hasPermi('bst:device:reboot')")
@ -66,7 +97,7 @@ public class DeviceIotController extends BaseController {
@PutMapping("/reboot")
public AjaxResult reboot(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
DeviceVO device = this.getDevice(id, sn);
return toAjax(deviceIotService.reboot(device, "管理员重启", true));
return executeWithLock(device, () -> success(deviceIotService.reboot(device, "管理员重启", true)));
}
@PreAuthorize("@ss.hasPermi('bst:device:unlockSeat')")
@ -74,7 +105,7 @@ public class DeviceIotController extends BaseController {
@PutMapping("/unlockSeat")
public AjaxResult unlockSeat(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
DeviceVO device = this.getDevice(id, sn);
return toAjax(deviceIotService.unlockSeat(device, "管理员开坐垫锁", true));
return executeWithLock(device, () -> success(deviceIotService.unlockSeat(device, "管理员开坐垫锁", true)));
}
@PreAuthorize("@ss.hasPermi('bst:device:refresh')")
@ -82,8 +113,10 @@ public class DeviceIotController extends BaseController {
@PutMapping("/refresh")
public AjaxResult refresh(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
DeviceVO device = this.getDevice(id, sn);
deviceIotService.refresh(device, IotConstants.ONLINE_TYPE_COMMAND);
return success(device);
return executeWithLock(device, () -> {
deviceIotService.refresh(device, IotConstants.ONLINE_TYPE_COMMAND);
return success(device);
});
}
@PreAuthorize("@ss.hasPermi('bst:device:music')")
@ -91,6 +124,6 @@ public class DeviceIotController extends BaseController {
@PutMapping("/music")
public AjaxResult music(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn, @RequestParam String music) {
DeviceVO device = this.getDevice(id, sn);
return success(deviceIotService.setMusic(device, music, "管理员设置声音", true));
return executeWithLock(device, () -> success(deviceIotService.setMusic(device, music, "管理员设置声音", true)));
}
}

View File

@ -128,7 +128,7 @@ iot:
# iot秘钥
accessKey: dJqF0qYhUbK/o1Pr9I5qxNoP14FlJLC+BFK2ZTjUX+lnKwoNYvBYsM/7Xu1ERIzSkUoxVkP/N1RMvGlBKMoBtA==
# 超时响应时间(秒)
timeout: 10
timeout: 5
# token过期时间
daysToExpire: 100
# 推送消息token