diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevMergeController.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevMergeController.java index da3b573..e1a6ce5 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevMergeController.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevMergeController.java @@ -3,6 +3,7 @@ package com.bonus.material.device.controller; import cn.hutool.core.convert.Convert; import com.bonus.common.biz.config.ListPagingUtil; import com.bonus.common.biz.domain.BmCompanyInfo; +import com.bonus.common.core.annotation.Excel; import com.bonus.common.core.utils.poi.ExcelUtil; import com.bonus.common.core.web.controller.BaseController; import com.bonus.common.core.web.domain.AjaxResult; @@ -13,14 +14,17 @@ import com.bonus.common.security.utils.SecurityUtils; import com.bonus.material.devchange.domain.MaDevInfo; import com.bonus.material.devchange.domain.MaDevInfoXlsx; import com.bonus.material.device.domain.DevInfo; +import com.bonus.material.device.domain.EquipmentImportDTO; import com.bonus.material.device.domain.dto.InfoMotionDto; import com.bonus.material.device.domain.vo.DevInfoVo; import com.bonus.material.device.domain.vo.DevMergeVo; import com.bonus.material.device.service.DevInfoService; import com.bonus.material.device.service.DevMergeService; +import com.bonus.material.utils.ReflectUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.ObjectUtils; +import org.apache.poi.ss.usermodel.Sheet; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -30,6 +34,7 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; +import java.lang.reflect.Field; import java.util.List; import java.util.Map; @@ -275,8 +280,8 @@ public class DevMergeController extends BaseController { @SysLog(title = "用户管理", businessType = OperaType.IMPORT, logType = 0, module = "系统管理->用户管理", details = "导入用户信息") public AjaxResult importData(MultipartFile file, String orderId) throws Exception { try { - ExcelUtil util = new ExcelUtil(MaDevInfoXlsx.class); - List list = util.importExcel(file.getInputStream()); + ExcelUtil util = new ExcelUtil(EquipmentImportDTO.class); + List list = util.importExcel(file.getInputStream(), 1); return service.importData(list, orderId); } catch (Exception e) { logger.error(e.toString(), e); @@ -285,5 +290,39 @@ public class DevMergeController extends BaseController { } + /** + * 下载导入模板 + */ + @PostMapping("/template") + public void downloadTemplate(HttpServletResponse response) { + // 1. 查询系统中的专业列表(核心:动态获取下拉选数据) + List professionList = service.listAllProfessionNames(); + // 转换为数组(工具类需要String[]类型) + String[] professionArray = professionList.toArray(new String[0]); + // 1.2 生产厂家列表 + List manufacturerList = service.listAllManufacturerNames(); + String[] manufacturerArray = manufacturerList.toArray(new String[0]); + + // 2. 反射修改@Excel注解的combo属性(关键:动态注入下拉选值) + try { + Field professionField = EquipmentImportDTO.class.getDeclaredField("profession"); + Excel excelAnnotation = professionField.getAnnotation(Excel.class); + // 通过反射修改注解的combo属性(需借助自定义的注解修改工具,或直接复用工具类逻辑) + // 方案1:使用反射工具修改注解属性(推荐) + ReflectUtils.setAnnotationValue(excelAnnotation, "combo", professionArray); + + Field manufacturerField = EquipmentImportDTO.class.getDeclaredField("manufacturer"); + Excel manufacturerExcel = manufacturerField.getAnnotation(Excel.class); + ReflectUtils.setAnnotationValue(manufacturerExcel, "combo", manufacturerArray); + + } catch (Exception e) { + throw new RuntimeException("设置专业下拉选失败", e); + } + + // 3. 调用工具类生成模板(自动触发下拉选生成) + ExcelUtil util = new ExcelUtil<>(EquipmentImportDTO.class); + // importTemplateExcel:专门生成导入模板的方法(仅表头+下拉选,无数据) + util.importTemplateExcel(response, "装备信息导入模板", "装备信息导入模板"); + } } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/domain/EquipmentImportDTO.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/domain/EquipmentImportDTO.java new file mode 100644 index 0000000..634aeec --- /dev/null +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/domain/EquipmentImportDTO.java @@ -0,0 +1,59 @@ +package com.bonus.material.device.domain; + +import com.bonus.common.core.annotation.Excel; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import java.math.BigDecimal; +import java.util.Date; + +// EquipmentImportDTO.java +@Data +public class EquipmentImportDTO { + + @Excel(name = "装备类目", sort = 1, combo = {}) + @NotBlank(message = "专业不能为空") + private String profession; + + @Excel(name = "装备名称", sort = 2) + @NotBlank(message = "装备名称不能为空") + private String equipmentName; + + @Excel(name = "规格型号", sort = 3) + private String specification; + + @Excel(name = "资产原值", sort = 4) + @DecimalMin(value = "0.00", message = "资产原值不能小于0") + private BigDecimal originalValue; + + @Excel(name = "生产厂家", sort = 5, combo = {}) + private String manufacturer; + + @Excel(name = "生产日期", sort = 6, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date productionDate; + + @Excel(name = "下次维保日期", sort = 7, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date nextMaintenanceDate; + + @Excel(name = "装备原始编码", sort = 8) + @NotBlank(message = "装备原始编码不能为空") + private String originalCode; + + @Excel(name = "最大使用年限", sort = 9) + @Min(value = 0, message = "最大使用年限不能小于0") + private Integer maxServiceYears; + + @Excel(name = "计数单位", sort = 10, combo = {}) + @NotBlank(message = "计数单位不能为空") + private String unit; + + @Excel(name = "采购日期", sort = 11, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date purchaseDate; +} + diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevMergeMapper.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevMergeMapper.java index cf27c80..21f776d 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevMergeMapper.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevMergeMapper.java @@ -75,6 +75,7 @@ public interface DevMergeMapper { Integer interDevice(MaDevInfo maDevInfo); Integer interDeviceXlsx(MaDevInfoXlsx maDevInfo); + List getDevice(MaDevInfo o); @@ -85,5 +86,15 @@ public interface DevMergeMapper { Integer updateDeviceByMaId(MaDevInfo maDevInfo); void delFile(Integer maId); + + List listAllProfessionNames(); + + List listAllManufacturerNames(); + + // 批量查询profession对应的typeId + Integer getTypeId(String professions); + + + Integer getManufacturer(String manufacturers); } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevMergeService.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevMergeService.java index f935238..e057897 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevMergeService.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevMergeService.java @@ -6,6 +6,7 @@ import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.material.devchange.domain.MaDevInfo; import com.bonus.material.devchange.domain.MaDevInfoXlsx; import com.bonus.material.device.domain.DevInfo; +import com.bonus.material.device.domain.EquipmentImportDTO; import com.bonus.material.device.domain.dto.DevInfoImpDto; import com.bonus.material.device.domain.dto.InfoMotionDto; import com.bonus.material.device.domain.vo.DevInfoVo; @@ -71,5 +72,9 @@ public interface DevMergeService { AjaxResult updateDevice(MaDevInfo maDevInfo); - AjaxResult importData(List list, String orderId); + AjaxResult importData(List list, String orderId); + + List listAllProfessionNames(); + + List listAllManufacturerNames(); } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevMergeServiceImpl.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevMergeServiceImpl.java index 121f8db..aacbbe5 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevMergeServiceImpl.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevMergeServiceImpl.java @@ -15,6 +15,7 @@ import com.bonus.common.core.utils.bean.BeanValidators; import com.bonus.common.core.utils.poi.ExcelUtil; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.security.utils.SecurityUtils; +import com.bonus.material.basic.domain.BmSlideShow; import com.bonus.material.book.domain.BookCarInfoDto; import com.bonus.material.devchange.domain.MaDevFile; import com.bonus.material.devchange.domain.MaDevInfo; @@ -22,6 +23,7 @@ import com.bonus.material.devchange.domain.MaDevInfoXlsx; import com.bonus.material.devchange.domain.MapBean; import com.bonus.material.devchange.mapper.MaDevInfoMapper; import com.bonus.material.device.domain.DevInfo; +import com.bonus.material.device.domain.EquipmentImportDTO; import com.bonus.material.device.domain.MaDevQc; import com.bonus.material.device.domain.Table; import com.bonus.material.device.domain.dto.DevInfoImpDto; @@ -51,10 +53,12 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; +import javax.validation.ConstraintViolation; import javax.validation.Validator; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -84,7 +88,8 @@ public class DevMergeServiceImpl implements DevMergeService { private DevInfoMapper devInfoMapper; @Autowired private MaDevInfoMapper mapper; - + @Autowired + private Validator validator; @Autowired private MaDevQcMapper qcMapper; @@ -431,13 +436,14 @@ public class DevMergeServiceImpl implements DevMergeService { * @param orderId * @return */ + @Transactional(rollbackFor = Exception.class) // 添加事务,确保数据一致性 @Override - public AjaxResult importData(List list, String orderId) { - if (Objects.isNull(list) || list.isEmpty()) { + public AjaxResult importData(List list, String orderId) { + // 1. 基础校验 + if (ObjectUtil.isEmpty(list) || list.isEmpty()) { return AjaxResult.error("表格内没有数据"); } - - if (Objects.isNull(orderId)) { + if (ObjectUtil.isEmpty(orderId)) { DevMergeVo o = new DevMergeVo(); o.setCreateUser(SecurityUtils.getLoginUser().getSysUser().getNickName()); o.setOrderNumber(generate()); @@ -445,69 +451,101 @@ public class DevMergeServiceImpl implements DevMergeService { orderId = o.getId(); } - for (MaDevInfoXlsx maDevInfo : list) { - + // 2. 批量校验DTO字段合法性(JSR-303) + List errorMessages = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { + EquipmentImportDTO item = list.get(i); + Set> violations = validator.validate(item); + if (!violations.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("第").append(i + 1).append("行数据错误:"); // 行号从1开始,方便用户定位 + for (ConstraintViolation violation : violations) { + sb.append(violation.getMessage()).append(";"); + } + errorMessages.add(sb.toString()); + } } - for (MaDevInfoXlsx maDevInfo : list) { - maDevInfo.setOrderId(Integer.valueOf(orderId)); + if (!errorMessages.isEmpty()) { + return AjaxResult.error("导入失败,存在以下错误:" + String.join(" ", errorMessages)); + } + + // 4. 遍历数据,进行最终校验和导入 + for (int i = 0; i < list.size(); i++) { + EquipmentImportDTO item = list.get(i); + StringBuilder rowError = new StringBuilder(); + rowError.append("第").append(i + 1).append("行:"); + + // 4.4 校验日期格式(若框架未自动处理,可添加) + // 示例:校验productionDate是否为有效日期(根据实际需求调整) + if (item.getProductionDate() != null && item.getPurchaseDate() != null && item.getProductionDate().after(item.getPurchaseDate())) { + rowError.append("生产日期不能晚于采购日期;"); + } + + // 4.5 若当前行有错误,记录并继续校验其他行 + if (rowError.length() > ("第" + (i + 1) + "行:").length()) { + errorMessages.add(rowError.toString()); + continue; + } + + // 5. 校验通过,执行导入逻辑 try { + MaDevInfo maDevInfo = new MaDevInfo(); + maDevInfo.setTypeId(devMergeMapper.getTypeId(item.getProfession())); + maDevInfo.setProductionDate(item.getProductionDate()); + maDevInfo.setPurchaseDate(item.getPurchaseDate()); + maDevInfo.setSpecificationModel(item.getSpecification()); + maDevInfo.setOriginalValue(item.getOriginalValue()); + maDevInfo.setName(item.getEquipmentName()); + maDevInfo.setManufacturerId(devMergeMapper.getManufacturer(item.getManufacturer())); // 注意:这里假设getManufacturer返回的是ID,需根据实际Mapper方法调整 + maDevInfo.setNextMaintenanceDate(item.getNextMaintenanceDate()); + maDevInfo.setOriginalCode(item.getOriginalCode()); + maDevInfo.setMaxServiceLifeYears(item.getMaxServiceYears()); + maDevInfo.setUnit(item.getUnit()); Long thisLoginUserDeptId = SecurityUtils.getLoginUser().getSysUser().getDeptId(); maDevInfo.setPropertyUnitId(Math.toIntExact(thisLoginUserDeptId)); maDevInfo.setCode(getString()); - Integer i = devMergeMapper.interDeviceXlsx(maDevInfo); - if (i > 0) { - devInfoMapper.deleteDevInfoProperties(Long.valueOf(maDevInfo.getMaId())); + + Integer insertResult = devMergeMapper.interDevice(maDevInfo); + if (insertResult > 0) { MaDevQc maDevQc = new MaDevQc(); maDevQc.setMaId(maDevInfo.getMaId()); maDevQc.setQcCode(maDevInfo.getCode()); maDevQc.setCreateBy(String.valueOf(SecurityUtils.getUserId())); maDevQc.setCreateTime(DateUtils.getNowDate()); - maDevQc.setQcCom(Optional.ofNullable(SecurityUtils.getLoginUser().getSysUser().getCompanyId()).orElse(SecurityUtils.getLoginUser().getSysUser().getDeptId()).toString()); + maDevQc.setQcCom(Optional.ofNullable(SecurityUtils.getLoginUser().getSysUser().getCompanyId()) + .orElse(SecurityUtils.getLoginUser().getSysUser().getDeptId()).toString()); maDevQc.setNextCheckTime(maDevInfo.getNextMaintenanceDate()); qcMapper.insertDevQc(maDevQc); - if (!CollectionUtils.isEmpty(maDevInfo.getPropertyVoList())) { - devInfoMapper.insertDevInfoProperties(Long.valueOf(maDevInfo.getMaId()), maDevInfo.getPropertyVoList()); - } - devMergeMapper.insertOrderDevReal(String.valueOf(maDevInfo.getOrderId()), Long.valueOf(maDevInfo.getMaId())); - maDevInfo.getAppearanceImages().forEach(item -> { - // 这里编写对每个 image 的处理逻辑,比如打印、处理等 - item.setFileType(1); - item.setMaId(maDevInfo.getMaId()); - item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid())); - devMergeMapper.interFile(item); - }); - - maDevInfo.getCertificates().forEach(item -> { - // 这里编写对每个 image 的处理逻辑,比如打印、处理等 - item.setFileType(2); - item.setMaId(maDevInfo.getMaId()); - item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid())); - devMergeMapper.interFile(item); - }); - - maDevInfo.getInspectionReports().forEach(item -> { - // 这里编写对每个 image 的处理逻辑,比如打印、处理等 - item.setFileType(3); - item.setMaId(maDevInfo.getMaId()); - item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid())); - devMergeMapper.interFile(item); - }); - - maDevInfo.getPurchaseInvoices().forEach(item -> { - // 这里编写对每个 image 的处理逻辑,比如打印、处理等 - item.setFileType(4); - item.setMaId(maDevInfo.getMaId()); - item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid())); - devMergeMapper.interFile(item); - }); + devMergeMapper.insertOrderDevReal(orderId, Long.valueOf(maDevInfo.getMaId())); } - return i > 0 ? AjaxResult.success() : AjaxResult.error(); } catch (Exception e) { - log.error(e.getMessage()); - return AjaxResult.error(); + // 记录导入异常,方便排查 + log.error("导入第{}行数据失败:{}", i + 1, e.getMessage(), e); + errorMessages.add("第" + (i + 1) + "行导入失败:系统错误"); } } - return null; + + // 6. 返回最终结果 + if (!errorMessages.isEmpty()) { + return AjaxResult.error("导入完成,但存在以下错误:" + String.join(" ", errorMessages)); + } + return AjaxResult.success("导入完成", orderId); + } + + /** + * @return + */ + @Override + public List listAllProfessionNames() { + return devMergeMapper.listAllProfessionNames(); + } + + /** + * @return + */ + @Override + public List listAllManufacturerNames() { + return devMergeMapper.listAllManufacturerNames(); } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/utils/ReflectUtils.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/utils/ReflectUtils.java new file mode 100644 index 0000000..a44fa17 --- /dev/null +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/utils/ReflectUtils.java @@ -0,0 +1,24 @@ +package com.bonus.material.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.Map; + +public class ReflectUtils { + + /** + * 修改注解的属性值 + */ + public static void setAnnotationValue(Annotation annotation, String attrName, Object value) throws Exception { + // 1. 获取注解的代理对象 + InvocationHandler handler = Proxy.getInvocationHandler(annotation); + // 2. 获取代理对象的memberValues字段(存储注解属性) + Field memberValuesField = handler.getClass().getDeclaredField("memberValues"); + memberValuesField.setAccessible(true); + // 3. 修改属性值 + Map memberValues = (Map) memberValuesField.get(handler); + memberValues.put(attrName, value); + } +} diff --git a/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/device/DevInfoMapper.xml b/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/device/DevInfoMapper.xml index f1810e9..662ee8a 100644 --- a/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/device/DevInfoMapper.xml +++ b/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/device/DevInfoMapper.xml @@ -241,62 +241,61 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + + diff --git a/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/toolProcess/ToolProcessMapper.xml b/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/toolProcess/ToolProcessMapper.xml index 93168dd..821bf8b 100644 --- a/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/toolProcess/ToolProcessMapper.xml +++ b/bonus-modules/bonus-material-mall/src/main/resources/mapper/material/toolProcess/ToolProcessMapper.xml @@ -125,8 +125,8 @@ GROUP BY ta.id, ta.`code` ORDER BY - ta.create_time DESC, - ta.status ASC + FIELD(ta.status, '0','1', '3', '2') ASC, + ta.create_time DESC