diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomPlatformIconController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomPlatformIconController.java index 4af5fca..e0b97b4 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomPlatformIconController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomPlatformIconController.java @@ -2,6 +2,7 @@ package com.ruoyi.web.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import com.ruoyi.common.core.controller.BaseController; @@ -21,6 +22,9 @@ public class RoomPlatformIconController extends BaseController { @Autowired private IRoomPlatformIconService roomPlatformIconService; + @Autowired + private RedisTemplate redisTemplate; + /** * 按房间ID查询该房间下所有地图平台图标(不分页) */ @@ -53,12 +57,19 @@ public class RoomPlatformIconController extends BaseController { } /** - * 删除 + * 删除(同步删除该实例在 Redis 中的平台样式,避免残留威力区/探测区) */ @PreAuthorize("@ss.hasPermi('system:roomPlatformIcon:remove')") @Log(title = "房间地图平台图标", businessType = BusinessType.DELETE) @DeleteMapping("/{id}") public AjaxResult remove(@PathVariable Long id) { + RoomPlatformIcon icon = roomPlatformIconService.selectById(id); + if (icon != null && icon.getRoomId() != null) { + String key = "room:" + icon.getRoomId() + ":platformIcons:platforms"; + redisTemplate.opsForHash().delete(key, String.valueOf(id)); + String oldKey = "room:" + icon.getRoomId() + ":route:0:platforms"; + redisTemplate.opsForHash().delete(oldKey, String.valueOf(id)); + } return toAjax(roomPlatformIconService.deleteById(id)); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java index 1048757..ee09762 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java @@ -58,34 +58,97 @@ public class RoutesController extends BaseController /** * 保存平台样式到 Redis + * 独立平台(routeId=0):若传 platformIconInstanceId 则按实例存储,每个拖上去的图标一套样式;否则按 platformId 存储(兼容旧逻辑)。 */ @PreAuthorize("@ss.hasPermi('system:routes:edit')") @PostMapping("/savePlatformStyle") public AjaxResult savePlatformStyle(@RequestBody PlatformStyleDTO dto) { - if (dto.getRoomId() == null || dto.getRouteId() == null || dto.getPlatformId() == null) { + if (dto.getRoomId() == null || dto.getRouteId() == null) { return AjaxResult.error("参数不完整"); } - String key = "room:" + dto.getRoomId() + ":route:" + dto.getRouteId() + ":platforms"; - redisTemplate.opsForHash().put(key, String.valueOf(dto.getPlatformId()), JSON.toJSONString(dto)); + Long routeId = dto.getRouteId(); + boolean isPlatformIcon = routeId != null && routeId == 0L; + if (isPlatformIcon) { + if (dto.getPlatformIconInstanceId() == null && dto.getPlatformId() == null) { + return AjaxResult.error("独立平台样式需传 platformIconInstanceId 或 platformId"); + } + } else { + if (dto.getPlatformId() == null) { + return AjaxResult.error("参数不完整"); + } + } + + String key = isPlatformIcon + ? ("room:" + dto.getRoomId() + ":platformIcons:platforms") + : ("room:" + dto.getRoomId() + ":route:" + routeId + ":platforms"); + String hashField = isPlatformIcon && dto.getPlatformIconInstanceId() != null + ? String.valueOf(dto.getPlatformIconInstanceId()) + : String.valueOf(dto.getPlatformId()); + redisTemplate.opsForHash().put(key, hashField, JSON.toJSONString(dto)); + + if (isPlatformIcon && dto.getPlatformIconInstanceId() != null) { + String oldKey = "room:" + dto.getRoomId() + ":route:0:platforms"; + redisTemplate.opsForHash().delete(oldKey, hashField); + } else if (isPlatformIcon) { + String oldKey = "room:" + dto.getRoomId() + ":route:0:platforms"; + redisTemplate.opsForHash().delete(oldKey, String.valueOf(dto.getPlatformId())); + } return success(); } /** * 从 Redis 获取平台样式 + * 独立平台(routeId=0):优先用 platformIconInstanceId 取该实例样式;未传时用 platformId(兼容旧数据)。 */ @PreAuthorize("@ss.hasPermi('system:routes:query')") @GetMapping("/getPlatformStyle") public AjaxResult getPlatformStyle(PlatformStyleDTO dto) { - if (dto.getRoomId() == null || dto.getRouteId() == null || dto.getPlatformId() == null) { + if (dto.getRoomId() == null || dto.getRouteId() == null) { return AjaxResult.error("参数不完整"); } - String key = "room:" + dto.getRoomId() + ":route:" + dto.getRouteId() + ":platforms"; - Object val = redisTemplate.opsForHash().get(key, String.valueOf(dto.getPlatformId())); - if (val != null) { - return success(JSON.parseObject(val.toString(), PlatformStyleDTO.class)); + Long routeId = dto.getRouteId(); + boolean isPlatformIcon = routeId != null && routeId == 0L; + if (isPlatformIcon) { + if (dto.getPlatformIconInstanceId() == null && dto.getPlatformId() == null) { + return AjaxResult.error("独立平台样式需传 platformIconInstanceId 或 platformId"); + } + } else { + if (dto.getPlatformId() == null) { + return AjaxResult.error("参数不完整"); + } } + + String key = isPlatformIcon + ? ("room:" + dto.getRoomId() + ":platformIcons:platforms") + : ("room:" + dto.getRoomId() + ":route:" + routeId + ":platforms"); + String hashField = isPlatformIcon && dto.getPlatformIconInstanceId() != null + ? String.valueOf(dto.getPlatformIconInstanceId()) + : String.valueOf(dto.getPlatformId()); + Object val = redisTemplate.opsForHash().get(key, hashField); + if (val == null && isPlatformIcon) { + String oldKey = "room:" + dto.getRoomId() + ":route:0:platforms"; + val = redisTemplate.opsForHash().get(oldKey, hashField); + } + if (val != null) return success(JSON.parseObject(val.toString(), PlatformStyleDTO.class)); + return success(); + } + + /** + * 删除独立平台图标样式(按实例删除,避免删除图标后仍残留威力区/探测区) + */ + @PreAuthorize("@ss.hasPermi('system:routes:edit')") + @DeleteMapping("/platformIconStyle") + public AjaxResult deletePlatformIconStyle(@RequestParam Long roomId, @RequestParam Long platformIconInstanceId) + { + if (roomId == null || platformIconInstanceId == null) { + return AjaxResult.error("roomId 与 platformIconInstanceId 不能为空"); + } + String key = "room:" + roomId + ":platformIcons:platforms"; + redisTemplate.opsForHash().delete(key, String.valueOf(platformIconInstanceId)); + String oldKey = "room:" + roomId + ":route:0:platforms"; + redisTemplate.opsForHash().delete(oldKey, String.valueOf(platformIconInstanceId)); return success(); } @@ -440,10 +503,16 @@ public class RoutesController extends BaseController int rows = routesService.deleteRoutesByIds(ids); if (rows > 0) { for (Long routeId : ids) { + // 清除该航线在所有房间下的导弹参数 Set keys = redisTemplate.keys("missile:params:*:" + routeId + ":*"); if (keys != null && !keys.isEmpty()) { redisTemplate.delete(keys); } + // 同步删除该航线在所有房间下的平台样式(探测区/威力区等),避免库表已删但 Redis 仍残留 + Set styleKeys = redisTemplate.keys("room:*:route:" + routeId + ":platforms"); + if (styleKeys != null && !styleKeys.isEmpty()) { + redisTemplate.delete(styleKeys); + } } } return toAjax(rows); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/PlatformStyleDTO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/PlatformStyleDTO.java index 7d9ef64..f9af395 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/PlatformStyleDTO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/PlatformStyleDTO.java @@ -1,6 +1,7 @@ package com.ruoyi.system.domain.dto; import java.io.Serializable; +import java.util.List; /** * 平台样式 DTO @@ -11,7 +12,10 @@ public class PlatformStyleDTO implements Serializable { private String roomId; private Long routeId; private Long platformId; - + + /** 独立平台图标实例 ID(拖到地图上的每条记录的 id,非平台类型 id)。routeId=0 时用此字段作 Redis hash field,实现每个实例独立样式 */ + private Long platformIconInstanceId; + /** 平台名称 */ private String platformName; @@ -27,6 +31,12 @@ public class PlatformStyleDTO implements Serializable { /** 平台颜色 */ private String platformColor; + /** 多探测区配置(支持叠加与独立显隐) */ + private List detectionZones; + + /** 多威力区配置(支持叠加与独立显隐) */ + private List powerZones; + /** 探测区半径(千米),整圆 */ private Double detectionZoneRadius; /** 探测区填充颜色 */ @@ -47,6 +57,151 @@ public class PlatformStyleDTO implements Serializable { /** 威力区是否在地图上显示 */ private Boolean powerZoneVisible; + public static class DetectionZoneDTO implements Serializable { + private static final long serialVersionUID = 1L; + + /** zone 唯一 id(前端生成,后端原样保存) */ + private String zoneId; + + /** 探测区半径(千米) */ + private Double radiusKm; + + /** 探测区填充颜色(css rgba 或 hex) */ + private String color; + + /** 探测区透明度 0-1 */ + private Double opacity; + + /** 是否在地图上显示 */ + private Boolean visible; + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public Double getRadiusKm() { + return radiusKm; + } + + public void setRadiusKm(Double radiusKm) { + this.radiusKm = radiusKm; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public Double getOpacity() { + return opacity; + } + + public void setOpacity(Double opacity) { + this.opacity = opacity; + } + + public Boolean getVisible() { + return visible; + } + + public void setVisible(Boolean visible) { + this.visible = visible; + } + } + + public static class PowerZoneDTO implements Serializable { + private static final long serialVersionUID = 1L; + + /** zone 唯一 id(前端生成,后端原样保存) */ + private String zoneId; + + /** 威力区半径(千米) */ + private Double radiusKm; + + /** 威力区扇形夹角(度) */ + private Double angleDeg; + + /** 威力区填充颜色(css rgba 或 hex) */ + private String color; + + /** 威力区透明度 0-1 */ + private Double opacity; + + /** 是否在地图上显示 */ + private Boolean visible; + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public Double getRadiusKm() { + return radiusKm; + } + + public void setRadiusKm(Double radiusKm) { + this.radiusKm = radiusKm; + } + + public Double getAngleDeg() { + return angleDeg; + } + + public void setAngleDeg(Double angleDeg) { + this.angleDeg = angleDeg; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public Double getOpacity() { + return opacity; + } + + public void setOpacity(Double opacity) { + this.opacity = opacity; + } + + public Boolean getVisible() { + return visible; + } + + public void setVisible(Boolean visible) { + this.visible = visible; + } + } + + public List getDetectionZones() { + return detectionZones; + } + + public void setDetectionZones(List detectionZones) { + this.detectionZones = detectionZones; + } + + public List getPowerZones() { + return powerZones; + } + + public void setPowerZones(List powerZones) { + this.powerZones = powerZones; + } + public String getRoomId() { return roomId; } @@ -71,6 +226,14 @@ public class PlatformStyleDTO implements Serializable { this.platformId = platformId; } + public Long getPlatformIconInstanceId() { + return platformIconInstanceId; + } + + public void setPlatformIconInstanceId(Long platformIconInstanceId) { + this.platformIconInstanceId = platformIconInstanceId; + } + public String getPlatformName() { return platformName; } diff --git a/ruoyi-ui/src/api/system/routes.js b/ruoyi-ui/src/api/system/routes.js index f519267..124a5e1 100644 --- a/ruoyi-ui/src/api/system/routes.js +++ b/ruoyi-ui/src/api/system/routes.js @@ -65,6 +65,15 @@ export function getPlatformStyle(query) { }) } +// 删除独立平台图标样式(按实例删除,避免删除图标后残留威力区/探测区) +export function deletePlatformIconStyle(roomId, platformIconInstanceId) { + return request({ + url: '/system/routes/platformIconStyle', + method: 'delete', + params: { roomId, platformIconInstanceId } + }) +} + // 保存4T数据到Redis(禁用防重复提交,因拖拽/调整大小可能快速连续触发保存) export function save4TData(data) { return request({ diff --git a/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue b/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue index e200595..4f13a65 100644 --- a/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue +++ b/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue @@ -504,6 +504,24 @@ + + + + + +