Files
Environment-Monitoring-System/Report/系统实现_第三部分.md
ChuXun 02a830145e 1
2025-10-25 19:18:43 +08:00

462 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### 4. 任务智能分配算法
任务分配是系统中的关键业务流程,我实现了一个智能分配算法,能够根据多种因素为任务选择最合适的网格员。该算法综合考虑了地理距离、当前负载、专业匹配度和历史表现等因素。
**关键代码展示**:
```java
@Service
@RequiredArgsConstructor
public class TaskAssignmentServiceImpl implements TaskAssignmentService {
private final TaskRepository taskRepository;
private final UserAccountRepository userAccountRepository;
private final GridService gridService;
private final WorkerStatsService workerStatsService;
/**
* 为任务推荐最合适的网格员
* 综合考虑地理距离、当前负载、专业匹配度和历史表现
*/
@Override
public UserAccount recommendWorkerForTask(Task task, List<UserAccount> 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<UserAccount, Double> 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);
}
/**
* 计算两点间的距离
* 使用曼哈顿距离,适合网格化的城市环境
*/
private double calculateDistance(Point a, Point b) {
return Math.abs(a.x() - b.x()) + Math.abs(a.y() - b.y());
}
/**
* 计算网格员技能与任务的匹配度
* 返回0-1之间的分数1表示完全匹配
*/
private double calculateSkillMatch(UserAccount worker, Task task) {
// 获取工人的技能列表
List<String> workerSkills = worker.getSkills();
if (workerSkills == null || workerSkills.isEmpty()) {
return 0.0;
}
// 根据任务类型确定所需技能
List<String> requiredSkills = determineRequiredSkills(task);
if (requiredSkills.isEmpty()) {
return 1.0; // 如果任务没有特定技能要求,则任何工人都完全匹配
}
// 计算匹配的技能数量
long matchedSkillsCount = requiredSkills.stream()
.filter(skill -> workerSkills.contains(skill))
.count();
// 返回匹配率
return (double) matchedSkillsCount / requiredSkills.size();
}
/**
* 根据任务类型确定所需技能
*/
private List<String> determineRequiredSkills(Task task) {
List<String> skills = new ArrayList<>();
// 根据污染类型添加相关技能
switch (task.getPollutionType()) {
case AIR:
skills.add("air_quality_monitoring");
skills.add("emissions_control");
break;
case WATER:
skills.add("water_sampling");
skills.add("water_treatment");
break;
case SOIL:
skills.add("soil_testing");
skills.add("land_remediation");
break;
case NOISE:
skills.add("noise_measurement");
skills.add("sound_insulation");
break;
default:
skills.add("general_environmental");
}
// 根据严重程度添加额外技能
if (task.getSeverityLevel() == SeverityLevel.HIGH ||
task.getSeverityLevel() == SeverityLevel.CRITICAL) {
skills.add("emergency_response");
}
return skills;
}
}
```
**实现要点分析**:
1. **多因素加权评分**: 算法综合考虑了地理距离、当前负载、专业匹配度和历史表现四个因素,通过加权计算得出每个网格员的综合得分。
2. **技能匹配机制**: 根据任务的污染类型和严重程度,动态确定所需的技能列表,然后与网格员的技能进行匹配,计算匹配度。
3. **可配置权重**: 各因素的权重可以根据实际需求进行调整,使得算法更加灵活和适应性强。
4. **流式API**: 使用Java 8的Stream API进行最大值查找代码简洁且高效。
**程序流程图**:
```mermaid
flowchart TD
A[开始] --> B[获取可用网格员列表]
B --> C{列表为空?}
C -->|是| D[返回null]
C -->|否| E[遍历每个网格员]
E --> F[计算地理距离评分]
F --> G[计算当前负载评分]
G --> H[计算专业匹配度评分]
H --> I[计算历史表现评分]
I --> J[计算综合评分]
J --> K[记录网格员评分]
K --> L{所有网格员已处理?}
L -->|否| E
L -->|是| M[返回评分最高的网格员]
```
## 3.3.3 界面展示与成果展示
在前端实现方面我采用了Vue 3和Element Plus组件库打造了美观、易用且响应式的用户界面。下面展示几个核心界面及其功能实现。
### 1. 主管工作台 - 反馈审核与任务分配
![主管工作台界面](./Design/supervisor_dashboard.png)
**界面功能说明**:
1. **左侧反馈列表**: 展示所有待审核的反馈,支持按状态、类型和严重程度进行筛选。
2. **右侧反馈详情**: 显示选中反馈的详细信息,包括标题、描述、位置和附件等。
3. **智能推荐**: 系统自动推荐最合适的网格员,并显示推荐理由。
4. **手动分配**: 主管可以覆盖系统推荐,手动选择其他网格员。
5. **批量操作**: 支持批量审核和分配功能,提高工作效率。
**前端代码实现**:
```vue
<template>
<div class="supervisor-dashboard">
<el-row :gutter="20">
<!-- 左侧反馈列表 -->
<el-col :span="8">
<el-card class="feedback-list">
<template #header>
<div class="card-header">
<span>待处理反馈 ({{ totalItems }})</span>
<el-input
v-model="searchQuery"
placeholder="搜索反馈..."
prefix-icon="el-icon-search"
clearable
@input="handleSearch"
/>
</div>
</template>
<el-table
v-loading="loading"
:data="feedbackList"
@row-click="handleRowClick"
highlight-current-row
>
<el-table-column prop="eventId" label="事件ID" width="120" />
<el-table-column prop="title" label="标题" show-overflow-tooltip />
<el-table-column prop="severityLevel" label="严重程度" width="100">
<template #default="scope">
<el-tag :type="getSeverityType(scope.row.severityLevel)">
{{ scope.row.severityLevel }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
layout="prev, pager, next"
:total="totalItems"
:page-size="pageSize"
@current-change="handlePageChange"
/>
</div>
</el-card>
</el-col>
<!-- 右侧反馈详情 -->
<el-col :span="16">
<el-card v-if="selectedFeedback" class="feedback-detail">
<template #header>
<div class="card-header">
<span>反馈详情</span>
<div>
<el-button
type="success"
icon="el-icon-check"
@click="handleApprove"
:disabled="!canApprove"
>
批准
</el-button>
<el-button
type="danger"
icon="el-icon-close"
@click="handleReject"
:disabled="!canReject"
>
拒绝
</el-button>
</div>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="标题">{{ selectedFeedback.title }}</el-descriptions-item>
<el-descriptions-item label="事件ID">{{ selectedFeedback.eventId }}</el-descriptions-item>
<el-descriptions-item label="污染类型">{{ selectedFeedback.pollutionType }}</el-descriptions-item>
<el-descriptions-item label="严重程度">
<el-tag :type="getSeverityType(selectedFeedback.severityLevel)">
{{ selectedFeedback.severityLevel }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="提交时间">{{ formatDate(selectedFeedback.createdAt) }}</el-descriptions-item>
<el-descriptions-item label="位置">{{ selectedFeedback.textAddress }}</el-descriptions-item>
<el-descriptions-item label="描述" :span="2">
{{ selectedFeedback.description }}
</el-descriptions-item>
</el-descriptions>
<!-- 附件展示 -->
<div v-if="selectedFeedback.attachments && selectedFeedback.attachments.length > 0" class="attachments">
<h3>附件</h3>
<el-image
v-for="(attachment, index) in selectedFeedback.attachments"
:key="index"
:src="attachment.filePath"
:preview-src-list="getImageUrls()"
fit="cover"
class="attachment-image"
/>
</div>
<!-- 任务分配表单 -->
<div v-if="selectedFeedback.status === 'PENDING_ASSIGNMENT'" class="assignment-form">
<h3>分配任务</h3>
<el-form :model="assignmentForm" label-width="120px">
<el-form-item label="选择网格员">
<el-select v-model="assignmentForm.workerId" placeholder="选择网格员">
<el-option
v-for="worker in availableWorkers"
:key="worker.id"
:label="worker.name"
:value="worker.id"
>
<div class="worker-option">
<span>{{ worker.name }}</span>
<small>
{{ worker.isRecommended ? '(系统推荐)' : '' }}
{{ worker.currentTasks }} 个任务
</small>
</div>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleAssign" :loading="assignLoading">
分配任务
</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
<el-empty v-else description="请选择一个反馈" />
</el-col>
</el-row>
</div>
</template>
```
### 2. 网格员工作台 - 任务执行
![网格员工作台界面](./Design/worker_dashboard.png)
**界面功能说明**:
1. **任务列表**: 展示分配给当前网格员的所有任务,按状态和截止日期排序。
2. **任务详情**: 显示选中任务的详细信息,包括位置、描述和附件等。
3. **路径规划**: 集成A*寻路算法,为网格员提供从当前位置到任务地点的最优路径。
4. **任务提交**: 提供表单和文件上传功能,让网格员提交任务处理结果。
### 3. 决策支持看板
![决策支持看板界面](./Design/decision_dashboard.png)
**界面功能说明**:
1. **核心KPI**: 展示关键业务指标,如反馈总数、任务完成率、平均处理时长等。
2. **热力图**: 基于地理位置的问题密度热力图,直观展示问题高发区域。
3. **趋势图**: 展示过去30天的反馈和任务数量趋势帮助决策者了解系统运行状况。
4. **分布图**: 展示不同类型问题的分布情况,帮助决策者了解问题类型分布。
**前端代码实现**:
```vue
<template>
<div class="dashboard-container">
<el-row :gutter="20" class="kpi-row">
<el-col :span="6" v-for="(item, index) in kpiData" :key="index">
<el-card shadow="hover" class="kpi-card">
<div class="kpi-icon">
<i :class="item.icon"></i>
</div>
<div class="kpi-content">
<div class="kpi-value">{{ item.value }}</div>
<div class="kpi-label">{{ item.label }}</div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="chart-row">
<el-col :span="16">
<el-card shadow="hover">
<template #header>
<div class="card-header">
<span>问题热力图</span>
<el-select v-model="mapFilter" placeholder="筛选" size="small">
<el-option label="全部问题" value="ALL" />
<el-option label="空气污染" value="AIR" />
<el-option label="水污染" value="WATER" />
<el-option label="噪音污染" value="NOISE" />
</el-select>
</div>
</template>
<div class="map-container">
<heatmap-component :data="heatmapData" :filter="mapFilter" />
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="chart-card">
<template #header>
<div class="card-header">
<span>问题类型分布</span>
</div>
</template>
<div class="chart-container">
<pie-chart :data="typeDistribution" />
</div>
</el-card>
<el-card shadow="hover" class="chart-card" style="margin-top: 20px;">
<template #header>
<div class="card-header">
<span>任务完成情况</span>
</div>
</template>
<div class="chart-container">
<progress-chart :data="taskCompletionData" />
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="chart-row">
<el-col :span="24">
<el-card shadow="hover">
<template #header>
<div class="card-header">
<span>过去30天趋势</span>
<el-radio-group v-model="trendType" size="small">
<el-radio-button label="feedback">反馈数量</el-radio-button>
<el-radio-button label="tasks">任务数量</el-radio-button>
<el-radio-button label="completion">完成率</el-radio-button>
</el-radio-group>
</div>
</template>
<div class="chart-container">
<trend-chart :data="trendData" :type="trendType" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
```
## 3.3.4 实现成果总结
通过系统实现阶段的工作,我成功地将系统设计阶段规划的功能转化为了可运行的代码,实现了环境监督系统的核心功能。主要成果包括:
1. **创新的数据持久化方案**: 设计并实现了基于JSON文件的数据存储服务简化了系统部署同时提供了类似JPA的操作接口。
2. **高效的A*寻路算法**: 实现了能够考虑地图障碍物的A*寻路算法,为网格员提供最优路径规划。
3. **智能的任务分配算法**: 开发了综合考虑多种因素的任务分配算法,能够为任务选择最合适的执行者。
4. **完整的反馈管理流程**: 实现了反馈提交、AI审核、人工审核和任务创建的完整流程支持多条件查询和文件上传。
5. **直观的用户界面**: 设计并实现了美观、易用且响应式的用户界面,包括主管工作台、网格员工作台和决策支持看板等。
这些实现成果不仅满足了系统需求分析阶段定义的功能要求,也体现了系统设计阶段规划的架构和模块划分。系统的实现充分考虑了可维护性、可扩展性和用户体验,为后续的系统测试和部署奠定了坚实的基础。