SOS
This commit is contained in:
@@ -5,6 +5,7 @@ import org.dromara.sis.sdk.hik.service.SdkBaseServer;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 物业模块
|
||||
@@ -12,6 +13,7 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableDubbo
|
||||
@EnableScheduling
|
||||
@SpringBootApplication
|
||||
public class SisApplication {
|
||||
public static void main(String[] args) {
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package org.dromara.sis.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* 应用配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class SOSAppConfig {
|
||||
|
||||
/**
|
||||
* 配置 RestTemplate 用于 API 调用
|
||||
* @return RestTemplate 实例
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
package org.dromara.sis.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 报警记录实体类,对应数据库表 alarm_record
|
||||
* 存储系统中的报警事件信息,包括设备信息、时间信息、处理状态等
|
||||
*/
|
||||
@Data
|
||||
@TableName("alarm_record")
|
||||
public class AlarmRecord implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 报警记录唯一标识,对应 API 返回的 Id 字段
|
||||
* 采用 INPUT 策略,使用 API 返回的实际 ID 值
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务编码,用于标识特定的报警任务
|
||||
*/
|
||||
private String taskCode;
|
||||
|
||||
/**
|
||||
* 设备ID,关联具体的报警设备
|
||||
*/
|
||||
private Integer deviceId;
|
||||
|
||||
/**
|
||||
* 设备名称,如 "7楼办公室"
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 设备许可证ID,用于唯一标识设备
|
||||
*/
|
||||
private String deviceLicenseId;
|
||||
|
||||
/**
|
||||
* 设备SIP号码,用于通信
|
||||
*/
|
||||
private String deviceSipNum;
|
||||
|
||||
/**
|
||||
* 设备所在经度
|
||||
*/
|
||||
private Double deviceLng;
|
||||
|
||||
/**
|
||||
* 设备所在纬度
|
||||
*/
|
||||
private Double deviceLat;
|
||||
|
||||
/**
|
||||
* 会议ID,关联报警处理过程中的会议
|
||||
*/
|
||||
private Long conferenceId;
|
||||
|
||||
/**
|
||||
* 会议SIP编码
|
||||
*/
|
||||
private String confSipCode;
|
||||
|
||||
/**
|
||||
* 报警状态,如 "finished"(已完成)、"noAnswer"(未接听)等
|
||||
*/
|
||||
private String state;
|
||||
|
||||
/**
|
||||
* 报警开始时间(Java Date 类型)
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 报警开始时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long startTimeUnix;
|
||||
|
||||
/**
|
||||
* 报警结束时间(Java Date 类型)
|
||||
*/
|
||||
private Date finishTime;
|
||||
|
||||
/**
|
||||
* 报警结束时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long finishTimeUnix;
|
||||
|
||||
/**
|
||||
* 响铃开始时间(Java Date 类型)
|
||||
*/
|
||||
private Date ringingTime;
|
||||
|
||||
/**
|
||||
* 响铃开始时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long ringingTimeUnix;
|
||||
|
||||
/**
|
||||
* 过期时间(Java Date 类型)
|
||||
*/
|
||||
private Date expireTime;
|
||||
|
||||
/**
|
||||
* 过期时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long expireTimeUnix;
|
||||
|
||||
/**
|
||||
* 呼叫过期时间(Java Date 类型)
|
||||
*/
|
||||
private Date callExpireTime;
|
||||
|
||||
/**
|
||||
* 呼叫过期时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long callExpireTimeUnix;
|
||||
|
||||
/**
|
||||
* 呼叫开始时间(Java Date 类型)
|
||||
*/
|
||||
private Date callTime;
|
||||
|
||||
/**
|
||||
* 呼叫开始时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long callTimeUnix;
|
||||
|
||||
/**
|
||||
* 设备是否带有摄像头(0-不带,1-带)
|
||||
*/
|
||||
private Integer deviceWithCamera;
|
||||
|
||||
/**
|
||||
* 公司编码,标识所属公司
|
||||
*/
|
||||
private String companyCode;
|
||||
|
||||
/**
|
||||
* 报警类型,如 "button"(按钮报警)
|
||||
*/
|
||||
private String alarmType;
|
||||
|
||||
/**
|
||||
* 业务类型,如 "normal"(正常业务)
|
||||
*/
|
||||
private String businessType;
|
||||
|
||||
/**
|
||||
* 分组ID,用于对设备进行分组管理
|
||||
*/
|
||||
private Integer groupId;
|
||||
|
||||
/**
|
||||
* 报告通知级别
|
||||
*/
|
||||
private Integer reportNotifyLevel;
|
||||
|
||||
/**
|
||||
* 是否挂起(0-未挂起,1-挂起)
|
||||
*/
|
||||
private Integer isHold;
|
||||
|
||||
/**
|
||||
* 显示的报警类型(可能为空)
|
||||
*/
|
||||
private String displayAlarmType;
|
||||
|
||||
/**
|
||||
* 接收类型(可能为空)
|
||||
*/
|
||||
private String acceptType;
|
||||
|
||||
/**
|
||||
* 分组名称(可能为空)
|
||||
*/
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 设备联系人(可能为空)
|
||||
*/
|
||||
private String deviceLinkman;
|
||||
|
||||
/**
|
||||
* 设备联系电话(可能为空)
|
||||
*/
|
||||
private String devicePhoneNum;
|
||||
|
||||
/**
|
||||
* 记录创建时间,由数据库自动填充
|
||||
* 使用 MyBatis-Plus 的自动填充功能,插入时自动设置为当前时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
package org.dromara.sis.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 报警任务操作记录实体类,对应数据库表 alarm_record_task_operator
|
||||
* 记录每个报警任务的操作人信息和处理时间
|
||||
*/
|
||||
@Data
|
||||
@TableName("alarm_record_task_operator")
|
||||
public class AlarmTaskOperator implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 关联的报警记录ID(外键)
|
||||
*/
|
||||
private Long alarmRecordId;
|
||||
|
||||
/**
|
||||
* 操作记录ID(来自API返回)
|
||||
*/
|
||||
private Integer operatorId;
|
||||
|
||||
/**
|
||||
* 关联的报警ID
|
||||
*/
|
||||
private Long alarmId;
|
||||
|
||||
/**
|
||||
* 任务编码
|
||||
*/
|
||||
private String taskCode;
|
||||
|
||||
/**
|
||||
* 操作人ID
|
||||
*/
|
||||
private Integer userId;
|
||||
|
||||
/**
|
||||
* 操作人类型(1-普通用户,2-管理员等,具体根据业务定义)
|
||||
*/
|
||||
private Integer userType;
|
||||
|
||||
/**
|
||||
* 操作人昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 操作人头像URL
|
||||
*/
|
||||
private String avatarUrl;
|
||||
|
||||
/**
|
||||
* 操作人SIP号码
|
||||
*/
|
||||
private String sipNum;
|
||||
|
||||
/**
|
||||
* 接听时间(Java Date 类型)
|
||||
*/
|
||||
private Date answerTime;
|
||||
|
||||
/**
|
||||
* 接听时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long answerTimeUnix;
|
||||
|
||||
/**
|
||||
* 处理完成时间(Java Date 类型)
|
||||
*/
|
||||
private Date finishTime;
|
||||
|
||||
/**
|
||||
* 处理完成时间的 Unix 时间戳(毫秒)
|
||||
*/
|
||||
private Long finishTimeUnix;
|
||||
|
||||
/**
|
||||
* 转接级别(如 "levelOne"、"levelTwo" 等)
|
||||
*/
|
||||
private String transferLevel;
|
||||
|
||||
/**
|
||||
* 记录创建时间,由数据库自动填充
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package org.dromara.sis.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import org.dromara.sis.domain.AlarmRecord;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 报警记录 Mapper 接口
|
||||
*/
|
||||
@Repository
|
||||
public interface AlarmRecordMapper extends BaseMapper<AlarmRecord> {
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package org.dromara.sis.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import org.dromara.sis.domain.AlarmTaskOperator;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 报警任务操作记录 Mapper 接口
|
||||
*/
|
||||
@Repository
|
||||
public interface AlarmTaskOperatorMapper extends BaseMapper<AlarmTaskOperator> {
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package org.dromara.sis.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.dromara.sis.domain.AlarmRecord;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 报警记录服务接口
|
||||
*/
|
||||
public interface AlarmRecordService extends IService<AlarmRecord> {
|
||||
|
||||
/**
|
||||
* 从 API 返回的 Map 数据转换为实体对象
|
||||
* @param map API 返回的单个报警记录数据
|
||||
* @return 转换后的实体对象
|
||||
*/
|
||||
AlarmRecord convertFromMap(Map<String, Object> map);
|
||||
|
||||
/**
|
||||
* 保存新的报警记录,并关联保存其操作记录
|
||||
* @param records 报警记录列表
|
||||
* @param operatorMap 操作记录映射,键为报警记录ID,值为对应操作记录列表
|
||||
* @return 成功保存的记录数
|
||||
*/
|
||||
int saveNewRecords(List<AlarmRecord> records, Map<Long, List<Map<String, Object>>> operatorMap);
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package org.dromara.sis.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.dromara.sis.domain.AlarmTaskOperator;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 报警任务操作记录服务接口
|
||||
*/
|
||||
public interface AlarmTaskOperatorService extends IService<AlarmTaskOperator> {
|
||||
|
||||
/**
|
||||
* 从 API 返回的 Map 数据转换为实体列表
|
||||
* @param alarmRecordId 关联的报警记录ID
|
||||
* @param operatorMaps API 返回的操作人数据列表
|
||||
* @return 转换后的实体列表
|
||||
*/
|
||||
List<AlarmTaskOperator> convertFromMaps(Long alarmRecordId, List<Map<String, Object>> operatorMaps);
|
||||
|
||||
/**
|
||||
* 批量保存操作记录,自动过滤已存在的记录
|
||||
* @param operators 操作记录列表
|
||||
* @return 成功保存的记录数
|
||||
*/
|
||||
int saveNewOperators(List<AlarmTaskOperator> operators);
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package org.dromara.sis.service;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API服务接口,定义与外部API通信的方法
|
||||
*/
|
||||
public interface ApiService {
|
||||
|
||||
/**
|
||||
* 获取报警记录列表
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return API返回的结果数据,包含状态码和记录列表
|
||||
*/
|
||||
Map<String, Object> fetchAlarmRecords(int pageNum, int pageSize);
|
||||
}
|
@@ -0,0 +1,166 @@
|
||||
package org.dromara.sis.service.impl;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.domain.AlarmRecord;
|
||||
import org.dromara.sis.domain.AlarmTaskOperator;
|
||||
import org.dromara.sis.mapper.AlarmRecordMapper;
|
||||
import org.dromara.sis.service.AlarmRecordService;
|
||||
import org.dromara.sis.service.AlarmTaskOperatorService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 报警记录服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlarmRecordServiceImpl extends ServiceImpl<AlarmRecordMapper, AlarmRecord> implements AlarmRecordService {
|
||||
|
||||
@Autowired
|
||||
private AlarmTaskOperatorService taskOperatorService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int saveNewRecords(List<AlarmRecord> records, Map<Long, List<Map<String, Object>>> operatorMap) {
|
||||
if (records == null || records.isEmpty()) return 0;
|
||||
|
||||
// 提取待插入记录的ID列表
|
||||
List<Long> ids = records.stream().map(AlarmRecord::getId).collect(Collectors.toList());
|
||||
|
||||
// 查询数据库中已存在的记录
|
||||
LambdaQueryWrapper<AlarmRecord> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(AlarmRecord::getId, ids);
|
||||
List<AlarmRecord> existingRecords = this.list(queryWrapper);
|
||||
List<Long> existingIds = existingRecords.stream().map(AlarmRecord::getId).collect(Collectors.toList());
|
||||
|
||||
// 过滤出新增记录
|
||||
List<AlarmRecord> newRecords = records.stream()
|
||||
.filter(record -> !existingIds.contains(record.getId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 批量插入新记录
|
||||
int savedCount = 0;
|
||||
if (!newRecords.isEmpty()) {
|
||||
if (this.saveBatch(newRecords)) {
|
||||
savedCount = newRecords.size();
|
||||
|
||||
// 保存关联的操作记录
|
||||
saveRelatedOperators(newRecords, operatorMap);
|
||||
}
|
||||
}
|
||||
return savedCount;
|
||||
}
|
||||
|
||||
private void saveRelatedOperators(List<AlarmRecord> newRecords, Map<Long, List<Map<String, Object>>> operatorMap) {
|
||||
if (operatorMap == null || operatorMap.isEmpty()) return;
|
||||
|
||||
// 收集需要保存的操作记录
|
||||
List<AlarmTaskOperator> allOperators = new ArrayList<>();
|
||||
for (AlarmRecord record : newRecords) {
|
||||
Long recordId = record.getId();
|
||||
if (operatorMap.containsKey(recordId)) {
|
||||
List<Map<String, Object>> operatorsData = operatorMap.get(recordId);
|
||||
List<AlarmTaskOperator> operators = taskOperatorService.convertFromMaps(recordId, operatorsData);
|
||||
allOperators.addAll(operators);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量保存操作记录
|
||||
if (!allOperators.isEmpty()) {
|
||||
taskOperatorService.saveNewOperators(allOperators);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlarmRecord convertFromMap(Map<String, Object> map) {
|
||||
if (map == null) return null;
|
||||
|
||||
AlarmRecord record = new AlarmRecord();
|
||||
record.setId(getLongValue(map, "Id"));
|
||||
record.setTaskCode((String) map.get("TaskCode"));
|
||||
record.setDeviceId(getIntValue(map, "DeviceId"));
|
||||
record.setDeviceName((String) map.get("DeviceName"));
|
||||
record.setDeviceLicenseId((String) map.get("DeviceLicenseId"));
|
||||
record.setDeviceSipNum((String) map.get("DeviceSipNum"));
|
||||
record.setDeviceLng(getDoubleValue(map, "DeviceLng"));
|
||||
record.setDeviceLat(getDoubleValue(map, "DeviceLat"));
|
||||
record.setConferenceId(getLongValue(map, "ConferenceId"));
|
||||
record.setConfSipCode((String) map.get("ConfSipCode"));
|
||||
record.setState((String) map.get("State"));
|
||||
|
||||
// 处理时间字段
|
||||
record.setStartTime(parseDate(map, "StartTime"));
|
||||
record.setStartTimeUnix(getLongValue(map, "StartTimeUnix"));
|
||||
record.setFinishTime(parseDate(map, "FinishTime"));
|
||||
record.setFinishTimeUnix(getLongValue(map, "FinishTimeUnix"));
|
||||
record.setRingingTime(parseDate(map, "RingingTime"));
|
||||
record.setRingingTimeUnix(getLongValue(map, "RingingTimeUnix"));
|
||||
record.setExpireTime(parseDate(map, "ExpireTime"));
|
||||
record.setExpireTimeUnix(getLongValue(map, "ExpireTimeUnix"));
|
||||
record.setCallExpireTime(parseDate(map, "CallExpireTime"));
|
||||
record.setCallExpireTimeUnix(getLongValue(map, "CallExpireTimeUnix"));
|
||||
record.setCallTime(parseDate(map, "CallTime"));
|
||||
record.setCallTimeUnix(getLongValue(map, "CallTimeUnix"));
|
||||
|
||||
record.setDeviceWithCamera(getIntValue(map, "DeviceWithCamera"));
|
||||
record.setCompanyCode((String) map.get("CompanyCode"));
|
||||
record.setAlarmType((String) map.get("AlarmType"));
|
||||
record.setBusinessType((String) map.get("BusinessType"));
|
||||
record.setGroupId(getIntValue(map, "GroupId"));
|
||||
record.setReportNotifyLevel(getIntValue(map, "ReportNotifyLevel"));
|
||||
record.setIsHold(getIntValue(map, "IsHold"));
|
||||
record.setDisplayAlarmType((String) map.get("DisplayAlarmType"));
|
||||
record.setAcceptType((String) map.get("AcceptType"));
|
||||
record.setGroupName((String) map.get("GroupName"));
|
||||
record.setDeviceLinkman((String) map.get("DeviceLinkman"));
|
||||
record.setDevicePhoneNum((String) map.get("DevicePhoneNum"));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
// 类型转换辅助方法
|
||||
private Long getLongValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Integer getIntValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Double getDoubleValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private java.util.Date parseDate(Map<String, Object> map, String key) {
|
||||
try {
|
||||
String dateStr = (String) map.get(key);
|
||||
if (dateStr != null && !dateStr.isEmpty() && !"1970-01-01 00:00:00".equals(dateStr)) {
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.parse(dateStr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("日期转换失败: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
package org.dromara.sis.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.domain.AlarmTaskOperator;
|
||||
import org.dromara.sis.mapper.AlarmTaskOperatorMapper;
|
||||
import org.dromara.sis.service.AlarmTaskOperatorService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 报警任务操作记录服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlarmTaskOperatorServiceImpl extends ServiceImpl<AlarmTaskOperatorMapper, AlarmTaskOperator> implements AlarmTaskOperatorService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int saveNewOperators(List<AlarmTaskOperator> operators) {
|
||||
if (operators == null || operators.isEmpty()) return 0;
|
||||
|
||||
// 生成唯一键集合 (alarmRecordId_operatorId)
|
||||
List<String> uniqueKeys = operators.stream()
|
||||
.map(op -> op.getAlarmRecordId() + "_" + op.getOperatorId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询已存在的记录(修正后的代码)
|
||||
LambdaQueryWrapper<AlarmTaskOperator> queryWrapper = new LambdaQueryWrapper<>();
|
||||
for (String uniqueKey : uniqueKeys) {
|
||||
String[] parts = uniqueKey.split("_");
|
||||
if (parts.length == 2) {
|
||||
Long alarmRecordId = Long.parseLong(parts[0]);
|
||||
Integer operatorId = Integer.parseInt(parts[1]);
|
||||
|
||||
if (!queryWrapper.isEmptyOfWhere()) {
|
||||
queryWrapper.or();
|
||||
}
|
||||
queryWrapper.nested(wrapper ->
|
||||
wrapper.eq(AlarmTaskOperator::getAlarmRecordId, alarmRecordId)
|
||||
.eq(AlarmTaskOperator::getOperatorId, operatorId)
|
||||
);
|
||||
}
|
||||
}
|
||||
List<AlarmTaskOperator> existingRecords = this.list(queryWrapper);
|
||||
|
||||
// 生成已存在的唯一键集合
|
||||
List<String> existingKeys = existingRecords.stream()
|
||||
.map(op -> op.getAlarmRecordId() + "_" + op.getOperatorId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 过滤出新记录
|
||||
List<AlarmTaskOperator> newRecords = operators.stream()
|
||||
.filter(op -> !existingKeys.contains(op.getAlarmRecordId() + "_" + op.getOperatorId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 批量插入新记录
|
||||
return newRecords.isEmpty() ? 0 : this.saveBatch(newRecords) ? newRecords.size() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlarmTaskOperator> convertFromMaps(Long alarmRecordId, List<Map<String, Object>> operatorMaps) {
|
||||
List<AlarmTaskOperator> result = new ArrayList<>();
|
||||
if (operatorMaps == null || operatorMaps.isEmpty()) return result;
|
||||
|
||||
for (Map<String, Object> map : operatorMaps) {
|
||||
AlarmTaskOperator operator = new AlarmTaskOperator();
|
||||
operator.setAlarmRecordId(alarmRecordId);
|
||||
operator.setOperatorId(getIntValue(map, "Id"));
|
||||
operator.setAlarmId(getLongValue(map, "AlarmId"));
|
||||
operator.setTaskCode((String) map.get("TaskCode"));
|
||||
operator.setUserId(getIntValue(map, "UserId"));
|
||||
operator.setUserType(getIntValue(map, "UserType"));
|
||||
operator.setNickName((String) map.get("NickName"));
|
||||
operator.setAvatarUrl((String) map.get("AvatarUrl"));
|
||||
operator.setSipNum((String) map.get("SipNum"));
|
||||
operator.setAnswerTime(parseDate(map, "AnswerTime"));
|
||||
operator.setAnswerTimeUnix(getLongValue(map, "AnswerTimeUnix"));
|
||||
operator.setFinishTime(parseDate(map, "FinishTime"));
|
||||
operator.setFinishTimeUnix(getLongValue(map, "FinishTimeUnix"));
|
||||
operator.setTransferLevel((String) map.get("TransferLevel"));
|
||||
result.add(operator);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 类型转换辅助方法
|
||||
private Long getLongValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Integer getIntValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private java.util.Date parseDate(Map<String, Object> map, String key) {
|
||||
try {
|
||||
String dateStr = (String) map.get(key);
|
||||
if (dateStr != null && !dateStr.isEmpty() && !"1970-01-01 00:00:00".equals(dateStr)) {
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.parse(dateStr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("日期转换失败: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package org.dromara.sis.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.service.ApiService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API 服务实现类
|
||||
* 负责与外部 API 进行通信
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ApiServiceImpl implements ApiService {
|
||||
|
||||
@Value("${api.url}")
|
||||
private String apiUrl;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public ApiServiceImpl(RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> fetchAlarmRecords(int pageNum, int pageSize) {
|
||||
try {
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("PageSize", pageSize);
|
||||
requestBody.put("PageNum", pageNum);
|
||||
|
||||
List<List<Map<String, Object>>> filters = List.of(
|
||||
List.of(Map.of("Key", "FinishTime", "Type", "str", "Value", "2025-07-27"))
|
||||
);
|
||||
requestBody.put("Filters", filters);
|
||||
|
||||
requestBody.put("OrderKey", "StartTime");
|
||||
requestBody.put("Desc", "0");
|
||||
requestBody.put("OrFirst", true);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
apiUrl,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
// 处理响应
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
Map<String, Object> result = response.getBody();
|
||||
if (result != null && result.get("Status").equals(0)) {
|
||||
return result;
|
||||
} else {
|
||||
log.error("API 返回错误状态: {}", result);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
log.error("API 请求失败,状态码: {}", response.getStatusCode());
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("调用 API 异常: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
package org.dromara.sis.task;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.domain.AlarmRecord;
|
||||
import org.dromara.sis.service.AlarmRecordService;
|
||||
import org.dromara.sis.service.ApiService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 数据同步定时任务
|
||||
* 每分钟执行一次,从 API 获取最新报警记录并同步到数据库
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DataSyncTask {
|
||||
|
||||
private final ApiService apiService;
|
||||
private final AlarmRecordService alarmRecordService;
|
||||
|
||||
@Value("${sync.pageSize:100}")
|
||||
private int pageSize;
|
||||
|
||||
public DataSyncTask(ApiService apiService, AlarmRecordService alarmRecordService) {
|
||||
this.apiService = apiService;
|
||||
this.alarmRecordService = alarmRecordService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时同步报警记录数据
|
||||
*/
|
||||
@Scheduled(fixedRate = 60 * 1000) // 每分钟执行一次
|
||||
public void syncAlarmRecords() {
|
||||
log.info("开始同步报警记录数据...");
|
||||
|
||||
int pageNum = 1;
|
||||
boolean hasMoreData = true;
|
||||
int totalSynced = 0;
|
||||
|
||||
try {
|
||||
while (hasMoreData) {
|
||||
// 调用 API 获取数据
|
||||
Map<String, Object> apiResult = apiService.fetchAlarmRecords(pageNum, pageSize);
|
||||
|
||||
if (apiResult == null) {
|
||||
log.error("API 返回空结果,停止同步");
|
||||
break;
|
||||
}
|
||||
|
||||
// 解析数据
|
||||
int totalNum = (int) apiResult.getOrDefault("TotalNum", 0);
|
||||
List<Map<String, Object>> recordList = (List<Map<String, Object>>) apiResult.get("RecordList");
|
||||
|
||||
if (recordList == null || recordList.isEmpty()) {
|
||||
log.info("没有更多数据可同步");
|
||||
hasMoreData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// 转换报警记录
|
||||
List<AlarmRecord> records = recordList.stream()
|
||||
.map(alarmRecordService::convertFromMap)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 提取操作记录数据
|
||||
Map<Long, List<Map<String, Object>>> operatorMap = new HashMap<>();
|
||||
for (Map<String, Object> recordMap : recordList) {
|
||||
Long recordId = (Long) recordMap.get("Id");
|
||||
List<Map<String, Object>> operators = (List<Map<String, Object>>) recordMap.get("TaskOperators");
|
||||
if (operators != null && !operators.isEmpty()) {
|
||||
operatorMap.put(recordId, operators);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存数据(包含操作记录)
|
||||
int savedCount = alarmRecordService.saveNewRecords(records, operatorMap);
|
||||
totalSynced += savedCount;
|
||||
|
||||
log.info("第 {} 页同步完成,共 {} 条记录,新增 {} 条", pageNum, recordList.size(), savedCount);
|
||||
|
||||
// 判断是否还有更多数据
|
||||
if (pageNum * pageSize >= totalNum) {
|
||||
hasMoreData = false;
|
||||
} else {
|
||||
pageNum++;
|
||||
}
|
||||
}
|
||||
|
||||
log.info("报警记录数据同步完成,共新增 {} 条记录", totalSynced);
|
||||
} catch (Exception e) {
|
||||
log.error("同步报警记录数据失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,6 +10,13 @@ spring:
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: @profiles.active@
|
||||
# API 配置
|
||||
api:
|
||||
url: https://norsos.lionking110.com/sos/v1/mntn/business/appId/alarm/list
|
||||
|
||||
# 同步配置
|
||||
sync:
|
||||
pageSize: 100
|
||||
|
||||
--- # nacos 配置
|
||||
spring:
|
||||
|
Reference in New Issue
Block a user