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 cfaccec..7d2b615 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 @@ -25,6 +25,7 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.beans.factory.annotation.Qualifier; import java.util.ArrayList; @@ -44,6 +45,10 @@ public class RoutesController extends BaseController @Autowired private RedisTemplate redisTemplate; + @Autowired + @Qualifier("fourTRedisTemplate") + private RedisTemplate fourTRedisTemplate; + /** * 保存平台样式到 Redis */ @@ -78,6 +83,45 @@ public class RoutesController extends BaseController } /** + * 保存4T数据到 Redis(按房间存储) + */ + @PreAuthorize("@ss.hasPermi('system:routes:edit')") + @PostMapping("/save4TData") + public AjaxResult save4TData(@RequestBody java.util.Map params) + { + Object roomId = params.get("roomId"); + Object data = params.get("data"); + if (roomId == null || data == null) { + return AjaxResult.error("参数不完整"); + } + String key = "room:" + String.valueOf(roomId) + ":4t"; + fourTRedisTemplate.opsForValue().set(key, data.toString()); + return success(); + } + + /** + * 从 Redis 获取4T数据 + */ + @PreAuthorize("@ss.hasPermi('system:routes:query')") + @GetMapping("/get4TData") + public AjaxResult get4TData(Long roomId) + { + if (roomId == null) { + return AjaxResult.error("房间ID不能为空"); + } + String key = "room:" + String.valueOf(roomId) + ":4t"; + String val = fourTRedisTemplate.opsForValue().get(key); + if (val != null && !val.isEmpty()) { + try { + return success(JSON.parseObject(val)); + } catch (Exception e) { + return success(val); + } + } + return success(); + } + + /** * 获取导弹发射参数列表(Redis,房间+航线+平台为 key,值为数组,每项含 angle/distance/launchTimeMinutesFromK/startLng/startLat/platformHeadingDeg) */ @PreAuthorize("@ss.hasPermi('system:routes:query')") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java index c8c49c9..3760365 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -9,6 +9,7 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.access.prepost.PreAuthorize; @@ -32,6 +33,7 @@ import com.ruoyi.system.domain.SysCache; public class CacheController { @Autowired + @Qualifier("stringRedisTemplate") private RedisTemplate redisTemplate; private final static List caches = new ArrayList(); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTimelineSegmentController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTimelineSegmentController.java new file mode 100644 index 0000000..25a06a1 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTimelineSegmentController.java @@ -0,0 +1,81 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.domain.entity.SysTimelineSegment; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.service.ISysTimelineSegmentService; + +@RestController +@RequestMapping("/system/timeline") +public class SysTimelineSegmentController extends BaseController +{ + @Autowired + private ISysTimelineSegmentService segmentService; + + @PreAuthorize("@ss.hasPermi('system:timeline:list')") + @GetMapping("/list") + public AjaxResult list(SysTimelineSegment segment) + { + List list = segmentService.selectSegmentList(segment); + return success(list); + } + + @GetMapping("/listByRoomId/{roomId}") + public AjaxResult listByRoomId(@PathVariable Long roomId) + { + List list = segmentService.selectSegmentListByRoomId(roomId); + return success(list); + } + + @PreAuthorize("@ss.hasPermi('system:timeline:query')") + @GetMapping(value = "/{segmentId}") + public AjaxResult getInfo(@PathVariable Long segmentId) + { + return success(segmentService.selectSegmentById(segmentId)); + } + + @PreAuthorize("@ss.hasPermi('system:timeline:add')") + @Log(title = "时间轴管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysTimelineSegment segment) + { + return toAjax(segmentService.insertSegment(segment)); + } + + @PreAuthorize("@ss.hasPermi('system:timeline:edit')") + @Log(title = "时间轴管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysTimelineSegment segment) + { + return toAjax(segmentService.updateSegment(segment)); + } + + @PreAuthorize("@ss.hasPermi('system:timeline:remove')") + @Log(title = "时间轴管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{segmentId}") + public AjaxResult remove(@PathVariable Long segmentId) + { + return toAjax(segmentService.deleteSegmentById(segmentId)); + } + + @PreAuthorize("@ss.hasPermi('system:timeline:remove')") + @Log(title = "时间轴管理", businessType = BusinessType.DELETE) + @DeleteMapping("/room/{roomId}") + public AjaxResult removeByRoomId(@PathVariable Long roomId) + { + return toAjax(segmentService.deleteSegmentByRoomId(roomId)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysTimelineSegment.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysTimelineSegment.java new file mode 100644 index 0000000..03476e4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysTimelineSegment.java @@ -0,0 +1,89 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class SysTimelineSegment +{ + private static final long serialVersionUID = 1L; + + private Long segmentId; + + private Long roomId; + + @NotBlank(message = "时间点不能为空") + @Size(min = 0, max = 8, message = "时间点长度不能超过8个字符") + private String segmentTime; + + @NotBlank(message = "时间段名称不能为空") + @Size(min = 0, max = 50, message = "时间段名称长度不能超过50个字符") + private String segmentName; + + @Size(max = 500, message = "时间段描述长度不能超过500个字符") + private String segmentDesc; + + public Long getSegmentId() + { + return segmentId; + } + + public void setSegmentId(Long segmentId) + { + this.segmentId = segmentId; + } + + @NotNull(message = "房间ID不能为空") + public Long getRoomId() + { + return roomId; + } + + public void setRoomId(Long roomId) + { + this.roomId = roomId; + } + + public String getSegmentTime() + { + return segmentTime; + } + + public void setSegmentTime(String segmentTime) + { + this.segmentTime = segmentTime; + } + + public String getSegmentName() + { + return segmentName; + } + + public void setSegmentName(String segmentName) + { + this.segmentName = segmentName; + } + + public String getSegmentDesc() + { + return segmentDesc; + } + + public void setSegmentDesc(String segmentDesc) + { + this.segmentDesc = segmentDesc; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("segmentId", getSegmentId()) + .append("roomId", getRoomId()) + .append("segmentTime", getSegmentTime()) + .append("segmentName", getSegmentName()) + .append("segmentDesc", getSegmentDesc()) + .toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java index 113521b..8a372c3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -63,6 +63,23 @@ public class RedisConfig extends CachingConfigurerSupport return template; } + /** + * 纯字符串 RedisTemplate,用于存储 4T 等 JSON 字符串,避免 FastJson 序列化问题 + * 命名为 fourTRedisTemplate 避免与 Spring Boot 自带的 stringRedisTemplate 冲突 + */ + @Bean("fourTRedisTemplate") + public RedisTemplate fourTRedisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + return template; + } + @Bean public DefaultRedisScript limitScript() { 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 f39f7c7..7d9ef64 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 @@ -27,6 +27,26 @@ public class PlatformStyleDTO implements Serializable { /** 平台颜色 */ private String platformColor; + /** 探测区半径(千米),整圆 */ + private Double detectionZoneRadius; + /** 探测区填充颜色 */ + private String detectionZoneColor; + /** 探测区透明度 0-1 */ + private Double detectionZoneOpacity; + /** 探测区是否在地图上显示 */ + private Boolean detectionZoneVisible; + + /** 威力区半径(千米) */ + private Double powerZoneRadius; + /** 威力区扇形夹角(度),如 60 表示 60 度扇形 */ + private Double powerZoneAngle; + /** 威力区填充颜色 */ + private String powerZoneColor; + /** 威力区透明度 0-1 */ + private Double powerZoneOpacity; + /** 威力区是否在地图上显示 */ + private Boolean powerZoneVisible; + public String getRoomId() { return roomId; } @@ -90,4 +110,76 @@ public class PlatformStyleDTO implements Serializable { public void setPlatformColor(String platformColor) { this.platformColor = platformColor; } + + public Double getPowerZoneRadius() { + return powerZoneRadius; + } + + public void setPowerZoneRadius(Double powerZoneRadius) { + this.powerZoneRadius = powerZoneRadius; + } + + public String getPowerZoneColor() { + return powerZoneColor; + } + + public void setPowerZoneColor(String powerZoneColor) { + this.powerZoneColor = powerZoneColor; + } + + public Double getDetectionZoneRadius() { + return detectionZoneRadius; + } + + public void setDetectionZoneRadius(Double detectionZoneRadius) { + this.detectionZoneRadius = detectionZoneRadius; + } + + public String getDetectionZoneColor() { + return detectionZoneColor; + } + + public void setDetectionZoneColor(String detectionZoneColor) { + this.detectionZoneColor = detectionZoneColor; + } + + public Double getDetectionZoneOpacity() { + return detectionZoneOpacity; + } + + public void setDetectionZoneOpacity(Double detectionZoneOpacity) { + this.detectionZoneOpacity = detectionZoneOpacity; + } + + public Boolean getDetectionZoneVisible() { + return detectionZoneVisible; + } + + public void setDetectionZoneVisible(Boolean detectionZoneVisible) { + this.detectionZoneVisible = detectionZoneVisible; + } + + public Double getPowerZoneAngle() { + return powerZoneAngle; + } + + public void setPowerZoneAngle(Double powerZoneAngle) { + this.powerZoneAngle = powerZoneAngle; + } + + public Double getPowerZoneOpacity() { + return powerZoneOpacity; + } + + public void setPowerZoneOpacity(Double powerZoneOpacity) { + this.powerZoneOpacity = powerZoneOpacity; + } + + public Boolean getPowerZoneVisible() { + return powerZoneVisible; + } + + public void setPowerZoneVisible(Boolean powerZoneVisible) { + this.powerZoneVisible = powerZoneVisible; + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTimelineSegmentMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTimelineSegmentMapper.java new file mode 100644 index 0000000..9325299 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTimelineSegmentMapper.java @@ -0,0 +1,22 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysTimelineSegment; + +public interface SysTimelineSegmentMapper +{ + public List selectSegmentList(SysTimelineSegment segment); + + public List selectSegmentListByRoomId(@Param("roomId") Long roomId); + + public SysTimelineSegment selectSegmentById(Long segmentId); + + public int insertSegment(SysTimelineSegment segment); + + public int updateSegment(SysTimelineSegment segment); + + public int deleteSegmentById(Long segmentId); + + public int deleteSegmentByRoomId(@Param("roomId") Long roomId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTimelineSegmentService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTimelineSegmentService.java new file mode 100644 index 0000000..7889654 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTimelineSegmentService.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysTimelineSegment; + +public interface ISysTimelineSegmentService +{ + public List selectSegmentList(SysTimelineSegment segment); + + public List selectSegmentListByRoomId(Long roomId); + + public SysTimelineSegment selectSegmentById(Long segmentId); + + public int insertSegment(SysTimelineSegment segment); + + public int updateSegment(SysTimelineSegment segment); + + public int deleteSegmentById(Long segmentId); + + public int deleteSegmentByRoomId(Long roomId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTimelineSegmentServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTimelineSegmentServiceImpl.java new file mode 100644 index 0000000..4623bc5 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTimelineSegmentServiceImpl.java @@ -0,0 +1,57 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysTimelineSegment; +import com.ruoyi.system.mapper.SysTimelineSegmentMapper; +import com.ruoyi.system.service.ISysTimelineSegmentService; + +@Service +public class SysTimelineSegmentServiceImpl implements ISysTimelineSegmentService +{ + @Autowired + private SysTimelineSegmentMapper segmentMapper; + + @Override + public List selectSegmentList(SysTimelineSegment segment) + { + return segmentMapper.selectSegmentList(segment); + } + + @Override + public List selectSegmentListByRoomId(Long roomId) + { + return segmentMapper.selectSegmentListByRoomId(roomId); + } + + @Override + public SysTimelineSegment selectSegmentById(Long segmentId) + { + return segmentMapper.selectSegmentById(segmentId); + } + + @Override + public int insertSegment(SysTimelineSegment segment) + { + return segmentMapper.insertSegment(segment); + } + + @Override + public int updateSegment(SysTimelineSegment segment) + { + return segmentMapper.updateSegment(segment); + } + + @Override + public int deleteSegmentById(Long segmentId) + { + return segmentMapper.deleteSegmentById(segmentId); + } + + @Override + public int deleteSegmentByRoomId(Long roomId) + { + return segmentMapper.deleteSegmentByRoomId(roomId); + } +} diff --git a/ruoyi-system/src/main/resources/mapper/system/RouteWaypointsMapper.xml b/ruoyi-system/src/main/resources/mapper/system/RouteWaypointsMapper.xml index a484b4d..7971efc 100644 --- a/ruoyi-system/src/main/resources/mapper/system/RouteWaypointsMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/RouteWaypointsMapper.xml @@ -27,7 +27,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + where 1=1 + + AND room_id = #{roomId} + + + AND segment_name like concat('%', #{segmentName}, '%') + + order by s.segment_time + + + + + + + + insert into sys_timeline_segment( + room_id, + segment_time, + segment_name, + segment_desc, + segment_id + )values( + #{roomId}, + #{segmentTime}, + #{segmentName}, + #{segmentDesc}, + #{segmentId} + ) + + + + update sys_timeline_segment + + segment_time = #{segmentTime}, + segment_name = #{segmentName}, + segment_desc = #{segmentDesc}, + + where segment_id = #{segmentId} + + + + delete from sys_timeline_segment where segment_id = #{segmentId} + + + + delete from sys_timeline_segment where room_id = #{roomId} + + + diff --git a/ruoyi-ui/src/api/system/routes.js b/ruoyi-ui/src/api/system/routes.js index 58c7677..8282b58 100644 --- a/ruoyi-ui/src/api/system/routes.js +++ b/ruoyi-ui/src/api/system/routes.js @@ -61,6 +61,25 @@ export function getPlatformStyle(query) { }) } +// 保存4T数据到Redis(禁用防重复提交,因拖拽/调整大小可能快速连续触发保存) +export function save4TData(data) { + return request({ + url: '/system/routes/save4TData', + method: 'post', + data, + headers: { repeatSubmit: false } + }) +} + +// 从Redis获取4T数据 +export function get4TData(params) { + return request({ + url: '/system/routes/get4TData', + method: 'get', + params + }) +} + // 获取导弹发射参数(Redis:房间+航线+平台为 key) export function getMissileParams(params) { return request({ diff --git a/ruoyi-ui/src/api/system/timeline.js b/ruoyi-ui/src/api/system/timeline.js new file mode 100644 index 0000000..11c9873 --- /dev/null +++ b/ruoyi-ui/src/api/system/timeline.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +export function listTimelineSegments(query) { + return request({ + url: '/system/timeline/list', + method: 'get', + params: query + }) +} + +export function getTimelineSegmentsByRoomId(roomId) { + return request({ + url: '/system/timeline/listByRoomId/' + roomId, + method: 'get' + }) +} + +export function getTimelineSegment(id) { + return request({ + url: '/system/timeline/' + id, + method: 'get' + }) +} + +export function addTimelineSegment(data) { + return request({ + url: '/system/timeline', + method: 'post', + data: data + }) +} + +export function updateTimelineSegment(data) { + return request({ + url: '/system/timeline', + method: 'put', + data: data + }) +} + +export function delTimelineSegment(id) { + return request({ + url: '/system/timeline/' + id, + method: 'delete' + }) +} + +export function delTimelineSegmentsByRoomId(roomId) { + return request({ + url: '/system/timeline/room/' + roomId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/assets/icons/svg/T.svg b/ruoyi-ui/src/assets/icons/svg/T.svg new file mode 100644 index 0000000..697e8c5 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/T.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/lang/en.js b/ruoyi-ui/src/lang/en.js index 57a7351..3d55431 100644 --- a/ruoyi-ui/src/lang/en.js +++ b/ruoyi-ui/src/lang/en.js @@ -71,6 +71,13 @@ export default { systemDescription: 'System Description', language: 'Language' }, + tools: { + routeCalculation: 'Route Calculation', + conflictDisplay: 'Conflict Display', + dataMaterials: 'Data Materials', + coordinateConversion: 'Coordinate Conversion', + generateGanttChart: 'Generate Gantt Chart' + }, favorites: { layerFavorites: 'Layer Favorites', routeFavorites: 'Route Favorites' diff --git a/ruoyi-ui/src/lang/zh.js b/ruoyi-ui/src/lang/zh.js index ece9c1f..11c1864 100644 --- a/ruoyi-ui/src/lang/zh.js +++ b/ruoyi-ui/src/lang/zh.js @@ -71,6 +71,13 @@ export default { systemDescription: '系统说明', language: '语言' }, + tools: { + routeCalculation: '航线计算', + conflictDisplay: '冲突显示', + dataMaterials: '数据资料', + coordinateConversion: '坐标转换', + generateGanttChart: '生成甘特图' + }, favorites: { layerFavorites: '图层收藏', routeFavorites: '航线收藏' diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js index 1015974..1770a53 100644 --- a/ruoyi-ui/src/router/index.js +++ b/ruoyi-ui/src/router/index.js @@ -70,6 +70,11 @@ export const constantRoutes = [ hidden: true }, { + path: '/ganttChart', + component: () => import('@/views/ganttChart'), + hidden: true + }, + { path: '/register', component: () => import('@/views/register'), hidden: true diff --git a/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue b/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue index f1ae068..7e45967 100644 --- a/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue +++ b/ruoyi-ui/src/views/cesiumMap/ContextMenu.vue @@ -7,8 +7,29 @@ - -