feat(wm-config): #72 阈值管理+信息发布+设备管理
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
<module>wm-dispatch</module>
|
||||
<module>wm-system</module>
|
||||
<module>wm-mobile-app</module>
|
||||
<module>wm-config</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent><groupId>com.water</groupId><artifactId>wm-parent</artifactId><version>1.0.0-SNAPSHOT</version></parent>
|
||||
<artifactId>wm-config</artifactId>
|
||||
<dependencies>
|
||||
<dependency><groupId>com.water</groupId><artifactId>wm-common</artifactId></dependency>
|
||||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
|
||||
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
|
||||
<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>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.water.config;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@MapperScan("com.water.config.mapper")
|
||||
public class ConfigApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConfigApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.water.config.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.config.entity.Announcement;
|
||||
import com.water.config.service.AnnouncementService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Tag(name = "公告通知管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/config/announcement")
|
||||
@RequiredArgsConstructor
|
||||
public class AnnouncementController {
|
||||
|
||||
private final AnnouncementService announcementService;
|
||||
|
||||
@Operation(summary = "分页查询公告")
|
||||
@GetMapping("/list")
|
||||
public R<Page<Announcement>> list(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) Integer type,
|
||||
@RequestParam(required = false) Integer publishStatus) {
|
||||
return R.ok(announcementService.pageAnnouncements(page, size, type, publishStatus));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取公告详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<Announcement> getById(@PathVariable Long id) {
|
||||
return R.ok(announcementService.getById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建公告(草稿)")
|
||||
@PostMapping
|
||||
public R<Announcement> create(@RequestBody Announcement announcement) {
|
||||
return R.ok(announcementService.createAnnouncement(announcement));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新公告")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @RequestBody Announcement announcement) {
|
||||
announcementService.updateAnnouncement(id, announcement);
|
||||
return R.ok("更新成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "发布公告")
|
||||
@PostMapping("/{id}/publish")
|
||||
public R<String> publish(@PathVariable Long id) {
|
||||
announcementService.publish(id);
|
||||
return R.ok("发布成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "撤回公告")
|
||||
@PostMapping("/{id}/withdraw")
|
||||
public R<String> withdraw(@PathVariable Long id) {
|
||||
announcementService.withdraw(id);
|
||||
return R.ok("撤回成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除公告")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
announcementService.deleteAnnouncement(id);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.water.config.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.config.entity.DeviceInfo;
|
||||
import com.water.config.entity.DeviceMaintenance;
|
||||
import com.water.config.service.DeviceManageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "设备管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/config/device")
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceManageController {
|
||||
|
||||
private final DeviceManageService deviceManageService;
|
||||
|
||||
@Operation(summary = "分页查询设备")
|
||||
@GetMapping("/list")
|
||||
public R<Page<DeviceInfo>> list(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String deviceName,
|
||||
@RequestParam(required = false) Integer category,
|
||||
@RequestParam(required = false) Integer deviceStatus) {
|
||||
return R.ok(deviceManageService.pageDevices(page, size, deviceName, category, deviceStatus));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取设备详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<DeviceInfo> getById(@PathVariable Long id) {
|
||||
return R.ok(deviceManageService.getById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建设备")
|
||||
@PostMapping
|
||||
public R<DeviceInfo> create(@RequestBody DeviceInfo device) {
|
||||
return R.ok(deviceManageService.createDevice(device));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新设备")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @RequestBody DeviceInfo device) {
|
||||
deviceManageService.updateDevice(id, device);
|
||||
return R.ok("更新成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除设备")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
deviceManageService.removeById(id);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "更新设备状态")
|
||||
@PutMapping("/{id}/status")
|
||||
public R<String> updateStatus(@PathVariable Long id, @RequestParam Integer deviceStatus) {
|
||||
deviceManageService.updateDeviceStatus(id, deviceStatus);
|
||||
return R.ok("状态更新成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "按分类查询设备")
|
||||
@GetMapping("/category/{category}")
|
||||
public R<List<DeviceInfo>> getByCategory(@PathVariable Integer category) {
|
||||
return R.ok(deviceManageService.getDevicesByCategory(category));
|
||||
}
|
||||
|
||||
@Operation(summary = "按状态查询设备")
|
||||
@GetMapping("/status/{status}")
|
||||
public R<List<DeviceInfo>> getByStatus(@PathVariable Integer status) {
|
||||
return R.ok(deviceManageService.getDevicesByStatus(status));
|
||||
}
|
||||
|
||||
@Operation(summary = "添加维保记录")
|
||||
@PostMapping("/maintenance")
|
||||
public R<DeviceMaintenance> addMaintenance(@RequestBody DeviceMaintenance maintenance) {
|
||||
return R.ok(deviceManageService.addMaintenance(maintenance));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询维保记录")
|
||||
@GetMapping("/maintenance")
|
||||
public R<Page<DeviceMaintenance>> pageMaintenances(@RequestParam(required = false) Long deviceId,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return R.ok(deviceManageService.pageMaintenances(deviceId, page, size));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新维保记录")
|
||||
@PutMapping("/maintenance/{id}")
|
||||
public R<String> updateMaintenance(@PathVariable Long id, @RequestBody DeviceMaintenance maintenance) {
|
||||
deviceManageService.updateMaintenance(id, maintenance);
|
||||
return R.ok("更新成功");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.water.config.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.config.entity.ThresholdChangeLog;
|
||||
import com.water.config.entity.ThresholdConfig;
|
||||
import com.water.config.service.ThresholdService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "阈值管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/config/threshold")
|
||||
@RequiredArgsConstructor
|
||||
public class ThresholdController {
|
||||
|
||||
private final ThresholdService thresholdService;
|
||||
|
||||
@Operation(summary = "分页查询阈值配置")
|
||||
@GetMapping("/list")
|
||||
public R<Page<ThresholdConfig>> list(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String metricCode,
|
||||
@RequestParam(required = false) Integer level) {
|
||||
return R.ok(thresholdService.pageThresholds(page, size, metricCode, level));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取阈值详情")
|
||||
@GetMapping("/{id}")
|
||||
public R<ThresholdConfig> getById(@PathVariable Long id) {
|
||||
return R.ok(thresholdService.getById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建阈值配置")
|
||||
@PostMapping
|
||||
public R<ThresholdConfig> create(@RequestBody ThresholdConfig config) {
|
||||
return R.ok(thresholdService.createThreshold(config));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新阈值配置")
|
||||
@PutMapping("/{id}")
|
||||
public R<String> update(@PathVariable Long id, @RequestBody ThresholdConfig config) {
|
||||
thresholdService.updateThreshold(id, config);
|
||||
return R.ok("更新成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "删除阈值配置")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<String> delete(@PathVariable Long id) {
|
||||
thresholdService.deleteThreshold(id);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "启用/禁用阈值")
|
||||
@PutMapping("/{id}/status")
|
||||
public R<String> toggleStatus(@PathVariable Long id, @RequestParam Integer status) {
|
||||
thresholdService.toggleStatus(id, status);
|
||||
return R.ok(status == 1 ? "已启用" : "已禁用");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取指标的全局阈值(多级)")
|
||||
@GetMapping("/global/{metricCode}")
|
||||
public R<List<ThresholdConfig>> getGlobalThresholds(@PathVariable String metricCode) {
|
||||
return R.ok(thresholdService.getGlobalThresholds(metricCode));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取设备阈值配置")
|
||||
@GetMapping("/device/{deviceId}")
|
||||
public R<List<ThresholdConfig>> getDeviceThresholds(@PathVariable Long deviceId) {
|
||||
return R.ok(thresholdService.getDeviceThresholds(deviceId));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取阈值变更历史")
|
||||
@GetMapping("/history")
|
||||
public R<Page<ThresholdChangeLog>> getChangeHistory(@RequestParam(required = false) Long thresholdId,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return R.ok(thresholdService.getChangeHistory(thresholdId, page, size));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.water.config.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 公告通知
|
||||
*/
|
||||
@Data
|
||||
@TableName("config_announcement")
|
||||
public class Announcement {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
/** 标题 */
|
||||
private String title;
|
||||
/** 内容 */
|
||||
private String content;
|
||||
/** 类型: 1-系统公告 2-维护通知 3-紧急通知 */
|
||||
private Integer type;
|
||||
/** 发布状态: 0-草稿 1-已发布 2-已撤回 */
|
||||
private Integer publishStatus;
|
||||
/** 发布渠道(JSON数组): ["sms","push","site"] */
|
||||
private String channels;
|
||||
/** 发布人 */
|
||||
private String publisher;
|
||||
/** 发布时间 */
|
||||
private LocalDateTime publishTime;
|
||||
/** 撤回时间 */
|
||||
private LocalDateTime withdrawTime;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.water.config.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 设备台账
|
||||
*/
|
||||
@Data
|
||||
@TableName("config_device_info")
|
||||
public class DeviceInfo {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
/** 设备编码 */
|
||||
private String deviceCode;
|
||||
/** 设备名称 */
|
||||
private String deviceName;
|
||||
/** 设备分类: 1-水表 2-压力传感器 3-流量计 4-水质监测仪 5-阀门 9-其他 */
|
||||
private Integer category;
|
||||
/** 品牌 */
|
||||
private String brand;
|
||||
/** 型号 */
|
||||
private String model;
|
||||
/** 安装位置 */
|
||||
private String location;
|
||||
/** 经度 */
|
||||
private Double longitude;
|
||||
/** 纬度 */
|
||||
private Double latitude;
|
||||
/** 设备状态: 0-离线 1-在线 2-故障 3-维修中 */
|
||||
private Integer deviceStatus;
|
||||
/** 安装日期 */
|
||||
private LocalDateTime installDate;
|
||||
/** 最后维护时间 */
|
||||
private LocalDateTime lastMaintenanceTime;
|
||||
/** 负责人 */
|
||||
private String responsiblePerson;
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.water.config.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 设备维保记录
|
||||
*/
|
||||
@Data
|
||||
@TableName("config_device_maintenance")
|
||||
public class DeviceMaintenance {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
/** 设备ID */
|
||||
private Long deviceId;
|
||||
/** 维保类型: 1-日常巡检 2-定期保养 3-故障维修 4-更换配件 */
|
||||
private Integer maintenanceType;
|
||||
/** 维保描述 */
|
||||
private String description;
|
||||
/** 维保人 */
|
||||
private String operator;
|
||||
/** 维保开始时间 */
|
||||
private LocalDateTime startTime;
|
||||
/** 维保结束时间 */
|
||||
private LocalDateTime endTime;
|
||||
/** 维保结果: 0-未完成 1-已完成 2-需要返修 */
|
||||
private Integer result;
|
||||
/** 费用 */
|
||||
private Double cost;
|
||||
/** 附件(JSON) */
|
||||
private String attachments;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.water.config.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 阈值变更记录
|
||||
*/
|
||||
@Data
|
||||
@TableName("config_threshold_change_log")
|
||||
public class ThresholdChangeLog {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
/** 关联阈值ID */
|
||||
private Long thresholdId;
|
||||
/** 变更前最小值 */
|
||||
private BigDecimal oldMinValue;
|
||||
/** 变更前最大值 */
|
||||
private BigDecimal oldMaxValue;
|
||||
/** 变更后最小值 */
|
||||
private BigDecimal newMinValue;
|
||||
/** 变更后最大值 */
|
||||
private BigDecimal newMaxValue;
|
||||
/** 变更前级别 */
|
||||
private Integer oldLevel;
|
||||
/** 变更后级别 */
|
||||
private Integer newLevel;
|
||||
/** 变更人 */
|
||||
private String operator;
|
||||
/** 变更原因 */
|
||||
private String reason;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.water.config.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 阈值配置
|
||||
*/
|
||||
@Data
|
||||
@TableName("config_threshold")
|
||||
public class ThresholdConfig {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
/** 指标编码 */
|
||||
private String metricCode;
|
||||
/** 指标名称 */
|
||||
private String metricName;
|
||||
/** 设备ID(可选,null表示全局) */
|
||||
private Long deviceId;
|
||||
/** 阈值级别: 1-预警 2-报警 3-紧急 */
|
||||
private Integer level;
|
||||
/** 最小值 */
|
||||
private BigDecimal minValue;
|
||||
/** 最大值 */
|
||||
private BigDecimal maxValue;
|
||||
/** 单位 */
|
||||
private String unit;
|
||||
/** 启用状态: 0-禁用 1-启用 */
|
||||
private Integer status;
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.water.config.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.config.entity.Announcement;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface AnnouncementMapper extends BaseMapper<Announcement> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.water.config.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.config.entity.DeviceInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DeviceInfoMapper extends BaseMapper<DeviceInfo> {
|
||||
|
||||
@Select("SELECT * FROM config_device_info WHERE device_status = #{status} AND deleted = 0")
|
||||
List<DeviceInfo> selectByDeviceStatus(Integer status);
|
||||
|
||||
@Select("SELECT * FROM config_device_info WHERE category = #{category} AND deleted = 0 ORDER BY device_code")
|
||||
List<DeviceInfo> selectByCategory(Integer category);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.water.config.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.config.entity.DeviceMaintenance;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DeviceMaintenanceMapper extends BaseMapper<DeviceMaintenance> {
|
||||
|
||||
@Select("SELECT * FROM config_device_maintenance WHERE device_id = #{deviceId} AND deleted = 0 ORDER BY start_time DESC")
|
||||
List<DeviceMaintenance> selectByDeviceId(Long deviceId);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.water.config.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.config.entity.ThresholdChangeLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ThresholdChangeLogMapper extends BaseMapper<ThresholdChangeLog> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.water.config.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.config.entity.ThresholdConfig;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ThresholdConfigMapper extends BaseMapper<ThresholdConfig> {
|
||||
|
||||
@Select("SELECT * FROM config_threshold WHERE metric_code = #{metricCode} AND device_id IS NULL AND status = 1 AND deleted = 0 ORDER BY level")
|
||||
List<ThresholdConfig> selectGlobalByMetricCode(String metricCode);
|
||||
|
||||
@Select("SELECT * FROM config_threshold WHERE device_id = #{deviceId} AND status = 1 AND deleted = 0 ORDER BY metric_code, level")
|
||||
List<ThresholdConfig> selectByDeviceId(Long deviceId);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.water.config.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.config.entity.Announcement;
|
||||
import com.water.config.mapper.AnnouncementMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 公告通知服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AnnouncementService extends ServiceImpl<AnnouncementMapper, Announcement> {
|
||||
|
||||
/**
|
||||
* 分页查询公告
|
||||
*/
|
||||
public Page<Announcement> pageAnnouncements(int page, int size, Integer type, Integer publishStatus) {
|
||||
LambdaQueryWrapper<Announcement> qw = new LambdaQueryWrapper<>();
|
||||
if (type != null) {
|
||||
qw.eq(Announcement::getType, type);
|
||||
}
|
||||
if (publishStatus != null) {
|
||||
qw.eq(Announcement::getPublishStatus, publishStatus);
|
||||
}
|
||||
qw.orderByDesc(Announcement::getCreatedAt);
|
||||
return this.page(new Page<>(page, size), qw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建公告(草稿)
|
||||
*/
|
||||
public Announcement createAnnouncement(Announcement announcement) {
|
||||
announcement.setPublishStatus(0);
|
||||
this.save(announcement);
|
||||
return announcement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新公告(仅草稿可更新)
|
||||
*/
|
||||
public void updateAnnouncement(Long id, Announcement announcement) {
|
||||
Announcement existing = this.getById(id);
|
||||
if (existing == null) {
|
||||
throw new BusinessException("公告不存在");
|
||||
}
|
||||
if (existing.getPublishStatus() != 0) {
|
||||
throw new BusinessException("已发布的公告不可修改");
|
||||
}
|
||||
announcement.setId(id);
|
||||
this.updateById(announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布公告
|
||||
*/
|
||||
@Transactional
|
||||
public void publish(Long id) {
|
||||
Announcement announcement = this.getById(id);
|
||||
if (announcement == null) {
|
||||
throw new BusinessException("公告不存在");
|
||||
}
|
||||
if (announcement.getPublishStatus() != 0) {
|
||||
throw new BusinessException("只有草稿状态的公告可以发布");
|
||||
}
|
||||
announcement.setPublishStatus(1);
|
||||
announcement.setPublishTime(LocalDateTime.now());
|
||||
this.updateById(announcement);
|
||||
// 多渠道发布
|
||||
dispatchChannels(announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回公告
|
||||
*/
|
||||
@Transactional
|
||||
public void withdraw(Long id) {
|
||||
Announcement announcement = this.getById(id);
|
||||
if (announcement == null) {
|
||||
throw new BusinessException("公告不存在");
|
||||
}
|
||||
if (announcement.getPublishStatus() != 1) {
|
||||
throw new BusinessException("只有已发布的公告可以撤回");
|
||||
}
|
||||
announcement.setPublishStatus(2);
|
||||
announcement.setWithdrawTime(LocalDateTime.now());
|
||||
this.updateById(announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除公告
|
||||
*/
|
||||
public void deleteAnnouncement(Long id) {
|
||||
Announcement existing = this.getById(id);
|
||||
if (existing == null) {
|
||||
throw new BusinessException("公告不存在");
|
||||
}
|
||||
if (existing.getPublishStatus() == 1) {
|
||||
throw new BusinessException("已发布的公告不可删除,请先撤回");
|
||||
}
|
||||
this.removeById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多渠道分发(模拟)
|
||||
*/
|
||||
private void dispatchChannels(Announcement announcement) {
|
||||
String channels = announcement.getChannels();
|
||||
if (channels == null || channels.isEmpty()) {
|
||||
log.info("公告 {} 无渠道配置,仅站内信发布", announcement.getId());
|
||||
return;
|
||||
}
|
||||
log.info("公告 {} 发布渠道: {}", announcement.getId(), channels);
|
||||
if (channels.contains("sms")) {
|
||||
log.info("→ 短信渠道已触发");
|
||||
}
|
||||
if (channels.contains("push")) {
|
||||
log.info("→ APP推送渠道已触发");
|
||||
}
|
||||
if (channels.contains("site")) {
|
||||
log.info("→ 站内信渠道已触发");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.water.config.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.config.entity.DeviceInfo;
|
||||
import com.water.config.entity.DeviceMaintenance;
|
||||
import com.water.config.mapper.DeviceInfoMapper;
|
||||
import com.water.config.mapper.DeviceMaintenanceMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设备管理服务
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceManageService extends ServiceImpl<DeviceInfoMapper, DeviceInfo> {
|
||||
|
||||
private final DeviceMaintenanceMapper maintenanceMapper;
|
||||
|
||||
/**
|
||||
* 分页查询设备
|
||||
*/
|
||||
public Page<DeviceInfo> pageDevices(int page, int size, String deviceName, Integer category, Integer deviceStatus) {
|
||||
LambdaQueryWrapper<DeviceInfo> qw = new LambdaQueryWrapper<>();
|
||||
if (deviceName != null && !deviceName.isEmpty()) {
|
||||
qw.like(DeviceInfo::getDeviceName, deviceName);
|
||||
}
|
||||
if (category != null) {
|
||||
qw.eq(DeviceInfo::getCategory, category);
|
||||
}
|
||||
if (deviceStatus != null) {
|
||||
qw.eq(DeviceInfo::getDeviceStatus, deviceStatus);
|
||||
}
|
||||
qw.orderByDesc(DeviceInfo::getCreatedAt);
|
||||
return this.page(new Page<>(page, size), qw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备
|
||||
*/
|
||||
public DeviceInfo createDevice(DeviceInfo device) {
|
||||
// 检查设备编码唯一性
|
||||
long count = this.count(new LambdaQueryWrapper<DeviceInfo>()
|
||||
.eq(DeviceInfo::getDeviceCode, device.getDeviceCode()));
|
||||
if (count > 0) {
|
||||
throw new BusinessException("设备编码已存在");
|
||||
}
|
||||
this.save(device);
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备
|
||||
*/
|
||||
public void updateDevice(Long id, DeviceInfo device) {
|
||||
DeviceInfo existing = this.getById(id);
|
||||
if (existing == null) {
|
||||
throw new BusinessException("设备不存在");
|
||||
}
|
||||
device.setId(id);
|
||||
this.updateById(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备状态
|
||||
*/
|
||||
public void updateDeviceStatus(Long id, Integer deviceStatus) {
|
||||
DeviceInfo device = this.getById(id);
|
||||
if (device == null) {
|
||||
throw new BusinessException("设备不存在");
|
||||
}
|
||||
device.setDeviceStatus(deviceStatus);
|
||||
this.updateById(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按分类查询设备
|
||||
*/
|
||||
public List<DeviceInfo> getDevicesByCategory(Integer category) {
|
||||
return baseMapper.selectByCategory(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按状态查询设备
|
||||
*/
|
||||
public List<DeviceInfo> getDevicesByStatus(Integer status) {
|
||||
return baseMapper.selectByDeviceStatus(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加维保记录
|
||||
*/
|
||||
@Transactional
|
||||
public DeviceMaintenance addMaintenance(DeviceMaintenance maintenance) {
|
||||
DeviceInfo device = this.getById(maintenance.getDeviceId());
|
||||
if (device == null) {
|
||||
throw new BusinessException("设备不存在");
|
||||
}
|
||||
maintenanceMapper.insert(maintenance);
|
||||
// 如果维保完成,更新设备最后维护时间
|
||||
if (maintenance.getResult() != null && maintenance.getResult() == 1) {
|
||||
device.setLastMaintenanceTime(LocalDateTime.now());
|
||||
if (device.getDeviceStatus() == 3) {
|
||||
device.setDeviceStatus(1); // 维修中 -> 在线
|
||||
}
|
||||
this.updateById(device);
|
||||
}
|
||||
return maintenance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备维保历史
|
||||
*/
|
||||
public Page<DeviceMaintenance> pageMaintenances(Long deviceId, int page, int size) {
|
||||
LambdaQueryWrapper<DeviceMaintenance> qw = new LambdaQueryWrapper<>();
|
||||
if (deviceId != null) {
|
||||
qw.eq(DeviceMaintenance::getDeviceId, deviceId);
|
||||
}
|
||||
qw.orderByDesc(DeviceMaintenance::getStartTime);
|
||||
return maintenanceMapper.selectPage(new Page<>(page, size), qw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新维保记录
|
||||
*/
|
||||
public void updateMaintenance(Long id, DeviceMaintenance maintenance) {
|
||||
maintenance.setId(id);
|
||||
maintenanceMapper.updateById(maintenance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备统计(按分类)
|
||||
*/
|
||||
public List<Long> getDeviceCountByCategory() {
|
||||
// 简化版:返回各分类的设备数量
|
||||
LambdaQueryWrapper<DeviceInfo> qw = new LambdaQueryWrapper<>();
|
||||
qw.select(DeviceInfo::getCategory);
|
||||
qw.groupBy(DeviceInfo::getCategory);
|
||||
return this.listMaps(qw).stream()
|
||||
.map(m -> ((Number) m.get("category")).longValue())
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.water.config.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.water.config.entity.ThresholdChangeLog;
|
||||
import com.water.config.entity.ThresholdConfig;
|
||||
import com.water.config.mapper.ThresholdChangeLogMapper;
|
||||
import com.water.config.mapper.ThresholdConfigMapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 阈值管理服务
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ThresholdService extends ServiceImpl<ThresholdConfigMapper, ThresholdConfig> {
|
||||
|
||||
private final ThresholdChangeLogMapper changeLogMapper;
|
||||
|
||||
/**
|
||||
* 分页查询阈值配置
|
||||
*/
|
||||
public Page<ThresholdConfig> pageThresholds(int page, int size, String metricCode, Integer level) {
|
||||
LambdaQueryWrapper<ThresholdConfig> qw = new LambdaQueryWrapper<>();
|
||||
if (metricCode != null && !metricCode.isEmpty()) {
|
||||
qw.like(ThresholdConfig::getMetricCode, metricCode);
|
||||
}
|
||||
if (level != null) {
|
||||
qw.eq(ThresholdConfig::getLevel, level);
|
||||
}
|
||||
qw.orderByAsc(ThresholdConfig::getMetricCode, ThresholdConfig::getLevel);
|
||||
return this.page(new Page<>(page, size), qw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建阈值配置
|
||||
*/
|
||||
@Transactional
|
||||
public ThresholdConfig createThreshold(ThresholdConfig config) {
|
||||
validateThreshold(config);
|
||||
this.save(config);
|
||||
recordChangeLog(config, null, "新建阈值配置");
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新阈值配置(记录变更)
|
||||
*/
|
||||
@Transactional
|
||||
public void updateThreshold(Long id, ThresholdConfig config) {
|
||||
ThresholdConfig old = this.getById(id);
|
||||
if (old == null) {
|
||||
throw new BusinessException("阈值配置不存在");
|
||||
}
|
||||
config.setId(id);
|
||||
validateThreshold(config);
|
||||
this.updateById(config);
|
||||
recordChangeLog(config, old, "更新阈值配置");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除阈值配置
|
||||
*/
|
||||
@Transactional
|
||||
public void deleteThreshold(Long id) {
|
||||
ThresholdConfig old = this.getById(id);
|
||||
if (old == null) {
|
||||
throw new BusinessException("阈值配置不存在");
|
||||
}
|
||||
this.removeById(id);
|
||||
recordChangeLog(old, old, "删除阈值配置");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某指标的全局阈值(多级)
|
||||
*/
|
||||
public List<ThresholdConfig> getGlobalThresholds(String metricCode) {
|
||||
return baseMapper.selectGlobalByMetricCode(metricCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某设备的阈值配置
|
||||
*/
|
||||
public List<ThresholdConfig> getDeviceThresholds(Long deviceId) {
|
||||
return baseMapper.selectByDeviceId(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取阈值变更历史
|
||||
*/
|
||||
public Page<ThresholdChangeLog> getChangeHistory(Long thresholdId, int page, int size) {
|
||||
LambdaQueryWrapper<ThresholdChangeLog> qw = new LambdaQueryWrapper<>();
|
||||
if (thresholdId != null) {
|
||||
qw.eq(ThresholdChangeLog::getThresholdId, thresholdId);
|
||||
}
|
||||
qw.orderByDesc(ThresholdChangeLog::getCreatedAt);
|
||||
return changeLogMapper.selectPage(new Page<>(page, size), qw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用阈值
|
||||
*/
|
||||
public void toggleStatus(Long id, Integer status) {
|
||||
ThresholdConfig config = this.getById(id);
|
||||
if (config == null) {
|
||||
throw new BusinessException("阈值配置不存在");
|
||||
}
|
||||
config.setStatus(status);
|
||||
this.updateById(config);
|
||||
}
|
||||
|
||||
private void validateThreshold(ThresholdConfig config) {
|
||||
if (config.getMinValue() != null && config.getMaxValue() != null) {
|
||||
if (config.getMinValue().compareTo(config.getMaxValue()) > 0) {
|
||||
throw new BusinessException("最小值不能大于最大值");
|
||||
}
|
||||
}
|
||||
if (config.getLevel() != null && (config.getLevel() < 1 || config.getLevel() > 3)) {
|
||||
throw new BusinessException("阈值级别必须在1-3之间");
|
||||
}
|
||||
}
|
||||
|
||||
private void recordChangeLog(ThresholdConfig newConfig, ThresholdConfig oldConfig, String reason) {
|
||||
ThresholdChangeLog log = new ThresholdChangeLog();
|
||||
log.setThresholdId(newConfig.getId());
|
||||
if (oldConfig != null) {
|
||||
log.setOldMinValue(oldConfig.getMinValue());
|
||||
log.setOldMaxValue(oldConfig.getMaxValue());
|
||||
log.setOldLevel(oldConfig.getLevel());
|
||||
}
|
||||
log.setNewMinValue(newConfig.getMinValue());
|
||||
log.setNewMaxValue(newConfig.getMaxValue());
|
||||
log.setNewLevel(newConfig.getLevel());
|
||||
log.setReason(reason);
|
||||
log.setOperator("system");
|
||||
changeLogMapper.insert(log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
server:
|
||||
port: 8090
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: wm-config
|
||||
datasource:
|
||||
driver-class-name: org.postgresql.Driver
|
||||
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:water}?currentSchema=public
|
||||
username: ${DB_USER:postgres}
|
||||
password: ${DB_PASS:postgres}
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_ADDR:localhost:8848}
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
|
||||
sa-token:
|
||||
token-name: Authorization
|
||||
timeout: 86400
|
||||
active-timeout: 1800
|
||||
@@ -0,0 +1,102 @@
|
||||
-- ============================================================
|
||||
-- wm-config DDL: 阈值管理 + 信息发布 + 设备管理
|
||||
-- ============================================================
|
||||
|
||||
-- 阈值配置表
|
||||
CREATE TABLE IF NOT EXISTS config_threshold (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
metric_code VARCHAR(64) NOT NULL,
|
||||
metric_name VARCHAR(128) NOT NULL,
|
||||
device_id BIGINT,
|
||||
level SMALLINT NOT NULL DEFAULT 1, -- 1-预警 2-报警 3-紧急
|
||||
min_value NUMERIC(12,4),
|
||||
max_value NUMERIC(12,4),
|
||||
unit VARCHAR(32),
|
||||
status SMALLINT NOT NULL DEFAULT 1, -- 0-禁用 1-启用
|
||||
remark VARCHAR(500),
|
||||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE config_threshold IS '阈值配置表';
|
||||
CREATE INDEX idx_threshold_metric ON config_threshold(metric_code);
|
||||
CREATE INDEX idx_threshold_device ON config_threshold(device_id);
|
||||
|
||||
-- 阈值变更记录表
|
||||
CREATE TABLE IF NOT EXISTS config_threshold_change_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
threshold_id BIGINT NOT NULL,
|
||||
old_min_value NUMERIC(12,4),
|
||||
old_max_value NUMERIC(12,4),
|
||||
new_min_value NUMERIC(12,4),
|
||||
new_max_value NUMERIC(12,4),
|
||||
old_level SMALLINT,
|
||||
new_level SMALLINT,
|
||||
operator VARCHAR(64),
|
||||
reason VARCHAR(500),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE config_threshold_change_log IS '阈值变更记录表';
|
||||
CREATE INDEX idx_change_log_threshold ON config_threshold_change_log(threshold_id);
|
||||
|
||||
-- 公告通知表
|
||||
CREATE TABLE IF NOT EXISTS config_announcement (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
content TEXT,
|
||||
type SMALLINT NOT NULL DEFAULT 1, -- 1-系统公告 2-维护通知 3-紧急通知
|
||||
publish_status SMALLINT NOT NULL DEFAULT 0, -- 0-草稿 1-已发布 2-已撤回
|
||||
channels VARCHAR(256), -- JSON: ["sms","push","site"]
|
||||
publisher VARCHAR(64),
|
||||
publish_time TIMESTAMP,
|
||||
withdraw_time TIMESTAMP,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE config_announcement IS '公告通知表';
|
||||
CREATE INDEX idx_announcement_status ON config_announcement(publish_status);
|
||||
|
||||
-- 设备台账表
|
||||
CREATE TABLE IF NOT EXISTS config_device_info (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
device_code VARCHAR(64) NOT NULL UNIQUE,
|
||||
device_name VARCHAR(128) NOT NULL,
|
||||
category SMALLINT NOT NULL DEFAULT 9, -- 1-水表 2-压力传感器 3-流量计 4-水质监测仪 5-阀门 9-其他
|
||||
brand VARCHAR(64),
|
||||
model VARCHAR(64),
|
||||
location VARCHAR(256),
|
||||
longitude DOUBLE PRECISION,
|
||||
latitude DOUBLE PRECISION,
|
||||
device_status SMALLINT NOT NULL DEFAULT 0, -- 0-离线 1-在线 2-故障 3-维修中
|
||||
install_date TIMESTAMP,
|
||||
last_maintenance_time TIMESTAMP,
|
||||
responsible_person VARCHAR(64),
|
||||
remark VARCHAR(500),
|
||||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE config_device_info IS '设备台账表';
|
||||
CREATE INDEX idx_device_code ON config_device_info(device_code);
|
||||
CREATE INDEX idx_device_category ON config_device_info(category);
|
||||
CREATE INDEX idx_device_status ON config_device_info(device_status);
|
||||
|
||||
-- 设备维保记录表
|
||||
CREATE TABLE IF NOT EXISTS config_device_maintenance (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
device_id BIGINT NOT NULL,
|
||||
maintenance_type SMALLINT NOT NULL DEFAULT 1, -- 1-日常巡检 2-定期保养 3-故障维修 4-更换配件
|
||||
description TEXT,
|
||||
operator VARCHAR(64),
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
result SMALLINT NOT NULL DEFAULT 0, -- 0-未完成 1-已完成 2-需要返修
|
||||
cost DOUBLE PRECISION,
|
||||
attachments TEXT, -- JSON
|
||||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE config_device_maintenance IS '设备维保记录表';
|
||||
CREATE INDEX idx_maintenance_device ON config_device_maintenance(device_id);
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.water.config;
|
||||
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.config.entity.Announcement;
|
||||
import com.water.config.mapper.AnnouncementMapper;
|
||||
import com.water.config.service.AnnouncementService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AnnouncementServiceTest {
|
||||
|
||||
@Mock
|
||||
private AnnouncementMapper announcementMapper;
|
||||
@InjectMocks
|
||||
private AnnouncementService announcementService;
|
||||
|
||||
private Announcement draft;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
draft = new Announcement();
|
||||
draft.setTitle("系统维护通知");
|
||||
draft.setContent("今晚22:00-次日06:00系统维护");
|
||||
draft.setType(2);
|
||||
draft.setChannels("[\"site\",\"sms\"]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAnnouncement_setsDraftStatus() {
|
||||
when(announcementMapper.insert(any())).thenReturn(1);
|
||||
|
||||
Announcement result = announcementService.createAnnouncement(draft);
|
||||
|
||||
assertEquals(0, result.getPublishStatus());
|
||||
verify(announcementMapper).insert(any(Announcement.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publish_draft_success() {
|
||||
Announcement existing = new Announcement();
|
||||
existing.setId(1L);
|
||||
existing.setPublishStatus(0);
|
||||
existing.setChannels("[\"site\"]");
|
||||
|
||||
when(announcementMapper.selectById(1L)).thenReturn(existing);
|
||||
when(announcementMapper.updateById(any())).thenReturn(1);
|
||||
|
||||
assertDoesNotThrow(() -> announcementService.publish(1L));
|
||||
verify(announcementMapper).updateById(argThat(a ->
|
||||
a.getPublishStatus() == 1 && a.getPublishTime() != null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publish_alreadyPublished_throws() {
|
||||
Announcement existing = new Announcement();
|
||||
existing.setId(1L);
|
||||
existing.setPublishStatus(1);
|
||||
|
||||
when(announcementMapper.selectById(1L)).thenReturn(existing);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> announcementService.publish(1L));
|
||||
assertEquals("只有草稿状态的公告可以发布", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withdraw_notPublished_throws() {
|
||||
Announcement existing = new Announcement();
|
||||
existing.setId(1L);
|
||||
existing.setPublishStatus(0);
|
||||
|
||||
when(announcementMapper.selectById(1L)).thenReturn(existing);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> announcementService.withdraw(1L));
|
||||
assertEquals("只有已发布的公告可以撤回", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void delete_published_throws() {
|
||||
Announcement existing = new Announcement();
|
||||
existing.setId(1L);
|
||||
existing.setPublishStatus(1);
|
||||
|
||||
when(announcementMapper.selectById(1L)).thenReturn(existing);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> announcementService.deleteAnnouncement(1L));
|
||||
assertEquals("已发布的公告不可删除,请先撤回", ex.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.water.config;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.config.entity.DeviceInfo;
|
||||
import com.water.config.entity.DeviceMaintenance;
|
||||
import com.water.config.mapper.DeviceInfoMapper;
|
||||
import com.water.config.mapper.DeviceMaintenanceMapper;
|
||||
import com.water.config.service.DeviceManageService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DeviceManageServiceTest {
|
||||
|
||||
@Mock
|
||||
private DeviceInfoMapper deviceInfoMapper;
|
||||
@Mock
|
||||
private DeviceMaintenanceMapper maintenanceMapper;
|
||||
@InjectMocks
|
||||
private DeviceManageService deviceManageService;
|
||||
|
||||
private DeviceInfo device;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
device = new DeviceInfo();
|
||||
device.setDeviceCode("WM-001");
|
||||
device.setDeviceName("1号水表");
|
||||
device.setCategory(1);
|
||||
device.setBrand("海天");
|
||||
device.setModel("HT-200");
|
||||
device.setDeviceStatus(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDevice_success() {
|
||||
when(deviceInfoMapper.selectCount(any())).thenReturn(0L);
|
||||
when(deviceInfoMapper.insert(any())).thenReturn(1);
|
||||
|
||||
DeviceInfo result = deviceManageService.createDevice(device);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals("WM-001", result.getDeviceCode());
|
||||
verify(deviceInfoMapper).insert(any(DeviceInfo.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDevice_duplicateCode_throws() {
|
||||
when(deviceInfoMapper.selectCount(any())).thenReturn(1L);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> deviceManageService.createDevice(device));
|
||||
assertEquals("设备编码已存在", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateDevice_notFound_throws() {
|
||||
when(deviceInfoMapper.selectById(999L)).thenReturn(null);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> deviceManageService.updateDevice(999L, device));
|
||||
assertEquals("设备不存在", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addMaintenance_deviceNotFound_throws() {
|
||||
DeviceMaintenance maintenance = new DeviceMaintenance();
|
||||
maintenance.setDeviceId(999L);
|
||||
|
||||
when(deviceInfoMapper.selectById(999L)).thenReturn(null);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> deviceManageService.addMaintenance(maintenance));
|
||||
assertEquals("设备不存在", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addMaintenance_completed_updatesDeviceTime() {
|
||||
DeviceInfo existingDevice = new DeviceInfo();
|
||||
existingDevice.setId(1L);
|
||||
existingDevice.setDeviceStatus(3); // 维修中
|
||||
|
||||
DeviceMaintenance maintenance = new DeviceMaintenance();
|
||||
maintenance.setDeviceId(1L);
|
||||
maintenance.setResult(1); // 已完成
|
||||
maintenance.setDescription("更换电池");
|
||||
|
||||
when(deviceInfoMapper.selectById(1L)).thenReturn(existingDevice);
|
||||
when(maintenanceMapper.insert(any())).thenReturn(1);
|
||||
when(deviceInfoMapper.updateById(any())).thenReturn(1);
|
||||
|
||||
DeviceMaintenance result = deviceManageService.addMaintenance(maintenance);
|
||||
|
||||
assertNotNull(result);
|
||||
verify(deviceInfoMapper).updateById(argThat(d ->
|
||||
d.getLastMaintenanceTime() != null && d.getDeviceStatus() == 1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.water.config;
|
||||
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.config.entity.ThresholdChangeLog;
|
||||
import com.water.config.entity.ThresholdConfig;
|
||||
import com.water.config.mapper.ThresholdChangeLogMapper;
|
||||
import com.water.config.mapper.ThresholdConfigMapper;
|
||||
import com.water.config.service.ThresholdService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ThresholdServiceTest {
|
||||
|
||||
@Mock
|
||||
private ThresholdConfigMapper thresholdConfigMapper;
|
||||
@Mock
|
||||
private ThresholdChangeLogMapper changeLogMapper;
|
||||
@InjectMocks
|
||||
private ThresholdService thresholdService;
|
||||
|
||||
private ThresholdConfig validConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
validConfig = new ThresholdConfig();
|
||||
validConfig.setMetricCode("water_pressure");
|
||||
validConfig.setMetricName("水压");
|
||||
validConfig.setLevel(1);
|
||||
validConfig.setMinValue(new BigDecimal("0.1"));
|
||||
validConfig.setMaxValue(new BigDecimal("0.8"));
|
||||
validConfig.setUnit("MPa");
|
||||
validConfig.setStatus(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createThreshold_success() {
|
||||
when(thresholdConfigMapper.insert(any())).thenReturn(1);
|
||||
when(changeLogMapper.insert(any())).thenReturn(1);
|
||||
|
||||
ThresholdConfig result = thresholdService.createThreshold(validConfig);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals("water_pressure", result.getMetricCode());
|
||||
assertEquals(1, result.getLevel());
|
||||
verify(thresholdConfigMapper).insert(any(ThresholdConfig.class));
|
||||
verify(changeLogMapper).insert(any(ThresholdChangeLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createThreshold_minGreaterThanMax_throws() {
|
||||
validConfig.setMinValue(new BigDecimal("1.0"));
|
||||
validConfig.setMaxValue(new BigDecimal("0.5"));
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> thresholdService.createThreshold(validConfig));
|
||||
assertEquals("最小值不能大于最大值", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createThreshold_invalidLevel_throws() {
|
||||
validConfig.setLevel(5);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> thresholdService.createThreshold(validConfig));
|
||||
assertEquals("阈值级别必须在1-3之间", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateThreshold_notFound_throws() {
|
||||
when(thresholdConfigMapper.selectById(999L)).thenReturn(null);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> thresholdService.updateThreshold(999L, validConfig));
|
||||
assertEquals("阈值配置不存在", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteThreshold_notFound_throws() {
|
||||
when(thresholdConfigMapper.selectById(999L)).thenReturn(null);
|
||||
|
||||
BusinessException ex = assertThrows(BusinessException.class,
|
||||
() -> thresholdService.deleteThreshold(999L));
|
||||
assertEquals("阈值配置不存在", ex.getMessage());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user