导入设备数据

This commit is contained in:
2025-11-18 15:34:46 +08:00
parent 6d58268874
commit 3798e52ee0
23 changed files with 1351 additions and 192 deletions

View File

@ -0,0 +1,54 @@
package com.fuyuanshen.equipment.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 读取表头 监听器
*
* @author: 默苍璃
* @date: 2025-11-1809:36
*/
@Slf4j
public class HeadValidateListener extends AnalysisEventListener<Map<Integer, String>> {
private List<String> headNames = new ArrayList<>();
/**
* When analysis one row trigger invoke function.
*
* @param headMap one row value. It is same as {@link AnalysisContext#readRowHolder()}
* @param context analysis context
*/
@Override
public void invoke(Map<Integer, String> headMap, AnalysisContext context) {
log.info("解析到一条数据: {}", JSON.toJSONString(headMap));
// 按顺序收集表头名称
for (int i = 0; i < headMap.size(); i++) {
headNames.add(headMap.get(i));
}
}
/**
* if have something to do after all analysis
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 读取完成后不需要额外操作
}
public List<String> getHeadNames() {
return headNames;
}
}

View File

@ -5,8 +5,7 @@ import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson2.JSON;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.core.utils.file.ImageCompressUtil;
import com.fuyuanshen.equipment.constants.DeviceConstants;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceType;
@ -46,38 +45,41 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
}
public ImportResult getImportResult() {
public ImportResult getImportResult1() {
ImportResult result = new ImportResult();
result.setSuccessCount(successCount);
result.setFailureCount(failureCount);
// 准备失败记录(包含图片数据)
// 准备失败记录
List<DeviceExcelImportDTO> failedRecordsWithImages = new ArrayList<>();
for (DeviceExcelImportDTO failedRecord : failedRecords) {
// 创建一个映射,用于存储失败记录行号与图片数据的关系
Map<Integer, byte[]> errorReportImageMap = new HashMap<>();
for (int i = 0; i < failedRecords.size(); i++) {
DeviceExcelImportDTO failedRecord = failedRecords.get(i);
// 创建副本,避免修改原始数据
DeviceExcelImportDTO recordWithImage = new DeviceExcelImportDTO();
BeanUtil.copyProperties(failedRecord, recordWithImage);
// 设置图片占位符,让用户知道这里有图片
// recordWithImage.setDevicePic("[图片]");
failedRecordsWithImages.add(recordWithImage);
// 获取原始行号
Integer rowIndex = null;
Integer originalRowIndex = null;
for (Map.Entry<Integer, DeviceExcelImportDTO> entry : rowDtoMap.entrySet()) {
if (entry.getValue() == failedRecord) {
rowIndex = entry.getKey();
originalRowIndex = entry.getKey();
break;
}
}
if (rowIndex != null) {
// 创建副本,避免修改原始数据
DeviceExcelImportDTO recordWithImage = new DeviceExcelImportDTO();
BeanUtil.copyProperties(failedRecord, recordWithImage);
// 设置图片数据
byte[] imageData = rowImageMap.get(rowIndex);
// 保存图片数据用于错误报告
if (originalRowIndex != null) {
byte[] imageData = rowImageMap.get(originalRowIndex);
if (imageData != null) {
// recordWithImage.setDevicePicFromBytes(imageData);
errorReportImageMap.put(i, imageData); // 使用新的列表索引
}
failedRecordsWithImages.add(recordWithImage);
} else {
failedRecordsWithImages.add(failedRecord);
}
}
@ -99,7 +101,8 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
// 保存错误报告到文件(包含图片)
File errorFile = new File(errorDir, fileName);
EasyExcel.write(errorFile, DeviceExcelImportDTO.class).registerWriteHandler(new ImageWriteHandler()) // 添加图片处理
EasyExcel.write(errorFile, DeviceExcelImportDTO.class)
.registerWriteHandler(new ImageWriteHandler(errorReportImageMap)) // 传递图片数据映射
.sheet("失败数据").doWrite(failedRecordsWithImages);
// 生成访问URL
@ -114,6 +117,79 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
return result;
}
public ImportResult getImportResult() {
ImportResult result = new ImportResult();
result.setSuccessCount(successCount);
result.setFailureCount(failureCount);
// 准备失败记录
List<DeviceExcelImportDTO> failedRecordsWithImages = new ArrayList<>();
for (int i = 0; i < failedRecords.size(); i++) {
DeviceExcelImportDTO failedRecord = failedRecords.get(i);
// 创建副本,避免修改原始数据
DeviceExcelImportDTO recordWithImage = new DeviceExcelImportDTO();
BeanUtil.copyProperties(failedRecord, recordWithImage);
// 获取原始行号
Integer originalRowIndex = null;
for (Map.Entry<Integer, DeviceExcelImportDTO> entry : rowDtoMap.entrySet()) {
if (entry.getValue() == failedRecord) {
originalRowIndex = entry.getKey();
break;
}
}
// 保存图片数据用于错误报告
if (originalRowIndex != null) {
byte[] imageData = rowImageMap.get(originalRowIndex);
if (imageData != null) {
// recordWithImage.setImageData(imageData);
recordWithImage.setDevicePic(imageData);
}
}
failedRecordsWithImages.add(recordWithImage);
}
result.setFailedRecords(failedRecordsWithImages);
// 生成错误报告
if (!failedRecordsWithImages.isEmpty()) {
try {
// 生成唯一的文件名
String fileName = "import_errors_" + System.currentTimeMillis() + ".xlsx";
// 创建错误报告目录
String errorDirPath = params.getFilePath() + DeviceConstants.ERROR_REPORT_DIR;
File errorDir = new File(errorDirPath);
if (!errorDir.exists() && !errorDir.mkdirs()) {
log.error("无法创建错误报告目录: {}", errorDirPath);
return result;
}
// 保存错误报告到文件(包含图片)
File errorFile = new File(errorDir, fileName);
EasyExcel.write(errorFile, DeviceExcelImportDTO.class)
.sheet("失败数据").doWrite(failedRecordsWithImages);
// 生成访问URL
SysOssVo upload = params.getOssService().upload(errorFile);
String url = upload.getUrl();
// 将http://替换为https://但不影响已经是https://的URL
if (url.startsWith("http://")) {
url = "https://" + url.substring(7);
}
result.setErrorExcelUrl(url);
log.info("错误报告已保存: {}", errorFile.getAbsolutePath());
log.info("错误报告已保存: {}", url);
} catch (Exception e) {
log.error("生成错误报告失败", e);
}
}
return result;
}
@Override
public void invoke(DeviceExcelImportDTO data, AnalysisContext context) {
@ -142,14 +218,232 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
private void processDataRowByRow() {
LoginUser loginUser = LoginHelper.getLoginUser();
// 如果没有数据,直接返回
if (rowIndexList.isEmpty()) {
return;
}
// 批量处理设备类型,减少数据库查询次数
Set<String> typeNames = new HashSet<>();
for (Integer rowIndex : rowIndexList) {
Device device = rowDeviceMap.get(rowIndex);
if (device != null && device.getTypeName() != null) {
typeNames.add(device.getTypeName());
}
}
// 批量查询所有设备类型
Map<String, DeviceType> deviceTypeMap = new HashMap<>();
if (!typeNames.isEmpty()) {
List<DeviceType> deviceTypes = params.getDeviceTypeService().queryByNames(new ArrayList<>(typeNames));
for (DeviceType deviceType : deviceTypes) {
deviceTypeMap.put(deviceType.getTypeName(), deviceType);
}
}
for (Integer rowIndex : rowIndexList) {
Device device = rowDeviceMap.get(rowIndex);
DeviceExcelImportDTO originalDto = rowDtoMap.get(rowIndex);
try {
// 从缓存中获取设备类型
DeviceType deviceType = deviceTypeMap.get(device.getTypeName());
DeviceForm deviceForm = new DeviceForm();
deviceForm.setDeviceName(device.getDeviceName());
deviceForm.setDeviceType(deviceType != null ? deviceType.getId() : null);
deviceForm.setTypeName(device.getTypeName());
deviceForm.setRemark(device.getRemark());
deviceForm.setDeviceMac(device.getDeviceMac());
deviceForm.setDeviceImei(device.getDeviceImei());
deviceForm.setBluetoothName(device.getBluetoothName());
deviceForm.setDevicePic(device.getDevicePic());
// 设置设备类型相关信息,供设备服务内部创建设备类型时使用
if (deviceType == null) {
deviceForm.setIsSupportBle(originalDto.getIsSupportBle());
deviceForm.setLocateMode(originalDto.getLocateMode());
deviceForm.setCommunicationMode(originalDto.getCommunicationMode());
deviceForm.setAppModelDictionary(originalDto.getAppModelDictionary());
deviceForm.setPcModelDictionary(originalDto.getPcModelDictionary());
}
params.getDeviceService().addDevice(deviceForm);
successCount++;
log.info("行 {} 数据插入成功", rowIndex);
} catch (Exception e) {
failureCount++;
originalDto.setErrorMessage(e.getMessage());
// originalDto.setErrorMessage("数据有误,请核对模板后,确认数据无误后,重新再试!!!");
failedRecords.add(originalDto);
log.error("行 {} 数据插入失败: {}", rowIndex, e.getMessage());
}
}
}
/**
* 处理图片数据
*/
private void processImages() {
// 如果没有数据行,直接返回
if (rowIndexList.isEmpty()) {
return;
}
// 检查是否有图片需要处理
try {
// 只在确实有图片时才打开文件处理
boolean hasImages = false;
try (OPCPackage opcPackage = OPCPackage.open(params.getFile().getInputStream())) {
ZipSecureFile.setMinInflateRatio(-1.0d);
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFDrawing drawing = sheet.getDrawingPatriarch();
if (drawing != null) {
for (XSSFShape shape : drawing.getShapes()) {
if (shape instanceof XSSFPicture) {
hasImages = true;
break;
}
}
}
} catch (Exception e) {
log.warn("检查图片时发生异常: {}", e.getMessage());
// 如果检查图片失败,继续处理数据
return;
}
// 如果没有图片,直接返回
if (!hasImages) {
log.info("未检测到图片,跳过图片处理");
return;
}
// 有图片时才进行处理
try (OPCPackage opcPackage = OPCPackage.open(params.getFile().getInputStream())) {
ZipSecureFile.setMinInflateRatio(-1.0d);
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFDrawing drawing = sheet.getDrawingPatriarch();
if (drawing == null) return;
for (XSSFShape shape : drawing.getShapes()) {
if (shape instanceof XSSFPicture) {
XSSFPicture picture = (XSSFPicture) shape;
XSSFClientAnchor anchor = picture.getPreferredSize();
int rowIndex = anchor.getRow1();
int colIndex = anchor.getCol1();
if (colIndex == 2) {
Device device = rowDeviceMap.get(rowIndex);
if (device != null) {
try {
byte[] imageData = picture.getPictureData().getData();
// 检查图片大小如果超过5MB则拒绝上传
if (imageData.length > 5 * 1024 * 1024) {
String errorMsg = "图片大小超过5MB限制请压缩后重新上传";
log.warn("行 {} 图片过大: {} bytes", rowIndex, imageData.length);
// 将错误信息添加到该行的DTO中
DeviceExcelImportDTO dto = rowDtoMap.get(rowIndex);
if (dto != null) {
dto.setErrorMessage(errorMsg);
failedRecords.add(dto);
failureCount++;
}
device.setDevicePic(null); // 设置为空,让插入继续
continue; // 跳过当前图片处理
}
// 表示Excel表格中的第3列因为索引从0开始计算
String extraValue = getCellValue(sheet, rowIndex, 2);
String imageUrl = uploadAndGenerateUrl(imageData, extraValue);
device.setDevicePic(imageUrl);
// 2. 保存图片数据到DTO用于错误报告
rowImageMap.put(rowIndex, imageData);
} catch (Exception e) {
log.error("行 {} 图片处理失败: {}", rowIndex, e.getMessage());
device.setDevicePic(null); // 设置为空,让插入继续
}
}
}
}
}
} catch (Exception e) {
log.error("图片处理失败:{}", e.getMessage(), e);
}
} catch (Exception e) {
log.warn("图片处理过程中发生异常: {}", e.getMessage());
}
}
private void processDataRowByRow1() {
for (Integer rowIndex : rowIndexList) {
Device device = rowDeviceMap.get(rowIndex);
DeviceExcelImportDTO originalDto = rowDtoMap.get(rowIndex);
try {
// 设备类型
DeviceType deviceType = params.getDeviceTypeService().queryByName(device.getTypeName());
// params.getDeviceService().save(device);
// 如果设备类型不存在,则创建新的设备类型
if (deviceType == null) {
DeviceType newDeviceType = new DeviceType();
newDeviceType.setTypeName(device.getTypeName());
newDeviceType.setIsSupportBle("".equals(originalDto.getIsSupportBle()) || "1".equals(originalDto.getIsSupportBle()));
// 设置定位方式
if (originalDto.getLocateMode() != null) {
switch (originalDto.getLocateMode()) {
case "":
newDeviceType.setLocateMode("0");
break;
case "GPS":
newDeviceType.setLocateMode("1");
break;
case "基站":
newDeviceType.setLocateMode("2");
break;
case "wifi":
newDeviceType.setLocateMode("3");
break;
case "北斗":
newDeviceType.setLocateMode("4");
break;
default:
newDeviceType.setLocateMode(originalDto.getLocateMode());
}
}
// 设置通讯方式
if (originalDto.getCommunicationMode() != null) {
switch (originalDto.getCommunicationMode()) {
case "4G":
newDeviceType.setCommunicationMode("0");
break;
case "蓝牙":
newDeviceType.setCommunicationMode("1");
break;
case "4G&蓝牙":
newDeviceType.setCommunicationMode("2");
break;
default:
newDeviceType.setCommunicationMode(originalDto.getCommunicationMode());
}
}
newDeviceType.setAppModelDictionary(originalDto.getAppModelDictionary());
newDeviceType.setPcModelDictionary(originalDto.getPcModelDictionary());
// 创建新的设备类型
params.getDeviceTypeService().create(newDeviceType);
// 重新查询确保获取到正确的ID
deviceType = params.getDeviceTypeService().queryByName(device.getTypeName());
}
DeviceForm deviceForm = new DeviceForm();
deviceForm.setDeviceName(device.getDeviceName());
deviceForm.setDeviceType(deviceType.getId());
@ -163,13 +457,19 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
log.info("行 {} 数据插入成功", rowIndex);
} catch (Exception e) {
failureCount++;
originalDto.setErrorMessage(e.getMessage());
// originalDto.setErrorMessage("数据有误,请核对模板后,确认数据无误后,重新再试!!!");
failedRecords.add(originalDto);
log.error("行 {} 数据插入失败: {}", rowIndex, e.getMessage());
}
}
}
private void processImages() {
/**
* 处理图片数据
*/
private void processImages1() {
try (OPCPackage opcPackage = OPCPackage.open(params.getFile().getInputStream())) {
ZipSecureFile.setMinInflateRatio(-1.0d);
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
@ -190,6 +490,22 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
if (device != null) {
try {
byte[] imageData = picture.getPictureData().getData();
// 检查图片大小如果超过5MB则拒绝上传
if (imageData.length > 5 * 1024 * 1024) {
String errorMsg = "图片大小超过5MB限制请压缩后重新上传";
log.warn("行 {} 图片过大: {} bytes", rowIndex, imageData.length);
// 将错误信息添加到该行的DTO中
DeviceExcelImportDTO dto = rowDtoMap.get(rowIndex);
if (dto != null) {
dto.setErrorMessage(errorMsg);
failedRecords.add(dto);
failureCount++;
}
device.setDevicePic(null); // 设置为空,让插入继续
continue; // 跳过当前图片处理
}
// 表示Excel表格中的第3列因为索引从0开始计算
String extraValue = getCellValue(sheet, rowIndex, 2);
String imageUrl = uploadAndGenerateUrl(imageData, extraValue);
@ -224,9 +540,16 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
return null;
}
try {
// 如果图片超过500KB则进行压缩优化
if (imageData.length > 500 * 1024) {
log.info("检测到大图片 ({} bytes),正在进行压缩优化...", imageData.length);
imageData = ImageCompressUtil.compressImage(imageData, 500 * 1024); // 压缩到500KB以下
}
String fileExtension = "jpg";
String newFileName = "PS_" + new Random(8) + "." + fileExtension;
SysOssVo upload = params.getOssService().upload(imageData, newFileName);
log.info("图片保存成功URL: {}", upload.getUrl());
return upload.getUrl();
} catch (Exception e) {
log.error("保存图片失败", e);
@ -234,4 +557,5 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
}
}
}
}