# 3.3 系统实现 ## 3.3.1 开发环境与技术栈 在本项目的开发过程中,我采用了现代化、高效且广泛应用于企业级开发的技术栈组合,确保系统的稳定性、可扩展性和维护性。 ### 后端技术栈 - **编程语言**: Java 17 - **框架**: Spring Boot 3.1.5 - **API文档**: Springdoc OpenAPI (Swagger) - **安全框架**: Spring Security + JWT - **构建工具**: Maven 3.9 - **数据存储**: 自定义JSON文件存储(模拟数据库) - **异步处理**: Spring Events - **日志框架**: SLF4J + Logback - **单元测试**: JUnit 5 + Mockito ### 前端技术栈 - **框架**: Vue 3 (Composition API) - **构建工具**: Vite - **UI组件库**: Element Plus - **状态管理**: Pinia - **路由**: Vue Router - **HTTP客户端**: Axios - **CSS预处理器**: SCSS - **图表库**: ECharts ### 开发工具 - **IDE**: IntelliJ IDEA / VS Code - **版本控制**: Git - **API测试**: Postman - **代码质量**: SonarLint - **调试工具**: Chrome DevTools ## 3.3.2 核心功能模块实现 在系统设计阶段,我们已经详细规划了各个功能模块。在实现阶段,我负责了多个核心模块的编码工作,下面将详细介绍这些模块的实现细节。 ### 1. 泛型JSON存储服务 在本项目中,我设计并实现了一个创新的数据持久化解决方案,使用JSON文件代替传统数据库进行数据存储。这种方案简化了系统部署,同时提供了类似于JPA的操作接口,使得业务层代码无需关心底层存储细节。 **关键代码展示**: ```java @Service public class JsonStorageService { private static final Path STORAGE_DIRECTORY = Paths.get("json-db"); private final ObjectMapper objectMapper; public JsonStorageService(ObjectMapper objectMapper) { this.objectMapper = objectMapper; try { if (!Files.exists(STORAGE_DIRECTORY)) { Files.createDirectories(STORAGE_DIRECTORY); } } catch (IOException e) { throw new UncheckedIOException("无法创建存储目录", e); } } // 同步写数据方法,保证线程安全 public synchronized void writeData(String fileName, List data) { try { Path filePath = STORAGE_DIRECTORY.resolve(fileName); // 使用writeValueAsString先转为格式化的JSON字符串,再写入文件,提高可读性 String jsonContent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(data); Files.write(filePath, jsonContent.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new UncheckedIOException("写入数据到 " + fileName + " 时出错", e); } } // 读数据方法 public List readData(String fileName, TypeReference> typeReference) { Path filePath = STORAGE_DIRECTORY.resolve(fileName); if (!Files.exists(filePath)) { return new ArrayList<>(); // 文件不存在是正常情况,返回空列表 } try { // 使用TypeReference来处理泛型擦除问题,确保JSON能被正确反序列化为List return objectMapper.readValue(filePath.toFile(), typeReference); } catch (IOException e) { log.warn("无法读取或解析文件: {}. 返回空列表。", fileName, e); return new ArrayList<>(); } } } ``` **实现要点分析**: 1. **泛型设计**: 通过Java泛型和TypeReference,实现了类型安全的数据读写,支持任意模型类。 2. **线程安全**: 使用synchronized关键字确保写操作的线程安全,防止并发写入导致的数据损坏。 3. **容错处理**: 对文件不存在、IO异常等情况进行了妥善处理,增强了系统的健壮性。 4. **格式化输出**: 使用Jackson的prettyPrinter功能,使生成的JSON文件具有良好的可读性,便于调试。 **程序流程图**: ```mermaid flowchart TD A[开始] --> B{文件存在?} B -->|是| C[读取文件内容] B -->|否| D[返回空列表] C --> E{解析JSON成功?} E -->|是| F[返回对象列表] E -->|否| G[记录警告日志] G --> D ``` ### 2. A*寻路算法实现 在网格与地图模块中,我实现了A*寻路算法,为网格员提供从当前位置到任务地点的最优路径规划。该算法考虑了地图障碍物,能够高效地找到最短路径。 **关键代码展示**: ```java @Service @RequiredArgsConstructor public class AStarService { private final MapGridRepository mapGridRepository; // 内部Node类,用于A*算法的节点表示 private static class Node { Point point; int g; // 从起点到当前节点的实际代价 int h; // 启发式:从当前节点到终点的估计代价 int f; // g + h Node parent; public Node(Point point, Node parent, int g, int h) { this.point = point; this.parent = parent; this.g = g; this.h = h; this.f = g + h; } } /** * 寻找两点间的最短路径,避开障碍物 * 地图数据(包括尺寸和障碍物)从数据库动态加载 */ public List findPath(Point start, Point end) { // 从数据库加载地图数据 List mapGrids = mapGridRepository.findAll(); if (mapGrids.isEmpty()) { return Collections.emptyList(); // 无地图数据 } // 动态确定地图尺寸和障碍物 int maxX = 0, maxY = 0; Set obstacles = new HashSet<>(); for (MapGrid gridCell : mapGrids) { if (gridCell.isObstacle() || gridCell.getCityName() == null || gridCell.getCityName().trim().isEmpty()) { obstacles.add(new Point(gridCell.getX(), gridCell.getY())); } if (gridCell.getX() > maxX) maxX = gridCell.getX(); if (gridCell.getY() > maxY) maxY = gridCell.getY(); } final int mapWidth = maxX + 1; final int mapHeight = maxY + 1; // A*算法核心实现 PriorityQueue openSet = new PriorityQueue<>(Comparator.comparingInt(n -> n.f)); Map allNodes = new HashMap<>(); Node startNode = new Node(start, null, 0, calculateHeuristic(start, end)); openSet.add(startNode); allNodes.put(start, startNode); while (!openSet.isEmpty()) { Node currentNode = openSet.poll(); if (currentNode.point.equals(end)) { return reconstructPath(currentNode); } for (Point neighborPoint : getNeighbors(currentNode.point, mapWidth, mapHeight)) { if (obstacles.contains(neighborPoint)) { continue; } int tentativeG = currentNode.g + 1; // 相邻节点间距离为1 Node neighborNode = allNodes.get(neighborPoint); if (neighborNode == null) { // 新节点 int h = calculateHeuristic(neighborPoint, end); neighborNode = new Node(neighborPoint, currentNode, tentativeG, h); allNodes.put(neighborPoint, neighborNode); openSet.add(neighborNode); } else if (tentativeG < neighborNode.g) { // 找到更好的路径 neighborNode.parent = currentNode; neighborNode.g = tentativeG; neighborNode.f = tentativeG + neighborNode.h; // 更新优先队列 openSet.remove(neighborNode); openSet.add(neighborNode); } } } return Collections.emptyList(); // 未找到路径 } // 计算启发式函数值(曼哈顿距离) private int calculateHeuristic(Point a, Point b) { return Math.abs(a.x() - b.x()) + Math.abs(a.y() - b.y()); } }