|
@@ -1,6 +1,7 @@
|
|
|
package com.zksy.web.controller.gasbasic;
|
|
package com.zksy.web.controller.gasbasic;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import com.zksy.base.domain.*;
|
|
import com.zksy.base.domain.*;
|
|
|
import com.zksy.base.mapper.*;
|
|
import com.zksy.base.mapper.*;
|
|
|
import com.zksy.common.annotation.Anonymous;
|
|
import com.zksy.common.annotation.Anonymous;
|
|
@@ -8,8 +9,15 @@ import com.zksy.common.core.domain.AjaxResult;
|
|
|
import io.swagger.annotations.Api;
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
|
|
+import org.springframework.http.MediaType;
|
|
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
@RestController
|
|
@RestController
|
|
|
@RequestMapping("/api/drill/practical")
|
|
@RequestMapping("/api/drill/practical")
|
|
|
@Api(tags = "演练管理-实战演练")
|
|
@Api(tags = "演练管理-实战演练")
|
|
@@ -18,16 +26,475 @@ public class DrillPracticalController {
|
|
|
@Autowired private DrillPracticalTemplateMapper templateMapper;
|
|
@Autowired private DrillPracticalTemplateMapper templateMapper;
|
|
|
@Autowired private DrillPracticalPlanMapper planMapper;
|
|
@Autowired private DrillPracticalPlanMapper planMapper;
|
|
|
@Autowired private DrillPracticalResultMapper resultMapper;
|
|
@Autowired private DrillPracticalResultMapper resultMapper;
|
|
|
|
|
+ @Autowired private DrillPracticalRecordMapper recordMapper;
|
|
|
|
|
+ @Autowired private DrillPracticalMonitoringMapper monitoringMapper;
|
|
|
|
|
+ @Autowired private ObjectMapper objectMapper;
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 一、演练模板 CRUD
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/template")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult templates() {
|
|
|
|
|
+ return AjaxResult.success(templateMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalTemplate>()
|
|
|
|
|
+ .orderByDesc(DrillPracticalTemplate::getCreateTime)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PostMapping("/template")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult saveTemplate(@RequestBody DrillPracticalTemplate t) {
|
|
|
|
|
+ t.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+ templateMapper.insert(t);
|
|
|
|
|
+ return AjaxResult.success(t.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PutMapping("/template")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult updateTemplate(@RequestBody DrillPracticalTemplate t) {
|
|
|
|
|
+ templateMapper.updateById(t);
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @DeleteMapping("/template/{id}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult deleteTemplate(@PathVariable Long id) {
|
|
|
|
|
+ templateMapper.deleteById(id);
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 二、演练计划 CRUD
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/plan")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult plans() {
|
|
|
|
|
+ return AjaxResult.success(planMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalPlan>()
|
|
|
|
|
+ .orderByDesc(DrillPracticalPlan::getCreateTime)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PostMapping("/plan")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult savePlan(@RequestBody DrillPracticalPlan p) {
|
|
|
|
|
+ p.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+ p.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ if (p.getStatus() == null) p.setStatus("计划中");
|
|
|
|
|
+ if (p.getProgress() == null) p.setProgress(0);
|
|
|
|
|
+ if (p.getParticipantCount() == null) p.setParticipantCount(0);
|
|
|
|
|
+ planMapper.insert(p);
|
|
|
|
|
+ return AjaxResult.success(p.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PutMapping("/plan")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult updatePlan(@RequestBody DrillPracticalPlan p) {
|
|
|
|
|
+ p.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ planMapper.updateById(p);
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @DeleteMapping("/plan/{id}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult deletePlan(@PathVariable Long id) {
|
|
|
|
|
+ planMapper.deleteById(id);
|
|
|
|
|
+ // 同时删除关联的演练记录和监测数据
|
|
|
|
|
+ List<DrillPracticalRecord> records = recordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalRecord>()
|
|
|
|
|
+ .eq(DrillPracticalRecord::getPlanId, id));
|
|
|
|
|
+ for (DrillPracticalRecord r : records) {
|
|
|
|
|
+ monitoringMapper.delete(new LambdaQueryWrapper<DrillPracticalMonitoring>()
|
|
|
|
|
+ .eq(DrillPracticalMonitoring::getRecordId, r.getId()));
|
|
|
|
|
+ }
|
|
|
|
|
+ recordMapper.delete(new LambdaQueryWrapper<DrillPracticalRecord>()
|
|
|
|
|
+ .eq(DrillPracticalRecord::getPlanId, id));
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 三、演练计划状态更新
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @PutMapping("/plan/status")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("更新演练计划状态")
|
|
|
|
|
+ public AjaxResult updatePlanStatus(@RequestParam Long planId, @RequestParam String status) {
|
|
|
|
|
+ DrillPracticalPlan plan = planMapper.selectById(planId);
|
|
|
|
|
+ if (plan != null) {
|
|
|
|
|
+ plan.setStatus(status);
|
|
|
|
|
+ plan.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ if ("已完成".equals(status)) {
|
|
|
|
|
+ plan.setProgress(100);
|
|
|
|
|
+ } else if ("进行中".equals(status) && (plan.getProgress() == null || plan.getProgress() == 0)) {
|
|
|
|
|
+ plan.setProgress(10);
|
|
|
|
|
+ }
|
|
|
|
|
+ planMapper.updateById(plan);
|
|
|
|
|
+ }
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 四、演练记录 CRUD
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/record/{planId}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("按计划ID获取演练记录")
|
|
|
|
|
+ public AjaxResult getRecord(@PathVariable Long planId) {
|
|
|
|
|
+ DrillPracticalRecord record = recordMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalRecord>()
|
|
|
|
|
+ .eq(DrillPracticalRecord::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalRecord::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (record == null) {
|
|
|
|
|
+ return AjaxResult.success(null);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 将 JSON 字符串字段解析为对象后返回
|
|
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
|
|
+ result.put("id", record.getId());
|
|
|
|
|
+ result.put("planId", record.getPlanId());
|
|
|
|
|
+ result.put("drillProjectName", record.getDrillProjectName());
|
|
|
|
|
+ result.put("recordTime", record.getRecordTime());
|
|
|
|
|
+ result.put("drillType", record.getDrillType());
|
|
|
|
|
+ result.put("participatingDept", record.getParticipatingDept());
|
|
|
|
|
+ result.put("responsiblePerson", record.getResponsiblePerson());
|
|
|
|
|
+ result.put("status", record.getStatus());
|
|
|
|
|
+ result.put("reportTime", record.getReportTime());
|
|
|
|
|
+ result.put("reportMethod", record.getReportMethod());
|
|
|
|
|
+ result.put("reporter", record.getReporter());
|
|
|
|
|
+ result.put("contactPhone", record.getContactPhone());
|
|
|
|
|
+ result.put("eventLocation", record.getEventLocation());
|
|
|
|
|
+ result.put("eventDescription", record.getEventDescription());
|
|
|
|
|
+ result.put("releaseContent", record.getReleaseContent());
|
|
|
|
|
+ result.put("dispatchInfo", parseJson(record.getDispatchInfo()));
|
|
|
|
|
+ result.put("decisionInfo", parseJson(record.getDecisionInfo()));
|
|
|
|
|
+ result.put("releaseChannels", parseJsonArray(record.getReleaseChannels()));
|
|
|
|
|
+ result.put("actions", parseJsonArray(record.getActions()));
|
|
|
|
|
+ return AjaxResult.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PostMapping("/record")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("新建演练记录")
|
|
|
|
|
+ public AjaxResult saveRecord(@RequestBody Map<String, Object> body) {
|
|
|
|
|
+ DrillPracticalRecord record = new DrillPracticalRecord();
|
|
|
|
|
+ mapToRecord(body, record);
|
|
|
|
|
+ record.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+ record.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ recordMapper.insert(record);
|
|
|
|
|
+ return AjaxResult.success(record.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PutMapping("/record")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("更新演练记录")
|
|
|
|
|
+ public AjaxResult updateRecord(@RequestBody Map<String, Object> body) {
|
|
|
|
|
+ Long id = toLong(body.get("id"));
|
|
|
|
|
+ if (id == null) return AjaxResult.error("记录ID不能为空");
|
|
|
|
|
+ DrillPracticalRecord record = recordMapper.selectById(id);
|
|
|
|
|
+ if (record == null) return AjaxResult.error("记录不存在");
|
|
|
|
|
+ mapToRecord(body, record);
|
|
|
|
|
+ record.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ recordMapper.updateById(record);
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 五、监测数据 CRUD
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/monitoring/{recordId}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("按记录ID获取监测数据列表")
|
|
|
|
|
+ public AjaxResult monitoring(@PathVariable Long recordId) {
|
|
|
|
|
+ return AjaxResult.success(monitoringMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalMonitoring>()
|
|
|
|
|
+ .eq(DrillPracticalMonitoring::getRecordId, recordId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalMonitoring::getCreateTime)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PostMapping("/monitoring")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("保存/更新监测数据")
|
|
|
|
|
+ public AjaxResult saveMonitoring(@RequestBody DrillPracticalMonitoring m) {
|
|
|
|
|
+ if (m.getId() != null) {
|
|
|
|
|
+ monitoringMapper.updateById(m);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ m.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+ monitoringMapper.insert(m);
|
|
|
|
|
+ }
|
|
|
|
|
+ return AjaxResult.success(m.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @DeleteMapping("/monitoring/{id}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("删除监测数据")
|
|
|
|
|
+ public AjaxResult deleteMonitoring(@PathVariable Long id) {
|
|
|
|
|
+ monitoringMapper.deleteById(id);
|
|
|
|
|
+ return AjaxResult.success();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 六、分析评估
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/analysis/{planId}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("获取演练分析结果")
|
|
|
|
|
+ public AjaxResult getAnalysis(@PathVariable Long planId) {
|
|
|
|
|
+ DrillPracticalResult result = resultMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalResult>()
|
|
|
|
|
+ .eq(DrillPracticalResult::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalResult::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (result == null) {
|
|
|
|
|
+ return AjaxResult.success(null);
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, Object> data = new LinkedHashMap<>();
|
|
|
|
|
+ data.put("id", result.getId());
|
|
|
|
|
+ data.put("planId", result.getPlanId());
|
|
|
|
|
+ data.put("skillScore", result.getSkillScore());
|
|
|
|
|
+ data.put("equipmentScore", result.getEquipmentScore());
|
|
|
|
|
+ data.put("responseScore", result.getResponseScore());
|
|
|
|
|
+ data.put("cooperationScore", result.getCooperationScore());
|
|
|
|
|
+ data.put("totalTasks", result.getTotalTasks());
|
|
|
|
|
+ data.put("completedTasks", result.getCompletedTasks());
|
|
|
|
|
+ data.put("completionRate", result.getCompletionRate());
|
|
|
|
|
+ data.put("overtimeTasks", result.getOvertimeTasks());
|
|
|
|
|
+ data.put("overallLevel", result.getOverallLevel());
|
|
|
|
|
+ data.put("evalConclusion", result.getEvalConclusion());
|
|
|
|
|
+ data.put("improvementSuggestions", parseJsonArray(result.getImprovementSuggestions()));
|
|
|
|
|
+ data.put("analyzeTime", result.getEvalTime() != null
|
|
|
|
|
+ ? result.getEvalTime().toString() : null);
|
|
|
|
|
+ return AjaxResult.success(data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PostMapping("/analysis")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("发送演练数据至分析评估(生成分析结果)")
|
|
|
|
|
+ public AjaxResult sendAnalysis(@RequestBody Map<String, Object> body) {
|
|
|
|
|
+ Long planId = toLong(body.get("planId"));
|
|
|
|
|
+ if (planId == null) return AjaxResult.error("计划ID不能为空");
|
|
|
|
|
+
|
|
|
|
|
+ // 先保存/更新演练记录
|
|
|
|
|
+ DrillPracticalRecord record = recordMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalRecord>()
|
|
|
|
|
+ .eq(DrillPracticalRecord::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalRecord::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ boolean isNew = (record == null);
|
|
|
|
|
+ if (isNew) record = new DrillPracticalRecord();
|
|
|
|
|
+ mapToRecord(body, record);
|
|
|
|
|
+ if (isNew) {
|
|
|
|
|
+ record.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+ recordMapper.insert(record);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ record.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ recordMapper.updateById(record);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 基于演练数据生成分析结果
|
|
|
|
|
+ List<?> monitoringInfo = (List<?>) body.get("monitoringInfo");
|
|
|
|
|
+ int monCount = monitoringInfo != null ? monitoringInfo.size() : 0;
|
|
|
|
|
+
|
|
|
|
|
+ Object dispatchInfo = body.get("dispatchInfo");
|
|
|
|
|
+ Object decisionInfo = body.get("decisionInfo");
|
|
|
|
|
+ Object releaseChannels = body.get("releaseChannels");
|
|
|
|
|
+ String releaseContent = (String) body.get("releaseContent");
|
|
|
|
|
+ List<?> actions = (List<?>) body.get("actions");
|
|
|
|
|
+
|
|
|
|
|
+ // 查找已有结果记录
|
|
|
|
|
+ DrillPracticalResult result = resultMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalResult>()
|
|
|
|
|
+ .eq(DrillPracticalResult::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalResult::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ boolean newResult = (result == null);
|
|
|
|
|
+ if (newResult) result = new DrillPracticalResult();
|
|
|
|
|
+ result.setPlanId(planId);
|
|
|
|
|
+
|
|
|
|
|
+ // 生成评分(基于数据完整度)
|
|
|
|
|
+ int score = 60;
|
|
|
|
|
+ if (monCount > 0) score += Math.min(15, monCount * 3);
|
|
|
|
|
+ if (dispatchInfo != null) score += 8;
|
|
|
|
|
+ if (decisionInfo != null) score += 8;
|
|
|
|
|
+ if (releaseChannels != null && releaseContent != null && !releaseContent.isEmpty()) score += 5;
|
|
|
|
|
+ if (score > 98) score = 98;
|
|
|
|
|
+
|
|
|
|
|
+ result.setSkillScore(score);
|
|
|
|
|
+ result.setEquipmentScore(Math.max(60, score - 7));
|
|
|
|
|
+ result.setResponseScore(Math.max(65, score - 3));
|
|
|
|
|
+ result.setCooperationScore(Math.max(62, score - 5));
|
|
|
|
|
+ result.setTotalTasks(5 + monCount);
|
|
|
|
|
+ result.setCompletedTasks(dispatchInfo != null && decisionInfo != null ? 5 + monCount : 3 + monCount);
|
|
|
|
|
+ int compRate = result.getTotalTasks() > 0
|
|
|
|
|
+ ? (result.getCompletedTasks() * 100 / result.getTotalTasks()) : 0;
|
|
|
|
|
+ result.setCompletionRate(compRate);
|
|
|
|
|
+ result.setOvertimeTasks(Math.max(0, monCount / 5));
|
|
|
|
|
+ result.setOverallLevel(score >= 90 ? "优秀" : score >= 80 ? "良好" : score >= 70 ? "合格" : "待改进");
|
|
|
|
|
+ result.setEvalConclusion(
|
|
|
|
|
+ "本次演练整体表现" + result.getOverallLevel() + ","
|
|
|
|
|
+ + "共录入监测数据" + monCount + "条,"
|
|
|
|
|
+ + (dispatchInfo != null ? "指挥调度信息已录入," : "指挥调度信息缺失,")
|
|
|
|
|
+ + (decisionInfo != null ? "决策信息已录入。" : "决策信息缺失。")
|
|
|
|
|
+ + "任务完成率" + compRate + "%。"
|
|
|
|
|
+ );
|
|
|
|
|
+ List<String> suggestions = new ArrayList<>();
|
|
|
|
|
+ if (monCount < 3) suggestions.add("增加监测数据采集密度,提高演练覆盖范围");
|
|
|
|
|
+ if (dispatchInfo == null) suggestions.add("完善指挥调度信息记录");
|
|
|
|
|
+ if (decisionInfo == null) suggestions.add("加强决策信息录入规范");
|
|
|
|
|
+ if (compRate < 80) suggestions.add("提高任务完成率,减少超时任务");
|
|
|
|
|
+ suggestions.add("加强多部门协同配合训练");
|
|
|
|
|
+ suggestions.add("定期复盘演练过程,持续优化应急预案");
|
|
|
|
|
+ try {
|
|
|
|
|
+ result.setImprovementSuggestions(objectMapper.writeValueAsString(suggestions));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ result.setImprovementSuggestions("[]");
|
|
|
|
|
+ }
|
|
|
|
|
+ result.setEvalTime(LocalDateTime.now());
|
|
|
|
|
+ if (newResult) {
|
|
|
|
|
+ result.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+ resultMapper.insert(result);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ resultMapper.updateById(result);
|
|
|
|
|
+ }
|
|
|
|
|
+ return AjaxResult.success("分析数据已发送,评估结果已生成");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 七、导出分析报告
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/report/{planId}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ @ApiOperation("导出演练分析报告")
|
|
|
|
|
+ public ResponseEntity<byte[]> exportReport(@PathVariable Long planId) {
|
|
|
|
|
+ DrillPracticalPlan plan = planMapper.selectById(planId);
|
|
|
|
|
+ DrillPracticalResult result = resultMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalResult>()
|
|
|
|
|
+ .eq(DrillPracticalResult::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalResult::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ DrillPracticalRecord record = recordMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalRecord>()
|
|
|
|
|
+ .eq(DrillPracticalRecord::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalRecord::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ List<DrillPracticalMonitoring> monList = new ArrayList<>();
|
|
|
|
|
+ if (record != null) {
|
|
|
|
|
+ monList = monitoringMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalMonitoring>()
|
|
|
|
|
+ .eq(DrillPracticalMonitoring::getRecordId, record.getId()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ sb.append("=".repeat(50)).append("\n");
|
|
|
|
|
+ sb.append(" 实战演练分析报告\n");
|
|
|
|
|
+ sb.append("=".repeat(50)).append("\n\n");
|
|
|
|
|
+ sb.append("演练名称:").append(plan != null ? plan.getName() : "—").append("\n");
|
|
|
|
|
+ sb.append("演练类型:实战演练\n");
|
|
|
|
|
+ sb.append("所属系统:").append(plan != null ? plan.getCategory() : "—").append("\n");
|
|
|
|
|
+ if (plan != null && plan.getEndTime() != null) {
|
|
|
|
|
+ sb.append("完成时间:").append(plan.getEndTime()).append("\n");
|
|
|
|
|
+ }
|
|
|
|
|
+ sb.append("\n--- 演练记录 ---\n");
|
|
|
|
|
+ if (record != null) {
|
|
|
|
|
+ sb.append("记录时间:").append(record.getRecordTime()).append("\n");
|
|
|
|
|
+ sb.append("参演部门:").append(record.getParticipatingDept()).append("\n");
|
|
|
|
|
+ sb.append("演练状态:").append(record.getStatus()).append("\n");
|
|
|
|
|
+ sb.append("事件描述:").append(record.getEventDescription()).append("\n");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sb.append("(暂无演练记录)\n");
|
|
|
|
|
+ }
|
|
|
|
|
+ sb.append("\n--- 监测数据 (").append(monList.size()).append("条) ---\n");
|
|
|
|
|
+ for (DrillPracticalMonitoring m : monList) {
|
|
|
|
|
+ sb.append(" - ").append(m.getSensorType())
|
|
|
|
|
+ .append(" | ").append(m.getLocation())
|
|
|
|
|
+ .append(" | 值: ").append(m.getValue()).append(m.getUnit())
|
|
|
|
|
+ .append(" | 等级: ").append(m.getAlarmLevel())
|
|
|
|
|
+ .append(" | 时间: ").append(m.getTime()).append("\n");
|
|
|
|
|
+ }
|
|
|
|
|
+ sb.append("\n--- 分析评估 ---\n");
|
|
|
|
|
+ if (result != null) {
|
|
|
|
|
+ sb.append("综合等级:").append(result.getOverallLevel()).append("\n");
|
|
|
|
|
+ sb.append("技能掌握:").append(result.getSkillScore()).append("/100\n");
|
|
|
|
|
+ sb.append("装备使用:").append(result.getEquipmentScore()).append("/100\n");
|
|
|
|
|
+ sb.append("响应时效:").append(result.getResponseScore()).append("/100\n");
|
|
|
|
|
+ sb.append("协同配合:").append(result.getCooperationScore()).append("/100\n");
|
|
|
|
|
+ sb.append("任务总数:").append(result.getTotalTasks()).append("\n");
|
|
|
|
|
+ sb.append("完成任务:").append(result.getCompletedTasks()).append("\n");
|
|
|
|
|
+ sb.append("完成率:").append(result.getCompletionRate()).append("%\n");
|
|
|
|
|
+ sb.append("超时任务:").append(result.getOvertimeTasks()).append("\n");
|
|
|
|
|
+ sb.append("\n评估结论:").append(result.getEvalConclusion()).append("\n");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sb.append("(暂无分析数据)\n");
|
|
|
|
|
+ }
|
|
|
|
|
+ sb.append("\n").append("=".repeat(50)).append("\n");
|
|
|
|
|
+ sb.append("报告生成时间:").append(LocalDateTime.now()).append("\n");
|
|
|
|
|
+
|
|
|
|
|
+ byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
|
|
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
+ headers.setContentType(MediaType.TEXT_PLAIN);
|
|
|
|
|
+ headers.set("Content-Disposition",
|
|
|
|
|
+ "attachment; filename=drill_report_" + planId + ".txt");
|
|
|
|
|
+ return ResponseEntity.ok().headers(headers).body(bytes);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 八、原有结果查询(保留,兼容旧前端)
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ @GetMapping("/result/{planId}")
|
|
|
|
|
+ @Anonymous
|
|
|
|
|
+ public AjaxResult result(@PathVariable Long planId) {
|
|
|
|
|
+ return AjaxResult.success(resultMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<DrillPracticalResult>()
|
|
|
|
|
+ .eq(DrillPracticalResult::getPlanId, planId)
|
|
|
|
|
+ .orderByDesc(DrillPracticalResult::getCreateTime)
|
|
|
|
|
+ .last("LIMIT 1")));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ // 私有工具方法
|
|
|
|
|
+ // ============================================================
|
|
|
|
|
+ private void mapToRecord(Map<String, Object> body, DrillPracticalRecord r) {
|
|
|
|
|
+ r.setPlanId(toLong(body.get("planId")));
|
|
|
|
|
+ r.setDrillProjectName((String) body.get("drillProjectName"));
|
|
|
|
|
+ r.setRecordTime((String) body.get("recordTime"));
|
|
|
|
|
+ r.setDrillType((String) body.get("drillType"));
|
|
|
|
|
+ r.setParticipatingDept((String) body.get("participatingDept"));
|
|
|
|
|
+ r.setResponsiblePerson((String) body.get("responsiblePerson"));
|
|
|
|
|
+ r.setStatus((String) body.get("status"));
|
|
|
|
|
+ r.setReportTime((String) body.get("reportTime"));
|
|
|
|
|
+ r.setReportMethod((String) body.get("reportMethod"));
|
|
|
|
|
+ r.setReporter((String) body.get("reporter"));
|
|
|
|
|
+ r.setContactPhone((String) body.get("contactPhone"));
|
|
|
|
|
+ r.setEventLocation((String) body.get("eventLocation"));
|
|
|
|
|
+ r.setEventDescription((String) body.get("eventDescription"));
|
|
|
|
|
+ r.setDispatchInfo(toJsonStr(body.get("dispatchInfo")));
|
|
|
|
|
+ r.setDecisionInfo(toJsonStr(body.get("decisionInfo")));
|
|
|
|
|
+ r.setReleaseChannels(toJsonStr(body.get("releaseChannels")));
|
|
|
|
|
+ r.setReleaseContent((String) body.get("releaseContent"));
|
|
|
|
|
+ r.setActions(toJsonStr(body.get("actions")));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Object parseJson(String json) {
|
|
|
|
|
+ if (json == null || json.isEmpty()) return null;
|
|
|
|
|
+ try { return objectMapper.readValue(json, Object.class); }
|
|
|
|
|
+ catch (Exception e) { return null; }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @GetMapping("/template") @Anonymous public AjaxResult templates() { return AjaxResult.success(templateMapper.selectList(new LambdaQueryWrapper<DrillPracticalTemplate>().orderByDesc(DrillPracticalTemplate::getCreateTime))); }
|
|
|
|
|
- @PostMapping("/template") @Anonymous public AjaxResult saveTemplate(@RequestBody DrillPracticalTemplate t) { templateMapper.insert(t); return AjaxResult.success(t.getId()); }
|
|
|
|
|
- @PutMapping("/template") @Anonymous public AjaxResult updateTemplate(@RequestBody DrillPracticalTemplate t) { templateMapper.updateById(t); return AjaxResult.success(); }
|
|
|
|
|
- @DeleteMapping("/template/{id}") @Anonymous public AjaxResult deleteTemplate(@PathVariable Long id) { templateMapper.deleteById(id); return AjaxResult.success(); }
|
|
|
|
|
|
|
+ private List<?> parseJsonArray(String json) {
|
|
|
|
|
+ if (json == null || json.isEmpty()) return null;
|
|
|
|
|
+ try { return objectMapper.readValue(json, List.class); }
|
|
|
|
|
+ catch (Exception e) { return null; }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @GetMapping("/plan") @Anonymous public AjaxResult plans() { return AjaxResult.success(planMapper.selectList(new LambdaQueryWrapper<DrillPracticalPlan>().orderByDesc(DrillPracticalPlan::getCreateTime))); }
|
|
|
|
|
- @PostMapping("/plan") @Anonymous public AjaxResult savePlan(@RequestBody DrillPracticalPlan p) { planMapper.insert(p); return AjaxResult.success(p.getId()); }
|
|
|
|
|
- @PutMapping("/plan") @Anonymous public AjaxResult updatePlan(@RequestBody DrillPracticalPlan p) { planMapper.updateById(p); return AjaxResult.success(); }
|
|
|
|
|
- @DeleteMapping("/plan/{id}") @Anonymous public AjaxResult deletePlan(@PathVariable Long id) { planMapper.deleteById(id); return AjaxResult.success(); }
|
|
|
|
|
|
|
+ private String toJsonStr(Object obj) {
|
|
|
|
|
+ if (obj == null) return null;
|
|
|
|
|
+ if (obj instanceof String) return (String) obj;
|
|
|
|
|
+ try { return objectMapper.writeValueAsString(obj); }
|
|
|
|
|
+ catch (Exception e) { return null; }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @GetMapping("/result/{planId}") @Anonymous public AjaxResult result(@PathVariable Long planId) { return AjaxResult.success(resultMapper.selectOne(new LambdaQueryWrapper<DrillPracticalResult>().eq(DrillPracticalResult::getPlanId, planId))); }
|
|
|
|
|
|
|
+ private Long toLong(Object val) {
|
|
|
|
|
+ if (val == null) return null;
|
|
|
|
|
+ if (val instanceof Number) return ((Number) val).longValue();
|
|
|
|
|
+ try { return Long.parseLong(val.toString()); }
|
|
|
|
|
+ catch (Exception e) { return null; }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|