diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevInfoController.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevInfoController.java index e5d6d69..b537cd8 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevInfoController.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/controller/DevInfoController.java @@ -7,6 +7,7 @@ import com.bonus.common.core.utils.poi.ExcelUtil; import com.bonus.common.core.web.controller.BaseController; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.core.web.page.TableDataInfo; +import com.bonus.common.security.annotation.RequiresPermissions; import com.bonus.common.security.utils.SecurityUtils; import com.bonus.material.device.domain.DevInfo; import com.bonus.material.device.domain.dto.DevInfoImpDto; @@ -26,6 +27,7 @@ import javax.validation.Valid; import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Objects; /** @@ -212,7 +214,7 @@ public class DevInfoController extends BaseController { util.exportExcel(response, list, "设备信息数据"); } - @ApiOperation(value = "装备批量录入") + /*@ApiOperation(value = "装备批量录入") @PostMapping("/importData") public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { String fileName = file.getOriginalFilename(); @@ -232,7 +234,7 @@ public class DevInfoController extends BaseController { Long userId = SecurityUtils.getLoginUser().getUserid(); String message = devInfoService.importMaProp(maPropInfoList, updateSupport, userId); return success(message); - } + }*/ @ApiOperation(value = "装备模版下载") @@ -241,4 +243,26 @@ public class DevInfoController extends BaseController { HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); devInfoService.downLoadTemplate(resp); } + + /** + * 设备批量导入模版下载 + */ + @ApiOperation(value = "设备批量导入模版下载") + @PostMapping("/downLoadDev") + public void downLoadDev(){ + HttpServletResponse resp = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse(); + devInfoService.downLoadDev(resp); + } + + /** + * 设备信息导入 + * @param file + * @return + */ + @ApiOperation(value = "设备信息导入") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file) + { + return devInfoService.importTbPeople(file); + } } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/domain/vo/DevTemplateVo.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/domain/vo/DevTemplateVo.java new file mode 100644 index 0000000..60e3c51 --- /dev/null +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/domain/vo/DevTemplateVo.java @@ -0,0 +1,83 @@ +package com.bonus.material.device.domain.vo; + +import com.bonus.common.core.annotation.Excel; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.List; + + +/** + * @author mashuai + * @date 2025/03/17 17:11 + */ +@EqualsAndHashCode(callSuper = false) +@Data +@ToString +public class DevTemplateVo { + + @ApiModelProperty(value = "装备名称") + @Excel(name = "装备名称") + private String deviceName; + + @ApiModelProperty(value = "类型id") + private Long typeId; + + @ApiModelProperty(value = "装备类别") + @Excel(name = "装备类目") + private String typeName; + + @Excel(name = "装备品牌") + @ApiModelProperty(value = "设备品牌") + private String brand; + + @Excel(name = "出厂日期") + @ApiModelProperty(value = "出厂日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private String productionDate; + + @ApiModelProperty(value = "联系人") + @Excel(name = "联系人") + private String person; + + @ApiModelProperty(value = "联系电话") + @Excel(name = "联系电话") + private String personPhone; + + @Excel(name = "上架数量(编码类型设备默传1)") + @ApiModelProperty(value = "上架数量") + private Integer deviceCount; + + @Excel(name = "唯一标识符") + @ApiModelProperty(value = "设备唯一标识符") + private String identifyCode; + + @ApiModelProperty(value = "检修人") + @Excel(name = "检修人") + private String checkMan; + + @ApiModelProperty(value = "检测日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Excel(name = "检修时间(例:2025-03-17)") + private String checkDate; + + @ApiModelProperty(value = "下次检测日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Excel(name = "下次检测时间(例:2025-03-17)") + private String nextCheckDate; + + @ApiModelProperty(value = "装备单位") + private String unitName; + + @ApiModelProperty(value = "设备天租价") + private Float dayLeasePrice; + + @ApiModelProperty(value = "装备证书详情列表") + private List dtoList; +} diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevInfoMapper.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevInfoMapper.java index f49bea8..0f3d43c 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevInfoMapper.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/mapper/DevInfoMapper.java @@ -243,5 +243,12 @@ public interface DevInfoMapper { DevInfoVo getBuyCompanyList(DevInfoVo infoVo); DevInfoVo selectDeptList(String companyId); + + /** + * 根据装备类型名称查询 + * @param typeName + * @return + */ + DevInfoVo selectDevTypeByName(String typeName); } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevInfoService.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevInfoService.java index 3ec495c..66b76c2 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevInfoService.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/DevInfoService.java @@ -8,6 +8,7 @@ import com.bonus.material.device.domain.DevInfo; import com.bonus.material.device.domain.dto.DevInfoImpDto; import com.bonus.material.device.domain.dto.InfoMotionDto; import com.bonus.material.device.domain.vo.DevInfoVo; +import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.util.List; @@ -116,4 +117,17 @@ public interface DevInfoService { * @return */ List getLeaseDevList(DevInfoVo devInfo); + + /** + * 设备批量导入模版下载 + * @param resp + */ + void downLoadDev(HttpServletResponse resp); + + /** + * 设备信息导入 + * @param file + * @return + */ + AjaxResult importTbPeople(MultipartFile file); } diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevInfoServiceImpl.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevInfoServiceImpl.java index cadaf0f..c6a9d16 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevInfoServiceImpl.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/device/service/impl/DevInfoServiceImpl.java @@ -2,6 +2,7 @@ package com.bonus.material.device.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.PhoneUtil; import com.bonus.common.biz.constant.MaterialConstants; import com.bonus.common.biz.domain.*; import com.bonus.common.biz.enums.HttpCodeEnum; @@ -12,6 +13,8 @@ import com.bonus.common.core.utils.DateUtils; import com.bonus.common.core.utils.StringUtils; import com.bonus.common.core.utils.bean.BeanUtils; import com.bonus.common.core.utils.bean.BeanValidators; +import com.bonus.common.core.utils.encryption.Sm4Utils; +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.book.domain.BookCarInfoDto; @@ -20,10 +23,7 @@ import com.bonus.material.device.domain.MaDevQc; import com.bonus.material.device.domain.Table; import com.bonus.material.device.domain.dto.DevInfoImpDto; import com.bonus.material.device.domain.dto.InfoMotionDto; -import com.bonus.material.device.domain.vo.DevInfoPropertyVo; -import com.bonus.material.device.domain.vo.DevInfoVo; -import com.bonus.material.device.domain.vo.DevNameVo; -import com.bonus.material.device.domain.vo.LeaseVo; +import com.bonus.material.device.domain.vo.*; import com.bonus.material.device.mapper.BmFileInfoMapper; import com.bonus.material.device.mapper.DevInfoMapper; import com.bonus.material.device.mapper.MaDevQcMapper; @@ -35,10 +35,13 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; @@ -47,6 +50,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.*; import java.util.stream.Collectors; @@ -870,6 +876,289 @@ public class DevInfoServiceImpl implements DevInfoService { return devInfoMapper.getLeaseDevList(devInfo); } + /** + * 设备批量导入模版下载 + * @param response + */ + @Override + public void downLoadDev(HttpServletResponse response) { + //模板名称 + String templateName = "MaDevTemplate.xlsx"; + OutputStream out = null; + InputStream input =null; + try { + input = this.getClass().getClassLoader().getResourceAsStream("template/MaDevTemplate.xlsx"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("content-Type", "application/vnd.ms-excel"); + response.setHeader("Content-Disposition", + "attachment;filename=" + new String((templateName).getBytes(), "iso-8859-1")); + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); + out = response.getOutputStream(); + // 缓冲区 + byte[] buffer = new byte[1024]; + int bytesToRead = -1; + // 通过循环将读入内容输出到浏览器中 + while ((bytesToRead = input.read(buffer)) != -1) { + out.write(buffer, 0, bytesToRead); + } + } catch (IOException e) { + log.error(e.getMessage()); + } finally { + IOUtils.closeQuietly(input); + IOUtils.closeQuietly(out); + } + } + + /** + * 设备信息导入 + * @param file + * @return + */ + @Override + public AjaxResult importTbPeople(MultipartFile file) { + String fileName = file.getOriginalFilename(); + if (fileName != null) { + String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1); + if (!MaterialConstants.XLSX.equalsIgnoreCase(fileExtension)) { + // 文件后缀名不符合要求 + return AjaxResult.error("导入失败:文件后缀名不符合要求,必须为xlsx结尾"); + } + } + try { + InputStream inputStream = file.getInputStream(); + Workbook workbook = new XSSFWorkbook(inputStream); + Sheet sheet = workbook.getSheetAt(0); + // 得到Excel的行数 + int totalRows = sheet.getPhysicalNumberOfRows(); + // 检查是否有行数 + if (totalRows <= 1) { + throw new IllegalArgumentException("导入失败:Excel文件中没有数据,请检查后重新导入"); + } + // 读取第一行表头 + Row headerRow = sheet.getRow(0); + if (headerRow == null) { + throw new IllegalArgumentException("导入失败:文件中没有表头"); + } + // 获取表头的列数 + int totalCells = headerRow.getPhysicalNumberOfCells(); + // 预期的表头列数为11列,可以根据实际需求修改这个条件 + if (totalCells != 11) { + throw new IllegalArgumentException("导入失败:表头列数与预期不符,请检查导入模板"); + } + // 获取数据行数 + int rowCount = sheet.getLastRowNum() + 1; + if (rowCount > 50) { + throw new IllegalArgumentException("导入失败:数据总条数不能超过 50 条"); + } + // 读取表头内容并验证每一列 + extractedText(headerRow, totalCells); + //读取Excel表格数据,做非空及格式判断 + //extractedCell(sheet, totalRows, totalCells); + ExcelUtil util = new ExcelUtil<>(DevTemplateVo.class); + List maDevList = util.importExcel(file.getInputStream()); + List templateVos = new ArrayList<>(); + List dtoList = new ArrayList<>(); + // 判断装备类目是否为空,查询装备id及价格 + if (!CollectionUtils.isEmpty(maDevList)) { + for (DevTemplateVo devTemplateVo : maDevList) { + if (StringUtils.isNotBlank(devTemplateVo.getTypeName())) { + // 根据装备类目查询装备id及价格 + DevInfoVo devType = devInfoMapper.selectDevTypeByName(devTemplateVo.getTypeName()); + if (devType != null) { + devTemplateVo.setTypeId(Long.valueOf(devType.getId())); + devTemplateVo.setTypeName(devType.getTypeName()); + devTemplateVo.setDayLeasePrice(devType.getDayLeasePrice()); + devTemplateVo.setUnitName(devType.getUnitName()); + if ("0".equals(devType.getManageType())) { + devTemplateVo.setDeviceCount(1); + } + } + } + } + // 对maDevList通过装备名称进行分组 + Map> map = maDevList.stream().collect(Collectors.groupingBy(DevTemplateVo::getDeviceName)); + for (Map.Entry> entry : map.entrySet()) { + // 如果map的数量大于1,则进行遍历 + if (entry.getValue().size() > 1) { + DevTemplateVo devTemplateVo = entry.getValue().get(0); + DevTemplateVo dto = new DevTemplateVo(); + List devTemplateVos = entry.getValue(); + for (DevTemplateVo templateVo : devTemplateVos) { + dto.setIdentifyCode(StringUtils.isNotBlank(templateVo.getIdentifyCode()) ? templateVo.getIdentifyCode() : null); + dto.setCheckMan(StringUtils.isNotBlank(templateVo.getCheckMan()) ? templateVo.getCheckMan() : null); + dto.setCheckDate(StringUtils.isNotBlank(templateVo.getCheckDate()) ? templateVo.getCheckDate() : null); + dto.setNextCheckDate(StringUtils.isNotBlank(templateVo.getNextCheckDate()) ? templateVo.getNextCheckDate() : null); + dtoList.add(dto); + } + devTemplateVo.setDtoList(dtoList); + templateVos.add(devTemplateVo); + } else { + DevTemplateVo devTemplateVo = entry.getValue().get(0); + templateVos.add(devTemplateVo); + } + } + } + return AjaxResult.success(templateVos); + } catch (IOException e) { + e.printStackTrace(); + } + return AjaxResult.error(HttpCodeEnum.FAIL.getCode(), HttpCodeEnum.FAIL.getMsg()); + } + + /** + * 读取Excel表格数据,做非空判断 + * @param sheet + * @param totalRows + * @param totalCells + */ + private void extractedCell(Sheet sheet, int totalRows, int totalCells) { + //读取Excel表格数据,做非空判断 + // 循环Excel行数 + DataFormatter dataFormatter = new DataFormatter(); + for (int r = 1; r < totalRows; r++) { + Row row = sheet.getRow(r); + // 循环Excel列数 + for (int c = 0; c < totalCells; c++) { + String cellValue = dataFormatter.formatCellValue(row.getCell(c)); + switch (c) { + case 0: + checkBlank(cellValue, r, c); + break; + case 3: + checkDate(cellValue, r, c); + break; + case 4: + checkBlank(cellValue, r, c); + checkPhone(cellValue, r, c); + break; + default: + throw new IllegalArgumentException( + String.format("第 %d 行,第 %d 列超出范围,请检查后重新导入", r + 1, c + 1)); + } + } + } + } + + /** + * 检查日期格式 + * @param cellValue + * @param rowIndex + * @param colIndex + */ + private void checkDate(String cellValue, int rowIndex, int colIndex) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + try { + // 尝试解析日期字符串 + LocalDate.parse(cellValue, formatter); + System.out.println(true); + } catch (DateTimeParseException e) { + // 解析失败,说明日期格式不符合要求 + if (StringUtils.isBlank(cellValue)) { + throw new IllegalArgumentException( + String.format("第 %d 行,第 %d 列出厂日期不符合格式要求,请检查后重新导入", rowIndex + 1, colIndex + 1)); + } + } + } + + /** + * 检查数据是否为空 + * @param cellValue + * @param rowIndex + * @param colIndex + */ + private void checkBlank(String cellValue, int rowIndex, int colIndex) { + if (StringUtils.isBlank(cellValue)) { + throw new IllegalArgumentException( + String.format("第 %d 行,第 %d 列数据为空,请检查后重新导入", rowIndex + 1, colIndex + 1)); + } + } + + /** + * 检查电话号码格式 + * @param cellValue + * @param rowIndex + * @param colIndex + */ + private void checkPhone(String cellValue, int rowIndex, int colIndex) { + if (!PhoneUtil.isMobile(cellValue)) { + throw new IllegalArgumentException( + String.format("第 %d 行,第 %d 列电话号码格式不正确,请检查后重新导入", rowIndex + 1, colIndex + 1)); + } + } + + /** + * 读取Excel表格数据,做非空及格式判断 + * @param headerRow + * @param totalCells + */ + private void extractedText(Row headerRow, int totalCells) { + for (int cellNum = 0; cellNum < totalCells; cellNum++) { + Cell cell = headerRow.getCell(cellNum); + // 获取单元格内容并去除首尾空格 + String headerValue = cell.getStringCellValue().trim(); + // 根据列索引进行验证 + switch (cellNum) { + case 0: + if (!"装备名称".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 1: + if (!"装备类目".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 2: + if (!"装备品牌".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 3: + if (!"出厂日期(例:2025-03-17)".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 4: + if (!"联系人".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 5: + if (!"联系电话".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 6: + if (!"上架数量(编码类型设备默传1)".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 7: + if (!"唯一标识符".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 8: + if (!"检修人".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 9: + if (!"检修时间(例:2025-03-17)".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + case 10: + if (!"下次检测时间(例:2025-03-17)".equals(headerValue)) { + throw new IllegalArgumentException("第 " + (cellNum + 1) + " 列表头列名与预期不符,请检查导入模板"); + } + break; + default: + break; + } + } + } + @Override public void insertOutType(String devInfo) { ObjectMapper objectMapper = new ObjectMapper(); 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 057658c..daf4887 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 @@ -1196,4 +1196,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" d.dept_id = #{companyId} AND d.del_flag = '0' + diff --git a/bonus-modules/bonus-material-mall/src/main/resources/template/MaDevTemplate.xlsx b/bonus-modules/bonus-material-mall/src/main/resources/template/MaDevTemplate.xlsx new file mode 100644 index 0000000..ff3cd35 Binary files /dev/null and b/bonus-modules/bonus-material-mall/src/main/resources/template/MaDevTemplate.xlsx differ