add:第一版测试版本功能优化。1.F机器人修改调度上报异常信息,后端存储异常信息和异常处理,增加异常信息和异常处理方法excel导入功能。2.添加密码校验、修改密码功能。

This commit is contained in:
2026-01-04 09:34:07 +08:00
parent 6e554b6bf7
commit acf269e92a
33 changed files with 1089 additions and 9 deletions

View File

@@ -27,6 +27,19 @@
<groupId>org.nl</groupId>
<artifactId>nl-business-api</artifactId>
</dependency>
<!-- easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- Sa-token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,122 @@
package org.nl.sys.modular.anomalyInfo.controller;
import com.alibaba.excel.EasyExcel;
import jakarta.annotation.Resource;
import org.nl.exception.BadRequestException;
import org.nl.logging.annotation.Log;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
import org.nl.sys.modular.anomalyInfo.listener.ErrorHandlingListener;
import org.nl.sys.modular.anomalyInfo.listener.ErrorInfoListener;
import org.nl.sys.modular.anomalyInfo.service.AnomalyInfoService;
import org.nl.sys.modular.anomalyInfo.service.ErrorHandlingService;
import org.nl.sys.modular.anomalyInfo.service.ErrorInfoService;
import org.nl.util.FileConstant;
import org.nl.util.ParseZip;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* @author dsh
* 2025/12/29
*/
@RestController
@RequestMapping("/anomalyInfo")
public class AnomalyInfoController {
@Resource
private AnomalyInfoService anomalyInfoService;
@Resource
private ErrorInfoService errorInfoService;
@Resource
private ErrorHandlingService errorHandlingService;
@Resource
private ParseZip parseZip;
@PostMapping("/queryErrorDataByCode")
@Log("根据异常编号查询异常数据")
public ResponseEntity<Object> queryErrorDataByCode(@RequestParam("code") String code) {
return new ResponseEntity<>(anomalyInfoService.queryErrorDataByCode(code), HttpStatus.OK);
}
@PostMapping("/importErrorInfoExcel")
@Log("导入excel异常信息")
public ResponseEntity<Object> importErrorInfoExcel(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
throw new BadRequestException("请选择文件上传");
}
// 创建监听器实例传入所需的Service
ErrorInfoListener listener = new ErrorInfoListener(errorInfoService);
// 读取Excel文件
// withFile(file.getInputStream())... 也可以
EasyExcel.read(file.getInputStream(), ErrorInfo.class, listener)
.sheet() // 默认读取第一个sheet
.doRead();
return new ResponseEntity<>("操作成功",HttpStatus.OK);
}
@PostMapping("/importErrorHandlingExcel")
@Log("导入excel异常处理信息")
public ResponseEntity<Object> importErrorHandlingExcel(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
throw new BadRequestException("请选择文件上传");
}
// 创建监听器实例传入所需的Service
ErrorHandlingListener listener = new ErrorHandlingListener(errorHandlingService);
// 读取Excel文件
// withFile(file.getInputStream())... 也可以
EasyExcel.read(file.getInputStream(), ErrorHandling.class, listener)
.sheet() // 默认读取第一个sheet
.doRead();
return new ResponseEntity<>("操作成功",HttpStatus.OK);
}
@PostMapping("/importErrorImage")
@Log("导入异常图片")
public ResponseEntity<Object> importErrorImage(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
throw new BadRequestException("文件不能为空");
}
String originalFilename = file.getOriginalFilename();
if (originalFilename == null ||
(!originalFilename.toLowerCase().endsWith(".zip"))) {
throw new BadRequestException("目前只支持ZIP格式");
}
try {
// 创建上传目录
File uploadDir = new File(FileConstant.ERROR_IMAGE_PATH);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 处理压缩文件
parseZip.processCompressedFile(file);
return new ResponseEntity<>("上传成功!",HttpStatus.OK);
} catch (Exception e) {
throw new BadRequestException("处理文件失败:{}"+e.getMessage());
}
}
}

View File

@@ -0,0 +1,64 @@
package org.nl.sys.modular.anomalyInfo.dao;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @author dsh
* 2025/12/29
*/
@Data
@TableName("error_handling")
public class ErrorHandling {
/**
* 异常处理标识
*/
@TableId
@ExcelProperty(index = 0,value = "标识")
private String error_handling_id;
/**
* 异常信息编码
*/
@ExcelProperty(index = 1,value = "异常编码")
private String error_code;
/**
* 异常处理类型(1 图文说明 2视频说明)
*/
@ExcelProperty(index = 2,value = "异常处理类型(1图文说明2视频说明)")
private Integer error_handling_type;
/**
* 异常处理说明
*/
@ExcelProperty(index = 3,value = "异常处理说明")
private String error_handling_title;
/**
* 中文异常处理说明
*/
@ExcelProperty(index = 4,value = "异常处理说明(中文)")
private String zh_error_handling_title;
/**
* 英文异常处理说明
*/
@ExcelProperty(index = 5,value = "异常处理说明(英文)")
private String en_error_handling_title;
/**
* 异常处理排序
*/
@ExcelProperty(index = 6,value = "异常处理排序")
private Integer error_handling_seq;
/**
* 异常处理地址(图片 或 视频地址)
*/
@ExcelProperty(index = 7,value = "异常处理地址(图片或视频地址)")
private String error_handling_addre;
}

View File

@@ -0,0 +1,76 @@
package org.nl.sys.modular.anomalyInfo.dao;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @author dsh
* 2025/12/29
*/
@Data
@TableName("error_info")
public class ErrorInfo {
/**
* 异常信息标识
*/
@TableId
@ExcelProperty(index = 0,value = "唯一标识")
private String error_id;
/**
* 异常信息名称
*/
@ExcelProperty(index = 1,value = "异常名称")
private String error_name;
/**
* 中文异常信息名称
*/
@ExcelProperty(index = 2,value = "异常名称(中文)")
private String zh_error_name;
/**
* 英文异常信息名称
*/
@ExcelProperty(index = 3,value = "异常名称(英文)")
private String en_error_name;
/**
* 异常信息编码
*/
@ExcelProperty(index = 4,value = "异常编号")
private String error_code;
/**
* 异常信息类别(1普通故障 2严重故障)
*/
@ExcelProperty(index = 5,value = "异常类别(1普通故障,2严重故障)")
private Integer error_category;
/**
* 异常说明
*/
@ExcelProperty(index = 6,value = "异常说明")
private String error_description;
/**
* 中文异常说明
*/
@ExcelProperty(index = 7,value = "异常说明(中文)")
private String zh_error_description;
/**
* 英文异常说明
*/
@ExcelProperty(index = 8,value = "异常说明(英文)")
private String en_error_description;
/**
* 异常信息类型(1电气 2导航)
*/
@ExcelProperty(index = 9,value = "异常类型(1电气,2导航)")
private Integer error_type;
}

View File

@@ -0,0 +1,26 @@
package org.nl.sys.modular.anomalyInfo.dto;
import lombok.Data;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
import java.util.List;
/**
* @author dsh
* 2025/12/29
*/
@Data
public class ErrorDataDto extends ErrorInfo {
/**
* 图文异常处理
*/
private List<ErrorHandling> graphicDescription;
/**
* 视频异常处理
*/
private List<ErrorHandling> videoDescription;
}

View File

@@ -0,0 +1,90 @@
package org.nl.sys.modular.anomalyInfo.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.extern.slf4j.Slf4j;
import org.nl.exception.BadRequestException;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @author dsh
* 2025/12/29
*/
@Slf4j
public class ErrorHandlingListener extends AnalysisEventListener<ErrorHandling> {
/**
* 每隔 BATCH_COUNT 条存储数据库然后清理list方便内存回收
*/
private static final int BATCH_COUNT = 1000; // 批处理阈值:cite[2]:cite[5]
private List<ErrorHandling> cachedDataList = new ArrayList<>(BATCH_COUNT);
/**
* 假设我们需要Service进行批量插入
* 也可以使用Mapper但Service层封装批量操作更常见
*/
private IService<ErrorHandling> service;
/**
* 通过构造器传入需要的Service或Mapper
*/
public ErrorHandlingListener(IService<ErrorHandling> service) {
this.service = service;
}
/**
* 每读一行数据,都会调用此方法
* @param data 一行数据类型是泛型T
* @param context
*/
@Override
public void invoke(ErrorHandling data, AnalysisContext context) {
cachedDataList.add(data);
// 达到BATCH_COUNT了需要去存储一次数据库防止数据几万条数据在内存容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList.clear();
}
}
/**
* 所有数据解析完成后,会调用此方法
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 确保最后一批数据也持久化到数据库
if (!cachedDataList.isEmpty()) {
saveData();
cachedDataList.clear();
}
log.info("所有Excel数据解析并导入完成");
}
/**
* 批量保存数据到数据库
* 使用MyBatis-Plus的saveBatch方法
*/
@Transactional(rollbackFor = Exception.class)
public void saveData() {
if (!cachedDataList.isEmpty()) {
// 清除所有异常信息 重新导入
service.remove(new QueryWrapper<ErrorHandling>());
// 第二个参数是批次大小
boolean saveResult = service.saveBatch(cachedDataList, BATCH_COUNT);
if (saveResult) {
log.info("成功批量插入 {} 条数据。", cachedDataList.size());
} else {
log.error("批量插入数据失败!");
throw new BadRequestException("操作失败");
}
}
}
}

View File

@@ -0,0 +1,91 @@
package org.nl.sys.modular.anomalyInfo.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.extern.slf4j.Slf4j;
import org.nl.exception.BadRequestException;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @author dsh
* 2025/12/29
*/
@Slf4j
public class ErrorInfoListener extends AnalysisEventListener<ErrorInfo> {
/**
* 每隔 BATCH_COUNT 条存储数据库然后清理list方便内存回收
*/
private static final int BATCH_COUNT = 1000; // 批处理阈值:cite[2]:cite[5]
private List<ErrorInfo> cachedDataList = new ArrayList<>(BATCH_COUNT);
/**
* 假设我们需要Service进行批量插入
* 也可以使用Mapper但Service层封装批量操作更常见
*/
private IService<ErrorInfo> service;
/**
* 通过构造器传入需要的Service或Mapper
*/
public ErrorInfoListener(IService<ErrorInfo> service) {
this.service = service;
}
/**
* 每读一行数据,都会调用此方法
* @param data 一行数据类型是泛型T
* @param context
*/
@Override
public void invoke(ErrorInfo data, AnalysisContext context) {
cachedDataList.add(data);
// 达到BATCH_COUNT了需要去存储一次数据库防止数据几万条数据在内存容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList.clear();
}
}
/**
* 所有数据解析完成后,会调用此方法
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 确保最后一批数据也持久化到数据库
if (!cachedDataList.isEmpty()) {
saveData();
cachedDataList.clear();
}
log.info("所有Excel数据解析并导入完成");
}
/**
* 批量保存数据到数据库
* 使用MyBatis-Plus的saveBatch方法
*/
@Transactional(rollbackFor = Exception.class)
public void saveData() {
if (!cachedDataList.isEmpty()) {
// 清除所有异常信息 重新导入
service.remove(new QueryWrapper<ErrorInfo>());
// 第二个参数是批次大小
boolean saveResult = service.saveBatch(cachedDataList, BATCH_COUNT);
if (saveResult) {
log.info("成功批量插入 {} 条数据。", cachedDataList.size());
} else {
log.error("批量插入数据失败!");
throw new BadRequestException("操作失败");
}
}
}
}

View File

@@ -0,0 +1,14 @@
package org.nl.sys.modular.anomalyInfo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
/**
* @author dsh
* 2025/12/29
*/
@Mapper
public interface ErrorHandlingMapper extends BaseMapper<ErrorHandling> {
}

View File

@@ -0,0 +1,14 @@
package org.nl.sys.modular.anomalyInfo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
/**
* @author dsh
* 2025/12/29
*/
@Mapper
public interface ErrorInfoMapper extends BaseMapper<ErrorInfo> {
}

View File

@@ -0,0 +1,36 @@
package org.nl.sys.modular.anomalyInfo.provider;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.nl.api.sys.anomalyInfo.api.ErrorInfoAPI;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
import org.nl.sys.modular.anomalyInfo.service.ErrorInfoService;
import org.springframework.stereotype.Service;
/**
* @author dsh
* 2025/12/30
*/
@Slf4j
@Service
public class ErrorInfoAPIProvider implements ErrorInfoAPI {
@Resource
private ErrorInfoService errorInfoService;
@Override
public JSONObject queryErrorInfoByCode(Integer code) {
if (ObjectUtil.isEmpty(code)){
log.info("异常码不能为空");
return null;
}
ErrorInfo errorInfo = errorInfoService.getOne(new LambdaQueryWrapper<>(ErrorInfo.class)
.eq(ErrorInfo::getError_code,code)
);
return errorInfo == null ? null : JSONObject.parseObject(JSONObject.toJSONString(errorInfo));
}
}

View File

@@ -0,0 +1,17 @@
package org.nl.sys.modular.anomalyInfo.service;
import org.nl.sys.modular.anomalyInfo.dto.ErrorDataDto;
/**
* @author dsh
* 2025/12/29
*/
public interface AnomalyInfoService {
/**
* 根据异常编码查询处理方法
* @param code
* @return
*/
ErrorDataDto queryErrorDataByCode(String code);
}

View File

@@ -0,0 +1,12 @@
package org.nl.sys.modular.anomalyInfo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
/**
* @author dsh
* 2025/12/29
*/
public interface ErrorHandlingService extends IService<ErrorHandling> {
}

View File

@@ -0,0 +1,11 @@
package org.nl.sys.modular.anomalyInfo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
/**
* @author dsh
* 2025/12/29
*/
public interface ErrorInfoService extends IService<ErrorInfo> {
}

View File

@@ -0,0 +1,47 @@
package org.nl.sys.modular.anomalyInfo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
import org.nl.sys.modular.anomalyInfo.dto.ErrorDataDto;
import org.nl.sys.modular.anomalyInfo.service.AnomalyInfoService;
import org.nl.sys.modular.anomalyInfo.mapper.ErrorHandlingMapper;
import org.nl.sys.modular.anomalyInfo.mapper.ErrorInfoMapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author dsh
* 2025/12/29
*/
@Service
public class AnomalyInfoServiceImpl implements AnomalyInfoService {
@Resource
private ErrorHandlingMapper errorHandlingMapper;
@Resource
private ErrorInfoMapper errorInfoMapper;
@Override
public ErrorDataDto queryErrorDataByCode(String code) {
ErrorDataDto errorDataDto = new ErrorDataDto();
// 异常信息
ErrorInfo errorInfo = errorInfoMapper.selectOne(new LambdaQueryWrapper<>(ErrorInfo.class).eq(ErrorInfo::getError_code,code));
BeanUtil.copyProperties(errorInfo,errorDataDto);
// 异常处理
List<ErrorHandling> errorHandlingList = errorHandlingMapper.selectList(new LambdaQueryWrapper<>(ErrorHandling.class).eq(ErrorHandling::getError_code,code));
// 图文说明
List<ErrorHandling> graphicDescription = errorHandlingList.stream().filter(errorHandling -> errorHandling.getError_handling_type() == 1).collect(Collectors.toList());
// 视频说明
List<ErrorHandling> videoDescription = errorHandlingList.stream().filter(errorHandling -> errorHandling.getError_handling_type() == 2).collect(Collectors.toList());
errorDataDto.setGraphicDescription(graphicDescription);
errorDataDto.setVideoDescription(videoDescription);
return errorDataDto;
}
}

View File

@@ -0,0 +1,15 @@
package org.nl.sys.modular.anomalyInfo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
import org.nl.sys.modular.anomalyInfo.service.ErrorHandlingService;
import org.nl.sys.modular.anomalyInfo.mapper.ErrorHandlingMapper;
import org.springframework.stereotype.Service;
/**
* @author dsh
* 2025/12/29
*/
@Service
public class ErrorHandlingServiceImpl extends ServiceImpl<ErrorHandlingMapper, ErrorHandling> implements ErrorHandlingService {
}

View File

@@ -0,0 +1,16 @@
package org.nl.sys.modular.anomalyInfo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
import org.nl.sys.modular.anomalyInfo.service.ErrorInfoService;
import org.nl.sys.modular.anomalyInfo.mapper.ErrorInfoMapper;
import org.springframework.stereotype.Service;
/**
* @author dsh
* 2025/12/29
*/
@Service
public class ErrorInfoServiceImpl extends ServiceImpl<ErrorInfoMapper, ErrorInfo> implements ErrorInfoService {
}

View File

@@ -0,0 +1,30 @@
package org.nl.sys.modular.secutiry.controller;
import jakarta.annotation.Resource;
import org.nl.logging.annotation.Log;
import org.nl.sys.modular.secutiry.param.SecurityParam;
import org.nl.sys.modular.secutiry.service.SecurityService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author dsh
* 2025/12/30
*/
@RestController
@RequestMapping("/security")
public class SecurityController {
@Resource
private SecurityService securityService;
@PostMapping("/verifyPasswords")
@Log("校验显示屏密码")
public ResponseEntity<Object> verifyPasswords(@RequestBody SecurityParam param){
return new ResponseEntity<>(securityService.verifyPasswords(param.getPassword()), HttpStatus.OK);
}
}

View File

@@ -0,0 +1,18 @@
package org.nl.sys.modular.secutiry.param;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* @author dsh
* 2025/12/30
*/
@Data
public class SecurityParam {
/**
* 密码
*/
@NotBlank(message = "密码不能为空")
private String password;
}

View File

@@ -0,0 +1,12 @@
package org.nl.sys.modular.secutiry.service;
import org.nl.response.WebResponse;
/**
* @author dsh
* 2025/12/30
*/
public interface SecurityService {
WebResponse verifyPasswords(String password);
}

View File

@@ -0,0 +1,35 @@
package org.nl.sys.modular.secutiry.service.impl;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.nl.api.setting.api.SettingAPI;
import org.nl.exception.BadRequestException;
import org.nl.response.WebResponse;
import org.nl.sys.modular.secutiry.service.SecurityService;
import org.nl.util.RsaUtils;
import org.springframework.stereotype.Service;
/**
* @author dsh
* 2025/12/30
*/
@Service
public class SecurityServiceImpl implements SecurityService {
@Resource
private SettingAPI settingAPI;
@Override
@SneakyThrows
public WebResponse verifyPasswords(String password) {
String new_password = RsaUtils.decryptByPrivateKey(RsaUtils.privateKey,password);
JSONObject setting = settingAPI.querySettingParamByCode("password").getJSONObject("data");
if (ObjectUtil.isEmpty(setting) || !setting.getString("value").equals(SaSecureUtil.md5BySalt(new_password,"salt"))) {
throw new BadRequestException("密码错误");
}
return WebResponse.requestOk();
}
}