1
This commit is contained in:
462
Report/系统实现_第三部分.md
Normal file
462
Report/系统实现_第三部分.md
Normal file
@@ -0,0 +1,462 @@
|
||||
### 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. **直观的用户界面**: 设计并实现了美观、易用且响应式的用户界面,包括主管工作台、网格员工作台和决策支持看板等。
|
||||
|
||||
这些实现成果不仅满足了系统需求分析阶段定义的功能要求,也体现了系统设计阶段规划的架构和模块划分。系统的实现充分考虑了可维护性、可扩展性和用户体验,为后续的系统测试和部署奠定了坚实的基础。
|
||||
Reference in New Issue
Block a user