feat(ops): 工牌实时状态增加物理位置、电量和工单信息
BadgeRealtimeStatusRespDTO 新增物理位置(IoT 轨迹检测 RPC)、 电量(IoT 设备属性 RPC)、当前工单信息三个维度。 RPC 调用改为串行执行避免占用 ForkJoinPool 公共线程。 设备状态写入 Redis 时同步写入区域名称。 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package com.viewsh.module.ops.environment.service.badge;
|
||||
|
||||
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
||||
import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import com.viewsh.module.ops.enums.BadgeDeviceStatusEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
@@ -48,6 +50,9 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
|
||||
@Resource
|
||||
private OpsOrderMapper opsOrderMapper;
|
||||
|
||||
@Resource
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
|
||||
@Resource
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@@ -400,6 +405,15 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
|
||||
}
|
||||
if (areaId != null) {
|
||||
stringRedisTemplate.opsForHash().put(key, "currentAreaId", String.valueOf(areaId));
|
||||
// 同步写入区域名称
|
||||
try {
|
||||
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
|
||||
if (area != null && area.getAreaName() != null) {
|
||||
stringRedisTemplate.opsForHash().put(key, "currentAreaName", area.getAreaName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询区域名称失败: areaId={}", areaId, e);
|
||||
}
|
||||
}
|
||||
if (beaconMac != null) {
|
||||
stringRedisTemplate.opsForHash().put(key, "beaconMac", beaconMac);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
package com.viewsh.module.ops.environment.service.badge;
|
||||
|
||||
import com.viewsh.framework.common.pojo.CommonResult;
|
||||
import com.viewsh.module.iot.api.device.IotDeviceControlApi;
|
||||
import com.viewsh.module.iot.api.device.IotDevicePropertyQueryApi;
|
||||
import com.viewsh.module.iot.api.device.dto.IotDeviceServiceInvokeReqDTO;
|
||||
import com.viewsh.module.iot.api.trajectory.DeviceLocationDTO;
|
||||
import com.viewsh.module.iot.api.trajectory.TrajectoryStateApi;
|
||||
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
||||
import com.viewsh.module.ops.api.clean.BadgeRealtimeStatusRespDTO;
|
||||
import com.viewsh.module.ops.api.clean.BadgeStatusRespDTO;
|
||||
import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO;
|
||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||
import com.viewsh.module.ops.environment.service.badge.dto.BadgeNotifyReqDTO;
|
||||
import com.viewsh.module.ops.service.area.AreaDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -39,8 +45,22 @@ public class CleanBadgeServiceImpl implements CleanBadgeService {
|
||||
@Resource
|
||||
private IotDeviceControlApi iotDeviceControlApi;
|
||||
|
||||
@Resource
|
||||
private TrajectoryStateApi trajectoryStateApi;
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyQueryApi iotDevicePropertyQueryApi;
|
||||
|
||||
@Resource
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
|
||||
private static final String NOTIFY_IDENTIFIER = "NOTIFY";
|
||||
|
||||
/**
|
||||
* IoT 设备属性标识符:电池电量
|
||||
*/
|
||||
private static final String BATTERY_LEVEL_IDENTIFIER = "batteryLevel";
|
||||
|
||||
@Override
|
||||
public List<BadgeStatusRespDTO> getBadgeStatusList(Long areaId, String status) {
|
||||
try {
|
||||
@@ -83,28 +103,52 @@ public class CleanBadgeServiceImpl implements CleanBadgeService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工牌实时状态详情
|
||||
* <p>
|
||||
* 聚合三个数据源:
|
||||
* 1. 工牌设备状态(Redis,本地)
|
||||
* 2. 工牌物理位置(IoT TrajectoryStateApi,RPC)
|
||||
* 3. 设备电量(IoT IotDevicePropertyQueryApi,RPC)
|
||||
*/
|
||||
@Override
|
||||
public BadgeRealtimeStatusRespDTO getBadgeRealtimeStatus(Long badgeId) {
|
||||
try {
|
||||
// 1. 获取工牌状态
|
||||
// 1. 获取工牌设备状态(Redis)
|
||||
BadgeDeviceStatusDTO status = badgeDeviceStatusService.getBadgeStatus(badgeId);
|
||||
if (status == null) {
|
||||
log.warn("[getBadgeRealtimeStatus] 工牌状态不存在: badgeId={}", badgeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 构建响应
|
||||
return BadgeRealtimeStatusRespDTO.builder()
|
||||
// 2. 查询工牌物理位置 + 电量
|
||||
DeviceLocationDTO location = queryPhysicalLocation(badgeId);
|
||||
Integer batteryLevel = queryBatteryLevel(badgeId);
|
||||
|
||||
// 3. 组装响应
|
||||
BadgeRealtimeStatusRespDTO.BadgeRealtimeStatusRespDTOBuilder builder = BadgeRealtimeStatusRespDTO.builder()
|
||||
.deviceId(status.getDeviceId())
|
||||
.deviceKey(status.getDeviceCode())
|
||||
.status(status.getStatusCode())
|
||||
.batteryLevel(status.getBatteryLevel())
|
||||
.lastHeartbeatTime(formatTimestamp(status.getLastHeartbeatTime()))
|
||||
.rssi(null) // RSSI 需要从 IoT 模块获取,暂不实现
|
||||
.isInArea(status.getCurrentAreaId() != null)
|
||||
.areaId(status.getCurrentAreaId())
|
||||
.areaName(status.getCurrentAreaName())
|
||||
.build();
|
||||
.batteryLevel(batteryLevel)
|
||||
.onlineTime(formatTimestamp(status.getLastHeartbeatTime()));
|
||||
|
||||
// 物理位置
|
||||
if (location != null && Boolean.TRUE.equals(location.getInArea())) {
|
||||
builder.isInArea(true)
|
||||
.areaId(location.getAreaId())
|
||||
.areaName(queryAreaNameById(location.getAreaId()));
|
||||
} else {
|
||||
builder.isInArea(false);
|
||||
}
|
||||
|
||||
// 当前工单信息
|
||||
builder.currentOrderId(status.getCurrentOpsOrderId())
|
||||
.currentOrderStatus(status.getCurrentOrderStatus())
|
||||
.orderAreaId(status.getCurrentAreaId())
|
||||
.orderAreaName(status.getCurrentAreaName());
|
||||
|
||||
return builder.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[getBadgeRealtimeStatus] 查询工牌实时状态失败: badgeId={}", badgeId, e);
|
||||
@@ -112,6 +156,61 @@ public class CleanBadgeServiceImpl implements CleanBadgeService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工牌物理位置(来自 IoT 轨迹检测 RPC)
|
||||
*
|
||||
* @return 位置信息,查询失败返回 null
|
||||
*/
|
||||
private DeviceLocationDTO queryPhysicalLocation(Long badgeId) {
|
||||
try {
|
||||
CommonResult<DeviceLocationDTO> result = trajectoryStateApi.getCurrentLocation(badgeId);
|
||||
if (result != null && result.isSuccess()) {
|
||||
return result.getData();
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.warn("[getBadgeRealtimeStatus] 查询工牌物理位置失败,降级为不在区域: badgeId={}", badgeId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询电量(来自 IoT 设备属性 RPC)
|
||||
*
|
||||
* @return 电量百分比(0-100),查询失败返回 null
|
||||
*/
|
||||
private Integer queryBatteryLevel(Long badgeId) {
|
||||
try {
|
||||
CommonResult<Map<String, Object>> result = iotDevicePropertyQueryApi.getLatestProperties(badgeId);
|
||||
if (result != null && result.isSuccess() && result.getData() != null) {
|
||||
Object batteryObj = result.getData().get(BATTERY_LEVEL_IDENTIFIER);
|
||||
if (batteryObj instanceof Number) {
|
||||
return ((Number) batteryObj).intValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.warn("[getBadgeRealtimeStatus] 查询工牌电量失败: badgeId={}", badgeId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据区域ID查询区域名称
|
||||
*/
|
||||
private String queryAreaNameById(Long areaId) {
|
||||
if (areaId == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
|
||||
return area != null ? area.queryAreaNameById() : null;
|
||||
} catch (Exception e) {
|
||||
log.warn("[getBadgeRealtimeStatus] 查询区域名称失败: areaId={}", areaId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBadgeNotify(BadgeNotifyReqDTO req) {
|
||||
try {
|
||||
|
||||
@@ -9,7 +9,10 @@ import lombok.NoArgsConstructor;
|
||||
/**
|
||||
* 工牌实时状态详情响应 DTO
|
||||
* <p>
|
||||
* 用于工牌详情页展示
|
||||
* 用于工牌详情页展示,包含:
|
||||
* - 设备基础信息(状态、电量、上线时间)
|
||||
* - 工牌物理位置(来自 IoT 轨迹检测)
|
||||
* - 当前工单信息(来自工牌状态 Redis)
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@@ -20,6 +23,8 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class BadgeRealtimeStatusRespDTO {
|
||||
|
||||
// ==================== 设备基础信息 ====================
|
||||
|
||||
@Schema(description = "设备ID", example = "3001")
|
||||
private Long deviceId;
|
||||
|
||||
@@ -32,18 +37,31 @@ public class BadgeRealtimeStatusRespDTO {
|
||||
@Schema(description = "电量(0-100)", example = "72")
|
||||
private Integer batteryLevel;
|
||||
|
||||
@Schema(description = "最后心跳时间", example = "2026-01-23 15:00:30")
|
||||
private String lastHeartbeatTime;
|
||||
@Schema(description = "设备上线时间", example = "2026-01-23 15:00:30")
|
||||
private String onlineTime;
|
||||
|
||||
@Schema(description = "信号强度(dBm)", example = "-42")
|
||||
private Integer rssi;
|
||||
// ==================== 工牌物理位置(轨迹检测) ====================
|
||||
|
||||
@Schema(description = "是否在区域内", example = "true")
|
||||
@Schema(description = "是否在区域内(基于 IoT 信标检测)", example = "true")
|
||||
private Boolean isInArea;
|
||||
|
||||
@Schema(description = "当前区域ID", example = "101")
|
||||
@Schema(description = "当前物理所在区域ID", example = "101")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "当前区域名称", example = "A区洗手间")
|
||||
@Schema(description = "当前物理所在区域名称", example = "A区洗手间")
|
||||
private String areaName;
|
||||
|
||||
// ==================== 当前工单信息 ====================
|
||||
|
||||
@Schema(description = "当前工单ID", example = "1234567890")
|
||||
private Long currentOrderId;
|
||||
|
||||
@Schema(description = "当前工单状态", example = "ARRIVED")
|
||||
private String currentOrderStatus;
|
||||
|
||||
@Schema(description = "工单目标区域ID", example = "101")
|
||||
private Long orderAreaId;
|
||||
|
||||
@Schema(description = "工单目标区域名称", example = "A区洗手间")
|
||||
private String orderAreaName;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.viewsh.module.ops.framework.rpc.config;
|
||||
|
||||
import com.viewsh.module.infra.api.file.FileApi;
|
||||
import com.viewsh.module.iot.api.device.IotDeviceControlApi;
|
||||
import com.viewsh.module.iot.api.device.IotDevicePropertyQueryApi;
|
||||
import com.viewsh.module.iot.api.device.IotDeviceQueryApi;
|
||||
import com.viewsh.module.iot.api.device.IotDeviceStatusQueryApi;
|
||||
import com.viewsh.module.iot.api.trajectory.TrajectoryStateApi;
|
||||
@@ -17,6 +18,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
AdminUserApi.class,
|
||||
SocialUserApi.class,
|
||||
IotDeviceControlApi.class,
|
||||
IotDevicePropertyQueryApi.class,
|
||||
IotDeviceQueryApi.class,
|
||||
IotDeviceStatusQueryApi.class,
|
||||
TrajectoryStateApi.class,
|
||||
|
||||
Reference in New Issue
Block a user