diff --git a/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java b/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java index d06e569..1afcb23 100644 --- a/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java +++ b/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java @@ -15,7 +15,8 @@ public interface ErrorCodeConstants { ErrorCode PROJECT_NAME_DUPLICATE = new ErrorCode(1_100_001_002, "项目名称`{}`已存在"); ErrorCode PROJECT_CODE_DUPLICATE = new ErrorCode(1_100_001_003, "项目编号`{}`已存在"); ErrorCode LICENSE_NOT_EXISTS = new ErrorCode(1_100_002_001, "License不存在"); - ErrorCode LICENSE_SN_DUPLICATE = new ErrorCode(1_100_001_002, "License SN`{}`已存在"); + ErrorCode LICENSE_SN_DUPLICATE = new ErrorCode(1_100_002_002, "License SN`{}`已存在"); + ErrorCode LICENSE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_100_002_003, "导入License数据不能为空!"); ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_100_003_001, "评论不存在"); ErrorCode COMMENT_EXITS_CHILDREN = new ErrorCode(1_100_003_002, "存在子评论,无法删除"); diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/customer/CustomerController.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/customer/CustomerController.java index 68c339f..afa301d 100644 --- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/customer/CustomerController.java +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/customer/CustomerController.java @@ -134,7 +134,7 @@ public class CustomerController { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); PageResult pageResult = customerService.getCustomerPage(pageReqVO); // 导出 Excel - ExcelUtils.write(response, "客户.xls", "数据", CustomerRespVO.class, + ExcelUtils.write(response, "客户.xlsx", "数据", CustomerRespVO.class, buildCustomerVOList(pageResult).getList()); } diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java index 522c2a4..5c41754 100644 --- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; @@ -16,7 +17,9 @@ import org.agt.framework.common.util.object.BeanUtils; import org.agt.framework.dict.core.DictFrameworkUtils; import org.agt.framework.excel.core.util.ExcelUtils; import org.agt.framework.translate.core.TranslateUtils; +import org.agt.module.license.controller.admin.license.vo.ImportRespVO; import org.agt.module.license.controller.admin.license.vo.LicenseDetailVO; +import org.agt.module.license.controller.admin.license.vo.LicenseImportExcelVO; import org.agt.module.license.controller.admin.license.vo.LicensePageReqVO; import org.agt.module.license.controller.admin.license.vo.LicenseRespVO; import org.agt.module.license.controller.admin.license.vo.LicenseSaveReqVO; @@ -31,9 +34,12 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.time.LocalDate; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static org.agt.framework.apilog.core.enums.OperateTypeEnum.EXPORT; @@ -168,7 +174,7 @@ public class LicenseController { } // 导出 Excel - ExcelUtils.write(response, "License.xls", "数据", LicenseRespVO.class, + ExcelUtils.write(response, "License.xlsx", "数据", LicenseRespVO.class, TranslateUtils.translate(results)); } @@ -178,4 +184,39 @@ public class LicenseController { Boolean isExists = licenseService.validateLicenseSnUnique(sn, id); return success(isExists); } + + @GetMapping("/get-import-template") + @Operation(summary = "获得导入用户模板") + public void importTemplate(HttpServletResponse response) throws IOException { + // 手动创建导出 demo + List list = Arrays.asList( + LicenseImportExcelVO.builder().serialNo("20002000") + .expiryDateExt(LocalDate.now()).userNumber(100).ranNumber(10).neListStr("AMF") + .activationCode("C097A721D68F07694FBA5EFB31626A623FB6A0FAE8E3002CA9C2561F3A9D54E63619E047F0E06862") + .build(), + LicenseImportExcelVO.builder().serialNo("20002000") + .expiryDateExt(LocalDate.now()).userNumber(100).ranNumber(10).neListStr("SMF_UDM") + .activationCode("C097A721D68F07694FBA5EFB31626A623FB6A0FAE8E3002CA9C2561F3A9D54E63619E047F0E06862") + .build(), + LicenseImportExcelVO.builder().serialNo("20012001") + .expiryDateExt(LocalDate.now()).userNumber(200).ranNumber(30).neListStr("IMS") + .activationCode("C097A721D68F07694FBA5EFB31626A623FB6A0FAE8E3002CA9C2561F3A9D54E63619E047F0E06862") + .build() + ); + // 输出 + ExcelUtils.write(response, "用户导入模板.xlsx", "用户列表", LicenseImportExcelVO.class, list); + } + + @PostMapping("/import") + @Operation(summary = "导入License") + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) + @PreAuthorize("@ss.hasPermission('system:user:import')") + public CommonResult importExcel(@RequestParam("file") MultipartFile file, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + List list = ExcelUtils.read(file, LicenseImportExcelVO.class); + return success(licenseService.importList(list, updateSupport)); + } } \ No newline at end of file diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/vo/ImportRespVO.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/vo/ImportRespVO.java new file mode 100644 index 0000000..459aa7d --- /dev/null +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/vo/ImportRespVO.java @@ -0,0 +1,24 @@ +package org.agt.module.license.controller.admin.license.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 导入 Response VO") +@Data +@Builder +public class ImportRespVO { + + @Schema(description = "创建成功的数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List creates; + + @Schema(description = "更新成功的数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updates; + + @Schema(description = "导入失败的集合,key 为导入关键字段,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failures; + +} diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/vo/LicenseImportExcelVO.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/vo/LicenseImportExcelVO.java new file mode 100644 index 0000000..77dc049 --- /dev/null +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/vo/LicenseImportExcelVO.java @@ -0,0 +1,47 @@ +package org.agt.module.license.controller.admin.license.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.time.LocalDate; + +/** + * License Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 设置 chain = false,避免导入有问题 +public class LicenseImportExcelVO { + + @Schema(description = "sn") + @ExcelProperty("SN") + private String serialNo; + + @Schema(description = "到期时间") + @ExcelProperty("到期时间") + private LocalDate expiryDateExt; + + @Schema(description = "网元") + @ExcelProperty("网元") + private String neListStr; + + @Schema(description = "激活码") + @ExcelProperty("激活码") + private String activationCode; + + @Schema(description = "用户数") + @ExcelProperty("用户数") + private Integer userNumber; + + @Schema(description = "基站数") + @ExcelProperty("基站数") + private Integer ranNumber; + +} diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/project/ProjectController.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/project/ProjectController.java index 08a669d..8243843 100644 --- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/project/ProjectController.java +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/project/ProjectController.java @@ -18,7 +18,6 @@ import org.agt.module.license.controller.admin.project.vo.ProjectPageReqVO; import org.agt.module.license.controller.admin.project.vo.ProjectRespVO; import org.agt.module.license.controller.admin.project.vo.ProjectSaveReqVO; import org.agt.module.license.dal.dataobject.project.ProjectDO; -import org.agt.module.license.dal.mysql.license.LicenseMapper; import org.agt.module.license.service.project.ProjectService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; @@ -46,9 +45,6 @@ public class ProjectController { @Resource private ProjectService projectService; - @Resource - private LicenseMapper licenseMapper; - @PostMapping("/create") @Operation(summary = "创建项目") @PreAuthorize("@ss.hasPermission('license:project:create')") @@ -107,7 +103,7 @@ public class ProjectController { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = projectService.getProjectPage(pageReqVO).getList(); // 导出 Excel - ExcelUtils.write(response, "项目.xls", "数据", ProjectRespVO.class, + ExcelUtils.write(response, "项目.xlsx", "数据", ProjectRespVO.class, TranslateUtils.translate(list)); } diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java index e65806f..93ef3f5 100644 --- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java @@ -2,6 +2,8 @@ package org.agt.module.license.service.license; import jakarta.validation.Valid; import org.agt.framework.common.pojo.PageResult; +import org.agt.module.license.controller.admin.license.vo.ImportRespVO; +import org.agt.module.license.controller.admin.license.vo.LicenseImportExcelVO; import org.agt.module.license.controller.admin.license.vo.LicensePageReqVO; import org.agt.module.license.controller.admin.license.vo.LicenseRespVO; import org.agt.module.license.controller.admin.license.vo.LicenseSaveReqVO; @@ -87,4 +89,6 @@ public interface LicenseService { Boolean validateLicenseSnUnique(String sn, Long id); List getLicenseHistory(Long id); + + ImportRespVO importList(List list, Boolean updateSupport); } \ No newline at end of file diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java index 45186ad..196320d 100644 --- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java +++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java @@ -13,7 +13,9 @@ import org.agt.framework.common.util.object.BeanUtils; import org.agt.framework.dict.core.DictFrameworkUtils; import org.agt.framework.web.core.util.WebFrameworkUtils; import org.agt.module.infra.api.file.FileApi; +import org.agt.module.license.controller.admin.license.vo.ImportRespVO; import org.agt.module.license.controller.admin.license.vo.LicenseDetailVO; +import org.agt.module.license.controller.admin.license.vo.LicenseImportExcelVO; import org.agt.module.license.controller.admin.license.vo.LicensePageReqVO; import org.agt.module.license.controller.admin.license.vo.LicenseRespVO; import org.agt.module.license.controller.admin.license.vo.LicenseSaveReqVO; @@ -47,11 +49,14 @@ import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import static org.agt.framework.common.exception.util.ServiceExceptionUtil.exception; +import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_IMPORT_LIST_IS_EMPTY; import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_NOT_EXISTS; import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_SN_DUPLICATE; @@ -587,6 +592,138 @@ public class LicenseServiceImpl implements LicenseService { return voList; } + @Override + @Transactional(rollbackFor = Exception.class) + public ImportRespVO importList(List list, Boolean updateSupport) { + if (CollUtil.isEmpty(list)) { + throw exception(LICENSE_IMPORT_LIST_IS_EMPTY); + } + + // 根据sn分组 + ImportRespVO respVO = ImportRespVO.builder().creates(new ArrayList<>()) + .updates(new ArrayList<>()).failures(new LinkedHashMap<>()).build(); + Map> map = list.stream().collect(Collectors.groupingBy(LicenseImportExcelVO::getSerialNo)); + for (Map.Entry> entry : map.entrySet()) { + String serialNo = entry.getKey(); + List importList = entry.getValue(); + if (StrUtil.isBlank(serialNo)) { + respVO.getFailures().put(serialNo, "SN不能为空"); + continue; + } + Long customerId = null; + Long projectId = null; + try { + String customerCode = serialNo.substring(0, 4); + String projectCode = serialNo.substring(4); + CustomerDO customerDO = customerMapper.selectOne(Wrappers.lambdaQuery().eq(CustomerDO::getCode, customerCode)); + customerId = customerDO.getId(); + ProjectDO projectDO = projectMapper.selectOne(Wrappers.lambdaQuery().eq(ProjectDO::getCode, projectCode)); + projectId = projectDO.getId(); + } catch (Exception e) { + log.info("导入报错:{}", e.getMessage()); + } + if (customerId == null || projectId == null) { + respVO.getFailures().put(serialNo, "SN不存在"); + continue; + } + LicenseImportExcelVO importVO = importList.get(0); + + List licenseDetails = new ArrayList<>(); + for (LicenseImportExcelVO importDetailVO : importList) { + + if (StrUtil.isBlank(importDetailVO.getActivationCode())) { + respVO.getFailures().put(serialNo, "激活码不能为空"); + continue; + } + String neListStr = importDetailVO.getNeListStr(); + List neLabels = StrUtil.split(neListStr, "_"); + + List neList = new ArrayList<>(); + for (String ne : neLabels) { + String value = DictFrameworkUtils.parseDictDataValue("lic_ne_all", ne); + if (StrUtil.isBlank(value)) { + value = DictFrameworkUtils.parseDictDataValue("lic_ne_5g", ne); + if (StrUtil.isBlank(value)) { + value = DictFrameworkUtils.parseDictDataValue("lic_ne_4g", ne); + if (StrUtil.isBlank(value)) { + value = DictFrameworkUtils.parseDictDataValue("lic_ne_23g", ne); + if (StrUtil.isBlank(value)) { + value = DictFrameworkUtils.parseDictDataValue("lic_ne_add", ne); + } + } + } + } + if (StrUtil.isNotBlank(value)) { + neList.add(Integer.parseInt(value)); + } + + } + if (CollUtil.isEmpty(neList)) { + respVO.getFailures().put(serialNo, "网元不能为空"); + continue; + } + + LicenseDetailDO licenseDetailDO = new LicenseDetailDO(); + licenseDetailDO.setNeList(neList); + licenseDetailDO.setActivationCode(importDetailVO.getActivationCode()); + licenseDetails.add(licenseDetailDO); + + } + if (CollUtil.isEmpty(licenseDetails)) { + respVO.getFailures().put(serialNo, "网元或激活码不能为空"); + continue; + } + + LicenseDO licenseDO = new LicenseDO(); + licenseDO.setCustomerId(customerId); + licenseDO.setProjectId(projectId); + licenseDO.setSerialNo(serialNo); + licenseDO.setStatus(LicenseStatusEnum.COMPLETED.getCode()); + licenseDO.setApplicant(WebFrameworkUtils.getLoginUserId()); + licenseDO.setExpiryDate(LocalDateTime.of(importVO.getExpiryDateExt(), LocalTime.of(23, 59, 59))); + licenseDO.setApplicationTime(LocalDateTime.now()); + licenseDO.setUserNumber(importVO.getUserNumber()); + licenseDO.setRanNumber(importVO.getRanNumber()); + licenseDO.setApprover(161L); + + LicenseDO license = licenseMapper.selectBySn(serialNo); + if (license == null) { + licenseMapper.insert(licenseDO); + for (LicenseDetailDO licenseDetail : licenseDetails) { + licenseDetail.setLicenseId(licenseDO.getId()); + } + + licenseDetailMapper.insertBatch(licenseDetails); + respVO.getCreates().add(serialNo); + continue; + } + + if (!updateSupport) { + respVO.getFailures().put(serialNo, "已存在该SN的License"); + continue; + } + licenseDO.setId(license.getId()); + licenseMapper.updateById(licenseDO); + for (LicenseDetailDO licenseDetail : licenseDetails) { + licenseDetail.setLicenseId(licenseDO.getId()); + } + + licenseDetailMapper.delete(Wrappers.lambdaQuery().eq(LicenseDetailDO::getLicenseId, licenseDO.getId())); + + List licenseHistoryDOS = licenseHistoryMapper.selectList(Wrappers.lambdaQuery().eq(LicenseHistoryDO::getLicenseId, licenseDO.getId())); + for (LicenseHistoryDO licenseHistoryDO : licenseHistoryDOS) { + licenseDetailHistoryMapper.delete(Wrappers.lambdaQuery().eq(LicenseDetailHistoryDO::getLicenseId, licenseHistoryDO.getId())); + } + licenseHistoryMapper.delete(Wrappers.lambdaQuery().eq(LicenseHistoryDO::getLicenseId, licenseDO.getId())); + + licenseDetailMapper.insertBatch(licenseDetails); + respVO.getUpdates().add(serialNo); + + } + + return respVO; + } + private void fillLicenseHistoryRespVO(LicenseRespVO license) { List licenseDetailDOS = licenseDetailHistoryMapper.selectList(Wrappers.lambdaQuery().eq(LicenseDetailHistoryDO::getLicenseId, license.getId())); List details = BeanUtils.toBean(licenseDetailDOS, LicenseDetailVO.class);