excel导入公共方法、工器具导入

This commit is contained in:
cwchen 2025-11-03 13:53:18 +08:00
parent 4ce60f2532
commit 1e3173cd6e
11 changed files with 462 additions and 24 deletions

View File

@ -1,6 +1,7 @@
package com.bonus.web.service.enterprise;
import com.alibaba.fastjson2.JSONObject;
import com.bonus.common.constant.Constants;
import com.bonus.common.constant.TableConstants;
import com.bonus.common.core.domain.AjaxResult;
import com.bonus.common.domain.file.po.ResourceFilePo;
@ -10,11 +11,14 @@ import com.bonus.common.domain.mainDatabase.dto.ToolDto;
import com.bonus.common.domain.mainDatabase.importdto.ToolImportDto;
import com.bonus.common.domain.mainDatabase.vo.TechnicalVo;
import com.bonus.common.domain.mainDatabase.vo.ToolVo;
import com.bonus.common.domain.validate.ValidateResult;
import com.bonus.common.enums.ErrorMessageEnum;
import com.bonus.common.utils.ValidatorsUtils;
import com.bonus.file.service.FileUploadService;
import com.bonus.file.service.SourceFileService;
import com.bonus.mainDataBase.service.IMDToolService;
import com.bonus.web.util.ImportExcelUtils;
import com.bonus.web.validateService.ToolValidateService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -33,6 +37,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @className:ToolService
@ -51,6 +56,9 @@ public class ToolService {
@Resource(name = "SourceFileService")
private SourceFileService sourceFileService;
@Resource(name = "ToolValidateService")
private ToolValidateService toolValidateService;
@Resource(name = "FileUploadService")
private FileUploadService fileUploadService;
@ -93,7 +101,7 @@ public class ToolService {
// 1.校验工器具名称是否重复
int result = imdToolService.isRepeat(dto);
if (result > 0) {
return AjaxResult.error("工器具名称重复");
return AjaxResult.error("工器具名称和规格重复");
}
// 2.添加工器具数据
imdToolService.operData(dto, 1);
@ -130,7 +138,7 @@ public class ToolService {
// 1.校验工器具名称是否重复
int result = imdToolService.isRepeat(dto);
if (result > 0) {
return AjaxResult.error("工器具名称重复");
return AjaxResult.error("工器具名称和规格重复");
}
// 2.修改主体库数据
imdToolService.operData(dto, 2);
@ -202,6 +210,7 @@ public class ToolService {
/**
* 企业知识库->工器具库->工器具导入
*
* @param dto
* @param file
* @return AjaxResult
@ -214,10 +223,21 @@ public class ToolService {
List<JSONObject> lstObj = importExcelUtils.readExcel(file, ToolImportDto.class, null);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
List<ToolDto> toolList = mapper.convertValue(lstObj, new TypeReference<List<ToolDto>>() {});
// 添加工器具数据
imdToolService.batchImportData(dto,toolList);
// 3.添加文件
List<ToolDto> toolList = mapper.convertValue(lstObj, new TypeReference<List<ToolDto>>() {
});
if (CollectionUtils.isEmpty(toolList)) {
return AjaxResult.error(ErrorMessageEnum.getByCode("EXCEL003").getMessage());
}
// 1.校验excel数据是否填写校验excel数据是否重复校验excel数据是否与数据库重复
verifyExcelData(toolList);
ValidateResult validateResult = validateToolData(toolList,dto);
if (!validateResult.isSuccess()) {
// 校验失败抛出异常或返回错误信息
return AjaxResult.error("数据校验失败: " + validateResult.getMessage());
}
// 4.添加工器具数据
imdToolService.batchImportData(dto, toolList);
// 5.添加文件
List<ResourceFilePo> files = new ArrayList<>();
for (ToolDto toolDto : toolList) {
for (ResourceFilePo po : toolDto.getFiles()) {
@ -233,4 +253,77 @@ public class ToolService {
}
return AjaxResult.success();
}
/**
* 校验数据是否合法
* @param toolList
* @return List<String>
* @author cwchen
* @date 2025/11/3 11:25
*/
public List<String> verifyExcelData(List<ToolDto> toolList) {
List<String> list = new ArrayList<>();
for (ToolDto toolDto : toolList) {
String validResult = validatorsUtils.valid(toolDto, ToolDto.IMPORT.class);
if (StringUtils.isNotBlank(validResult)) {
list.add(validResult);
}
}
return list;
}
/**
* 异步执行所有校验并汇总结果
*/
public ValidateResult validateToolData(List<ToolDto> toolList,ToolDto dto) {
long startTime = System.currentTimeMillis();
try {
// 异步执行所有校验任务
CompletableFuture<ValidateResult> task1 = toolValidateService.verifyExcelData(toolList);
CompletableFuture<ValidateResult> task2 = toolValidateService.verifyExcelDuplicate(toolList);
CompletableFuture<ValidateResult> task3 = toolValidateService.verifyDatabaseDuplicate(toolList,dto);
// 等待所有任务完成
CompletableFuture.allOf(task1, task2, task3).join();
// 获取各个任务的结果
ValidateResult result1 = task1.get();
ValidateResult result2 = task2.get();
ValidateResult result3 = task3.get();
// 汇总结果
return aggregateResults(result1, result2, result3);
} catch (Exception e) {
return new ValidateResult("汇总校验", false, "校验过程发生异常: " + e.getMessage());
} finally {
long endTime = System.currentTimeMillis();
System.out.println("校验总耗时: " + (endTime - startTime) + "ms");
}
}
/**
* 汇总所有校验结果
*/
private ValidateResult aggregateResults(ValidateResult... results) {
ValidateResult summary = new ValidateResult("汇总校验", true, "所有校验通过");
List<String> allErrors = new ArrayList<>();
for (ValidateResult result : results) {
if (!result.isSuccess()) {
summary.setSuccess(false);
allErrors.add("" + result.getTaskName() + "" + result.getMessage());
// 添加详细错误信息
if (result.getErrorDetails() != null) {
allErrors.addAll(result.getErrorDetails());
}
}
}
if (!summary.isSuccess()) {
summary.setErrorDetails(allErrors);
summary.setMessage("共发现" + allErrors.size() + "个问题,请检查后重新导入");
}
return summary;
}
}

View File

@ -27,27 +27,27 @@ public class ImportExcelJsonDataUtil {
obj.put("rowNo", row.getRowNum() + 1);
if (row.getCell(1) != null) {
// 工器具名称
obj.put("toolName", row.getCell(1).getStringCellValue());
obj.put("toolName", row.getCell(1).getStringCellValue().trim());
}
if (row.getCell(2) != null) {
// 规格型号
obj.put("model", row.getCell(2).getStringCellValue());
obj.put("model", row.getCell(2).getStringCellValue().trim());
}
if (row.getCell(3) != null) {
// 单位
obj.put("unit", row.getCell(3).getStringCellValue());
obj.put("unit", row.getCell(3).getStringCellValue().trim());
}
if (row.getCell(4) != null) {
// 技术参数
obj.put("technicalParameters", row.getCell(4).getStringCellValue());
obj.put("technicalParameters", row.getCell(4).getStringCellValue().trim());
}
if (row.getCell(5) != null) {
// 主要作用
obj.put("mainFunction", row.getCell(5).getStringCellValue());
obj.put("mainFunction", row.getCell(5).getStringCellValue().trim());
}
if (row.getCell(6) != null) {
// 备注
obj.put("remark", row.getCell(6).getStringCellValue());
obj.put("remark", row.getCell(6).getStringCellValue().trim());
}
// 工器具图片
String result = "TOOL" + "_" + row.getRowNum();

View File

@ -0,0 +1,142 @@
package com.bonus.web.validateService;
import com.bonus.common.domain.mainDatabase.dto.ToolDto;
import com.bonus.common.domain.validate.ValidateResult;
import com.bonus.common.utils.ValidatorsUtils;
import com.bonus.mainDataBase.service.IMDToolService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* @className: ToolValidateService
* @author: cwchen
* @date: 2025-11-03-13:20
* @version: 1.0
* @description: 工器具异步校验服务类
*/
@Service(value = "ToolValidateService")
@Slf4j
public class ToolValidateService {
@Resource(name = "ValidatorsUtils")
private ValidatorsUtils validatorsUtils;
@Resource(name = "IMDToolService")
private IMDToolService imdToolService;
/**
* 1. 校验Excel数据是否填写合法
*/
@Async("taskExecutor")
public CompletableFuture<ValidateResult> verifyExcelData(List<ToolDto> toolList) {
ValidateResult result = new ValidateResult("数据填写校验", true, "数据填写完整");
try {
for (int i = 0; i < toolList.size(); i++) {
ToolDto tool = toolList.get(i);
int rowNumber = i + 1;
result.setSuccess(false);
String validResult = validatorsUtils.valid(tool, ToolDto.IMPORT.class);
if (StringUtils.isNotBlank(validResult)) {
result.addErrorDetail("" + rowNumber + "行:" + validResult);
}
}
if (!result.isSuccess()) {
result.setMessage("数据填写不完整,共发现" + result.getErrorDetails().size() + "处错误");
}
} catch (Exception e) {
result.setSuccess(false);
result.setMessage("数据填写校验异常: " + e.getMessage());
}
return CompletableFuture.completedFuture(result);
}
/**
* 2. 校验Excel数据是否重复
*/
@Async("taskExecutor")
public CompletableFuture<ValidateResult> verifyExcelDuplicate(List<ToolDto> toolList) {
ValidateResult result = new ValidateResult("Excel数据重复校验", true, "无重复数据");
try {
// 检查工器具名称重复
Map<String, Integer> toolKeyCount = new HashMap<>();
for (int i = 0; i < toolList.size(); i++) {
ToolDto tool = toolList.get(i);
String key = tool.getToolName() + "_" + tool.getModel();
toolKeyCount.put(key, toolKeyCount.getOrDefault(key, 0) + 1);
}
// 找出重复的数据
for (int i = 0; i < toolList.size(); i++) {
ToolDto tool = toolList.get(i);
String key = tool.getToolName() + "_" + tool.getModel();
if (toolKeyCount.get(key) > 1) {
result.setSuccess(false);
result.addErrorDetail("" + (i + 1) + "行:工器具名称:'" + tool.getToolName() +
"' 型号:'" + tool.getModel() + "' 在Excel中重复");
}
}
if (!result.isSuccess()) {
result.setMessage("发现Excel内重复数据" + result.getErrorDetails().size() + "处重复");
}
} catch (Exception e) {
result.setSuccess(false);
result.setMessage("Excel重复校验异常: " + e.getMessage());
}
return CompletableFuture.completedFuture(result);
}
/**
* 3. 校验Excel数据是否与数据库重复
*/
@Async("taskExecutor")
public CompletableFuture<ValidateResult> verifyDatabaseDuplicate(List<ToolDto> toolList,ToolDto dto) {
ValidateResult result = new ValidateResult("数据库重复校验", true, "无数据库重复数据");
try {
// 从数据库查询已存在的工器具
List<String> existingTools = queryExistingToolsFromDatabase(dto);
for (int i = 0; i < toolList.size(); i++) {
ToolDto tool = toolList.get(i);
String key = tool.getToolName();
if (existingTools.contains(key)) {
result.setSuccess(false);
result.addErrorDetail("" + (i + 1) + "行:工器具名称: '" + tool.getToolName() +
"' 型号: '" + tool.getModel() + "' 在数据库中已存在");
}
}
if (!result.isSuccess()) {
result.setMessage("发现数据库重复数据,共" + result.getErrorDetails().size() + "处重复");
}
} catch (Exception e) {
result.setSuccess(false);
result.setMessage("数据库重复校验异常: " + e.getMessage());
}
return CompletableFuture.completedFuture(result);
}
/**
* 从数据库查询已存在的工器具
*/
private List<String> queryExistingToolsFromDatabase(ToolDto dto) {
List<String> existingTools = new ArrayList<>();
existingTools = imdToolService.findExistingTools(dto);
return existingTools;
}
}

View File

@ -0,0 +1,44 @@
package com.bonus.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @className:AsyncConfig
* @author:cwchen
* @date:2025-11-03-13:11
* @version:1.0
* @description:线程池配置
*/
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 队列容量
executor.setQueueCapacity(100);
// 线程存活时间
executor.setKeepAliveSeconds(60);
// 线程名称前缀
executor.setThreadNamePrefix("task-executor-");
// 拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}

View File

@ -33,46 +33,46 @@ public class ToolDto {
/**
* 企业id
*/
@NotNull(message = "企业id不能为空", groups = {UPDATE.class,DELETE.class, QUERY.class})
@NotNull(message = "企业id不能为空", groups = {ADD.class,UPDATE.class,DELETE.class, QUERY.class})
private Long enterpriseId;
/**
* 工器具名称
*/
@NotBlank(message = "工器具名称不能为空", groups = {ADD.class, UPDATE.class})
@Length(max = 64, message = "工器具名称字符长度不能超过64", groups = {ADD.class, UPDATE.class})
@NotBlank(message = "工器具名称不能为空", groups = {IMPORT.class,ADD.class, UPDATE.class})
@Length(max = 64, message = "工器具名称字符长度不能超过64", groups = {IMPORT.class,ADD.class, UPDATE.class})
private String toolName;
/**
* 规格型号
*/
@NotBlank(message = "规格型号不能为空", groups = {ADD.class, UPDATE.class})
@Length(max = 32, message = "规格型号字符长度不能超过32", groups = {ADD.class, UPDATE.class})
@NotBlank(message = "规格型号不能为空", groups = {IMPORT.class,ADD.class, UPDATE.class})
@Length(max = 32, message = "规格型号字符长度不能超过32", groups = {IMPORT.class,ADD.class, UPDATE.class})
private String model;
/**
* 单位
*/
@NotBlank(message = "单位不能为空", groups = {ADD.class, UPDATE.class})
@Length(max = 32, message = "单位字符长度不能超过32", groups = {ADD.class, UPDATE.class})
@NotBlank(message = "单位不能为空", groups = {IMPORT.class,ADD.class, UPDATE.class})
@Length(max = 32, message = "单位字符长度不能超过32", groups = {IMPORT.class,ADD.class, UPDATE.class})
private String unit;
/**
* 技术参数
*/
@Length(max = 100, message = "技术参数字符长度不能超过100", groups = {ADD.class, UPDATE.class})
@Length(max = 100, message = "技术参数字符长度不能超过100", groups = {IMPORT.class,ADD.class, UPDATE.class})
private String technicalParameters;
/**
* 主要作用
*/
@Length(max = 100, message = "主要作用字符长度不能超过100", groups = {ADD.class, UPDATE.class})
@Length(max = 100, message = "主要作用字符长度不能超过100", groups = {IMPORT.class,ADD.class, UPDATE.class})
private String mainFunction;
/**
* 备注
*/
@Length(max = 200, message = "备注字符长度不能超过200", groups = {ADD.class, UPDATE.class})
@Length(max = 200, message = "备注字符长度不能超过200", groups = {IMPORT.class,ADD.class, UPDATE.class})
private String remark;
/**
@ -136,4 +136,10 @@ public class ToolDto {
*/
public interface DELETE {
}
/**
* 导入条件限制
*/
public interface IMPORT {
}
}

View File

@ -0,0 +1,39 @@
package com.bonus.common.domain.validate;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
/**
* @className:ValidateResult
* @author:cwchen
* @date:2025-11-03-13:30
* @version:1.0
* @description:校验结果类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ValidateResult {
private String taskName;
private boolean success;
private String message;
private List<String> errorDetails;
public ValidateResult(String taskName, boolean success, String message) {
this.taskName = taskName;
this.success = success;
this.message = message;
this.errorDetails = new ArrayList<>();
}
public void addErrorDetail(String detail) {
if (errorDetails == null) {
errorDetails = new ArrayList<>();
}
errorDetails.add(detail);
}
}

View File

@ -0,0 +1,81 @@
package com.bonus.common.enums;
/**
* @className:ErrorMessageEnum
* @author:cwchen
* @date:2025-11-03-11:28
* @version:1.0
* @description:基础错误信息枚举
*/
public enum ErrorMessageEnum {
// 系统错误
SYSTEM_ERROR("SYS001", "系统错误"),
DATABASE_ERROR("SYS002", "数据库操作失败"),
NETWORK_ERROR("SYS003", "网络连接异常"),
// 业务错误
DATA_NOT_FOUND("BIZ001", "数据不存在"),
DATA_ALREADY_EXISTS("BIZ002", "数据已存在"),
PARAM_VALIDATION_FAILED("BIZ003", "参数校验失败"),
OPERATION_NOT_ALLOWED("BIZ004", "操作不允许"),
// 文件操作错误
FILE_UPLOAD_FAILED("FILE001", "文件上传失败"),
FILE_NOT_EXISTS("FILE002", "文件不存在"),
FILE_FORMAT_ERROR("FILE003", "文件格式错误"),
FILE_SIZE_EXCEEDED("FILE004", "文件大小超限"),
// Excel导入错误
EXCEL_IMPORT_FAILED("EXCEL001", "Excel导入失败"),
EXCEL_TEMPLATE_ERROR("EXCEL002", "Excel模板错误"),
EXCEL_DATA_EMPTY("EXCEL003", "Excel数据为空"),
EXCEL_DATA_FORMAT_ERROR("EXCEL004", "Excel数据格式错误"),
// 权限错误
UNAUTHORIZED("AUTH001", "未授权访问"),
FORBIDDEN("AUTH002", "访问被禁止"),
TOKEN_EXPIRED("AUTH003", "Token已过期"),
// 工具管理相关错误
TOOL_IMPORT_FAILED("TOOL001", "工具导入失败"),
TOOL_NOT_FOUND("TOOL002", "工具不存在"),
TOOL_ALREADY_EXISTS("TOOL003", "工具已存在"),
TOOL_PARAM_ERROR("TOOL004", "工具参数错误");
private final String code;
private final String message;
ErrorMessageEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
// 根据code获取枚举
public static ErrorMessageEnum getByCode(String code) {
for (ErrorMessageEnum error : values()) {
if (error.getCode().equals(code)) {
return error;
}
}
return SYSTEM_ERROR;
}
// 格式化消息支持参数替换
public String formatMessage(Object... args) {
return String.format(this.message, args);
}
@Override
public String toString() {
return "[" + code + "] " + message;
}
}

View File

@ -57,4 +57,13 @@ public interface IMDToolMapper {
* @date 2025/11/3 10:56
*/
void batchImportData(@Param("params")ToolDto dto, @Param("list")List<ToolDto> toolList);
/**
* 企业知识库->工器具库->查询已存在的工器具
* @param dto
* @return List<String>
* @author cwchen
* @date 2025/11/3 13:38
*/
List<String> findExistingTools(ToolDto dto);
}

View File

@ -55,4 +55,13 @@ public interface IMDToolService {
* @date 2025/11/3 10:55
*/
void batchImportData(ToolDto dto, List<ToolDto> toolList);
/**
* 企业知识库->工器具库->查询已存在的工器具
* @param dto
* @return List<String>
* @author cwchen
* @date 2025/11/3 13:36
*/
List<String> findExistingTools(ToolDto dto);
}

View File

@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -56,4 +57,14 @@ public class MDToolServiceImpl implements IMDToolService {
public void batchImportData(ToolDto dto, List<ToolDto> toolList) {
imdToolMapper.batchImportData(dto,toolList);
}
@Override
public List<String> findExistingTools(ToolDto dto) {
try {
return Optional.ofNullable(imdToolMapper.findExistingTools(dto)).orElse(new ArrayList<>());
} catch (Exception e) {
log.error(e.toString(),e);
return new ArrayList<>();
}
}
}

View File

@ -104,10 +104,14 @@
<!--企业知识库->工器具库->查询工器具名称是否重复-->
<select id="isRepeat" resultType="java.lang.Integer">
<if test="toolId == null">
SELECT COUNT(*) FROM tb_enterprise_tool WHERE tool_name = #{toolName} AND del_flag = '0'
SELECT COUNT(*) FROM tb_enterprise_tool WHERE tool_name = #{toolName} AND model = #{model} AND del_flag = '0'
</if>
<if test="toolId!=null">
SELECT COUNT(*) FROM tb_enterprise_tool WHERE tool_id !=#{toolId} AND tool_name = #{toolName} AND del_flag = '0'
SELECT COUNT(*) FROM tb_enterprise_tool WHERE tool_id !=#{toolId} AND tool_name = #{toolName} AND model = #{model} AND del_flag = '0'
</if>
</select>
<!--企业知识库->工器具库->查询已存在的工器具-->
<select id="findExistingTools" resultType="java.lang.String">
SELECT CONCAT(tool_name,'_',model) FROM tb_enterprise_tool WHERE enterprise_id = #{enterpriseId} AND del_flag = '0'
</select>
</mapper>