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

18 KiB
Raw Blame History

4. 任务智能分配算法

任务分配是系统中的关键业务流程,我实现了一个智能分配算法,能够根据多种因素为任务选择最合适的网格员。该算法综合考虑了地理距离、当前负载、专业匹配度和历史表现等因素。

关键代码展示:

@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进行最大值查找代码简洁且高效。

程序流程图:

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. 批量操作: 支持批量审核和分配功能,提高工作效率。

前端代码实现:

<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. 分布图: 展示不同类型问题的分布情况,帮助决策者了解问题类型分布。

前端代码实现:

<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. 直观的用户界面: 设计并实现了美观、易用且响应式的用户界面,包括主管工作台、网格员工作台和决策支持看板等。

这些实现成果不仅满足了系统需求分析阶段定义的功能要求,也体现了系统设计阶段规划的架构和模块划分。系统的实现充分考虑了可维护性、可扩展性和用户体验,为后续的系统测试和部署奠定了坚实的基础。