feat: 导入License

This commit is contained in:
caiyuchao
2025-08-06 18:36:25 +08:00
parent 16453c00df
commit d6b54b630c
8 changed files with 258 additions and 8 deletions

View File

@@ -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, "存在子评论,无法删除");

View File

@@ -134,7 +134,7 @@ public class CustomerController {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
PageResult<CustomerDO> pageResult = customerService.getCustomerPage(pageReqVO);
// 导出 Excel
ExcelUtils.write(response, "客户.xls", "数据", CustomerRespVO.class,
ExcelUtils.write(response, "客户.xlsx", "数据", CustomerRespVO.class,
buildCustomerVOList(pageResult).getList());
}

View File

@@ -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<LicenseImportExcelVO> 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<ImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
List<LicenseImportExcelVO> list = ExcelUtils.read(file, LicenseImportExcelVO.class);
return success(licenseService.importList(list, updateSupport));
}
}

View File

@@ -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<String> creates;
@Schema(description = "更新成功的数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> updates;
@Schema(description = "导入失败的集合key 为导入关键字段value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, String> failures;
}

View File

@@ -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;
}

View File

@@ -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<ProjectRespVO> list = projectService.getProjectPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "项目.xls", "数据", ProjectRespVO.class,
ExcelUtils.write(response, "项目.xlsx", "数据", ProjectRespVO.class,
TranslateUtils.translate(list));
}

View File

@@ -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<LicenseRespVO> getLicenseHistory(Long id);
ImportRespVO importList(List<LicenseImportExcelVO> list, Boolean updateSupport);
}

View File

@@ -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<LicenseImportExcelVO> 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<String, List<LicenseImportExcelVO>> map = list.stream().collect(Collectors.groupingBy(LicenseImportExcelVO::getSerialNo));
for (Map.Entry<String, List<LicenseImportExcelVO>> entry : map.entrySet()) {
String serialNo = entry.getKey();
List<LicenseImportExcelVO> 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.<CustomerDO>lambdaQuery().eq(CustomerDO::getCode, customerCode));
customerId = customerDO.getId();
ProjectDO projectDO = projectMapper.selectOne(Wrappers.<ProjectDO>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<LicenseDetailDO> licenseDetails = new ArrayList<>();
for (LicenseImportExcelVO importDetailVO : importList) {
if (StrUtil.isBlank(importDetailVO.getActivationCode())) {
respVO.getFailures().put(serialNo, "激活码不能为空");
continue;
}
String neListStr = importDetailVO.getNeListStr();
List<String> neLabels = StrUtil.split(neListStr, "_");
List<Integer> 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.<LicenseDetailDO>lambdaQuery().eq(LicenseDetailDO::getLicenseId, licenseDO.getId()));
List<LicenseHistoryDO> licenseHistoryDOS = licenseHistoryMapper.selectList(Wrappers.<LicenseHistoryDO>lambdaQuery().eq(LicenseHistoryDO::getLicenseId, licenseDO.getId()));
for (LicenseHistoryDO licenseHistoryDO : licenseHistoryDOS) {
licenseDetailHistoryMapper.delete(Wrappers.<LicenseDetailHistoryDO>lambdaQuery().eq(LicenseDetailHistoryDO::getLicenseId, licenseHistoryDO.getId()));
}
licenseHistoryMapper.delete(Wrappers.<LicenseHistoryDO>lambdaQuery().eq(LicenseHistoryDO::getLicenseId, licenseDO.getId()));
licenseDetailMapper.insertBatch(licenseDetails);
respVO.getUpdates().add(serialNo);
}
return respVO;
}
private void fillLicenseHistoryRespVO(LicenseRespVO license) {
List<LicenseDetailHistoryDO> licenseDetailDOS = licenseDetailHistoryMapper.selectList(Wrappers.<LicenseDetailHistoryDO>lambdaQuery().eq(LicenseDetailHistoryDO::getLicenseId, license.getId()));
List<LicenseDetailVO> details = BeanUtils.toBean(licenseDetailDOS, LicenseDetailVO.class);