导入设备数据
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user