7.9 KiB
7.9 KiB
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的操作接口,使得业务层代码无需关心底层存储细节。
关键代码展示:
@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 <T> void writeData(String fileName, List<T> 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 <T> List<T> readData(String fileName, TypeReference<List<T>> typeReference) {
Path filePath = STORAGE_DIRECTORY.resolve(fileName);
if (!Files.exists(filePath)) {
return new ArrayList<>(); // 文件不存在是正常情况,返回空列表
}
try {
// 使用TypeReference来处理泛型擦除问题,确保JSON能被正确反序列化为List<T>
return objectMapper.readValue(filePath.toFile(), typeReference);
} catch (IOException e) {
log.warn("无法读取或解析文件: {}. 返回空列表。", fileName, e);
return new ArrayList<>();
}
}
}
实现要点分析:
- 泛型设计: 通过Java泛型和TypeReference,实现了类型安全的数据读写,支持任意模型类。
- 线程安全: 使用synchronized关键字确保写操作的线程安全,防止并发写入导致的数据损坏。
- 容错处理: 对文件不存在、IO异常等情况进行了妥善处理,增强了系统的健壮性。
- 格式化输出: 使用Jackson的prettyPrinter功能,使生成的JSON文件具有良好的可读性,便于调试。
程序流程图:
flowchart TD
A[开始] --> B{文件存在?}
B -->|是| C[读取文件内容]
B -->|否| D[返回空列表]
C --> E{解析JSON成功?}
E -->|是| F[返回对象列表]
E -->|否| G[记录警告日志]
G --> D
2. A*寻路算法实现
在网格与地图模块中,我实现了A*寻路算法,为网格员提供从当前位置到任务地点的最优路径规划。该算法考虑了地图障碍物,能够高效地找到最短路径。
关键代码展示:
@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<Point> findPath(Point start, Point end) {
// 从数据库加载地图数据
List<MapGrid> mapGrids = mapGridRepository.findAll();
if (mapGrids.isEmpty()) {
return Collections.emptyList(); // 无地图数据
}
// 动态确定地图尺寸和障碍物
int maxX = 0, maxY = 0;
Set<Point> 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<Node> openSet = new PriorityQueue<>(Comparator.comparingInt(n -> n.f));
Map<Point, Node> 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());
}
}