462 lines
18 KiB
Markdown
462 lines
18 KiB
Markdown
### 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. 主管工作台 - 反馈审核与任务分配
|
||
|
||

|
||
|
||
**界面功能说明**:
|
||
|
||
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. 网格员工作台 - 任务执行
|
||
|
||

|
||
|
||
**界面功能说明**:
|
||
|
||
1. **任务列表**: 展示分配给当前网格员的所有任务,按状态和截止日期排序。
|
||
2. **任务详情**: 显示选中任务的详细信息,包括位置、描述和附件等。
|
||
3. **路径规划**: 集成A*寻路算法,为网格员提供从当前位置到任务地点的最优路径。
|
||
4. **任务提交**: 提供表单和文件上传功能,让网格员提交任务处理结果。
|
||
|
||
### 3. 决策支持看板
|
||
|
||

|
||
|
||
**界面功能说明**:
|
||
|
||
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. **直观的用户界面**: 设计并实现了美观、易用且响应式的用户界面,包括主管工作台、网格员工作台和决策支持看板等。
|
||
|
||
这些实现成果不仅满足了系统需求分析阶段定义的功能要求,也体现了系统设计阶段规划的架构和模块划分。系统的实现充分考虑了可维护性、可扩展性和用户体验,为后续的系统测试和部署奠定了坚实的基础。 |