### 2.4 算法设计 本节详细描述了系统中的核心算法设计,这些算法是系统智能化和高效运行的关键。 #### 2.4.1 A* 寻路算法 A*寻路算法是系统中的核心算法之一,用于为网格员规划从当前位置到任务目标点的最优路径。该算法在网格与地图模块中发挥着重要作用。 **算法目的**: 为网格员提供从当前位置到任务地点的最优(最短或最快)路径,避开障碍物。 **算法输入**: - `startNode`: 起始点坐标 (x, y)。 - `endNode`: 目标点坐标 (x, y)。 - `grid`: 包含障碍物信息的地图网格数据。 **核心逻辑**: 1. 维护一个开放列表(`openList`)和一个关闭列表(`closedList`)。 - `openList`: 存储待探索的节点,使用优先队列实现,按F值排序。 - `closedList`: 存储已探索过的节点。 2. 从 `openList` 中选取F值(G值+H值)最小的节点作为当前节点。 - G值: 从起点到当前节点的实际代价。 - H值: 从当前节点到终点的预估代价(启发函数)。 - F值: G值 + H值,表示经过当前节点到达终点的总代价估计。 3. 遍历当前节点的相邻节点(上、下、左、右四个方向)。 4. 对于每个相邻节点: - 如果是障碍物或已在`closedList`中,则跳过。 - 如果不在`openList`中,计算其G值、H值和F值,将其加入`openList`,并记录其父节点为当前节点。 - 如果已在`openList`中,检查经由当前节点到达该相邻节点的路径是否更优(G值更小)。如果更优,则更新其G值、F值和父节点。 5. 将当前节点从`openList`移除,加入`closedList`。 6. 重复步骤2-5,直到: - 找到目标节点(当前节点为终点)。 - 或`openList`为空(无法找到路径)。 7. 如果找到目标节点,通过回溯父节点构建从起点到终点的路径。 **启发函数选择**: 系统采用曼哈顿距离(Manhattan Distance)作为启发函数,计算公式为: ``` h(n) = |n.x - goal.x| + |n.y - goal.y| ``` 这种启发函数适合网格化的移动模式,只允许上、下、左、右四个方向的移动。 **算法实现**: ```java public List findPath(Point start, Point end) { // 加载地图数据和障碍物信息 List mapGrids = mapGridRepository.findAll(); Set obstacles = new HashSet<>(); for (MapGrid gridCell : mapGrids) { if (gridCell.isObstacle()) { obstacles.add(new Point(gridCell.getX(), gridCell.getY())); } } // 初始化开放列表和所有节点映射 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); // 开始A*算法主循环 while (!openSet.isEmpty()) { Node currentNode = openSet.poll(); // 找到目标 if (currentNode.point.equals(end)) { return reconstructPath(currentNode); } // 探索相邻节点 for (Point neighborPoint : getNeighbors(currentNode.point)) { 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()); } // 获取相邻节点(上、下、左、右) private List getNeighbors(Point point) { List neighbors = new ArrayList<>(4); int[] dx = {0, 0, 1, -1}; // 右、左、下、上 int[] dy = {1, -1, 0, 0}; for (int i = 0; i < 4; i++) { int newX = point.x() + dx[i]; int newY = point.y() + dy[i]; // 检查边界 if (newX >= 0 && newX < mapWidth && newY >= 0 && newY < mapHeight) { neighbors.add(new Point(newX, newY)); } } return neighbors; } // 重建路径 private List reconstructPath(Node node) { LinkedList path = new LinkedList<>(); while (node != null) { path.addFirst(node.point); node = node.parent; } return path; } ``` **算法输出**: 从起点到终点的一系列坐标点列表,表示规划好的路径。如果无法找到路径,则返回空列表。 **算法应用**: 在`PathfindingController`中通过`/api/pathfinding/find`接口暴露,网格员可以通过该接口获取从当前位置到任务地点的最优路径。 #### 2.4.2 任务智能分配算法 任务智能分配算法是系统的另一个核心算法,用于在多个可用网格员中,为新任务选择最合适的执行者。 **算法目的**: 在多个可用网格员中,为新任务选择最合适的执行者,优化任务分配效率和完成质量。 **算法输入**: - `task`: 需要分配的任务对象。 - `availableWorkers`: 可用的网格员列表。 **核心逻辑**: 这是一个复合策略算法,综合考虑多个因素,通过加权评分机制选择最合适的网格员: 1. **地理距离评分**: - 计算每个网格员当前位置到任务位置的距离。 - 距离越近,评分越高。 - 使用公式: `distanceScore = maxDistance - distance`,其中`maxDistance`是一个预设的最大考虑距离。 2. **当前负载评分**: - 评估每个网格员当前正在处理的任务数量。 - 任务数量越少,评分越高。 - 使用公式: `loadScore = maxTasks - currentTasks`,其中`maxTasks`是一个预设的最大任务数。 3. **专业匹配度评分**: - 根据任务类型和网格员的专业技能进行匹配。 - 匹配度越高,评分越高。 - 使用公式: `skillScore = matchedSkills / requiredSkills.size()`。 4. **历史表现评分**: - 考虑网格员的历史完成率和质量评分。 - 表现越好,评分越高。 - 使用公式: `performanceScore = (completionRate * 0.7) + (qualityRating * 0.3)`。 5. **综合评分计算**: - 对上述各项评分进行加权求和。 - 使用公式: `totalScore = (distanceScore * w1) + (loadScore * w2) + (skillScore * w3) + (performanceScore * w4)`。 - 其中w1、w2、w3、w4是各项评分的权重,且满足`w1 + w2 + w3 + w4 = 1`。 6. **选择最高评分的网格员**: - 根据综合评分对网格员进行排序。 - 选择评分最高的网格员作为推荐人选。 **算法实现**: ```java public UserAccount recommendWorkerForTask(Task task, List availableWorkers) { if (availableWorkers.isEmpty()) { return null; } // 权重配置 final double DISTANCE_WEIGHT = 0.4; final double LOAD_WEIGHT = 0.3; final double SKILL_WEIGHT = 0.2; final double PERFORMANCE_WEIGHT = 0.1; // 任务位置 Point taskLocation = new Point(task.getGridX(), task.getGridY()); // 计算每个网格员的评分 Map workerScores = new HashMap<>(); for (UserAccount worker : availableWorkers) { // 1. 地理距离评分 Point workerLocation = new Point(worker.getGridX(), worker.getGridY()); double distance = calculateDistance(workerLocation, taskLocation); double maxDistance = 10.0; // 最大考虑距离 double distanceScore = Math.max(0, maxDistance - distance) / maxDistance; // 2. 当前负载评分 int currentTasks = taskRepository.countByAssigneeIdAndStatusIn( worker.getId(), Arrays.asList(TaskStatus.ASSIGNED, TaskStatus.IN_PROGRESS)); int maxTasks = 5; // 最大任务数 double loadScore = (double)(maxTasks - currentTasks) / maxTasks; // 3. 专业匹配度评分 double skillScore = calculateSkillMatch(worker, task); // 4. 历史表现评分 double completionRate = workerStatsService.getCompletionRate(worker.getId()); double qualityRating = workerStatsService.getAverageQualityRating(worker.getId()); double performanceScore = (completionRate * 0.7) + (qualityRating * 0.3); // 5. 综合评分 double totalScore = (distanceScore * DISTANCE_WEIGHT) + (loadScore * LOAD_WEIGHT) + (skillScore * SKILL_WEIGHT) + (performanceScore * PERFORMANCE_WEIGHT); workerScores.put(worker, totalScore); } // 选择评分最高的网格员 return workerScores.entrySet().stream() .max(Map.Entry.comparingByValue()) .map(Map.Entry::getKey) .orElse(null); } ``` **算法输出**: 推荐的最佳网格员对象。如果没有合适的网格员,则返回null。 **算法应用**: 在`TaskAssignmentService`中实现,当主管触发自动分配或系统基于反馈自动创建任务时调用。 ### 2.5 数据持久化设计 系统采用了一种独特的数据持久化方案,不使用传统的关系型数据库,而是以JSON文件的形式存储所有数据。这种设计简化了部署和配置,特别适合快速迭代和中小型应用场景。 #### 2.5.1 JSON文件存储架构 系统的数据持久化层基于以下架构: 1. **核心存储服务**: - `JsonStorageService`: 提供对JSON文件的读写操作,包括序列化和反序列化。 - `FileSystemService`: 处理文件系统操作,如创建、读取、更新和删除文件。 2. **Repository层**: - 为每种核心模型提供专门的Repository类,如`UserRepository`、`FeedbackRepository`等。 - 这些Repository类模拟了类似JPA的接口,提供CRUD操作和查询功能。 - 内部使用`JsonStorageService`进行实际的文件操作。 3. **并发控制**: - 使用文件锁机制确保在并发环境下的数据一致性。 - 实现了简单的乐观锁定策略,通过版本号检测冲突。 4. **索引和查询优化**: - 在内存中维护索引结构,加速常见查询操作。 - 支持基于字段值的过滤和排序。 #### 2.5.2 核心JSON文件结构 系统中的每个核心模型对应一个JSON文件,下面详细描述了这些文件的结构。 ##### `users.json` - 用户账户数据 存储系统中所有用户的账户信息,包括认证信息和角色权限。 | 字段名 | JSON数据类型 | 约束/说明 | 描述 | | --------------------- | ----------------- | ------------------------ | ---------------------------------------- | | `id` | `Number` | **唯一标识**, 自增 | 用户的唯一标识符 | | `name` | `String` | 非空 | 用户姓名 | | `phone` | `String` | 非空, **唯一** | 手机号码,可用于登录 | | `email` | `String` | 非空, **唯一** | 电子邮箱,可用于登录 | | `password` | `String` | 非空, 长度>=8 | 加密后的用户密码 | | `gender` | `String` | (ENUM) | 性别 (MALE, FEMALE, OTHER) | | `role` | `String` | (ENUM) | 用户角色 (ADMIN, SUPERVISOR, GRID_WORKER等) | | `status` | `String` | 非空, (ENUM) | 账户状态 (ACTIVE, INACTIVE, SUSPENDED) | | `grid_x` | `Number` | | 关联的网格X坐标 (主要用于网格员) | | `grid_y` | `Number` | | 关联的网格Y坐标 (主要用于网格员) | | `region` | `String` | | 所属区域或地区 | | `level` | `String` | (ENUM) | 用户等级 (JUNIOR, SENIOR, EXPERT) | | `skills` | `Array` | | 技能列表 (JSON数组格式的字符串) | | `enabled` | `Boolean` | 非空, 默认 `true` | 账户是否启用 | | `current_latitude` | `Number` | | 当前纬度坐标 (用于实时定位) | | `current_longitude` | `Number` | | 当前经度坐标 (用于实时定位) | | `failed_login_attempts` | `Number` | 默认 `0` | 连续失败登录次数 | | `lockout_end_time` | `String` | | 账户锁定截止时间 | | `created_at` | `String` | 非空 | 记录创建时间 | | `updated_at` | `String` | 非空 | 记录最后更新时间 | ##### `feedback.json` - 环境问题反馈数据 存储用户提交的所有环境问题反馈信息。 | 字段名 | JSON数据类型 | 约束/说明 | 描述 | | ---------------- | --------------- | ------------------------ | ---------------------------------------- | | `id` | `Number` | **唯一标识**, 自增 | 反馈的唯一标识符 | | `event_id` | `String` | 非空, **唯一** | 人类可读的事件ID | | `title` | `String` | 非空 | 反馈标题 | | `description` | `String` | | 问题详细描述 | | `pollution_type` | `String` | 非空, (ENUM) | 污染类型 (AIR, WATER, SOIL, NOISE) | | `severity_level` | `String` | 非空, (ENUM) | 严重程度 (LOW, MEDIUM, HIGH, CRITICAL) | | `status` | `String` | 非空, (ENUM) | 反馈状态 (PENDING_REVIEW, PROCESSED等) | | `text_address` | `String` | | 文字描述的地址 | | `grid_x` | `Number` | | 事发地网格X坐标 | | `grid_y` | `Number` | | 事发地网格Y坐标 | | `latitude` | `Number` | | 事发地纬度 | | `longitude` | `Number` | | 事发地经度 | | `submitter_id` | `Number` | 外键 (FK) -> users.id (可为空) | 提交者ID (公众提交时可为空) | | `attachments` | `Array` | | 附件列表 (包含文件路径等信息) | | `created_at` | `String` | 非空 | 记录创建时间 | | `updated_at` | `String` | 非空 | 记录最后更新时间 | ##### `tasks.json` - 任务数据 存储系统中所有工作任务的信息。 | 字段名 | JSON数据类型 | 约束/说明 | 描述 | | ---------------- | --------------- | ------------------------ | ---------------------------------------- | | `id` | `Number` | **唯一标识**, 自增 | 任务的唯一标识符 | | `feedback_id` | `Number` | 外键 (FK) -> feedback.id (可为空) | 关联的原始反馈ID | | `assignee_id` | `Number` | 外键 (FK) -> users.id (可为空) | 任务执行人(网格员)ID | | `created_by` | `Number` | 外键 (FK) -> users.id | 任务创建人(主管)ID | | `status` | `String` | 非空, (ENUM) | 任务状态 (CREATED, ASSIGNED, IN_PROGRESS等) | | `title` | `String` | | 任务标题 | | `description` | `String` | | 任务详细描述 | | `pollution_type` | `String` | (ENUM) | 污染类型 | | `severity_level` | `String` | (ENUM) | 严重程度 | | `text_address` | `String` | | 任务地点文字描述 | | `grid_x` | `Number` | | 任务地点网格X坐标 | | `grid_y` | `Number` | | 任务地点网格Y坐标 | | `latitude` | `Number` | | 任务地点纬度 | | `longitude` | `Number` | | 任务地点经度 | | `assigned_at` | `String` | | 任务分配时间 | | `completed_at` | `String` | | 任务完成时间 | | `created_at` | `String` | 非空 | 记录创建时间 | | `updated_at` | `String` | 非空 | 记录最后更新时间 | | `deadline` | `String` | | 任务截止日期 | | `history` | `Array` | | 任务状态历史记录 | | `submissions` | `Array` | | 任务提交记录 | ##### `grids.json` - 业务网格数据 存储系统中定义的地理网格信息。 | 字段名 | JSON数据类型 | 约束/说明 | 描述 | | --------------- | --------------- | ------------------- | ---------------------------------- | | `id` | `Number` | **唯一标识**, 自增 | 网格的唯一标识符 | | `grid_x` | `Number` | | 网格X坐标 | | `grid_y` | `Number` | | 网格Y坐标 | | `city_name` | `String` | | 所属城市 | | `district_name` | `String` | | 所属区县 | | `description` | `String` | | 网格描述信息 | | `is_obstacle` | `Boolean` | 默认 `false` | 是否为障碍物(如禁区) | | `created_at` | `String` | 非空 | 记录创建时间 | | `updated_at` | `String` | 非空 | 记录最后更新时间 | ##### `assignments.json` - 任务分配记录数据 存储任务分配的详细记录。 | 字段名 | JSON数据类型 | 约束/说明 | 描述 | | ---------------- | --------------- | ------------------------ | -------------------------- | | `id` | `Number` | **唯一标识**, 自增 | 分配记录的唯一标识符 | | `task_id` | `Number` | 非空, 外键 (FK) -> tasks.id | 关联的任务ID | | `assigner_id` | `Number` | 非空, 外键 (FK) -> users.id | 分配者(主管)ID | | `status` | `String` | 非空, (ENUM) | 分配状态 | | `remarks` | `String` | | 分配备注 | | `assignment_time`| `String` | 非空 | 分配时间 | | `deadline` | `String` | | 任务截止日期 | | `created_at` | `String` | 非空 | 记录创建时间 | | `updated_at` | `String` | 非空 | 记录最后更新时间 |