WEB:导入设备数据

This commit is contained in:
2025-07-04 11:02:22 +08:00
parent e8aee3039a
commit d456236599
17 changed files with 1106 additions and 38 deletions

View File

@ -0,0 +1,34 @@
package com.fuyuanshen.equipment.excel;
import com.fuyuanshen.customer.mapper.CustomerMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.system.service.ISysOssService;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;
/**
* @author: 默苍璃
* @date: 2025-06-1414:56
*/
@AllArgsConstructor
@NoArgsConstructor(force = true)
@Getter
@Setter
@Builder
public class DeviceImportParams {
private DeviceService deviceService;
private DeviceMapper deviceMapper;
private CustomerMapper customerMapper;
private DeviceTypeMapper deviceTypeMapper;
private ISysOssService ossService;
private MultipartFile file;
private String filePath;
private String ip;
private String username;
private Long userId;
private String tenantId;
}

View File

@ -0,0 +1,260 @@
package com.fuyuanshen.equipment.excel;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
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.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.constants.DeviceConstants;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO;
import com.fuyuanshen.equipment.domain.dto.ImportResult;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.handler.ImageWriteHandler;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.xssf.usermodel.*;
import java.io.File;
import java.util.*;
@Slf4j
public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportDTO> {
// 存储图片数据的映射
private final Map<Integer, byte[]> rowImageMap = new HashMap<>();
private final DeviceImportParams params;
private final Map<Integer, Device> rowDeviceMap = new HashMap<>();
private final Map<Integer, DeviceExcelImportDTO> rowDtoMap = new HashMap<>();
private final List<Integer> rowIndexList = new ArrayList<>();
private int successCount = 0;
private int failureCount = 0;
private final List<DeviceExcelImportDTO> failedRecords = new ArrayList<>();
public UploadDeviceDataListener(DeviceImportParams params) {
this.params = params;
}
public ImportResult getImportResult() {
ImportResult result = new ImportResult();
result.setSuccessCount(successCount);
result.setFailureCount(failureCount);
// 准备失败记录(包含图片数据)
List<DeviceExcelImportDTO> failedRecordsWithImages = new ArrayList<>();
for (DeviceExcelImportDTO failedRecord : failedRecords) {
// 获取原始行号
Integer rowIndex = null;
for (Map.Entry<Integer, DeviceExcelImportDTO> entry : rowDtoMap.entrySet()) {
if (entry.getValue() == failedRecord) {
rowIndex = entry.getKey();
break;
}
}
if (rowIndex != null) {
// 创建副本,避免修改原始数据
DeviceExcelImportDTO recordWithImage = new DeviceExcelImportDTO();
BeanUtil.copyProperties(failedRecord, recordWithImage);
// 设置图片数据
byte[] imageData = rowImageMap.get(rowIndex);
if (imageData != null) {
recordWithImage.setDevicePicFromBytes(imageData);
}
failedRecordsWithImages.add(recordWithImage);
} else {
failedRecordsWithImages.add(failedRecord);
}
}
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).registerWriteHandler(new ImageWriteHandler()) // 添加图片处理
.sheet("失败数据").doWrite(failedRecordsWithImages);
// 生成访问URL
// String errorExcelUrl = params.getIp() + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.ERROR_REPORT_DIR + "/" + fileName;
SysOssVo upload = params.getOssService().upload(errorFile);
result.setErrorExcelUrl(upload.getUrl());
log.info("错误报告已保存: {}", errorFile.getAbsolutePath());
} catch (Exception e) {
log.error("生成错误报告失败", e);
}
}
return result;
}
@Override
public void invoke(DeviceExcelImportDTO data, AnalysisContext context) {
log.info("解析到一条数据: {}", JSON.toJSONString(data));
int rowIndex = context.readRowHolder().getRowIndex();
Device device = new Device();
BeanUtil.copyProperties(data, device, true);
rowDeviceMap.put(rowIndex, device);
rowDtoMap.put(rowIndex, data);
rowIndexList.add(rowIndex);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
try {
processImages();
processDataRowByRow();
log.info("数据处理完成!成功:{}条,失败:{}条", successCount, failureCount);
} catch (Exception e) {
log.error("数据处理失败:{}", e.getMessage(), e);
throw new RuntimeException("导入失败", e);
}
}
private void processDataRowByRow() {
LoginUser loginUser = LoginHelper.getLoginUser();
for (Integer rowIndex : rowIndexList) {
Device device = rowDeviceMap.get(rowIndex);
DeviceExcelImportDTO originalDto = rowDtoMap.get(rowIndex);
try {
DeviceQueryCriteria criteria = new DeviceQueryCriteria();
criteria.setDeviceMac(device.getDeviceMac());
criteria.setTenantId(params.getTenantId());
List<Device> deviceList = params.getDeviceMapper().findAll(criteria);
if (!deviceList.isEmpty()) {
throw new RuntimeException("设备MAC重复");
}
device.setTenantId(params.getTenantId());
// 设备类型
QueryWrapper<DeviceType> wrapper = new QueryWrapper<>();
wrapper.eq("type_name", device.getTypeName());
wrapper.eq("customer_id", params.getUserId());
List<DeviceType> deviceTypes = params.getDeviceTypeMapper().selectList(wrapper);
if (CollectionUtil.isNotEmpty(deviceTypes)) {
device.setDeviceType(deviceTypes.get(0).getId());
}
params.getDeviceService().save(device);
successCount++;
log.info("行 {} 数据插入成功", rowIndex);
} catch (Exception e) {
failureCount++;
failedRecords.add(originalDto);
log.error("行 {} 数据插入失败: {}", rowIndex, e.getMessage());
}
}
}
private void processImages() {
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();
String extraValue = getCellValue(sheet, rowIndex, 4);
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);
}
}
private String getCellValue(XSSFSheet sheet, int rowIndex, int colIndex) {
XSSFRow row = sheet.getRow(rowIndex);
if (row == null) return null;
XSSFCell cell = row.getCell(colIndex);
if (cell == null) return null;
return cell.toString();
}
private String uploadAndGenerateUrl(byte[] imageData, String deviceMac) {
if (imageData == null || imageData.length == 0) {
log.warn("图片数据为空");
return null;
}
try {
String fileExtension = "jpg";
String newFileName = "PS_" + new Random(8) + "." + fileExtension;
// String targetDirPath = params.getFilePath() + DeviceConstants.FILE_ACCESS_ISOLATION;
// File targetDir = new File(targetDirPath);
//
// if (!targetDir.exists() && !targetDir.mkdirs()) {
// log.error("无法创建目录: {}", targetDirPath);
// return null;
// }
//
// File newFile = new File(targetDir, newFileName);
// Files.write(newFile.toPath(), imageData);
//
// return params.getIp() + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.FILE_ACCESS_ISOLATION + "/" + newFileName;
SysOssVo upload = params.getOssService().upload(imageData, newFileName);
return upload.getUrl();
} catch (Exception e) {
log.error("保存图片失败", e);
return null;
}
}
}