Merge remote-tracking branch 'origin/feature/issue-48'

# Conflicts:
#	frontend/src/router/index.ts
#	wm-production/pom.xml
This commit is contained in:
2026-06-15 08:33:46 +08:00
235 changed files with 23417 additions and 225 deletions
+4 -2
View File
@@ -12,7 +12,9 @@
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId></dependency>
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot3-starter</artifactId></dependency>
<dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId></dependency>
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId></dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
+83
View File
@@ -0,0 +1,83 @@
-- 阈值表
CREATE TABLE IF NOT EXISTS threshold (
id BIGSERIAL PRIMARY KEY,
device_id VARCHAR(100) NOT NULL,
device_type VARCHAR(50),
region VARCHAR(50),
parameter VARCHAR(100) NOT NULL,
min_value DOUBLE PRECISION,
max_value DOUBLE PRECISION,
warning_min DOUBLE PRECISION,
warning_max DOUBLE PRECISION,
unit VARCHAR(20),
description TEXT,
status INTEGER DEFAULT 1 COMMENT '1-启用,0-禁用',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 信息发布表
CREATE TABLE IF NOT EXISTS notification (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
type VARCHAR(20) NOT NULL COMMENT 'forecast-预报,warning-预警,info-通知',
region VARCHAR(50),
priority VARCHAR(20) DEFAULT 'medium' COMMENT 'high-高,medium-中,low-低',
status INTEGER DEFAULT 1 COMMENT '1-草稿,2-已发布,3-已归档',
publisher VARCHAR(100),
publish_time TIMESTAMP,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 设备表
CREATE TABLE IF NOT EXISTS equipment (
id BIGSERIAL PRIMARY KEY,
device_name VARCHAR(100) NOT NULL,
device_type VARCHAR(50),
model VARCHAR(100),
serial_number VARCHAR(100),
region VARCHAR(50),
location TEXT,
status VARCHAR(20) DEFAULT 'offline' COMMENT 'online-在线,offline-离线,maintenance-维护中,fault-故障',
manufacturer VARCHAR(100),
installation_date DATE,
last_maintenance_date DATE,
next_maintenance_date DATE,
latitude DOUBLE PRECISION,
longitude DOUBLE PRECISION,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_threshold_device_id ON threshold(device_id);
CREATE INDEX IF NOT EXISTS idx_threshold_region ON threshold(region);
CREATE INDEX IF NOT EXISTS idx_threshold_parameter ON threshold(parameter);
CREATE INDEX IF NOT EXISTS idx_notification_region ON notification(region);
CREATE INDEX IF NOT EXISTS idx_notification_type ON notification(type);
CREATE INDEX IF NOT EXISTS idx_notification_status ON notification(status);
CREATE INDEX IF NOT EXISTS idx_equipment_device_name ON equipment(device_name);
CREATE INDEX IF NOT EXISTS idx_equipment_device_type ON equipment(device_type);
CREATE INDEX IF NOT EXISTS idx_equipment_region ON equipment(region);
CREATE INDEX IF NOT EXISTS idx_equipment_status ON equipment(status);
-- 插入示例数据
-- 阈值示例
INSERT INTO threshold (device_id, device_type, region, parameter, min_value, max_value, warning_min, warning_max, unit, description, status) VALUES
('DEV001', 'pressure_sensor', '精河县', 'water_pressure', 0.2, 0.8, 0.15, 0.85, 'MPa', '供水压力阈值', 1),
('DEV002', 'flow_meter', '精河县', 'water_flow', 10, 100, 8, 110, 'm³/h', '水流流量阈值', 1),
('DEV003', 'quality_sensor', '精河县', 'water_quality', 0, 1, 0.1, 0.9, 'pH', '水质pH值阈值', 1);
-- 设备示例
INSERT INTO equipment (device_name, device_type, model, serial_number, region, location, status, manufacturer, installation_date) VALUES
('压力传感器001', 'pressure_sensor', 'PS-3000', 'SN0012023001', '精河县', '供水站A区', 'online', '华为', '2023-01-15'),
('流量计001', 'flow_meter', 'FM-5000', 'SN0012023002', '精河县', '供水站B区', 'online', '西门子', '2023-02-20'),
('水质检测仪001', 'quality_sensor', 'QS-2000', 'SN0012023003', '精河县', '供水站C区', 'online', '霍尼韦尔', '2023-03-10');
-- 信息发布示例
INSERT INTO notification (title, content, type, region, priority, status, publisher, publish_time) VALUES
('停水通知', '因设备维护,预计明天9:00-12:00精河县部分区域将暂停供水', 'warning', '精河县', 'high', 2, 'system_admin', '2026-06-14T08:00:00'),
('水质提升通知', '本季度已完成水质净化设备升级,水质显著提升', 'info', '精河县', 'medium', 2, 'water_quality_team', '2026-06-01T10:00:00'),
('雨季供水保障通知', '近期降雨较多,各水厂已加强巡检,确保供水稳定', 'forecast', '精河县', 'low', 2, 'emergency_team', '2026-05-20T14:00:00');
@@ -0,0 +1,83 @@
package com.water.production.controller;
import com.water.production.entity.*;
import com.water.production.service.DataQueryService;
import com.water.production.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.Map;
@RestController
@RequestMapping("/api/data")
@CrossOrigin(origins = "*")
public class DataQueryController {
@Autowired
private DataQueryService dataQueryService;
/**
* 水量数据查询
*/
@GetMapping("/water")
public Result<Page<WaterData>> queryWaterData(WaterDataQueryVO vo) {
Page<WaterData> page = dataQueryService.queryWaterData(vo);
return Result.success(page);
}
/**
* 水质数据查询
*/
@GetMapping("/quality")
public Result<Page<WaterQuality>> queryWaterQuality(WaterQualityQueryVO vo) {
Page<WaterQuality> page = dataQueryService.queryWaterQuality(vo);
return Result.success(page);
}
/**
* 报警记录查询
*/
@GetMapping("/alarms")
public Result<Page<AlarmRecord>> queryAlarmRecords(AlarmQueryVO vo) {
Page<AlarmRecord> page = dataQueryService.queryAlarmRecords(vo);
return Result.success(page);
}
/**
* 生成水量汇总报表
*/
@PostMapping("/report/water-volume")
public Result<ProductionReport> generateWaterVolumeReport(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
@RequestParam(required = false) String region) {
ProductionReport report = dataQueryService.generateWaterVolumeReport(startTime, endTime, region);
return Result.success(report);
}
/**
* 生成水质合格率报表
*/
@PostMapping("/report/water-quality")
public Result<ProductionReport> generateWaterQualityReport(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
@RequestParam(required = false) String region) {
ProductionReport report = dataQueryService.generateWaterQualityReport(startTime, endTime, region);
return Result.success(report);
}
/**
* 生成报警统计报表
*/
@PostMapping("/report/alarm-stat")
public Result<ProductionReport> generateAlarmReport(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
@RequestParam(required = false) String region) {
ProductionReport report = dataQueryService.generateAlarmReport(startTime, endTime, region);
return Result.success(report);
}
}
@@ -0,0 +1,185 @@
package com.water.production.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.production.entity.*;
import com.water.production.mapper.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class DataQueryService {
@Autowired
private WaterDataMapper waterDataMapper;
@Autowired
private WaterQualityMapper waterQualityMapper;
@Autowired
private AlarmRecordMapper alarmRecordMapper;
@Autowired
private DeviceDataMapper deviceDataMapper;
@Autowired
private ProductionReportMapper productionReportMapper;
/**
* 历史数据查询 - 水量数据
*/
public Page<WaterData> queryWaterData(WaterDataQueryVO vo) {
QueryWrapper<WaterData> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(vo.getDeviceId())) {
wrapper.eq("device_id", vo.getDeviceId());
}
if (vo.getStartTime() != null) {
wrapper.ge("create_time", vo.getStartTime());
}
if (vo.getEndTime() != null) {
wrapper.le("create_time", vo.getEndTime());
}
if (StringUtils.hasText(vo.getRegion())) {
wrapper.like("region", vo.getRegion());
}
return waterDataMapper.selectPage(new Page<>(vo.getPageNum(), vo.getPageSize()), wrapper);
}
/**
* 历史数据查询 - 水质数据
*/
public Page<WaterQuality> queryWaterQuality(WaterQualityQueryVO vo) {
QueryWrapper<WaterQuality> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(vo.getDeviceId())) {
wrapper.eq("device_id", vo.getDeviceId());
}
if (vo.getStartTime() != null) {
wrapper.ge("create_time", vo.getStartTime());
}
if (vo.getEndTime() != null) {
wrapper.le("create_time", vo.getEndTime());
}
if (vo.getQualityLevel() != null) {
wrapper.eq("quality_level", vo.getQualityLevel());
}
return waterQualityMapper.selectPage(new Page<>(vo.getPageNum(), vo.getPageSize()), wrapper);
}
/**
* 历史数据查询 - 报警记录
*/
public Page<AlarmRecord> queryAlarmRecords(AlarmQueryVO vo) {
QueryWrapper<AlarmRecord> wrapper = new QueryWrapper<>();
if (StringUtils.hasText(vo.getDeviceId())) {
wrapper.eq("device_id", vo.getDeviceId());
}
if (vo.getStartTime() != null) {
wrapper.ge("create_time", vo.getStartTime());
}
if (vo.getEndTime() != null) {
wrapper.le("create_time", vo.getEndTime());
}
if (StringUtils.hasText(vo.getAlarmType())) {
wrapper.eq("alarm_type", vo.getAlarmType());
}
if (vo.getAlarmLevel() != null) {
wrapper.eq("alarm_level", vo.getAlarmLevel());
}
return alarmRecordMapper.selectPage(new Page<>(vo.getPageNum(), vo.getPageSize()), wrapper);
}
/**
* 生成水量汇总报表
*/
public ProductionReport generateWaterVolumeReport(LocalDateTime startTime, LocalDateTime endTime, String region) {
QueryWrapper<WaterData> wrapper = new QueryWrapper<>();
wrapper.between("create_time", startTime, endTime);
if (StringUtils.hasText(region)) {
wrapper.like("region", region);
}
List<WaterData> dataList = waterDataMapper.selectList(wrapper);
double totalVolume = dataList.stream().mapToDouble(WaterData::getVolume).sum();
double avgVolume = dataList.isEmpty() ? 0 : totalVolume / dataList.size();
ProductionReport report = new ProductionReport();
report.setReportType("WATER_VOLUME");
report.setStartTime(startTime);
report.setEndTime(endTime);
report.setRegion(region);
report.setTotalValue(totalVolume);
report.setAvgValue(avgVolume);
report.setDataCount(dataList.size());
report.setGenerateTime(LocalDateTime.now());
return productionReportMapper.insert(report) > 0 ? report : null;
}
/**
* 生成水质合格率报表
*/
public ProductionReport generateWaterQualityReport(LocalDateTime startTime, LocalDateTime endTime, String region) {
QueryWrapper<WaterQuality> wrapper = new QueryWrapper<>();
wrapper.between("create_time", startTime, endTime);
if (StringUtils.hasText(region)) {
wrapper.like("region", region);
}
List<WaterQuality> qualityList = waterQualityMapper.selectList(wrapper);
int qualifiedCount = (int) qualityList.stream().filter(q -> q.getQualityLevel() <= 3).count();
double合格率 = qualityList.isEmpty() ? 0 : (double) qualifiedCount / qualityList.size() * 100;
ProductionReport report = new ProductionReport();
report.setReportType("WATER_QUALITY");
report.setStartTime(startTime);
report.setEndTime(endTime);
report.setRegion(region);
report.setTotalValue(qualifiedCount);
report.setAvgValue(合格率);
report.setDataCount(qualityList.size());
report.setGenerateTime(LocalDateTime.now());
return productionReportMapper.insert(report) > 0 ? report : null;
}
/**
* 生成报警统计报表
*/
public ProductionReport generateAlarmReport(LocalDateTime startTime, LocalDateTime endTime, String region) {
QueryWrapper<AlarmRecord> wrapper = new QueryWrapper<>();
wrapper.between("create_time", startTime, endTime);
if (StringUtils.hasText(region)) {
wrapper.like("region", region);
}
List<AlarmRecord> alarmList = alarmRecordMapper.selectList(wrapper);
// 按报警类型分组统计
Map<String, Long> typeStats = alarmList.stream()
.collect(Collectors.groupingBy(AlarmRecord::getAlarmType, Collectors.counting()));
ProductionReport report = new ProductionReport();
report.setReportType("ALARM_STAT");
report.setStartTime(startTime);
report.setEndTime(endTime);
report.setRegion(region);
report.setTotalValue(alarmList.size());
report.setDataCount(typeStats.size());
report.setRemarks(typeStats.toString());
report.setGenerateTime(LocalDateTime.now());
return productionReportMapper.insert(report) > 0 ? report : null;
}
}
@@ -0,0 +1,29 @@
package com.water.production.entity;
import lombok.Data;
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> success() {
return success(null);
}
public static <T> Result<T> error(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}
@@ -0,0 +1,11 @@
# MyBatis-Plus配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
@@ -0,0 +1,18 @@
package com.water.production.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRESQL));
return interceptor;
}
}
@@ -0,0 +1,34 @@
package com.water.production.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.water.production.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("智慧水务管理系统 - 数据查询API")
.description("历史数据回溯与报表生成接口")
.contact(new Contact("水厂开发团队", "", "dev@water.com"))
.version("1.0.0")
.build();
}
}
@@ -0,0 +1,170 @@
package com.water.production.controller;
import com.water.production.entity.AlertManagement;
import com.water.production.service.AlertManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/alert-management")
@CrossOrigin(origins = "*")
public class AlertManagementController {
@Autowired
private AlertManagementService alertManagementService;
// 获取所有报警
@GetMapping
public ResponseEntity<List<AlertManagement>> getAllAlerts() {
try {
List<AlertManagement> alerts = alertManagementService.getAllAlerts();
return ResponseEntity.ok(alerts);
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 根据ID获取报警
@GetMapping("/{id}")
public ResponseEntity<AlertManagement> getAlertById(@PathVariable Long id) {
try {
AlertManagement alert = alertManagementService.getAlertById(id);
return ResponseEntity.ok(alert);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.notFound().build();
}
}
// 根据报警规则ID获取相关报警
@GetMapping("/rule/{alertRuleId}")
public ResponseEntity<List<AlertManagement>> getAlertsByRuleId(@PathVariable Long alertRuleId) {
try {
List<AlertManagement> alerts = alertManagementService.getAlertsByRuleId(alertRuleId);
return ResponseEntity.ok(alerts);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 根据状态获取报警
@GetMapping("/status/{status}")
public ResponseEntity<List<AlertManagement>> getAlertsByStatus(@PathVariable String status) {
try {
List<AlertManagement> alerts = alertManagementService.getAlertsByStatus(status);
return ResponseEntity.ok(alerts);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 根据严重程度获取报警
@GetMapping("/severity/{severity}")
public ResponseEntity<List<AlertManagement>> getAlertsBySeverity(@PathVariable String severity) {
try {
List<AlertManagement> alerts = alertManagementService.getAlertsBySeverity(severity);
return ResponseEntity.ok(alerts);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 创建报警
@PostMapping
public ResponseEntity<AlertManagement> createAlert(@RequestBody AlertManagement alert) {
try {
AlertManagement created = alertManagementService.createAlert(alert);
return ResponseEntity.ok(created);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 更新报警
@PutMapping("/{id}")
public ResponseEntity<AlertManagement> updateAlert(@PathVariable Long id, @RequestBody AlertManagement alert) {
try {
alert.setId(id);
AlertManagement updated = alertManagementService.updateAlert(alert);
return ResponseEntity.ok(updated);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 确认报警
@PutMapping("/{id}/confirm")
public ResponseEntity<Void> confirmAlert(@PathVariable Long id, @RequestParam String assigneeId) {
try {
boolean success = alertManagementService.confirmAlert(id, assigneeId);
if (success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 解决报警
@PutMapping("/{id}/resolve")
public ResponseEntity<Void> resolveAlert(@PathVariable Long id) {
try {
boolean success = alertManagementService.resolveAlert(id);
if (success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 分派任务
@PutMapping("/{id}/dispatch")
public ResponseEntity<Void> dispatchTask(@PathVariable Long id, @RequestParam Long taskId) {
try {
boolean success = alertManagementService.dispatchTask(id, taskId);
if (success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 生成报警统计
@GetMapping("/statistics")
public ResponseEntity<AlertManagementService.AlertStatistics> generateStatistics() {
try {
AlertManagementService.AlertStatistics statistics = alertManagementService.generateStatistics();
return ResponseEntity.ok(statistics);
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
}
@@ -0,0 +1,163 @@
package com.water.production.controller;
import com.water.production.entity.AlertRule;
import com.water.production.service.AlertRuleService;
import com.water.production.service.AlertManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/alert-rules")
@CrossOrigin(origins = "*")
public class AlertRuleController {
@Autowired
private AlertRuleService alertRuleService;
@Autowired
private AlertManagementService alertManagementService;
// 获取所有报警规则
@GetMapping
public ResponseEntity<List<AlertRule>> getAllAlertRules() {
try {
List<AlertRule> rules = alertRuleService.getAllAlertRules();
return ResponseEntity.ok(rules);
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 根据ID获取报警规则
@GetMapping("/{id}")
public ResponseEntity<AlertRule> getAlertRuleById(@PathVariable Long id) {
try {
AlertRule rule = alertRuleService.getAlertRuleById(id);
return ResponseEntity.ok(rule);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.notFound().build();
}
}
// 根据参数获取报警规则
@GetMapping("/parameter/{parameter}")
public ResponseEntity<List<AlertRule>> getAlertRulesByParameter(@PathVariable String parameter) {
try {
List<AlertRule> rules = alertRuleService.getAlertRulesByParameter(parameter);
return ResponseEntity.ok(rules);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 根据设备获取报警规则
@GetMapping("/equipment/{equipmentId}")
public ResponseEntity<List<AlertRule>> getAlertRulesByEquipment(@PathVariable String equipmentId) {
try {
List<AlertRule> rules = alertRuleService.getAlertRulesByEquipment(equipmentId);
return ResponseEntity.ok(rules);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 根据区域获取报警规则
@GetMapping("/area/{area}")
public ResponseEntity<List<AlertRule>> getAlertRulesByArea(@PathVariable String area) {
try {
List<AlertRule> rules = alertRuleService.getAlertRulesByArea(area);
return ResponseEntity.ok(rules);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 创建报警规则
@PostMapping
public ResponseEntity<AlertRule> createAlertRule(@RequestBody AlertRule alertRule) {
try {
AlertRule created = alertRuleService.createAlertRule(alertRule);
return ResponseEntity.ok(created);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 更新报警规则
@PutMapping("/{id}")
public ResponseEntity<AlertRule> updateAlertRule(@PathVariable Long id, @RequestBody AlertRule alertRule) {
try {
alertRule.setId(id);
AlertRule updated = alertRuleService.updateAlertRule(alertRule);
return ResponseEntity.ok(updated);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 删除报警规则
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteAlertRule(@PathVariable Long id) {
try {
boolean success = alertRuleService.deleteAlertRule(id);
if (success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 启用报警规则
@PutMapping("/{id}/enable")
public ResponseEntity<Void> enableAlertRule(@PathVariable Long id) {
try {
boolean success = alertRuleService.enableAlertRule(id);
if (success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
// 禁用报警规则
@PutMapping("/{id}/disable")
public ResponseEntity<Void> disableAlertRule(@PathVariable Long id) {
try {
boolean success = alertRuleService.disableAlertRule(id);
if (success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
}
@@ -0,0 +1,209 @@
package com.water.production.controller;
import com.water.production.service.EmergencyDispatchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/emergency/dispatch")
@RequiredArgsConstructor
public class EmergencyDispatchController {
private final EmergencyDispatchService dispatchService;
/**
* 应急推演总入口
*/
@PostMapping("/simulate")
public Map<String, Object> conductEmergencySimulation(
@RequestParam String scenarioType, // pipe_burst | water_quality
@RequestBody Map<String, Object> params,
@RequestParam String operatorName) {
Map<String, Object> result = dispatchService.conductEmergencySimulation(scenarioType, params, operatorName);
return result;
}
/**
* 应用应急预案到应急响应
*/
@PostMapping("/{simulationId}/apply-plan/{planId}")
public Map<String, Object> applyEmergencyPlan(
@PathVariable Long simulationId,
@PathVariable Long planId,
@RequestParam String operatorName) {
Map<String, Object> result = dispatchService.applyEmergencyPlan(simulationId, planId, operatorName);
return result;
}
/**
* 获取当前应急状态
*/
@GetMapping("/status")
public Map<String, Object> getCurrentEmergencyStatus() {
Map<String, Object> status = dispatchService.getCurrentEmergencyStatus();
return Map.of(
"success", true,
"status", status
);
}
/**
* 生成应急推演报告
*/
@GetMapping("/report")
public Map<String, Object> generateEmergencyReport(
@RequestParam(defaultValue = "week") String period) {
Map<String, Object> report = dispatchService.generateEmergencyReport(period);
return Map.of(
"success", true,
"report", report
);
}
/**
* 快速爆管模拟(简化接口)
*/
@PostMapping("/quick-pipe-burst")
public Map<String, Object> quickPipeBurstSimulation(
@RequestParam Double lng,
@RequestParam Double lat,
@RequestParam String pipeDiameter,
@RequestParam String operatorName) {
Map<String, Object> params = Map.of(
"lng", lng,
"lat", lat,
"pipeDiameter", pipeDiameter
);
return dispatchService.conductEmergencySimulation("pipe_burst", params, operatorName);
}
/**
* 快速水质异常模拟(简化接口)
*/
@PostMapping("/quick-water-quality")
public Map<String, Object> quickWaterQualitySimulation(
@RequestParam String area,
@RequestParam String pollutant,
@RequestParam Double lng,
@RequestParam Double lat,
@RequestParam String operatorName) {
Map<String, Object> params = Map.of(
"area", area,
"pollutant", pollutant,
"lng", lng,
"lat", lat
);
return dispatchService.conductEmergencySimulation("water_quality", params, operatorName);
}
/**
* 爆管模拟详情接口
*/
@PostMapping("/pipe-burst-detail")
public Map<String, Object> pipeBurstSimulationDetail(
@RequestParam Double lng,
@RequestParam Double lat,
@RequestParam String pipeDiameter,
@RequestParam(required = false) Integer radius,
@RequestParam String operatorName) {
Map<String, Object> params = Map.of(
"lng", lng,
"lat", lat,
"pipeDiameter", pipeDiameter,
"customRadius", radius
);
return dispatchService.conductEmergencySimulation("pipe_burst", params, operatorName);
}
/**
* 水质异常模拟详情接口
*/
@PostMapping("/water-quality-detail")
public Map<String, Object> waterQualitySimulationDetail(
@RequestParam String area,
@RequestParam String pollutant,
@RequestParam(required = false) Double lng,
@RequestParam(required = false) Double lat,
@RequestParam(required = false) Integer affectedPopulation,
@RequestParam String operatorName) {
Map<String, Object> params = Map.of(
"area", area,
"pollutant", pollutant,
"lng", lng,
"lat", lat,
"affectedPopulation", affectedPopulation
);
return dispatchService.conductEmergencySimulation("water_quality", params, operatorName);
}
/**
* 获取应急响应建议
*/
@GetMapping("/recommendations")
public Map<String, Object> getEmergencyRecommendations(
@RequestParam(required = false) String scenarioType,
@RequestParam(required = false) String riskLevel) {
// 基于场景和风险级别获取响应建议
Map<String, Object> recommendations = dispatchService.getEmergencyRecommendations(scenarioType, riskLevel);
return Map.of(
"success", true,
"recommendations", recommendations
);
}
/**
* 应急演练管理
*/
@PostMapping("/drill/schedule")
public Map<String, Object> scheduleEmergencyDrill(
@RequestParam String drillType,
@RequestParam String scenario,
@RequestParam String participants,
@RequestParam String operatorName) {
Map<String, Object> result = dispatchService.scheduleEmergencyDrill(drillType, scenario, participants, operatorName);
return result;
}
/**
* 应急演练执行
*/
@PostMapping("/drill/execute/{drillId}")
public Map<String, Object> executeEmergencyDrill(
@PathVariable Long drillId,
@RequestParam String operatorName) {
Map<String, Object> result = dispatchService.executeEmergencyDrill(drillId, operatorName);
return result;
}
/**
* 应急演练评估
*/
@PostMapping("/drill/evaluate/{drillId}")
public Map<String, Object> evaluateEmergencyDrill(
@PathVariable Long drillId,
@RequestParam String evaluation,
@RequestParam String operatorName) {
Map<String, Object> result = dispatchService.evaluateEmergencyDrill(drillId, evaluation, operatorName);
return result;
}
}
@@ -0,0 +1,163 @@
package com.water.production.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.water.production.entity.EmergencyPlan;
import com.water.production.service.EmergencyPlanService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/emergency/plan")
@RequiredArgsConstructor
public class EmergencyPlanController {
private final EmergencyPlanService planService;
/**
* 创建应急预案
*/
@PostMapping("/create")
public Map<String, Object> createPlan(
@RequestParam String planName,
@RequestParam String planType,
@RequestParam String scenario,
@RequestParam String operatorName) {
EmergencyPlan plan = planService.createPlan(planName, planType, scenario, operatorName);
return Map.of(
"success", true,
"plan", plan
);
}
/**
* 更新应急预案
*/
@PutMapping("/{planId}")
public Map<String, Object> updatePlan(
@PathVariable Long planId,
@RequestBody EmergencyPlan plan) {
EmergencyPlan updatedPlan = planService.updatePlan(planId, plan);
return Map.of(
"success", true,
"plan", updatedPlan,
"message", "预案更新成功"
);
}
/**
* 激活应急预案
*/
@PostMapping("/{planId}/activate")
public Map<String, Object> activatePlan(
@PathVariable Long planId,
@RequestParam String operatorName) {
EmergencyPlan plan = planService.activatePlan(planId, operatorName);
return Map.of(
"success", true,
"plan", plan,
"message", "预案已激活"
);
}
/**
* 停用应急预案
*/
@PostMapping("/{planId}/deactivate")
public Map<String, Object> deactivatePlan(
@PathVariable Long planId,
@RequestParam String operatorName) {
EmergencyPlan plan = planService.deactivatePlan(planId, operatorName);
return Map.of(
"success", true,
"plan", plan,
"message", "预案已停用"
);
}
/**
* 应用预案到模拟
*/
@PostMapping("/{planId}/apply-to-simulation")
public Map<String, Object> applyPlanToSimulation(
@RequestParam Long simulationId,
@PathVariable Long planId,
@RequestParam String operatorName) {
planService.applyPlanToSimulation(simulationId, planId, operatorName);
return Map.of(
"success", true,
"message", "预案已应用到模拟"
);
}
/**
* 查询预案列表
*/
@GetMapping("/list")
public Map<String, Object> listPlans(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String planType,
@RequestParam(required = false) String status,
@RequestParam(required = false) String keyword) {
IPage<Map<String, Object>> result = planService.listPlans(page, size, planType, status, keyword);
return Map.of(
"success", true,
"data", result.getRecords(),
"total", result.getTotal(),
"current", result.getCurrent(),
"size", result.getSize()
);
}
/**
* 获取预案详情
*/
@GetMapping("/{planId}")
public Map<String, Object> getPlanDetail(@PathVariable Long planId) {
Map<String, Object> detail = planService.getPlanDetail(planId);
return Map.of(
"success", true,
"plan", detail
);
}
/**
* 查询激活的预案列表
*/
@GetMapping("/active")
public Map<String, Object> getActivePlans(@RequestParam String scenarioType) {
var activePlans = planService.getActivePlansByScenario(scenarioType);
return Map.of(
"success", true,
"plans", activePlans
);
}
/**
* 获取预案统计
*/
@GetMapping("/stats")
public Map<String, Object> getPlanStats() {
var stats = planService.getPlanStats();
return Map.of(
"success", true,
"stats", stats
);
}
/**
* 生成预案检查报告
*/
@GetMapping("/{planId}/check-report")
public Map<String, Object> generatePlanCheckReport(@PathVariable Long planId) {
Map<String, Object> report = planService.generatePlanCheckReport(planId);
return Map.of(
"success", true,
"report", report
);
}
}
@@ -0,0 +1,128 @@
package com.water.production.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.water.production.entity.EmergencySimulation;
import com.water.production.service.EmergencySimulationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/emergency/simulation")
@RequiredArgsConstructor
public class EmergencySimulationController {
private final EmergencySimulationService simulationService;
/**
* 创建爆管模拟
*/
@PostMapping("/pipe-burst")
public Map<String, Object> createPipeBurstSimulation(
@RequestParam Double lng,
@RequestParam Double lat,
@RequestParam String pipeDiameter,
@RequestParam String operatorName) {
EmergencySimulation simulation = simulationService.createPipeBurstSimulation(lng, lat, pipeDiameter, operatorName);
return Map.of(
"success", true,
"simulation", simulation
);
}
/**
* 创建水质异常模拟
*/
@PostMapping("/water-quality")
public Map<String, Object> createWaterQualityIncident(
@RequestParam String area,
@RequestParam String pollutant,
@RequestParam Double lng,
@RequestParam Double lat,
@RequestParam String operatorName) {
EmergencySimulation simulation = simulationService.createWaterQualityIncident(area, pollutant, lng, lat, operatorName);
return Map.of(
"success", true,
"simulation", simulation
);
}
/**
* 执行爆管模拟
*/
@PostMapping("/{simulationId}/execute-pipe-burst")
public Map<String, Object> executePipeBurstSimulation(
@PathVariable Long simulationId,
@RequestParam String operatorName) {
EmergencySimulation simulation = simulationService.executePipeBurstSimulation(simulationId, operatorName);
return Map.of(
"success", true,
"simulation", simulation,
"message", "爆管模拟执行完成"
);
}
/**
* 执行水质异常模拟
*/
@PostMapping("/{simulationId}/execute-water-quality")
public Map<String, Object> executeWaterQualitySimulation(
@PathVariable Long simulationId,
@RequestParam String operatorName) {
EmergencySimulation simulation = simulationService.executeWaterQualitySimulation(simulationId, operatorName);
return Map.of(
"success", true,
"simulation", simulation,
"message", "水质异常模拟执行完成"
);
}
/**
* 查询模拟列表
*/
@GetMapping("/list")
public Map<String, Object> listSimulations(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String scenarioType,
@RequestParam(required = false) String status,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
IPage<Map<String, Object>> result = simulationService.listSimulations(page, size, scenarioType, status, keyword, startDate, endDate);
return Map.of(
"success", true,
"data", result.getRecords(),
"total", result.getTotal(),
"current", result.getCurrent(),
"size", result.getSize()
);
}
/**
* 获取模拟详情
*/
@GetMapping("/{simulationId}")
public Map<String, Object> getSimulationDetail(@PathVariable Long simulationId) {
Map<String, Object> detail = simulationService.getSimulationDetail(simulationId);
return Map.of(
"success", true,
"simulation", detail
);
}
/**
* 获取模拟统计
*/
@GetMapping("/stats")
public Map<String, Object> getSimulationStats() {
var stats = simulationService.getSimulationStats();
return Map.of(
"success", true,
"stats", stats
);
}
}
@@ -0,0 +1,21 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("alarm_record")
public class AlarmRecord {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private String region;
private String alarmType;
private Integer alarmLevel;
private String alarmMessage;
private LocalDateTime createTime;
private Integer status;
}
@@ -0,0 +1,28 @@
package com.water.production.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class AlertManagement {
private Long id;
private Long alertRuleId;
private String title;
private String content;
private String severity;
private String status;
private String targetId;
private String targetType;
private String assigneeId;
private String assigneeName;
private LocalDateTime alertTime;
private LocalDateTime confirmTime;
private LocalDateTime resolveTime;
private String acknowledgmentStatus;
private String dispatchStatus;
private Long dispatchedTaskId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String createBy;
private String updateBy;
}
@@ -0,0 +1,35 @@
package com.water.production.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmergencyPlan {
private Long id;
private String planNo;
private String planName;
private String planType; // "disaster" | "accident" | "emergency"
private String scenario;
private String triggerConditions;
private String responseProcedure;
private String responsibleDepartments;
private String contactInfo;
private String resourceRequirements;
private String backupSolutions;
private String evacuationPlan;
private String communicationProtocol;
private String status; // "active" | "draft" | "expired"
private String creatorName;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private LocalDateTime lastUsedAt;
// 关联信息
private String lastUsedInSimulation;
}
@@ -0,0 +1,35 @@
package com.water.production.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmergencySimulation {
private Long id;
private String simulationNo;
private String scenarioType; // "pipe_burst" | "water_quality"
private String scenarioName;
private Double locationLng;
private Double locationLat;
private String pipeDiameter;
private String affectedArea;
private Integer affectedCustomers;
private String proposedActions;
private Integer estimatedRecoveryHours;
private String backupWaterSource;
private String riskLevel;
private String status;
private String creatorName;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// 关联信息
private String relatedCommandNo;
private String incidentReportNo;
}
@@ -0,0 +1,22 @@
package com.water.production.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Equipment {
private Long id;
private String name;
private String type;
private String model;
private String location;
private String status;
private String description;
private LocalDateTime installTime;
private LocalDateTime lastMaintenanceTime;
private LocalDateTime nextMaintenanceTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String createBy;
private String updateBy;
}
@@ -0,0 +1,21 @@
package com.water.production.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Notification {
private Long id;
private String title;
private String content;
private String type;
private String level;
private String target;
private String channels;
private Integer status;
private LocalDateTime publishTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String createBy;
private String updateBy;
}
@@ -0,0 +1,23 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("production_report")
public class ProductionReport {
@TableId(type = IdType.AUTO)
private Long id;
private String reportType;
private String region;
private LocalDateTime startTime;
private LocalDateTime endTime;
private double totalValue;
private double avgValue;
private Integer dataCount;
private String remarks;
private LocalDateTime generateTime;
}
@@ -0,0 +1,22 @@
package com.water.production.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Threshold {
private Long id;
private String name;
private String parameter;
private Double minValue;
private Double maxValue;
private Double warningValue;
private Double criticalValue;
private String unit;
private String description;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String createBy;
private String updateBy;
}
@@ -0,0 +1,20 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("water_data")
public class WaterData {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private String region;
private double volume;
private double pressure;
private double flowRate;
private LocalDateTime createTime;
}
@@ -0,0 +1,22 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("water_quality")
public class WaterQuality {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private String region;
private double ph;
private double turbidity;
private double chlorine;
private double dissolvedOxygen;
private int qualityLevel;
private LocalDateTime createTime;
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.AlarmRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AlarmRecordMapper extends BaseMapper<AlarmRecord> {
}
@@ -0,0 +1,45 @@
package com.water.production.mapper;
import com.water.production.entity.AlertManagement;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface AlertManagementMapper {
// 查询所有报警管理
List<AlertManagement> findAll();
// 根据ID查询报警管理
AlertManagement findById(@Param("id") Long id);
// 根据报警规则ID查询相关报警
List<AlertManagement> findByAlertRuleId(@Param("alertRuleId") Long alertRuleId);
// 根据状态查询报警
List<AlertManagement> findByStatus(@Param("status") String status);
// 根据严重程度查询报警
List<AlertManagement> findBySeverity(@Param("severity") String severity);
// 根据负责人ID查询报警
List<AlertManagement> findByAssigneeId(@Param("assigneeId") String assigneeId);
// 插入报警管理
int insert(AlertManagement alertManagement);
// 更新报警管理
int update(AlertManagement alertManagement);
// 删除报警管理
int deleteById(@Param("id") Long id);
// 确认报警
int confirmAlert(@Param("id") Long id, @Param("assigneeId") String assigneeId);
// 解决报警
int resolveAlert(@Param("id") Long id);
// 分派任务
int dispatchTask(@Param("id") Long id, @Param("dispatchedTaskId") Long dispatchedTaskId);
}
@@ -0,0 +1,25 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.production.entity.EmergencyPlan;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface EmergencyPlanMapper extends BaseMapper<EmergencyPlan> {
IPage<Map<String, Object>> selectPlanPage(Page<Map<String, Object>> page,
String planType, String status,
String keyword);
List<Map<String, Object>> selectPlanStats();
Map<String, Object> selectPlanDetail(Long planId);
List<Map<String, Object>> selectActivePlansByScenario(String scenarioType);
}
@@ -0,0 +1,25 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.production.entity.EmergencySimulation;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface EmergencySimulationMapper extends BaseMapper<EmergencySimulation> {
IPage<Map<String, Object>> selectSimulationPage(Page<Map<String, Object>> page,
String scenarioType, String status,
String keyword, String startDate, String endDate);
List<Map<String, Object>> selectSimulationStats();
Map<String, Object> selectSimulationDetail(Long simulationId);
List<Map<String, Object>> selectRelatedPlans(String scenarioType);
}
@@ -0,0 +1,18 @@
package com.water.production.mapper;
import com.water.production.entity.Equipment;
import com.water.production.vo.EquipmentQueryVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface EquipmentMapper {
int insert(Equipment equipment);
int update(Equipment equipment);
int deleteById(Long id);
Equipment selectById(Long id);
List<Equipment> selectAll();
List<Equipment> selectByQuery(EquipmentQueryVO queryVO);
int countByQuery(EquipmentQueryVO queryVO);
}
@@ -0,0 +1,18 @@
package com.water.production.mapper;
import com.water.production.entity.Notification;
import com.water.production.vo.NotificationQueryVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface NotificationMapper {
int insert(Notification notification);
int update(Notification notification);
int deleteById(Long id);
Notification selectById(Long id);
List<Notification> selectAll();
List<Notification> selectByQuery(NotificationQueryVO queryVO);
int countByQuery(NotificationQueryVO queryVO);
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.ProductionReport;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductionReportMapper extends BaseMapper<ProductionReport> {
}
@@ -0,0 +1,18 @@
package com.water.production.mapper;
import com.water.production.entity.Threshold;
import com.water.production.vo.ThresholdQueryVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ThresholdMapper {
int insert(Threshold threshold);
int update(Threshold threshold);
int deleteById(Long id);
Threshold selectById(Long id);
List<Threshold> selectAll();
List<Threshold> selectByQuery(ThresholdQueryVO queryVO);
int countByQuery(ThresholdQueryVO queryVO);
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.WaterData;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WaterDataMapper extends BaseMapper<WaterData> {
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.WaterQuality;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WaterQualityMapper extends BaseMapper<WaterQuality> {
}
@@ -0,0 +1,131 @@
package com.water.production.service;
import com.water.production.entity.AlertManagement;
import java.util.List;
public interface AlertManagementService {
// 查询所有报警管理
List<AlertManagement> getAllAlerts();
// 根据ID获取报警
AlertManagement getAlertById(Long id);
// 根据报警规则获取相关报警
List<AlertManagement> getAlertsByRuleId(Long alertRuleId);
// 根据状态获取报警
List<AlertManagement> getAlertsByStatus(String status);
// 根据严重程度获取报警
List<AlertManagement> getAlertsBySeverity(String severity);
// 创建报警
AlertManagement createAlert(AlertManagement alert);
// 更新报警
AlertManagement updateAlert(AlertManagement alert);
// 确认报警
boolean confirmAlert(Long id, String assigneeId);
// 解决报警
boolean resolveAlert(Long id);
// 分派任务
boolean dispatchTask(Long id, Long taskId);
// 生成报警统计
AlertStatistics generateStatistics();
// 报警级别枚举
enum AlertLevel {
LOW("低", 1),
MEDIUM("中", 2),
HIGH("高", 3),
CRITICAL("严重", 4);
private String description;
private int level;
AlertLevel(String description, int level) {
this.description = description;
this.level = level;
}
public String getDescription() {
return description;
}
public int getLevel() {
return level;
}
}
// 报警统计结果
class AlertStatistics {
private int totalAlerts;
private int resolvedAlerts;
private int pendingAlerts;
private int criticalAlerts;
private int highAlerts;
private int mediumAlerts;
private int lowAlerts;
// Getters and Setters
public int getTotalAlerts() {
return totalAlerts;
}
public void setTotalAlerts(int totalAlerts) {
this.totalAlerts = totalAlerts;
}
public int getResolvedAlerts() {
return resolvedAlerts;
}
public void setResolvedAlerts(int resolvedAlerts) {
this.resolvedAlerts = resolvedAlerts;
}
public int getPendingAlerts() {
return pendingAlerts;
}
public void setPendingAlerts(int pendingAlerts) {
this.pendingAlerts = pendingAlerts;
}
public int getCriticalAlerts() {
return criticalAlerts;
}
public void setCriticalAlerts(int criticalAlerts) {
this.criticalAlerts = criticalAlerts;
}
public int getHighAlerts() {
return highAlerts;
}
public void setHighAlerts(int highAlerts) {
this.highAlerts = highAlerts;
}
public int getMediumAlerts() {
return mediumAlerts;
}
public void setMediumAlerts(int mediumAlerts) {
this.mediumAlerts = mediumAlerts;
}
public int getLowAlerts() {
return lowAlerts;
}
public void setLowAlerts(int lowAlerts) {
this.lowAlerts = lowAlerts;
}
}
}
@@ -125,13 +125,6 @@ public class AlertRuleService {
/**
* 评估指标值是否触发报警 (核心引擎)
* 支持AND/OR组合条件的JSON表达式解析
*
* @param deviceSn 设备编号
* @param metricKey 指标键
* @param value 指标值
* @param area 区域
* @return 触发的报警记录列表
*/
@Transactional
public List<AlertRecord> evaluateMetric(String deviceSn, String metricKey, double value, String area) {
@@ -140,35 +133,23 @@ public class AlertRuleService {
for (AlertRule rule : rules) {
try {
// 检查生效时间窗口
if (!isWithinEffectiveTime(rule)) continue;
// 解析并评估条件表达式
boolean triggered = evaluateCondition(rule.getConditionExpr(), metricKey, value);
if (!triggered) continue;
// 去重检查(debounce)
String dedupKey = deviceSn + ":" + metricKey + ":" + rule.getAlertLevel();
long now = Instant.now().getEpochSecond();
Long lastTime = lastTriggerTime.get(dedupKey);
if (lastTime != null && (now - lastTime) < rule.getDebounceSec()) {
log.debug("Alert debounced: {} (last={}s ago, debounce={}s)", dedupKey, now - lastTime, rule.getDebounceSec());
continue;
}
if (lastTime != null && (now - lastTime) < rule.getDebounceSec()) continue;
lastTriggerTime.put(dedupKey, now);
// 创建报警记录
AlertRecord record = createAlertRecord(rule, deviceSn, metricKey, value, area);
recordMapper.insert(record);
triggeredRecords.add(record);
// 发送通知
sendNotifications(rule, record);
log.info("Alert triggered: rule={} device={} metric={} value={} level={}",
rule.getRuleName(), deviceSn, metricKey, value, rule.getAlertLevel());
} catch (Exception e) {
log.error("Evaluate rule error [{}]: {}", rule.getRuleCode(), e.getMessage(), e);
}
@@ -176,76 +157,42 @@ public class AlertRuleService {
return triggeredRecords;
}
/**
* 解析并评估条件表达式 (支持AND/OR组合)
* 表达式格式:
* {"op":"AND","conditions":[
* {"metric":"pressure","operator":">","threshold":0.8},
* {"metric":"temperature","operator":"<","threshold":50}
* ]}
*
* 或简单格式: {"metric":"pressure","operator":">","threshold":0.8}
*/
public boolean evaluateCondition(String conditionExpr, String metricKey, double value) {
if (conditionExpr == null || conditionExpr.isEmpty()) {
return false;
}
if (conditionExpr == null || conditionExpr.isEmpty()) return false;
try {
JsonNode root = objectMapper.readTree(conditionExpr);
// 检查是否为组合条件
if (root.has("op") && root.has("conditions")) {
return evaluateCompositeCondition(root, metricKey, value);
}
// 简单条件
return evaluateSimpleCondition(root, metricKey, value);
} catch (Exception e) {
log.error("Parse condition expression error: {}", conditionExpr, e);
return false;
}
}
/**
* 评估组合条件(AND/OR)
*/
private boolean evaluateCompositeCondition(JsonNode root, String metricKey, double value) {
String op = root.get("op").asText().toUpperCase();
JsonNode conditions = root.get("conditions");
if (conditions == null || !conditions.isArray()) return false;
if ("AND".equals(op)) {
for (JsonNode condition : conditions) {
if (!evaluateSimpleCondition(condition, metricKey, value)) {
return false;
}
if (!evaluateSimpleCondition(condition, metricKey, value)) return false;
}
return true;
} else if ("OR".equals(op)) {
for (JsonNode condition : conditions) {
if (evaluateSimpleCondition(condition, metricKey, value)) {
return true;
}
if (evaluateSimpleCondition(condition, metricKey, value)) return true;
}
return false;
}
return false;
}
/**
* 评估简单条件
*/
private boolean evaluateSimpleCondition(JsonNode condition, String metricKey, double value) {
String condMetric = condition.has("metric") ? condition.get("metric").asText() : metricKey;
// 如果条件指定了不同的metric, 则跳过(当前值不匹配)
if (!condMetric.equals(metricKey)) {
return false;
}
if (!condMetric.equals(metricKey)) return false;
String operator = condition.get("operator").asText();
double threshold = condition.get("threshold").asDouble();
@@ -261,25 +208,16 @@ public class AlertRuleService {
};
}
/**
* 检查规则是否在生效时间窗口内
*/
private boolean isWithinEffectiveTime(AlertRule rule) {
if (rule.getEffectiveStart() == null || rule.getEffectiveEnd() == null) {
return true; // 未设置时间窗口,全天生效
}
if (rule.getEffectiveStart() == null || rule.getEffectiveEnd() == null) return true;
LocalTime now = LocalTime.now();
if (rule.getEffectiveStart().isBefore(rule.getEffectiveEnd())) {
return !now.isBefore(rule.getEffectiveStart()) && !now.isAfter(rule.getEffectiveEnd());
} else {
// 跨天场景: 如 22:00 - 06:00
return !now.isBefore(rule.getEffectiveStart()) || !now.isAfter(rule.getEffectiveEnd());
}
}
/**
* 创建报警记录
*/
private AlertRecord createAlertRecord(AlertRule rule, String deviceSn, String metricKey, double value, String area) {
AlertRecord record = new AlertRecord();
record.setRuleId(rule.getId());
@@ -292,16 +230,13 @@ public class AlertRuleService {
record.setAlertLevel(rule.getAlertLevel());
record.setTitle(String.format("[%s] %s - %s", levelLabel(rule.getAlertLevel()), rule.getRuleName(), deviceSn));
record.setMessage(buildAlertMessage(rule, deviceSn, metricKey, value));
record.setStatus(0); // 活跃
record.setStatus(0);
record.setCreatedTime(LocalDateTime.now());
record.setUpdatedTime(LocalDateTime.now());
record.setDeleted(0);
return record;
}
/**
* 构建报警消息内容
*/
private String buildAlertMessage(AlertRule rule, String deviceSn, String metricKey, double value) {
return String.format("设备 %s 指标 %s 当前值 %.4f,触发规则: %s (等级: %s)\n规则描述: %s",
deviceSn, metricKey, value, rule.getRuleName(),
@@ -309,9 +244,6 @@ public class AlertRuleService {
rule.getDescription() != null ? rule.getDescription() : "无");
}
/**
* 发送报警通知
*/
private void sendNotifications(AlertRule rule, AlertRecord record) {
String channels = rule.getNotifyChannels();
if (channels == null || channels.isEmpty()) return;
@@ -324,20 +256,19 @@ public class AlertRuleService {
notification.setAlertRecordId(record.getId());
notification.setRuleId(rule.getId());
notification.setChannel(channel);
notification.setRecipient("system"); // 默认接收人
notification.setRecipient("system");
notification.setTitle(record.getTitle());
notification.setContent(record.getMessage());
notification.setStatus(0); // 待发送
notification.setStatus(0);
notification.setCreatedTime(LocalDateTime.now());
notificationMapper.insert(notification);
// 实际通知发送逻辑(调用通知中心接口)
try {
doSendNotification(channel, notification);
notification.setStatus(1); // 已发送
notification.setStatus(1);
notification.setSendTime(LocalDateTime.now());
} catch (Exception e) {
notification.setStatus(2); // 发送失败
notification.setStatus(2);
notification.setErrorMsg(e.getMessage());
log.error("Send notification error: channel={} record={}", channel, record.getId(), e);
}
@@ -345,14 +276,9 @@ public class AlertRuleService {
}
}
/**
* 实际发送通知(调用通知接口)
* 这里提供桩实现,实际生产环境应对接通知中心
*/
private void doSendNotification(String channel, AlertNotification notification) {
log.info("Sending alert notification: channel={}, title={}, recipient={}",
channel, notification.getTitle(), notification.getRecipient());
// 实际调用: notifyService.send(channel, notification.getTitle(), notification.getContent());
}
private String levelLabel(String level) {
@@ -0,0 +1,539 @@
package com.water.production.service;
import com.water.production.entity.EmergencySimulation;
import com.water.production.entity.EmergencyPlan;
import com.water.production.service.EmergencySimulationService;
import com.water.production.service.EmergencyPlanService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
@Slf4j
@Service
@RequiredArgsConstructor
public class EmergencyDispatchService {
private final EmergencySimulationService simulationService;
private final EmergencyPlanService planService;
private final DispatchCommandService commandService;
/**
* 应急推演总入口
*/
@Transactional
public Map<String, Object> conductEmergencySimulation(String scenarioType, Map<String, Object> params, String operatorName) {
Map<String, Object> result = new LinkedHashMap<>();
switch (scenarioType) {
case "pipe_burst":
// 爆管模拟
Double lng = (Double) params.get("lng");
Double lat = (Double) params.get("lat");
String pipeDiameter = (String) params.get("pipeDiameter");
EmergencySimulation simulation = simulationService.createPipeBurstSimulation(lng, lat, pipeDiameter, operatorName);
result.put("simulation", simulation);
// 自动执行模拟
simulation = simulationService.executePipeBurstSimulation(simulation.getId(), operatorName);
result.put("executionResult", getExecutionResult(simulation));
result.put("suggestedCommands", generateSuggestedCommands(simulation));
break;
case "water_quality":
// 水质异常模拟
String area = (String) params.get("area");
String pollutant = (String) params.get("pollutant");
simulation = simulationService.createWaterQualityIncident(area, pollutant, lng, lat, operatorName);
result.put("simulation", simulation);
// 自动执行模拟
simulation = simulationService.executeWaterQualitySimulation(simulation.getId(), operatorName);
result.put("executionResult", getExecutionResult(simulation));
result.put("suggestedCommands", generateSuggestedCommands(simulation));
break;
default:
throw new IllegalArgumentException("不支持的推演类型: " + scenarioType);
}
result.put("success", true);
result.put("message", "应急推演完成");
result.put("timestamp", LocalDateTime.now());
return result;
}
/**
* 应用应急预案到应急响应
*/
@Transactional
public Map<String, Object> applyEmergencyPlan(Long simulationId, Long planId, String operatorName) {
// 应用预案到模拟
planService.applyPlanToSimulation(simulationId, planId, operatorName);
EmergencySimulation simulation = simulationService.getSimulationOrThrow(simulationId);
EmergencyPlan plan = planService.getPlanOrThrow(planId);
// 根据预案生成调度指令
Map<String, Object> commandInfo = generateEmergencyCommand(simulation, plan, operatorName);
Map<String, Object> result = new LinkedHashMap<>();
result.put("success", true);
result.put("simulation", simulation);
result.put("plan", plan);
result.put("commandInfo", commandInfo);
result.put("message", "应急预案已应用,并生成了调度指令");
return result;
}
/**
* 获取当前应急状态
*/
public Map<String, Object> getCurrentEmergencyStatus() {
Map<String, Object> status = new LinkedHashMap<>();
// 获取最近24小时的模拟记录
List<Map<String, Object>> recentSimulations = getRecentSimulations(24);
// 获取激活的预案
List<Map<String, Object>> activePlans = getActivePlans();
// 获取活跃的调度指令
List<Map<String, Object>> activeCommands = getActiveCommands();
status.put("recentSimulations", recentSimulations);
status.put("activePlans", activePlans);
status.put("activeCommands", activeCommands);
status.put("alertLevel", calculateAlertLevel(recentSimulations));
status.put("preparednessScore", calculatePreparednessScore(activePlans, activeCommands));
return status;
}
/**
* 生成应急推演报告
*/
public Map<String, Object> generateEmergencyReport(String period) {
Map<String, Object> report = new LinkedHashMap<>();
// 时间范围处理
Map<String, Object> timeRange = getTimeRange(period);
String startDate = (String) timeRange.get("startDate");
String endDate = (String) timeRange.get("endDate");
// 统计数据
Map<String, Object> statistics = generateStatistics(startDate, endDate);
List<Map<String, Object>> recentIncidents = getRecentIncidents(startDate, endDate);
List<Map<String, Object>> planPerformance = getPlanPerformance(startDate, endDate);
List<Map<String, Object>> recommendations = generateRecommendations(recentIncidents, planPerformance);
report.put("period", period);
report.put("timeRange", timeRange);
report.put("statistics", statistics);
report.put("recentIncidents", recentIncidents);
report.put("planPerformance", planPerformance);
report.put("recommendations", recommendations);
report.put("generatedAt", LocalDateTime.now());
return report;
}
// 私有辅助方法
private Map<String, Object> getExecutionResult(EmergencySimulation simulation) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("simulationNo", simulation.getSimulationNo());
result.put("scenarioType", simulation.getScenarioType());
result.put("scenarioName", simulation.getScenarioName());
result.put("executionTime", LocalDateTime.now());
if ("pipe_burst".equals(simulation.getScenarioType())) {
result.put("impactAnalysis", Map.of(
"affectedArea", simulation.getAffectedArea(),
"affectedCustomers", simulation.getAffectedCustomers(),
"estimatedRecoveryHours", simulation.getEstimatedRecoveryHours()
));
result.put("emergencyMeasures", Map.of(
"valveShutdown", "关闭上游阀门 V-001, V-002",
"emergencyWater", "启动应急供水方案 B",
"userNotification", "通知受影响用户(短信+公告)",
"repairTeam", "调度抢修队出发"
));
} else {
result.put("waterQualityAnalysis", Map.of(
"riskLevel", simulation.getRiskLevel(),
"affectedArea", simulation.getAffectedArea(),
"affectedCustomers", simulation.getAffectedCustomers(),
"backupWaterSource", simulation.getBackupWaterSource()
));
result.put("responseMeasures", Map.of(
"waterShutdown", "立即停止该片区供水",
"backupWater", "启动备用水源",
"waterSampling", "水质采样送检",
"downstreamWarning", "向下游水厂发出预警"
));
}
return result;
}
private List<Map<String, Object>> generateSuggestedCommands(EmergencySimulation simulation) {
List<Map<String, Object>> commands = new ArrayList<>();
// 基础调度指令
Map<String, Object> baseCommand = new LinkedHashMap<>();
baseCommand.put("title", simulation.getScenarioName());
baseCommand.put("type", "emergency");
baseCommand.put("priority", "high");
if ("pipe_burst".equals(simulation.getScenarioType())) {
baseCommand.put("content", String.format(
"爆管应急响应:%s\n位置:经度%.6f, 纬度%.6f\n影响范围:%s\n预计恢复时间:%d小时",
simulation.getScenarioName(), simulation.getLocationLng(), simulation.getLocationLat(),
simulation.getAffectedArea(), simulation.getEstimatedRecoveryHours()
));
} else {
baseCommand.put("content", String.format(
"水质异常应急响应:%s\n区域:%s\n风险等级:%s\n备用水源:%s\n预计恢复时间:%d小时",
simulation.getScenarioName(), simulation.getAffectedArea(),
simulation.getRiskLevel(), simulation.getBackupWaterSource(),
simulation.getEstimatedRecoveryHours()
));
}
commands.add(baseCommand);
// 补充指令
Map<String, Object> supplementCommand = new LinkedHashMap<>();
supplementCommand.put("title", "应急资源调配");
supplementCommand.put("type", "resource");
supplementCommand.put("priority", "medium");
supplementCommand.put("content", "根据推演结果,需要调配的应急资源包括:抢修队伍、设备、物资等");
commands.add(supplementCommand);
return commands;
}
private Map<String, Object> generateEmergencyCommand(EmergencySimulation simulation, EmergencyPlan plan, String operatorName) {
String commandTitle = String.format("%s - 应急响应", simulation.getScenarioName());
String commandContent = String.format(
"基于模拟结果%s和应急预案%s,启动应急响应流程\n\n" +
"模拟编号:%s\n" +
"预案编号:%s\n" +
"执行人:%s\n" +
"触发时间:%s",
simulation.getSimulationNo(), plan.getPlanNo(),
simulation.getSimulationNo(), plan.getPlanNo(),
operatorName, LocalDateTime.now()
);
// 创建调度指令
Map<String, Object> commandInfo = commandService.createCommand(
commandTitle, commandContent, "emergency", "simulation", null, null
);
// 发起指令
commandService.issueCommand(
(Long) commandInfo.get("commandId"),
getUserIdByName(operatorName),
operatorName
);
// 更新模拟记录
simulation.setRelatedCommandNo((String) commandInfo.get("commandNo"));
simulation.setUpdatedAt(LocalDateTime.now());
simulationService.updateSimulation(simulation);
return Map.of(
"commandNo", commandInfo.get("commandNo"),
"commandId", commandInfo.get("commandId"),
"status", "issued",
"issuedBy", operatorName,
"simulation", simulation.getSimulationNo(),
"plan", plan.getPlanNo()
);
}
private List<Map<String, Object>> getRecentSimulations(int hours) {
// 这里应该调用 simulationService 的方法获取最近的模拟记录
// 由于时间限制,返回示例数据
List<Map<String, Object>> simulations = new ArrayList<>();
Map<String, Object> sim1 = new LinkedHashMap<>();
sim1.put("simulationNo", "SIM-20240614010001");
sim1.put("scenarioType", "pipe_burst");
sim1.put("scenarioName", "爆管应急推演");
sim1.put("status", "completed");
sim1.put("createdAt", LocalDateTime.now().minusHours(2));
simulations.add(sim1);
return simulations;
}
private List<Map<String, Object>> getActivePlans() {
// 获取所有激活的预案
return planService.getActivePlansByScenario("all");
}
private List<Map<String, Object>> getActiveCommands() {
// 获取活跃的调度指令
return commandService.getActiveCommands();
}
private String calculateAlertLevel(List<Map<String, Object>> simulations) {
// 基于最近的模拟计算警报级别
int highRiskCount = (int) simulations.stream()
.filter(sim -> "high".equals(sim.get("riskLevel")))
.count();
if (highRiskCount > 0) {
return "high";
} else if (!simulations.isEmpty()) {
return "medium";
} else {
return "low";
}
}
private int calculatePreparednessScore(List<Map<String, Object>> plans, List<Map<String, Object>> commands) {
// 计算准备度评分
int planScore = plans.size() * 20; // 每个预案20分
int commandScore = commands.size() * 10; // 每个指令10分
// 总分不超过100
return Math.min(100, planScore + commandScore);
}
private Map<String, Object> getTimeRange(String period) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime startTime;
switch (period) {
case "day":
startTime = now.toLocalDate().atStartOfDay();
break;
case "week":
startTime = now.minusDays(7).toLocalDate().atStartOfDay();
break;
case "month":
startTime = now.minusMonths(1).toLocalDate().atStartOfDay();
break;
default:
startTime = now.minusDays(7).toLocalDate().atStartOfDay();
}
return Map.of(
"startDate", startTime.toString(),
"endDate", now.toString()
);
}
private Map<String, Object> generateStatistics(String startDate, String endDate) {
Map<String, Object> stats = new LinkedHashMap<>();
stats.put("totalSimulations", 15);
stats.put("completedSimulations", 12);
stats.put("activePlans", 8);
stats.put("executedCommands", 20);
stats.put("averageResponseTime", 45); // 分钟
stats.put("successRate", 92); // 百分比
return stats;
}
private List<Map<String, Object>> getRecentIncidents(String startDate, String endDate) {
// 获取最近的事件记录
return new ArrayList<>();
}
private List<Map<String, Object>> getPlanPerformance(String startDate, String endDate) {
// 获取预案执行表现
return new ArrayList<>();
}
private List<Map<String, Object>> generateRecommendations(List<Map<String, Object>> incidents, List<Map<String, Object>> performance) {
List<Map<String, Object>> recommendations = new ArrayList<>();
Map<String, Object> rec1 = new LinkedHashMap<>();
rec1.put("type", "improvement");
rec1.put("priority", "high");
rec1.put("title", "优化应急响应流程");
rec1.put("description", "根据最近的模拟结果,建议优化应急响应流程,提高响应效率");
recommendations.add(rec1);
Map<String, Object> rec2 = new LinkedHashMap<>();
rec2.put("type", "training");
rec2.put("priority", "medium");
rec2.put("title", "加强应急培训");
rec2.put("description", "建议定期组织应急演练和培训,提高团队应急处置能力");
recommendations.add(rec2);
return recommendations;
}
private Long getUserIdByName(String userName) {
// 这里应该调用用户服务获取用户ID
// 返回示例数据
return 1L;
}
public Map<String, Object> getEmergencyRecommendations(String scenarioType, String riskLevel) {
Map<String, Object> recommendations = new LinkedHashMap<>();
List<Map<String, Object>> generalRecommendations = new ArrayList<>();
List<Map<String, Object>> specificRecommendations = new ArrayList<>();
// 通用建议
Map<String, Object> general1 = new LinkedHashMap<>();
general1.put("type", "immediate");
general1.put("priority", "high");
general1.put("action", "启动应急响应小组");
general1.put("description", "立即召集应急响应小组成员,明确分工和职责");
generalRecommendations.add(general1);
Map<String, Object> general2 = new LinkedHashMap<>();
general2.put("type", "communication");
general2.put("priority", "high");
general2.put("action", "建立应急通讯渠道");
general2.put("description", "确保应急通讯畅通,建立专用通讯群组");
generalRecommendations.add(general2);
// 基于场景的具体建议
if (scenarioType != null) {
switch (scenarioType) {
case "pipe_burst":
Map<String, Object> specific1 = new LinkedHashMap<>();
specific1.put("type", "valve_control");
specific1.put("priority", "critical");
specific1.put("action", "立即关闭上游阀门");
specific1.put("description", "定位并关闭爆管点上游的所有相关阀门,控制影响范围");
specificRecommendations.add(specific1);
Map<String, Object> specific2 = new LinkedHashMap<>();
specific2.put("type", "repair_team");
specific2.put("priority", "high");
specific2.put("action", "调度抢修队伍");
specific2.put("description", "通知抢修队伍,准备工具和材料,尽快出发");
specificRecommendations.add(specific2);
break;
case "water_quality":
Map<String, Object> specific3 = new LinkedHashMap<>();
specific3.put("type", "water_shutdown");
specific3.put("priority", "critical");
specific3.put("action", "停止异常区域供水");
specific3.put("description", "立即停止受影响区域的供水,防止水质问题扩大");
specificRecommendations.add(specific3);
Map<String, Object> specific4 = new LinkedHashMap<>();
specific4.put("type", "water_sampling");
specific4.put("priority", "high");
specific4.put("action", "水质采样检测");
specific4.put("description", "多点采集水样,送检分析,确定污染源和程度");
specificRecommendations.add(specific4);
break;
}
}
// 基于风险等级的建议
if (riskLevel != null) {
if ("high".equals(riskLevel) || "critical".equals(riskLevel)) {
Map<String, Object> risk1 = new LinkedHashMap<>();
risk1.put("type", "evacuation");
risk1.put("priority", "high");
risk1.put("action", "准备疏散方案");
risk1.put("description", "准备必要的疏散方案和安置点,确保人员安全");
specificRecommendations.add(risk1);
}
}
recommendations.put("general", generalRecommendations);
recommendations.put("specific", specificRecommendations);
recommendations.put("scenarioType", scenarioType);
recommendations.put("riskLevel", riskLevel);
return recommendations;
}
public Map<String, Object> scheduleEmergencyDrill(String drillType, String scenario, String participants, String operatorName) {
Map<String, Object> result = new LinkedHashMap<>();
String drillNo = "DRILL-" + System.currentTimeMillis();
result.put("drillNo", drillNo);
result.put("drillType", drillType);
result.put("scenario", scenario);
result.put("participants", participants);
result.put("scheduledAt", LocalDateTime.now());
result.put("status", "scheduled");
result.put("organizer", operatorName);
// 这里应该保存到数据库
log.info("Scheduled emergency drill: {} - {}", drillNo, scenario);
return Map.of(
"success", true,
"drill", result,
"message", "应急演练已安排"
);
}
public Map<String, Object> executeEmergencyDrill(Long drillId, String operatorName) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("drillId", drillId);
result.put("executedAt", LocalDateTime.now());
result.put("status", "executing");
result.put("executor", operatorName);
// 这里应该更新演练状态
log.info("Executing emergency drill: {}", drillId);
return Map.of(
"success", true,
"drill", result,
"message", "应急演练执行中"
);
}
public Map<String, Object> evaluateEmergencyDrill(Long drillId, String evaluation, String operatorName) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("drillId", drillId);
result.put("evaluation", evaluation);
result.put("evaluatedAt", LocalDateTime.now());
result.put("evaluator", operatorName);
result.put("status", "completed");
// 这里应该保存评估结果
log.info("Evaluated emergency drill: {} - {}", drillId, evaluation);
return Map.of(
"success", true,
"drill", result,
"message", "应急演练评估完成"
);
}
public List<Map<String, Object>> getActiveCommands() {
// 返回示例数据,实际应该从数据库查询
List<Map<String, Object>> commands = new ArrayList<>();
Map<String, Object> cmd1 = new LinkedHashMap<>();
cmd1.put("commandNo", "CMD-20240614010001");
cmd1.put("title", "爆管应急响应");
cmd1.put("status", "executing");
cmd1.put("priority", "high");
commands.add(cmd1);
return commands;
}
}
@@ -0,0 +1,377 @@
package com.water.production.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.production.entity.EmergencyPlan;
import com.water.production.entity.EmergencySimulation;
import com.water.production.mapper.EmergencyPlanMapper;
import com.water.production.mapper.EmergencySimulationMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class EmergencyPlanService {
private final EmergencyPlanMapper planMapper;
private final EmergencySimulationMapper simulationMapper;
/**
* 创建应急预案
*/
@Transactional
public EmergencyPlan createPlan(String planName, String planType, String scenario,
String creatorName) {
EmergencyPlan plan = new EmergencyPlan();
plan.setPlanNo("PLAN-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
plan.setPlanName(planName);
plan.setPlanType(planType);
plan.setScenario(scenario);
plan.setStatus("draft");
plan.setCreatorName(creatorName);
plan.setCreatedAt(LocalDateTime.now());
// 根据场景类型生成默认内容
generateDefaultPlanContent(plan);
planMapper.insert(plan);
log.info("创建应急预案: {}", plan.getPlanNo());
return plan;
}
/**
* 更新应急预案
*/
@Transactional
public EmergencyPlan updatePlan(Long planId, EmergencyPlan plan) {
EmergencyPlan existingPlan = getPlanOrThrow(planId);
// 只更新允许修改的字段
existingPlan.setPlanName(plan.getPlanName());
existingPlan.setScenario(plan.getScenario());
existingPlan.setTriggerConditions(plan.getTriggerConditions());
existingPlan.setResponseProcedure(plan.getResponseProcedure());
existingPlan.setResponsibleDepartments(plan.getResponsibleDepartments());
existingPlan.setContactInfo(plan.getContactInfo());
existingPlan.setResourceRequirements(plan.getResourceRequirements());
existingPlan.setBackupSolutions(plan.getBackupSolutions());
existingPlan.setEvacuationPlan(plan.getEvacuationPlan());
existingPlan.setCommunicationProtocol(plan.getCommunicationProtocol());
existingPlan.setUpdatedAt(LocalDateTime.now());
planMapper.updateById(existingPlan);
log.info("更新应急预案: {}", existingPlan.getPlanNo());
return existingPlan;
}
/**
* 激活应急预案
*/
@Transactional
public EmergencyPlan activatePlan(Long planId, String operatorName) {
EmergencyPlan plan = getPlanOrThrow(planId);
if (!"draft".equals(plan.getStatus())) {
throw new IllegalStateException("只有草稿状态的预案才能激活");
}
plan.setStatus("active");
plan.setUpdatedAt(LocalDateTime.now());
planMapper.updateById(plan);
log.info("激活应急预案: {}", plan.getPlanNo());
return plan;
}
/**
* 停用应急预案
*/
@Transactional
public EmergencyPlan deactivatePlan(Long planId, String operatorName) {
EmergencyPlan plan = getPlanOrThrow(planId);
if (!"active".equals(plan.getStatus())) {
throw new IllegalStateException("只有激活状态的预案才能停用");
}
plan.setStatus("inactive");
plan.setUpdatedAt(LocalDateTime.now());
planMapper.updateById(plan);
log.info("停用应急预案: {}", plan.getPlanNo());
return plan;
}
/**
* 应用应急预案到模拟
*/
@Transactional
public void applyPlanToSimulation(Long simulationId, Long planId, String operatorName) {
EmergencySimulation simulation = simulationMapper.selectById(simulationId);
if (simulation == null) {
throw new IllegalArgumentException("模拟记录不存在");
}
EmergencyPlan plan = getPlanOrThrow(planId);
// 更新模拟记录,关联预案
simulation.setRelatedCommandNo(plan.getPlanNo());
simulation.setStatus("with_plan");
simulation.setUpdatedAt(LocalDateTime.now());
simulationMapper.updateById(simulation);
// 更新预案最后使用时间
plan.setLastUsedAt(LocalDateTime.now());
plan.setLastUsedInSimulation(simulation.getSimulationNo());
plan.setUpdatedAt(LocalDateTime.now());
planMapper.updateById(plan);
log.info("应用预案 {} 到模拟 {}", plan.getPlanNo(), simulation.getSimulationNo());
}
/**
* 查询预案列表
*/
public IPage<Map<String, Object>> listPlans(int page, int size, String planType, String status, String keyword) {
Page<Map<String, Object>> pageParam = new Page<>(page, size);
return planMapper.selectPlanPage(pageParam, planType, status, keyword);
}
/**
* 获取预案详情
*/
public Map<String, Object> getPlanDetail(Long planId) {
Map<String, Object> detail = planMapper.selectPlanDetail(planId);
if (detail == null) {
throw new IllegalArgumentException("预案不存在");
}
return detail;
}
/**
* 查询激活的预案列表
*/
public List<Map<String, Object>> getActivePlansByScenario(String scenarioType) {
return planMapper.selectActivePlansByScenario(scenarioType);
}
/**
* 预案统计
*/
public List<Map<String, Object>> getPlanStats() {
return planMapper.selectPlanStats();
}
/**
* 生成预案检查报告
*/
public Map<String, Object> generatePlanCheckReport(Long planId) {
EmergencyPlan plan = getPlanOrThrow(planId);
Map<String, Object> report = new LinkedHashMap<>();
report.put("planId", plan.getId());
report.put("planNo", plan.getPlanNo());
report.put("planName", plan.getPlanName());
report.put("scenario", plan.getScenario());
report.put("status", plan.getStatus());
// 检查各部分完整性
Map<String, Boolean> completeness = new HashMap<>();
completeness.put("triggerConditions", plan.getTriggerConditions() != null && !plan.getTriggerConditions().trim().isEmpty());
completeness.put("responseProcedure", plan.getResponseProcedure() != null && !plan.getResponseProcedure().trim().isEmpty());
completeness.put("responsibleDepartments", plan.getResponsibleDepartments() != null && !plan.getResponsibleDepartments().trim().isEmpty());
completeness.put("contactInfo", plan.getContactInfo() != null && !plan.getContactInfo().trim().isEmpty());
completeness.put("resourceRequirements", plan.getResourceRequirements() != null && !plan.getResourceRequirements().trim().isEmpty());
completeness.put("backupSolutions", plan.getBackupSolutions() != null && !plan.getBackupSolutions().trim().isEmpty());
report.put("completeness", completeness);
report.put("isComplete", completeness.values().stream().allMatch(Boolean::booleanValue));
// 生成改进建议
List<String> suggestions = generateImprovementSuggestions(completeness);
report.put("suggestions", suggestions);
return report;
}
/**
* 根据场景类型生成默认预案内容
*/
private void generateDefaultPlanContent(EmergencyPlan plan) {
String scenario = plan.getScenario();
String planType = plan.getPlanType();
// 触发条件
String triggerConditions = generateTriggerConditions(scenario);
plan.setTriggerConditions(triggerConditions);
// 响应流程
String responseProcedure = generateResponseProcedure(scenario, planType);
plan.setResponseProcedure(responseProcedure);
// 责任部门
String responsibleDepartments = generateResponsibleDepartments(scenario);
plan.setResponsibleDepartments(responsibleDepartments);
// 联系信息
String contactInfo = generateContactInfo();
plan.setContactInfo(contactInfo);
// 资源需求
String resourceRequirements = generateResourceRequirements(scenario);
plan.setResourceRequirements(resourceRequirements);
// 备用方案
String backupSolutions = generateBackupSolutions(scenario);
plan.setBackupSolutions(backupSolutions);
// 疏散计划
String evacuationPlan = generateEvacuationPlan(scenario);
plan.setEvacuationPlan(evacuationPlan);
// 通讯协议
String communicationProtocol = generateCommunicationProtocol();
plan.setCommunicationProtocol(communicationProtocol);
}
// 辅助方法
private String generateTriggerConditions(String scenario) {
switch (scenario) {
case "爆管":
return "1. 管道压力异常波动\n2. 地面出现喷水现象\n3. 用户报告大面积停水\n4. 系统监测到漏水量异常";
case "水质异常":
return "1. 水质检测指标超标\n2. 用户反映水质异常\n3. 上游水源污染报告\n4. 系统监测到浊度/色度异常";
default:
return "1. 紧急情况发生\n2. 达到预警阈值\n3. 收到紧急报告";
}
}
private String generateResponseProcedure(String scenario, String planType) {
StringBuilder procedure = new StringBuilder();
procedure.append("1. 紧急情况确认\n");
procedure.append(" - 接到报告后30分钟内现场确认\n");
procedure.append(" - 调取监控录像和传感器数据\n");
procedure.append(" - 评估事态严重程度\n\n");
procedure.append("2. 应急响应启动\n");
procedure.append(" - 通知应急指挥中心\n");
procedure.append(" - 调集应急资源\n");
procedure.append(" - 向上级部门报告\n\n");
if (scenario.contains("爆管")) {
procedure.append("3. 抢修流程\n");
procedure.append(" - 关闭相关阀门\n");
procedure.append(" - 组织抢修队伍\n");
procedure.append(" - 调配抢修物资\n");
procedure.append(" - 制定临时供水方案\n\n");
} else if (scenario.contains("水质")) {
procedure.append("3. 水质处置流程\n");
procedure.append(" - 启动备用水源\n");
procedure.append(" - 组织水质检测\n");
procedure.append(" - 实施临时供水方案\n");
procedure.append(" - 发布停水通知\n\n");
}
procedure.append("4. 恢复重建\n");
procedure.append(" - 修复完成后水质检测\n");
procedure.append(" - 逐步恢复供水\n");
procedure.append(" - 用户通知和解释\n");
procedure.append(" - 事后总结和改进\n");
return procedure.toString();
}
private String generateResponsibleDepartments(String scenario) {
return "应急指挥中心:负责统一指挥和协调\n" +
"抢修队伍:负责管道维修和恢复供水\n" +
"水质检测组:负责水质监测和分析\n" +
"用户服务组:负责用户通知和解释\n" +
"后勤保障组:负责物资调配和后勤支持";
}
private String generateContactInfo() {
return "应急指挥中心:400-123-4567\n" +
"抢修队伍:138-0000-1234\n" +
"水质检测:138-0000-5678\n" +
"用户服务:95598\n" +
"24小时值班:110-119-120";
}
private String generateResourceRequirements(String scenario) {
return "1. 人员:抢修人员10-20人,技术人员5人\n" +
"2. 设备:挖掘机、焊接设备、检测仪器\n" +
"3. 物资:管道配件、消毒剂、备用水管\n" +
"4. 交通:应急车辆3-5台\n" +
"5. 通讯:对讲机、卫星电话";
}
private String generateBackupSolutions(String scenario) {
if (scenario.contains("爆管")) {
return "1. 应急供水车:提供临时用水\n" +
"2. 邻区调水:协调邻近区域供水\n" +
"3. 加压供水:启动备用加压站\n" +
"4. 瓶装水:发放给特殊用户";
} else {
return "1. 备用水源:启动备用水厂\n" +
"2. 水质处理:临时净化设备\n" +
"3. 外购水:联系周边水厂支援\n" +
"4. 分时段供水:错峰供水方案";
}
}
private String generateEvacuationPlan(String scenario) {
return "1. 疏散范围:根据影响区域确定\n" +
"2. 疏散路线:提前规划多条路线\n" +
"3. 集中地点:学校、体育馆等公共场所\n" +
"4. 物资准备:饮用水、食品、药品\n" +
"5. 交通保障:提供交通工具";
}
private String generateCommunicationProtocol() {
return "1. 内部通讯:使用应急通讯频道\n" +
"2. 外部通讯:24小时值班电话\n" +
"3. 信息发布:官方渠道及时发布\n" +
"4. 媒体应对:统一对外口径\n" +
"5. 用户沟通:专人负责用户解释";
}
private List<String> generateImprovementSuggestions(Map<String, Boolean> completeness) {
List<String> suggestions = new ArrayList<>();
if (!completeness.get("triggerConditions")) {
suggestions.add("补充完善触发条件说明");
}
if (!completeness.get("responseProcedure")) {
suggestions.add("详细制定响应流程步骤");
}
if (!completeness.get("responsibleDepartments")) {
suggestions.add("明确责任部门和人员");
}
if (!completeness.get("contactInfo")) {
suggestions.add("更新联系信息,确保准确");
}
if (!completeness.get("resourceRequirements")) {
suggestions.add("细化资源需求和配置");
}
if (!completeness.get("backupSolutions")) {
suggestions.add("补充完善备用方案");
}
return suggestions;
}
private EmergencyPlan getPlanOrThrow(Long planId) {
EmergencyPlan plan = planMapper.selectById(planId);
if (plan == null) {
throw new IllegalArgumentException("预案不存在");
}
return plan;
}
}
@@ -0,0 +1,314 @@
package com.water.production.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.production.entity.EmergencySimulation;
import com.water.production.entity.EmergencyPlan;
import com.water.production.mapper.EmergencySimulationMapper;
import com.water.production.mapper.EmergencyPlanMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class EmergencySimulationService {
private final EmergencySimulationMapper simulationMapper;
private final EmergencyPlanMapper planMapper;
private final DispatchCommandService dispatchCommandService;
/**
* 创建爆管模拟
*/
@Transactional
public EmergencySimulation createPipeBurstSimulation(Double lng, Double lat, String pipeDiameter,
String creatorName) {
EmergencySimulation simulation = new EmergencySimulation();
simulation.setSimulationNo("SIM-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
simulation.setScenarioType("pipe_burst");
simulation.setScenarioName("爆管应急推演");
simulation.setLocationLng(lng);
simulation.setLocationLat(lat);
simulation.setPipeDiameter(pipeDiameter);
simulation.setStatus("draft");
simulation.setCreatorName(creatorName);
simulation.setCreatedAt(LocalDateTime.now());
// 分析影响区域和方案
Map<String, Object> analysis = analyzePipeBurstImpact(lng, lat, pipeDiameter);
simulation.setAffectedArea((String) analysis.get("affectedArea"));
simulation.setAffectedCustomers((Integer) analysis.get("affectedCustomers"));
simulation.setProposedActions(formatActions((List<String>) analysis.get("suggestedActions")));
simulation.setEstimatedRecoveryHours((Integer) analysis.get("estimatedRecoveryHours"));
simulationMapper.insert(simulation);
log.info("创建爆管模拟: {}", simulation.getSimulationNo());
return simulation;
}
/**
* 创建水质异常推演
*/
@Transactional
public EmergencySimulation createWaterQualityIncident(String area, String pollutant,
Double lng, Double lat, String creatorName) {
EmergencySimulation simulation = new EmergencySimulation();
simulation.setSimulationNo("SIM-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
simulation.setScenarioType("water_quality");
simulation.setScenarioName("水质异常应急推演");
simulation.setLocationLng(lng);
simulation.setLocationLat(lat);
simulation.setAffectedArea(area);
simulation.setStatus("draft");
simulation.setCreatorName(creatorName);
simulation.setCreatedAt(LocalDateTime.now());
// 分析水质异常影响和方案
Map<String, Object> analysis = analyzeWaterQualityImpact(area, pollutant);
simulation.setAffectedCustomers((Integer) analysis.get("affectedCustomers"));
simulation.setProposedActions(formatActions((List<String>) analysis.get("suggestedActions")));
simulation.setRiskLevel((String) analysis.get("riskLevel"));
simulation.setBackupWaterSource((String) analysis.get("backupWaterSource"));
simulation.setEstimatedRecoveryHours((Integer) analysis.get("estimatedRecoveryHours"));
simulationMapper.insert(simulation);
log.info("创建水质异常模拟: {}", simulation.getSimulationNo());
return simulation;
}
/**
* 执行爆管模拟
*/
@Transactional
public EmergencySimulation executePipeBurstSimulation(Long simulationId, String operatorName) {
EmergencySimulation simulation = getSimulationOrThrow(simulationId);
if (!"pipe_burst".equals(simulation.getScenarioType())) {
throw new IllegalArgumentException("该模拟不是爆管模拟");
}
simulation.setStatus("executing");
simulation.setUpdatedAt(LocalDateTime.now());
simulationMapper.updateById(simulation);
// 模拟执行逻辑
Map<String, Object> executionResult = executeSimulationLogic(simulation);
simulation.setStatus("completed");
simulation.setUpdatedAt(LocalDateTime.now());
simulationMapper.updateById(simulation);
log.info("完成爆管模拟执行: {}", simulation.getSimulationNo());
return simulation;
}
/**
* 执行水质异常模拟
*/
@Transactional
public EmergencySimulation executeWaterQualitySimulation(Long simulationId, String operatorName) {
EmergencySimulation simulation = getSimulationOrThrow(simulationId);
if (!"water_quality".equals(simulation.getScenarioType())) {
throw new IllegalArgumentException("该模拟不是水质异常模拟");
}
simulation.setStatus("executing");
simulation.setUpdatedAt(LocalDateTime.now());
simulationMapper.updateById(simulation);
// 模拟执行逻辑
Map<String, Object> executionResult = executeSimulationLogic(simulation);
simulation.setStatus("completed");
simulation.setUpdatedAt(LocalDateTime.now());
simulationMapper.updateById(simulation);
log.info("完成水质异常模拟执行: {}", simulation.getSimulationNo());
return simulation;
}
/**
* 查询模拟列表
*/
public IPage<Map<String, Object>> listSimulations(int page, int size, String scenarioType,
String status, String keyword, String startDate, String endDate) {
Page<Map<String, Object>> pageParam = new Page<>(page, size);
return simulationMapper.selectSimulationPage(pageParam, scenarioType, status, keyword, startDate, endDate);
}
/**
* 获取模拟详情
*/
public Map<String, Object> getSimulationDetail(Long simulationId) {
Map<String, Object> detail = simulationMapper.selectSimulationDetail(simulationId);
if (detail == null) {
throw new IllegalArgumentException("模拟记录不存在");
}
// 获取相关预案
List<Map<String, Object>> relatedPlans = simulationMapper.selectRelatedPlans((String) detail.get("scenario_type"));
detail.put("relatedPlans", relatedPlans);
return detail;
}
/**
* 模拟统计
*/
public List<Map<String, Object>> getSimulationStats() {
return simulationMapper.selectSimulationStats();
}
/**
* 分析爆管影响
*/
private Map<String, Object> analyzePipeBurstImpact(Double lng, Double lat, String pipeDiameter) {
Map<String, Object> result = new LinkedHashMap<>();
// 基于管道直径和位置计算影响范围
double impactRadius = calculateImpactRadius(pipeDiameter);
String areaDescription = String.format("半径%.0fm圆形区域", impactRadius);
// 模拟计算受影响用户数量
int affectedCustomers = (int) (Math.PI * impactRadius * impactRadius / 1000 * 50); // 假设每平米0.05用户
// 生成建议操作
List<String> actions = new ArrayList<>();
actions.add("关闭上游阀门 V-001, V-002");
actions.add("启动应急供水方案 B");
actions.add("通知受影响用户(短信+公告)");
actions.add("调度抢修队出发");
// 根据管道直径估算恢复时间
int recoveryHours = 2 + getRecoveryHoursByDiameter(pipeDiameter);
result.put("affectedArea", areaDescription);
result.put("affectedCustomers", affectedCustomers);
result.put("suggestedActions", actions);
result.put("estimatedRecoveryHours", recoveryHours);
return result;
}
/**
* 分析水质异常影响
*/
private Map<String, Object> analyzeWaterQualityImpact(String area, String pollutant) {
Map<String, Object> result = new LinkedHashMap<>();
// 根据污染物类型确定风险等级
String riskLevel = determineRiskLevel(pollutant);
// 模拟受影响用户数量
int affectedCustomers = getAffectedCustomersByArea(area);
// 生成建议操作
List<String> actions = new ArrayList<>();
actions.add("立即停止该片区供水");
actions.add("启动备用水源");
actions.add("水质采样送检");
actions.add("向下游水厂发出预警");
// 根据风险等级估算恢复时间
int recoveryHours = riskLevel.equals("critical") ? 8 : (riskLevel.equals("high") ? 4 : 2);
// 确定备用水源
String backupSource = determineBackupWaterSource(area);
result.put("affectedCustomers", affectedCustomers);
result.put("suggestedActions", actions);
result.put("riskLevel", riskLevel);
result.put("backupWaterSource", backupSource);
result.put("estimatedRecoveryHours", recoveryHours);
return result;
}
/**
* 执行模拟逻辑
*/
private Map<String, Object> executeSimulationLogic(EmergencySimulation simulation) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("simulationNo", simulation.getSimulationNo());
result.put("executionTime", LocalDateTime.now());
// 模拟执行结果
if ("pipe_burst".equals(simulation.getScenarioType())) {
result.put("actualAffectedCustomers", simulation.getAffectedCustomers() + (int)(Math.random() * 20 - 10));
result.put("actualRecoveryHours", simulation.getEstimatedRecoveryHours() + (int)(Math.random() * 2 - 1));
result.put("actualCost", 50000 + (int)(Math.random() * 30000));
} else {
result.put("actualAffectedCustomers", simulation.getAffectedCustomers() + (int)(Math.random() * 15 - 7));
result.put("actualRecoveryHours", simulation.getEstimatedRecoveryHours() + (int)(Math.random() * 2 - 1));
result.put("waterQualityIndex", 85 + (int)(Math.random() * 10));
}
return result;
}
// 辅助方法
private double calculateImpactRadius(String pipeDiameter) {
switch (pipeDiameter) {
case "DN50": return 200;
case "DN80": return 350;
case "DN100": return 500;
case "DN150": return 700;
default: return 500;
}
}
private int getRecoveryHoursByDiameter(String pipeDiameter) {
switch (pipeDiameter) {
case "DN50": return 1;
case "DN80": return 2;
case "DN100": return 3;
case "DN150": return 4;
default: return 3;
}
}
private String determineRiskLevel(String pollutant) {
if (pollutant.contains("重金属") || pollutant.contains("剧毒")) {
return "critical";
} else if (pollutant.contains("细菌") || pollutant.contains("病毒")) {
return "high";
} else {
return "medium";
}
}
private int getAffectedCustomersByArea(String area) {
// 简化的区域人口估算
switch (area) {
case "市区": return 5000;
case "郊区": return 2000;
case "工业区": return 3000;
default: return 1500;
}
}
private String determineBackupWaterSource(String area) {
if (area.contains("市区")) {
return "备用水厂A";
} else if (area.contains("工业区")) {
return "应急水车调度";
} else {
return "深水井备用系统";
}
}
private String formatActions(List<String> actions) {
return String.join("\n", actions);
}
public void updateSimulation(EmergencySimulation simulation) {
simulationMapper.updateById(simulation);
}
}
@@ -0,0 +1,15 @@
package com.water.production.service;
import com.water.production.entity.Notification;
import com.water.production.vo.NotificationQueryVO;
import java.util.List;
public interface NotificationService {
int createNotification(Notification notification);
int updateNotification(Notification notification);
int deleteNotification(Long id);
Notification getNotificationById(Long id);
List<Notification> getAllNotifications();
List<Notification> getNotificationsByQuery(NotificationQueryVO queryVO);
int getNotificationCountByQuery(NotificationQueryVO queryVO);
}
@@ -0,0 +1,15 @@
package com.water.production.service;
import com.water.production.entity.Threshold;
import com.water.production.vo.ThresholdQueryVO;
import java.util.List;
public interface ThresholdService {
int createThreshold(Threshold threshold);
int updateThreshold(Threshold threshold);
int deleteThreshold(Long id);
Threshold getThresholdById(Long id);
List<Threshold> getAllThresholds();
List<Threshold> getThresholdsByQuery(ThresholdQueryVO queryVO);
int getThresholdCountByQuery(ThresholdQueryVO queryVO);
}
@@ -0,0 +1,187 @@
package com.water.production.service.impl;
import com.water.production.entity.AlertManagement;
import com.water.production.mapper.AlertManagementMapper;
import com.water.production.service.AlertManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
@Service
@Transactional
public class AlertManagementServiceImpl implements AlertManagementService {
@Autowired
private AlertManagementMapper alertManagementMapper;
@Override
public List<AlertManagement> getAllAlerts() {
return alertManagementMapper.findAll();
}
@Override
public AlertManagement getAlertById(Long id) {
if (id == null) {
throw new IllegalArgumentException("报警ID不能为空");
}
AlertManagement alert = alertManagementMapper.findById(id);
if (alert == null) {
throw new RuntimeException("未找到ID为 " + id + " 的报警");
}
return alert;
}
@Override
public List<AlertManagement> getAlertsByRuleId(Long alertRuleId) {
if (alertRuleId == null) {
throw new IllegalArgumentException("报警规则ID不能为空");
}
return alertManagementMapper.findByAlertRuleId(alertRuleId);
}
@Override
public List<AlertManagement> getAlertsByStatus(String status) {
if (!StringUtils.hasText(status)) {
throw new IllegalArgumentException("状态不能为空");
}
return alertManagementMapper.findByStatus(status);
}
@Override
public List<AlertManagement> getAlertsBySeverity(String severity) {
if (!StringUtils.hasText(severity)) {
throw new IllegalArgumentException("严重程度不能为空");
}
return alertManagementMapper.findBySeverity(severity);
}
@Override
public AlertManagement createAlert(AlertManagement alert) {
if (alert == null) {
throw new IllegalArgumentException("报警不能为空");
}
alert.setCreateTime(LocalDateTime.now());
alert.setUpdateTime(LocalDateTime.now());
alert.setStatus("PENDING"); // 默认待确认
int result = alertManagementMapper.insert(alert);
if (result <= 0) {
throw new RuntimeException("创建报警失败");
}
return alert;
}
@Override
public AlertManagement updateAlert(AlertManagement alert) {
if (alert == null || alert.getId() == null) {
throw new IllegalArgumentException("报警和ID不能为空");
}
// 检查报警是否存在
AlertManagement existingAlert = getAlertById(alert.getId());
if (existingAlert == null) {
throw new RuntimeException("未找到ID为 " + alert.getId() + " 的报警");
}
alert.setUpdateTime(LocalDateTime.now());
int result = alertManagementMapper.update(alert);
if (result <= 0) {
throw new RuntimeException("更新报警失败");
}
return alert;
}
@Override
public boolean confirmAlert(Long id, String assigneeId) {
if (id == null || !StringUtils.hasText(assigneeId)) {
throw new IllegalArgumentException("报警ID和负责人ID不能为空");
}
int result = alertManagementMapper.confirmAlert(id, assigneeId);
return result > 0;
}
@Override
public boolean resolveAlert(Long id) {
if (id == null) {
throw new IllegalArgumentException("报警ID不能为空");
}
AlertManagement existingAlert = getAlertById(id);
if (existingAlert == null) {
throw new RuntimeException("未找到ID为 " + id + " 的报警");
}
int result = alertManagementMapper.resolveAlert(id);
return result > 0;
}
@Override
public boolean dispatchTask(Long id, Long taskId) {
if (id == null || taskId == null) {
throw new IllegalArgumentException("报警ID和任务ID不能为空");
}
int result = alertManagementMapper.dispatchTask(id, taskId);
return result > 0;
}
@Override
public AlertStatistics generateStatistics() {
AlertStatistics statistics = new AlertStatistics();
// 获取所有报警
List<AlertManagement> allAlerts = getAllAlerts();
statistics.setTotalAlerts(allAlerts.size());
// 统计已解决和待处理的报警
int resolvedCount = 0;
int pendingCount = 0;
int criticalCount = 0;
int highCount = 0;
int mediumCount = 0;
int lowCount = 0;
for (AlertManagement alert : allAlerts) {
if ("RESOLVED".equals(alert.getStatus())) {
resolvedCount++;
} else {
pendingCount++;
}
if (alert.getSeverity() != null) {
switch (alert.getSeverity().toUpperCase()) {
case "CRITICAL":
criticalCount++;
break;
case "HIGH":
highCount++;
break;
case "MEDIUM":
mediumCount++;
break;
case "LOW":
lowCount++;
break;
}
}
}
statistics.setResolvedAlerts(resolvedCount);
statistics.setPendingAlerts(pendingCount);
statistics.setCriticalAlerts(criticalCount);
statistics.setHighAlerts(highCount);
statistics.setMediumAlerts(mediumCount);
statistics.setLowAlerts(lowCount);
return statistics;
}
}
@@ -0,0 +1,179 @@
package com.water.production.service.impl;
import com.water.production.entity.AlertRule;
import com.water.production.mapper.AlertRuleMapper;
import com.water.production.service.AlertRuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
@Service
@Transactional
public class AlertRuleServiceImpl implements AlertRuleService {
@Autowired
private AlertRuleMapper alertRuleMapper;
@Override
public List<AlertRule> getAllAlertRules() {
return alertRuleMapper.findAll();
}
@Override
public AlertRule getAlertRuleById(Long id) {
if (id == null) {
throw new IllegalArgumentException("报警规则ID不能为空");
}
AlertRule rule = alertRuleMapper.findById(id);
if (rule == null) {
throw new RuntimeException("未找到ID为 " + id + " 的报警规则");
}
return rule;
}
@Override
public List<AlertRule> getAlertRulesByParameter(String parameter) {
if (!StringUtils.hasText(parameter)) {
throw new IllegalArgumentException("参数不能为空");
}
return alertRuleMapper.findByParameter(parameter);
}
@Override
public List<AlertRule> getAlertRulesByEquipment(String equipmentId) {
if (!StringUtils.hasText(equipmentId)) {
throw new IllegalArgumentException("设备ID不能为空");
}
return alertRuleMapper.findByTargetEquipmentId(equipmentId);
}
@Override
public List<AlertRule> getAlertRulesByArea(String area) {
if (!StringUtils.hasText(area)) {
throw new IllegalArgumentException("区域不能为空");
}
return alertRuleMapper.findByTargetArea(area);
}
@Override
public AlertRule createAlertRule(AlertRule alertRule) {
if (alertRule == null) {
throw new IllegalArgumentException("报警规则不能为空");
}
if (!validateAlertRule(alertRule)) {
throw new IllegalArgumentException("报警规则验证失败");
}
alertRule.setCreateTime(LocalDateTime.now());
alertRule.setUpdateTime(LocalDateTime.now());
alertRule.setStatus(1); // 默认启用
int result = alertRuleMapper.insert(alertRule);
if (result <= 0) {
throw new RuntimeException("创建报警规则失败");
}
return alertRule;
}
@Override
public AlertRule updateAlertRule(AlertRule alertRule) {
if (alertRule == null || alertRule.getId() == null) {
throw new IllegalArgumentException("报警规则和ID不能为空");
}
// 检查规则是否存在
AlertRule existingRule = getAlertRuleById(alertRule.getId());
if (existingRule == null) {
throw new RuntimeException("未找到ID为 " + alertRule.getId() + " 的报警规则");
}
if (!validateAlertRule(alertRule)) {
throw new IllegalArgumentException("报警规则验证失败");
}
alertRule.setUpdateTime(LocalDateTime.now());
int result = alertRuleMapper.update(alertRule);
if (result <= 0) {
throw new RuntimeException("更新报警规则失败");
}
return alertRule;
}
@Override
public boolean deleteAlertRule(Long id) {
if (id == null) {
throw new IllegalArgumentException("报警规则ID不能为空");
}
AlertRule existingRule = getAlertRuleById(id);
if (existingRule == null) {
throw new RuntimeException("未找到ID为 " + id + " 的报警规则");
}
int result = alertRuleMapper.deleteById(id);
return result > 0;
}
@Override
public boolean enableAlertRule(Long id) {
if (id == null) {
throw new IllegalArgumentException("报警规则ID不能为空");
}
int result = alertRuleMapper.updateStatus(id, 1);
return result > 0;
}
@Override
public boolean disableAlertRule(Long id) {
if (id == null) {
throw new IllegalArgumentException("报警规则ID不能为空");
}
int result = alertRuleMapper.updateStatus(id, 0);
return result > 0;
}
@Override
public boolean validateAlertRule(AlertRule alertRule) {
if (alertRule == null) {
return false;
}
if (!StringUtils.hasText(alertRule.getName())) {
return false;
}
if (!StringUtils.hasText(alertRule.getParameter())) {
return false;
}
// 检查阈值关系
if (alertRule.getMinValue() != null && alertRule.getMaxValue() != null) {
if (alertRule.getMinValue() >= alertRule.getMaxValue()) {
return false;
}
}
if (alertRule.getWarningValue() != null && alertRule.getCriticalValue() != null) {
if (alertRule.getWarningValue() >= alertRule.getCriticalValue()) {
return false;
}
}
if (alertRule.getSeverityLevel() != null &&
(alertRule.getSeverityLevel() < 1 || alertRule.getSeverityLevel() > 4)) {
return false;
}
return true;
}
}
@@ -0,0 +1,16 @@
package com/water/production/vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class AlarmQueryVO {
private Integer pageNum = 1;
private Integer pageSize = 10;
private String deviceId;
private String region;
private String alarmType;
private Integer alarmLevel;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
@@ -0,0 +1,14 @@
package com.water.production.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class WaterDataQueryVO {
private Integer pageNum = 1;
private Integer pageSize = 10;
private String deviceId;
private String region;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
@@ -0,0 +1,15 @@
package com.water.production.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class WaterQualityQueryVO {
private Integer pageNum = 1;
private Integer pageSize = 10;
private String deviceId;
private String region;
private Integer qualityLevel;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
@@ -15,3 +15,14 @@ spring:
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
# MyBatis-Plus配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
@@ -0,0 +1,58 @@
-- 应急推演模块 DDL
-- 应急推演记录表
CREATE TABLE IF NOT EXISTS prod_emergency_simulation (
id BIGSERIAL PRIMARY KEY,
simulation_no VARCHAR(64) NOT NULL UNIQUE,
scenario_type VARCHAR(32) NOT NULL, -- pipe_burst | water_quality
scenario_name VARCHAR(100) NOT NULL,
location_lng DOUBLE PRECISION,
location_lat DOUBLE PRECISION,
pipe_diameter VARCHAR(20),
affected_area TEXT,
affected_customers INTEGER,
proposed_actions TEXT,
estimated_recovery_hours INTEGER,
backup_water_source VARCHAR(100),
risk_level VARCHAR(20),
status VARCHAR(32) NOT NULL DEFAULT 'draft', -- draft | executing | completed | with_plan
related_command_no VARCHAR(64),
incident_report_no VARCHAR(64),
creator_name VARCHAR(64),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 应急预案表
CREATE TABLE IF NOT EXISTS prod_emergency_plan (
id BIGSERIAL PRIMARY KEY,
plan_no VARCHAR(64) NOT NULL UNIQUE,
plan_name VARCHAR(100) NOT NULL,
plan_type VARCHAR(32) NOT NULL, -- disaster | accident | emergency
scenario VARCHAR(100) NOT NULL,
trigger_conditions TEXT,
response_procedure TEXT,
responsible_departments TEXT,
contact_info TEXT,
resource_requirements TEXT,
backup_solutions TEXT,
evacuation_plan TEXT,
communication_protocol TEXT,
status VARCHAR(32) NOT NULL DEFAULT 'draft', -- draft | active | inactive | expired
creator_name VARCHAR(64),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_used_at TIMESTAMP,
last_used_in_simulation VARCHAR(64)
);
-- 索引创建
CREATE INDEX IF NOT EXISTS idx_sim_scenario_type ON prod_emergency_simulation(scenario_type);
CREATE INDEX IF NOT EXISTS idx_sim_status ON prod_emergency_simulation(status);
CREATE INDEX IF NOT EXISTS idx_sim_created ON prod_emergency_simulation(created_at);
CREATE INDEX IF NOT EXISTS idx_sim_location ON prod_emergency_simulation(location_lng, location_lat);
CREATE INDEX IF NOT EXISTS idx_plan_plan_type ON prod_emergency_plan(plan_type);
CREATE INDEX IF NOT EXISTS idx_plan_status ON prod_emergency_plan(status);
CREATE INDEX IF NOT EXISTS idx_plan_scenario ON prod_emergency_plan(scenario);
CREATE INDEX IF NOT EXISTS idx_plan_created ON prod_emergency_plan(created_at);
CREATE INDEX IF NOT EXISTS idx_plan_last_used ON prod_emergency_plan(last_used_at);
@@ -0,0 +1,53 @@
-- 应急推演模块初始化数据
-- 插入示例应急预案
INSERT INTO prod_emergency_plan (plan_no, plan_name, plan_type, scenario, trigger_conditions, response_procedure, responsible_departments, contact_info, resource_requirements, backup_solutions, evacuation_plan, communication_protocol, status, creator_name, created_at, updated_at) VALUES
('PLAN-20240614010001', '爆管应急预案', 'disaster', '爆管',
'1. 管道压力异常波动\n2. 地面出现喷水现象\n3. 用户报告大面积停水\n4. 系统监测到漏水量异常',
'1. 紧急情况确认\n - 接到报告后30分钟内现场确认\n - 调取监控录像和传感器数据\n - 评估事态严重程度\n\n2. 应急响应启动\n - 通知应急指挥中心\n - 调集应急资源\n - 向上级部门报告\n\n3. 抢修流程\n - 关闭相关阀门\n - 组织抢修队伍\n - 调配抢修物资\n - 制定临时供水方案\n\n4. 恢复重建\n - 修复完成后水质检测\n - 逐步恢复供水\n - 用户通知和解释\n - 事后总结和改进',
'应急指挥中心:负责统一指挥和协调\n抢修队伍:负责管道维修和恢复供水\n水质检测组:负责水质监测和分析\n用户服务组:负责用户通知和解释\n后勤保障组:负责物资调配和后勤支持',
'应急指挥中心:400-123-4567\n抢修队伍:138-0000-1234\n水质检测:138-0000-5678\n用户服务:95598\n24小时值班:110-119-120',
'1. 人员:抢修人员10-20人,技术人员5人\n2. 设备:挖掘机、焊接设备、检测仪器\n3. 物资:管道配件、消毒剂、备用水管\n4. 交通:应急车辆3-5台\n5. 通讯:对讲机、卫星电话',
'1. 应急供水车:提供临时用水\n2. 邻区调水:协调邻近区域供水\n3. 加压供水:启动备用加压站\n4. 瓶装水:发放给特殊用户',
'1. 疏散范围:根据影响区域确定\n2. 疏散路线:提前规划多条路线\n3. 集中地点:学校、体育馆等公共场所\n4. 物资准备:饮用水、食品、药品\n5. 交通保障:提供交通工具',
'1. 内部通讯:使用应急通讯频道\n2. 外部通讯:24小时值班电话\n3. 信息发布:官方渠道及时发布\n4. 媒体应对:统一对外口径\n5. 用户沟通:专人负责用户解释',
'active', 'system', NOW(), NOW());
INSERT INTO prod_emergency_plan (plan_no, plan_name, plan_type, scenario, trigger_conditions, response_procedure, responsible_departments, contact_info, resource_requirements, backup_solutions, evacuation_plan, communication_protocol, status, creator_name, created_at, updated_at) VALUES
('PLAN-20240614010002', '水质异常应急预案', 'emergency', '水质异常',
'1. 水质检测指标超标\n2. 用户反映水质异常\n3. 上游水源污染报告\n4. 系统监测到浊度/色度异常',
'1. 紧急情况确认\n - 接到报告后15分钟内现场确认\n - 多点采集水样进行检测\n - 评估污染程度和范围\n\n2. 应急响应启动\n - 通知应急指挥中心\n - 启动备用水源\n - 向相关部门报告\n\n3. 水质处置流程\n - 立即停止该片区供水\n - 启动备用水源\n - 组织水质检测\n - 实施临时供水方案\n - 发布停水通知\n\n4. 恢复重建\n - 水质达标后恢复供水\n - 全面清洗管道系统\n - 用户通知和解释\n - 事后总结和改进',
'应急指挥中心:负责统一指挥和协调\n水质检测组:负责水质监测和分析\n抢修队伍:负责管道系统修复\n用户服务组:负责用户通知和解释\n后勤保障组:负责物资调配和后勤支持',
'应急指挥中心:400-123-4567\n水质检测:138-0000-5678\n用户服务:95598\n24小时值班:110-119-120',
'1. 人员:检测人员5-10人,技术人员3人\n2. 设备:水质检测仪器、净化设备\n3. 物资:消毒剂、净化材料、采样瓶\n4. 交通:应急车辆2-3台\n5. 通讯:对讲机、卫星电话',
'1. 备用水源:启动备用水厂\n2. 水质处理:临时净化设备\n3. 外购水:联系周边水厂支援\n4. 分时段供水:错峰供水方案',
'1. 疏散范围:根据污染区域确定\n2. 疏散路线:避开污染区域\n3. 集中地点:清洁区域公共场所\n4. 物资准备:瓶装水、食品、药品\n5. 交通保障:提供安全交通工具',
'1. 内部通讯:使用应急通讯频道\n2. 外部通讯:24小时值班电话\n3. 信息发布:官方渠道及时发布\n4. 媒体应对:统一对外口径\n5. 用户沟通:专人负责用户解释',
'active', 'system', NOW(), NOW());
-- 插入示例应急推演记录
INSERT INTO prod_emergency_simulation (simulation_no, scenario_type, scenario_name, location_lng, location_lat, pipe_diameter, affected_area, affected_customers, proposed_actions, estimated_recovery_hours, status, creator_name, created_at, updated_at) VALUES
('SIM-20240614010001', 'pipe_burst', '爆管应急推演', 116.4074, 39.9042, 'DN100', '半径500m圆形区域', 230,
'关闭上游阀门 V-001, V-002\n启动应急供水方案 B\n通知受影响用户(短信+公告)\n调度抢修队出发', 4, 'completed', 'system', NOW(), NOW());
INSERT INTO prod_emergency_simulation (simulation_no, scenario_type, scenario_name, location_lng, location_lat, affected_area, affected_customers, proposed_actions, risk_level, backup_water_source, estimated_recovery_hours, status, creator_name, created_at, updated_at) VALUES
('SIM-20240614010002', 'water_quality', '水质异常应急推演', 116.4074, 39.9042, '市中心区域', 5000,
'立即停止该片区供水\n启动备用水源\n水质采样送检\n向下游水厂发出预警', 4, 'high', '备用水厂A', 8, 'completed', 'system', NOW(), NOW());
-- 创建示例调度指令关联
UPDATE prod_emergency_simulation
SET related_command_no = 'CMD-20240614010001'
WHERE simulation_no = 'SIM-20240614010001';
UPDATE prod_emergency_simulation
SET related_command_no = 'CMD-20240614010002'
WHERE simulation_no = 'SIM-20240614010002';
-- 更新预案使用记录
UPDATE prod_emergency_plan
SET last_used_at = NOW(), last_used_in_simulation = 'SIM-20240614010001'
WHERE plan_no = 'PLAN-20240614010001';
UPDATE prod_emergency_plan
SET last_used_at = NOW(), last_used_in_simulation = 'SIM-20240614010002'
WHERE plan_no = 'PLAN-20240614010002';
+66
View File
@@ -0,0 +1,66 @@
-- 水量数据表
CREATE TABLE IF NOT EXISTS water_data (
id BIGSERIAL PRIMARY KEY,
device_id VARCHAR(50) NOT NULL,
region VARCHAR(100),
volume DOUBLE PRECISION,
pressure DOUBLE PRECISION,
flow_rate DOUBLE PRECISION,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 水质数据表
CREATE TABLE IF NOT EXISTS water_quality (
id BIGSERIAL PRIMARY KEY,
device_id VARCHAR(50) NOT NULL,
region VARCHAR(100),
ph DOUBLE PRECISION,
turbidity DOUBLE PRECISION,
chlorine DOUBLE PRECISION,
dissolved_oxygen DOUBLE PRECISION,
quality_level INTEGER,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 报警记录表
CREATE TABLE IF NOT EXISTS alarm_record (
id BIGSERIAL PRIMARY KEY,
device_id VARCHAR(50) NOT NULL,
region VARCHAR(100),
alarm_type VARCHAR(50),
alarm_level INTEGER,
alarm_message TEXT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status INTEGER DEFAULT 0 -- 0:未处理, 1:处理中, 2:已处理
);
-- 生产报表表
CREATE TABLE IF NOT EXISTS production_report (
id BIGSERIAL PRIMARY KEY,
report_type VARCHAR(50) NOT NULL,
region VARCHAR(100),
start_time TIMESTAMP,
end_time TIMESTAMP,
total_value DOUBLE PRECISION,
avg_value DOUBLE PRECISION,
data_count INTEGER,
remarks TEXT,
generate_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_water_data_device ON water_data(device_id);
CREATE INDEX IF NOT EXISTS idx_water_data_region ON water_data(region);
CREATE INDEX IF NOT EXISTS idx_water_data_time ON water_data(create_time);
CREATE INDEX IF NOT EXISTS idx_water_quality_device ON water_quality(device_id);
CREATE INDEX IF NOT EXISTS idx_water_quality_region ON water_quality(region);
CREATE INDEX IF NOT EXISTS idx_water_quality_time ON water_quality(create_time);
CREATE INDEX IF NOT EXISTS idx_alarm_record_device ON alarm_record(device_id);
CREATE INDEX IF NOT EXISTS idx_alarm_record_region ON alarm_record(region);
CREATE INDEX IF NOT EXISTS idx_alarm_record_time ON alarm_record(create_time);
CREATE INDEX IF NOT EXISTS idx_report_type ON production_report(report_type);
CREATE INDEX IF NOT EXISTS idx_report_region ON production_report(region);
CREATE INDEX IF NOT EXISTS idx_report_time ON production_report(generate_time);
@@ -0,0 +1,83 @@
package com.water.production.controller;
import com.water.production.entity.Equipment;
import com.water.production.service.EquipmentService;
import com.water.production.vo.EquipmentQueryVO;
import com.water.production.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/equipment")
@Tag(name = "设备管理", description = "设备列表查询接口")
public class EquipmentController {
@Autowired
private EquipmentService equipmentService;
@PostMapping("/save")
@Operation(summary = "保存设备信息")
public Result<Boolean> saveEquipment(@RequestBody Equipment equipment) {
boolean success = equipmentService.saveEquipment(equipment);
return success ? Result.success(true) : Result.error("保存失败");
}
@PutMapping("/update")
@Operation(summary = "更新设备信息")
public Result<Boolean> updateEquipment(@RequestBody Equipment equipment) {
boolean success = equipmentService.updateEquipment(equipment);
return success ? Result.success(true) : Result.error("更新失败");
}
@DeleteMapping("/delete/{id}")
@Operation(summary = "删除设备信息")
public Result<Boolean> deleteEquipment(@PathVariable Long id) {
boolean success = equipmentService.deleteEquipment(id);
return success ? Result.success(true) : Result.error("删除失败");
}
@GetMapping("/list")
@Operation(summary = "获取所有设备列表")
public Result<List<Equipment>> list() {
List<Equipment> equipmentList = equipmentService.list();
return Result.success(equipmentList);
}
@PostMapping("/query")
@Operation(summary = "查询设备列表")
public Result<List<Equipment>> query(@RequestBody EquipmentQueryVO queryVO) {
List<Equipment> equipmentList = equipmentService.queryEquipment(queryVO);
return Result.success(equipmentList);
}
@GetMapping("/name/{deviceName}")
@Operation(summary = "根据设备名称查询(模糊匹配)")
public Result<List<Equipment>> getByName(@PathVariable String deviceName) {
List<Equipment> equipmentList = equipmentService.getEquipmentByName(deviceName);
return Result.success(equipmentList);
}
@GetMapping("/type/{deviceType}")
@Operation(summary = "根据设备类型查询")
public Result<List<Equipment>> getByType(@PathVariable String deviceType) {
List<Equipment> equipmentList = equipmentService.getEquipmentByType(deviceType);
return Result.success(equipmentList);
}
@GetMapping("/region/{region}")
@Operation(summary = "根据区域查询")
public Result<List<Equipment>> getByRegion(@PathVariable String region) {
List<Equipment> equipmentList = equipmentService.getEquipmentByRegion(region);
return Result.success(equipmentList);
}
@GetMapping("/status/{status}")
@Operation(summary = "根据状态查询")
public Result<List<Equipment>> getByStatus(@PathVariable String status) {
List<Equipment> equipmentList = equipmentService.getEquipmentByStatus(status);
return Result.success(equipmentList);
}
}
@@ -0,0 +1,91 @@
package com.water.production.controller;
import com.water.production.entity.Notification;
import com.water.production.service.NotificationService;
import com.water.production.vo.NotificationQueryVO;
import com.water.production.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/notification")
@Tag(name = "信息发布", description = "预报/预警信息发布接口")
public class NotificationController {
@Autowired
private NotificationService notificationService;
@PostMapping("/save")
@Operation(summary = "保存草稿")
public Result<Boolean> saveNotification(@RequestBody Notification notification) {
notification.setStatus(1); // draft
boolean success = notificationService.save(notification);
return success ? Result.success(true) : Result.error("保存失败");
}
@PutMapping("/update")
@Operation(summary = "更新信息")
public Result<Boolean> updateNotification(@RequestBody Notification notification) {
boolean success = notificationService.updateById(notification);
return success ? Result.success(true) : Result.error("更新失败");
}
@PutMapping("/publish/{id}")
@Operation(summary = "发布信息")
public Result<Boolean> publishNotification(@PathVariable Long id, @RequestParam String publisher) {
boolean success = notificationService.publishNotification(id, publisher);
return success ? Result.success(true) : Result.error("发布失败");
}
@PutMapping("/archive/{id}")
@Operation(summary = "归档信息")
public Result<Boolean> archiveNotification(@PathVariable Long id) {
boolean success = notificationService.archiveNotification(id);
return success ? Result.success(true) : Result.error("归档失败");
}
@DeleteMapping("/delete/{id}")
@Operation(summary = = "删除信息")
public Result<Boolean> deleteNotification(@PathVariable Long id) {
boolean success = notificationService.removeById(id);
return success ? Result.success(true) : Result.error("删除失败");
}
@GetMapping("/list")
@Operation(summary = "获取所有信息列表")
public Result<List<Notification>> list() {
List<Notification> notifications = notificationService.list();
return Result.success(notifications);
}
@PostMapping("/query")
@Operation(summary = "查询信息列表")
public Result<List<Notification>> query(@RequestBody NotificationQueryVO queryVO) {
List<Notification> notifications = notificationService.queryNotifications(queryVO);
return Result.success(notifications);
}
@GetMapping("/region/{region}")
@Operation(summary = "根据区域获取信息")
public Result<List<Notification>> getByRegion(@PathVariable String region) {
List<Notification> notifications = notificationService.getNotificationsByRegion(region);
return Result.success(notifications);
}
@GetMapping("/type/{type}")
@Operation(summary = = "根据类型获取信息")
public Result<List<Notification>> getByType(@PathVariable String type) {
List<Notification> notifications = notificationService.getNotificationsByType(type);
return Result.success(notifications);
}
@GetMapping("/active")
@Operation(summary = "获取已发布信息")
public Result<List<Notification>> getActiveNotifications() {
List<Notification> notifications = notificationService.getActiveNotifications();
return Result.success(notifications);
}
}
@@ -0,0 +1,77 @@
package com.water.production.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.production.entity.Threshold;
import com.water.production.service.ThresholdService;
import com.water.production.vo.ThresholdQueryVO;
import com.water.production.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/threshold")
@Tag(name = "阈值管理", description = "报警阈值编辑接口")
public class ThresholdController {
@Autowired
private ThresholdService thresholdService;
@PostMapping("/save")
@Operation(summary = "保存阈值配置")
public Result<Boolean> saveThreshold(@RequestBody Threshold threshold) {
boolean success = thresholdService.saveThreshold(threshold);
return success ? Result.success(true) : Result.error("保存失败");
}
@PutMapping("/update")
@Operation(summary = "更新阈值配置")
public Result<Boolean> updateThreshold(@RequestBody Threshold threshold) {
boolean success = thresholdService.updateThreshold(threshold);
return success ? Result.success(true) : Result.error("更新失败");
}
@DeleteMapping("/delete/{id}")
@Operation(summary = "删除阈值配置")
public Result<Boolean> deleteThreshold(@PathVariable Long id) {
boolean success = thresholdService.deleteThreshold(id);
return success ? Result.success(true) : Result.error("删除失败");
}
@GetMapping("/list")
@Operation(summary = "获取阈值配置列表")
public Result<List<Threshold>> list() {
List<Threshold> thresholds = thresholdService.list();
return Result.success(thresholds);
}
@PostMapping("/query")
@Operation(summary = "查询阈值配置")
public Result<List<Threshold>> query(@RequestBody ThresholdQueryVO queryVO) {
List<Threshold> thresholds = thresholdService.queryThresholds(queryVO);
return Result.success(thresholds);
}
@GetMapping("/device/{deviceId}")
@Operation(summary = "根据设备ID获取阈值")
public Result<List<Threshold>> getByDeviceId(@PathVariable String deviceId) {
List<Threshold> thresholds = thresholdService.getThresholdsByDevice(deviceId);
return Result.success(thresholds);
}
@GetMapping("/region/{region}")
@Operation(summary = "根据区域获取阈值")
public Result<List<Threshold>> getByRegion(@PathVariable String region) {
List<Threshold> thresholds = thresholdService.getThresholdsByRegion(region);
return Result.success(thresholds);
}
@GetMapping("/parameter/{parameter}")
@Operation(summary = "根据参数获取阈值")
public Result<List<Threshold>> getByParameter(@PathVariable String parameter) {
List<Threshold> thresholds = thresholdService.getThresholdsByParameter(parameter);
return Result.success(thresholds);
}
}
@@ -0,0 +1,29 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("equipment")
public class Equipment {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceName;
private String deviceType;
private String model;
private String serialNumber;
private String region;
private String location;
private String status; // online, offline, maintenance, fault
private String manufacturer;
private String installationDate;
private String lastMaintenanceDate;
private String nextMaintenanceDate;
private Double latitude;
private Double longitude;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@@ -0,0 +1,24 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("notification")
public class Notification {
@TableId(type = IdType.AUTO)
private Long id;
private String title;
private String content;
private String type; // forecast, warning, info
private String region;
private String priority; // high, medium, low
private Integer status; // draft, published, archived
private String publisher;
private LocalDateTime publishTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@@ -0,0 +1,27 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("threshold")
public class Threshold {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private String deviceType;
private String region;
private String parameter;
private Double minValue;
private Double maxValue;
private Double warningMin;
private Double warningMax;
private String unit;
private String description;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.Equipment;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EquipmentMapper extends BaseMapper<Equipment> {
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.Notification;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface NotificationMapper extends BaseMapper<Notification> {
}
@@ -0,0 +1,9 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.Threshold;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ThresholdMapper extends BaseMapper<Threshold> {
}
@@ -0,0 +1,17 @@
package com.water.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.water.production.entity.Equipment;
import com.water.production.vo.EquipmentQueryVO;
import java.util.List;
public interface EquipmentService extends IService<Equipment> {
List<Equipment> getEquipmentByName(String deviceName);
List<Equipment> getEquipmentByType(String deviceType);
List<Equipment> getEquipmentByRegion(String region);
List<Equipment> getEquipmentByStatus(String status);
boolean saveEquipment(Equipment equipment);
boolean updateEquipment(Equipment equipment);
boolean deleteEquipment(Long id);
List<Equipment> queryEquipment(EquipmentQueryVO queryVO);
}
@@ -0,0 +1,15 @@
package com.water.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.water.production.entity.Notification;
import com.water.production.vo.NotificationQueryVO;
import java.util.List;
public interface NotificationService extends IService<Notification> {
List<Notification> getNotificationsByRegion(String region);
List<Notification> getNotificationsByType(String type);
List<Notification> getActiveNotifications();
boolean publishNotification(Long id, String publisher);
boolean archiveNotification(Long id);
List<Notification> queryNotifications(NotificationQueryVO queryVO);
}
@@ -0,0 +1,16 @@
package com.water.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.water.production.entity.Threshold;
import com.water.production.vo.ThresholdQueryVO;
import java.util.List;
public interface ThresholdService extends IService<Threshold> {
List<Threshold> getThresholdsByDevice(String deviceId);
List<Threshold> getThresholdsByRegion(String region);
List<Threshold> getThresholdsByParameter(String parameter);
boolean saveThreshold(Threshold threshold);
boolean updateThreshold(Threshold threshold);
boolean deleteThreshold(Long id);
List<Threshold> queryThresholds(ThresholdQueryVO queryVO);
}
@@ -0,0 +1,84 @@
package com.water.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.water.production.entity.Equipment;
import com.water.production.mapper.EquipmentMapper;
import com.water.production.service.EquipmentService;
import com.water.production.vo.EquipmentQueryVO;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment> implements EquipmentService {
@Override
public List<Equipment> getEquipmentByName(String deviceName) {
LambdaQueryWrapper<Equipment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(Equipment::getDeviceName, deviceName);
return list(queryWrapper);
}
@Override
public List<Equipment> getEquipmentByType(String deviceType) {
LambdaQueryWrapper<Equipment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Equipment::getDeviceType, deviceType);
return list(queryWrapper);
}
@Override
public List<Equipment> getEquipmentByRegion(String region) {
LambdaQueryWrapper<Equipment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Equipment::getRegion, region);
return list(queryWrapper);
}
@Override
public List<Equipment> getEquipmentByStatus(String status) {
LambdaQueryWrapper<Equipment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Equipment::getStatus, status);
return list(queryWrapper);
}
@Override
public boolean saveEquipment(Equipment equipment) {
equipment.setCreateTime(java.time.LocalDateTime.now());
equipment.setUpdateTime(java.time.LocalDateTime.now());
return save(equipment);
}
@Override
public boolean updateEquipment(Equipment equipment) {
equipment.setUpdateTime(java.time.LocalDateTime.now());
return updateById(equipment);
}
@Override
public boolean deleteEquipment(Long id) {
return removeById(id);
}
@Override
public List<Equipment> queryEquipment(EquipmentQueryVO queryVO) {
LambdaQueryWrapper<Equipment> queryWrapper = new LambdaQueryWrapper<>();
if (queryVO.getDeviceName() != null && !queryVO.getDeviceName().isEmpty()) {
queryWrapper.like(Equipment::getDeviceName, queryVO.getDeviceName());
}
if (queryVO.getDeviceType() != null && !queryVO.getDeviceType().isEmpty()) {
queryWrapper.eq(Equipment::getDeviceType, queryVO.getDeviceType());
}
if (queryVO.getRegion() != null && !queryVO.getRegion().isEmpty()) {
queryWrapper.eq(Equipment::getRegion, queryVO.getRegion());
}
if (queryVO.getStatus() != null && !queryVO.getStatus().isEmpty()) {
queryWrapper.eq(Equipment::getStatus, queryVO.getStatus());
}
if (queryVO.getManufacturer() != null && !queryVO.getManufacturer().isEmpty()) {
queryWrapper.eq(Equipment::getManufacturer, queryVO.getManufacturer());
}
queryWrapper.orderByDesc(Equipment::getCreateTime);
return list(queryWrapper);
}
}
@@ -0,0 +1,83 @@
package com.water.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.water.production.entity.Notification;
import com.water.production.mapper.NotificationMapper;
import com.water.production.service.NotificationService;
import com.water.production.vo.NotificationQueryVO;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class NotificationServiceImpl extends ServiceImpl<NotificationMapper, Notification> implements NotificationService {
@Override
public List<Notification> getNotificationsByRegion(String region) {
LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Notification::getRegion, region)
.orderByDesc(Notification::getPublishTime);
return list(queryWrapper);
}
@Override
public List<Notification> getNotificationsByType(String type) {
LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Notification::getType, type)
.orderByDesc(Notification::getPublishTime);
return list(queryWrapper);
}
@Override
public List<Notification> getActiveNotifications() {
LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Notification::getStatus, 2) // published
.orderByDesc(Notification::getPublishTime);
return list(queryWrapper);
}
@Override
public boolean publishNotification(Long id, String publisher) {
Notification notification = getById(id);
if (notification != null) {
notification.setStatus(2); // published
notification.setPublisher(publisher);
notification.setPublishTime(java.time.LocalDateTime.now());
notification.setUpdateTime(java.time.LocalDateTime.now());
return updateById(notification);
}
return false;
}
@Override
public boolean archiveNotification(Long id) {
Notification notification = getById(id);
if (notification != null) {
notification.setStatus(3); // archived
notification.setUpdateTime(java.time.LocalDateTime.now());
return updateById(notification);
}
return false;
}
@Override
public List<Notification> queryNotifications(NotificationQueryVO queryVO) {
LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
if (queryVO.getRegion() != null && !queryVO.getRegion().isEmpty()) {
queryWrapper.eq(Notification::getRegion, queryVO.getRegion());
}
if (queryVO.getType() != null && !queryVO.getType().isEmpty()) {
queryWrapper.eq(Notification::getType, queryVO.getType());
}
if (queryVO.getStatus() != null) {
queryWrapper.eq(Notification::getStatus, queryVO.getStatus());
}
if (queryVO.getPriority() != null && !queryVO.getPriority().isEmpty()) {
queryWrapper.eq(Notification::getPriority, queryVO.getPriority());
}
queryWrapper.orderByDesc(Notification::getCreateTime);
return list(queryWrapper);
}
}
@@ -0,0 +1,73 @@
package com.water.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.water.production.entity.Threshold;
import com.water.production.mapper.ThresholdMapper;
import com.water.production.service.ThresholdService;
import com.water.production.vo.ThresholdQueryVO;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ThresholdServiceImpl extends ServiceImpl<ThresholdMapper, Threshold> implements ThresholdService {
@Override
public List<Threshold> getThresholdsByDevice(String deviceId) {
LambdaQueryWrapper<Threshold> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Threshold::getDeviceId, deviceId);
return list(queryWrapper);
}
@Override
public List<Threshold> getThresholdsByRegion(String region) {
LambdaQueryWrapper<Threshold> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Threshold::getRegion, region);
return list(queryWrapper);
}
@Override
public List<Threshold> getThresholdsByParameter(String parameter) {
LambdaQueryWrapper<Threshold> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Threshold::getParameter, parameter);
return list(queryWrapper);
}
@Override
public boolean saveThreshold(Threshold threshold) {
threshold.setCreateTime(java.time.LocalDateTime.now());
threshold.setUpdateTime(java.time.LocalDateTime.now());
return save(threshold);
}
@Override
public boolean updateThreshold(Threshold threshold) {
threshold.setUpdateTime(java.time.LocalDateTime.now());
return updateById(threshold);
}
@Override
public boolean deleteThreshold(Long id) {
return removeById(id);
}
@Override
public List<Threshold> queryThresholds(ThresholdQueryVO queryVO) {
LambdaQueryWrapper<Threshold> queryWrapper = new LambdaQueryWrapper<>();
if (queryVO.getDeviceId() != null && !queryVO.getDeviceId().isEmpty()) {
queryWrapper.eq(Threshold::getDeviceId, queryVO.getDeviceId());
}
if (queryVO.getRegion() != null && !queryVO.getRegion().isEmpty()) {
queryWrapper.eq(Threshold::getRegion, queryVO.getRegion());
}
if (queryVO.getParameter() != null && !queryVO.getParameter().isEmpty()) {
queryWrapper.eq(Threshold::getParameter, queryVO.getParameter());
}
if (queryVO.getStatus() != null) {
queryWrapper.eq(Threshold::getStatus, queryVO.getStatus());
}
return list(queryWrapper);
}
}
@@ -0,0 +1,23 @@
package com.water.production.vo;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
@Data
@Schema(description = "设备查询参数")
public class EquipmentQueryVO {
@Schema(description = "设备名称(模糊查询)")
private String deviceName;
@Schema(description = "设备类型")
private String deviceType;
@Schema(description = "区域")
private String region;
@Schema(description = = "状态:online-在线,offline-离线,maintenance-维护中,fault-故障")
private String status;
@Schema(description = "制造商")
private String manufacturer;
}
@@ -0,0 +1,20 @@
package com.water.production.vo;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
@Data
@Schema(description = "信息发布查询参数")
public class NotificationQueryVO {
@Schema(description = "区域")
private String region;
@Schema(description = "类型:forecast-预报,warning-预警,info-通知")
private String type;
@Schema(description = "状态:1-草稿,2-已发布,3-已归档")
private Integer status;
@Schema(description = "优先级:high-高,medium-中,low-低")
private String priority;
}
@@ -0,0 +1,20 @@
package com.water.production.vo;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
@Data
@Schema(description = "阈值查询参数")
public class ThresholdQueryVO {
@Schema(description = "设备ID")
private String deviceId;
@Schema(description = "区域")
private String region;
@Schema(description = "参数名称")
private String parameter;
@Schema(description = "状态:1-启用,0-禁用")
private Integer status;
}
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.water.production.mapper.AlarmRecordMapper">
<select id="selectPage" resultType="com.water.production.entity.AlarmRecord">
SELECT * FROM alarm_record
<where>
<if test="deviceId != null and deviceId != ''">
AND device_id = #{deviceId}
</if>
<if test="region != null and region != ''">
AND region LIKE CONCAT('%', #{region}, '%')
</if>
<if test="alarmType != null and alarmType != ''">
AND alarm_type = #{alarmType}
</if>
<if test="alarmLevel != null">
AND alarm_level = #{alarmLevel}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
<if test="endTime != null">
AND create_time &lt;= #{endTime}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.water.production.mapper.ProductionReportMapper">
<insert id="insert" parameterType="com.water.production.entity.ProductionReport">
INSERT INTO production_report (
report_type, region, start_time, end_time,
total_value, avg_value, data_count, remarks, generate_time
) VALUES (
#{reportType}, #{region}, #{startTime}, #{endTime},
#{totalValue}, #{avgValue}, #{dataCount}, #{remarks}, #{generateTime}
)
RETURNING id
</insert>
</mapper>
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.water.production.mapper.WaterDataMapper">
<select id="selectPage" resultType="com.water.production.entity.WaterData">
SELECT * FROM water_data
<where>
<if test="deviceId != null and deviceId != ''">
AND device_id = #{deviceId}
</if>
<if test="region != null and region != ''">
AND region LIKE CONCAT('%', #{region}, '%')
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
<if test="endTime != null">
AND create_time &lt;= #{endTime}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.water.production.mapper.WaterQualityMapper">
<select id="selectPage" resultType="com.water.production.entity.WaterQuality">
SELECT * FROM water_quality
<where>
<if test="deviceId != null and deviceId != ''">
AND device_id = #{deviceId}
</if>
<if test="region != null and region != ''">
AND region LIKE CONCAT('%', #{region}, '%')
</if>
<if test="qualityLevel != null">
AND quality_level = #{qualityLevel}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
<if test="endTime != null">
AND create_time &lt;= #{endTime}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>