diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/SisApplication.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/SisApplication.java index 9d5806fa..3235c677 100644 --- a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/SisApplication.java +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/SisApplication.java @@ -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) { diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java new file mode 100644 index 00000000..94788ba2 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java @@ -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(); + } +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/domain/AlarmRecord.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/domain/AlarmRecord.java new file mode 100644 index 00000000..6cb61678 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/domain/AlarmRecord.java @@ -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; +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/domain/AlarmTaskOperator.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/domain/AlarmTaskOperator.java new file mode 100644 index 00000000..388b5cd6 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/domain/AlarmTaskOperator.java @@ -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; +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/mapper/AlarmRecordMapper.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/mapper/AlarmRecordMapper.java new file mode 100644 index 00000000..72aa362d --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/mapper/AlarmRecordMapper.java @@ -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 { +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/mapper/AlarmTaskOperatorMapper.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/mapper/AlarmTaskOperatorMapper.java new file mode 100644 index 00000000..71758569 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/mapper/AlarmTaskOperatorMapper.java @@ -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 { +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/AlarmRecordService.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/AlarmRecordService.java new file mode 100644 index 00000000..dc650a55 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/AlarmRecordService.java @@ -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 { + + /** + * 从 API 返回的 Map 数据转换为实体对象 + * @param map API 返回的单个报警记录数据 + * @return 转换后的实体对象 + */ + AlarmRecord convertFromMap(Map map); + + /** + * 保存新的报警记录,并关联保存其操作记录 + * @param records 报警记录列表 + * @param operatorMap 操作记录映射,键为报警记录ID,值为对应操作记录列表 + * @return 成功保存的记录数 + */ + int saveNewRecords(List records, Map>> operatorMap); +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/AlarmTaskOperatorService.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/AlarmTaskOperatorService.java new file mode 100644 index 00000000..b20d9042 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/AlarmTaskOperatorService.java @@ -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 { + + /** + * 从 API 返回的 Map 数据转换为实体列表 + * @param alarmRecordId 关联的报警记录ID + * @param operatorMaps API 返回的操作人数据列表 + * @return 转换后的实体列表 + */ + List convertFromMaps(Long alarmRecordId, List> operatorMaps); + + /** + * 批量保存操作记录,自动过滤已存在的记录 + * @param operators 操作记录列表 + * @return 成功保存的记录数 + */ + int saveNewOperators(List operators); +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/ApiService.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/ApiService.java new file mode 100644 index 00000000..77a44d1d --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/ApiService.java @@ -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 fetchAlarmRecords(int pageNum, int pageSize); +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/AlarmRecordServiceImpl.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/AlarmRecordServiceImpl.java new file mode 100644 index 00000000..043448a7 --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/AlarmRecordServiceImpl.java @@ -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 implements AlarmRecordService { + + @Autowired + private AlarmTaskOperatorService taskOperatorService; + + @Override + @Transactional(rollbackFor = Exception.class) + public int saveNewRecords(List records, Map>> operatorMap) { + if (records == null || records.isEmpty()) return 0; + + // 提取待插入记录的ID列表 + List ids = records.stream().map(AlarmRecord::getId).collect(Collectors.toList()); + + // 查询数据库中已存在的记录 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(AlarmRecord::getId, ids); + List existingRecords = this.list(queryWrapper); + List existingIds = existingRecords.stream().map(AlarmRecord::getId).collect(Collectors.toList()); + + // 过滤出新增记录 + List 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 newRecords, Map>> operatorMap) { + if (operatorMap == null || operatorMap.isEmpty()) return; + + // 收集需要保存的操作记录 + List allOperators = new ArrayList<>(); + for (AlarmRecord record : newRecords) { + Long recordId = record.getId(); + if (operatorMap.containsKey(recordId)) { + List> operatorsData = operatorMap.get(recordId); + List operators = taskOperatorService.convertFromMaps(recordId, operatorsData); + allOperators.addAll(operators); + } + } + + // 批量保存操作记录 + if (!allOperators.isEmpty()) { + taskOperatorService.saveNewOperators(allOperators); + } + } + + @Override + public AlarmRecord convertFromMap(Map 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 map, String key) { + Object value = map.get(key); + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return null; + } + + private Integer getIntValue(Map map, String key) { + Object value = map.get(key); + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return null; + } + + private Double getDoubleValue(Map map, String key) { + Object value = map.get(key); + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + return null; + } + + private java.util.Date parseDate(Map 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; + } +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/AlarmTaskOperatorServiceImpl.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/AlarmTaskOperatorServiceImpl.java new file mode 100644 index 00000000..24a5e7bb --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/AlarmTaskOperatorServiceImpl.java @@ -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 implements AlarmTaskOperatorService { + + @Override + @Transactional(rollbackFor = Exception.class) + public int saveNewOperators(List operators) { + if (operators == null || operators.isEmpty()) return 0; + + // 生成唯一键集合 (alarmRecordId_operatorId) + List uniqueKeys = operators.stream() + .map(op -> op.getAlarmRecordId() + "_" + op.getOperatorId()) + .collect(Collectors.toList()); + + // 查询已存在的记录(修正后的代码) + LambdaQueryWrapper 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 existingRecords = this.list(queryWrapper); + + // 生成已存在的唯一键集合 + List existingKeys = existingRecords.stream() + .map(op -> op.getAlarmRecordId() + "_" + op.getOperatorId()) + .collect(Collectors.toList()); + + // 过滤出新记录 + List 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 convertFromMaps(Long alarmRecordId, List> operatorMaps) { + List result = new ArrayList<>(); + if (operatorMaps == null || operatorMaps.isEmpty()) return result; + + for (Map 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 map, String key) { + Object value = map.get(key); + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return null; + } + + private Integer getIntValue(Map map, String key) { + Object value = map.get(key); + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return null; + } + + private java.util.Date parseDate(Map 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; + } +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java new file mode 100644 index 00000000..e16dc0ea --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java @@ -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 fetchAlarmRecords(int pageNum, int pageSize) { + try { + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + // 构建请求体 + Map requestBody = new HashMap<>(); + requestBody.put("PageSize", pageSize); + requestBody.put("PageNum", pageNum); + + List>> 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> requestEntity = new HttpEntity<>(requestBody, headers); + ResponseEntity response = restTemplate.exchange( + apiUrl, + HttpMethod.POST, + requestEntity, + Map.class + ); + + // 处理响应 + if (response.getStatusCode() == HttpStatus.OK) { + Map 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; + } + } +} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java new file mode 100644 index 00000000..a2b8b30f --- /dev/null +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java @@ -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 apiResult = apiService.fetchAlarmRecords(pageNum, pageSize); + + if (apiResult == null) { + log.error("API 返回空结果,停止同步"); + break; + } + + // 解析数据 + int totalNum = (int) apiResult.getOrDefault("TotalNum", 0); + List> recordList = (List>) apiResult.get("RecordList"); + + if (recordList == null || recordList.isEmpty()) { + log.info("没有更多数据可同步"); + hasMoreData = false; + break; + } + + // 转换报警记录 + List records = recordList.stream() + .map(alarmRecordService::convertFromMap) + .collect(Collectors.toList()); + + // 提取操作记录数据 + Map>> operatorMap = new HashMap<>(); + for (Map recordMap : recordList) { + Long recordId = (Long) recordMap.get("Id"); + List> operators = (List>) 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); + } + } +} diff --git a/ruoyi-modules/Sis/src/main/resources/application.yml b/ruoyi-modules/Sis/src/main/resources/application.yml index ed056a75..739a643c 100644 --- a/ruoyi-modules/Sis/src/main/resources/application.yml +++ b/ruoyi-modules/Sis/src/main/resources/application.yml @@ -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: