Merge branch 'plp'

# Conflicts:
#	yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/criticalcomponent/CriticalComponentServiceImpl.java
plp
86158 2 hours ago
commit 1a9dadec7e

@ -8,9 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.*;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper;
@ -19,14 +17,17 @@ import cn.iocoder.yudao.module.erp.service.product.ErpProductCategoryService;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
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 org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@ -169,4 +170,49 @@ public class ErpProductController {
pageResult.getList());
}
@GetMapping("/get-import-template")
@Operation(summary = "获取产品导入模板")
@ApiAccessLog(operateType = EXPORT)
public void getImportTemplate(HttpServletResponse response) throws IOException {
// 准备模板数据
List<ErpProductImportExcelVO> list = new ArrayList<>();
list.add(ErpProductImportExcelVO.builder()
.name("备件1")
.barCode("BZ001")
.unitName("个")
.status(0) // 启用状态
.standard("红色")
.remark("测试产品")
.expiryDay(30)
.safetyNumber(new BigDecimal("10.00"))
.build());
list.add(ErpProductImportExcelVO.builder()
.name("备件2")
.barCode("BZ002")
.unitName("个")
.status(0) // 启用状态
.standard("标准")
.remark("备件说明")
.expiryDay(30)
.safetyNumber(new BigDecimal("5.00"))
.build());
// 输出
ExcelUtils.write(response, "产品导入模板.xls", "数据", ErpProductImportExcelVO.class, list);
}
@PostMapping("/import")
@Operation(summary = "导入产品")
@Parameters({
@Parameter(name = "file", description = "Excel 文件", required = true),
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
})
public CommonResult<ErpProductImportRespVO> importExcel(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
// 1. 读取 Excel 数据
List<ErpProductImportExcelVO> list = ExcelUtils.read(file, ErpProductImportExcelVO.class);
// 2. 调用 Service 并返回结果
return success(productService.importProductList(list, updateSupport));
}
}

@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product;
import com.alibaba.excel.annotation.ExcelIgnore;
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.math.BigDecimal;
import java.time.LocalDateTime;
/**
* ERP Excel VO
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false)
public class ErpProductImportExcelVO {
@ExcelProperty("产品名称")
private String name;
@ExcelProperty("产品条码")
private String barCode;
// @ExcelIgnore
// private String categoryName;
@ExcelIgnore
private String subCategoryName;
@ExcelProperty("单位")
private String unitName;
@ExcelProperty("产品状态")
private Integer status;
@ExcelProperty("产品规格")
private String standard;
@ExcelProperty("产品备注")
private String remark;
@ExcelProperty("保质期天数")
private Integer expiryDay;
@ExcelProperty("预警库存")
private BigDecimal safetyNumber;
// 以下是辅助字段不在Excel中显示
@ExcelIgnore
private Long categoryId; // 分类ID
@ExcelIgnore
private Long subCategoryId; // 子分类ID
@ExcelIgnore
private Long unitId; // 单位ID
@ExcelIgnore
private LocalDateTime createTime;
}

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product;
import lombok.Builder;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* ERP Excel VO
*/
@Data
@Builder
public class ErpProductImportRespVO {
/**
*
*/
private List<String> createNames;
/**
*
*/
private List<String> updateNames;
/**
* key: value:
*/
private Map<String, String> failureNames;
public ErpProductImportRespVO() {
this.createNames = new ArrayList<>();
this.updateNames = new ArrayList<>();
this.failureNames = new LinkedHashMap<>();
}
public ErpProductImportRespVO(List<String> createNames, List<String> updateNames, Map<String, String> failureNames) {
this.createNames = createNames;
this.updateNames = updateNames;
this.failureNames = failureNames;
}
}

@ -27,6 +27,10 @@ public interface ErpProductCategoryMapper extends BaseMapperX<ErpProductCategory
return selectOne(ErpProductCategoryDO::getParentId, parentId, ErpProductCategoryDO::getName, name);
}
default ErpProductCategoryDO selectByName(String name) {
return selectOne(ErpProductCategoryDO::getName, name);
}
default Long selectCountByParentId(Long parentId) {
return selectCount(ErpProductCategoryDO::getParentId, parentId);
}

@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
@ -57,5 +58,9 @@ public interface ErpProductMapper extends BaseMapperX<ErpProductDO> {
.in("category_id" ,categoryIds));
}
default List<ErpProductDO> selectListByNameIn(Collection<String> names) {
return selectList(new LambdaQueryWrapperX<ErpProductDO>().in(ErpProductDO::getName, names));
}
}

@ -76,4 +76,12 @@ public interface ErpProductCategoryService {
return convertMap(getProductCategoryList(ids), ErpProductCategoryDO::getId);
}
/**
*
*
* @param name
* @return
*/
ErpProductCategoryDO getProductCategoryByName(String name);
}

@ -146,4 +146,9 @@ public class ErpProductCategoryServiceImpl implements ErpProductCategoryService
return erpProductCategoryMapper.selectBatchIds(ids);
}
@Override
public ErpProductCategoryDO getProductCategoryByName(String name) {
return erpProductCategoryMapper.selectByName(name);
}
}

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.erp.service.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportExcelVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;
@ -123,4 +125,13 @@ public interface ErpProductService {
return convertMap(getProductList(ids), ErpProductDO::getId);
}
List<ErpProductDO> selectByCategorys(List<Integer> categoryIds);
/**
*
*
* @param importProducts
* @param isUpdateSupport
* @return
*/
ErpProductImportRespVO importProductList(List<ErpProductImportExcelVO> importProducts, boolean isUpdateSupport);
}

@ -1,10 +1,15 @@
package cn.iocoder.yudao.module.erp.service.product;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportExcelVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;
@ -16,17 +21,23 @@ import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductCategoryMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper;
import cn.iocoder.yudao.module.erp.service.stock.ErpStockService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
@ -178,11 +189,15 @@ public class ErpProductServiceImpl implements ErpProductService {
}
Map<Long, ErpProductCategoryDO> categoryMap = productCategoryService.getProductCategoryMap(
convertSet(list, ErpProductDO::getCategoryId));
Map<Long, ErpProductCategoryDO> subCategoryMap = productCategoryService.getProductCategoryMap(
convertSet(list, ErpProductDO::getSubCategoryId));
Map<Long, ErpProductUnitDO> unitMap = productUnitService.getProductUnitMap(
convertSet(list, ErpProductDO::getUnitId));
return BeanUtils.toBean(list, ErpProductRespVO.class, product -> {
MapUtils.findAndThen(categoryMap, product.getCategoryId(),
category -> product.setCategoryName(category.getName()));
MapUtils.findAndThen(subCategoryMap, product.getSubCategoryId(),
subCategory -> product.setSubCategoryName(subCategory.getName()));
MapUtils.findAndThen(unitMap, product.getUnitId(),
unit -> product.setUnitName(unit.getName()));
});
@ -206,4 +221,126 @@ public class ErpProductServiceImpl implements ErpProductService {
List<ErpProductDO> selectByCategorys(List<Integer> categoryIds){
return productMapper.selectByCategorys(categoryIds);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ErpProductImportRespVO importProductList(List<ErpProductImportExcelVO> importProducts, boolean isUpdateSupport) {
// 1. 参数校验
if (CollUtil.isEmpty(importProducts)) {
throw exception(PRODUCT_NOT_EXISTS);
}
// 2. 初始化返回结果
ErpProductImportRespVO respVO = ErpProductImportRespVO.builder()
.createNames(new ArrayList<>())
.updateNames(new ArrayList<>())
.failureNames(new LinkedHashMap<>())
.build();
// 3. 检查Excel中是否有重复的产品名称
Set<String> excelNames = new HashSet<>();
Set<String> duplicateNamesInExcel = new HashSet<>();
for (ErpProductImportExcelVO importProduct : importProducts) {
if (!excelNames.add(importProduct.getName())) {
duplicateNamesInExcel.add(importProduct.getName());
}
}
// 记录Excel中重复的产品名称
for (String duplicateName : duplicateNamesInExcel) {
respVO.getFailureNames().put(duplicateName, "Excel中存在重复的产品名称: " + duplicateName);
}
// 4. 获取数据库中已存在的产品
Set<String> uniqueNamesToCheck = new HashSet<>(excelNames);
uniqueNamesToCheck.removeAll(duplicateNamesInExcel); // 排除Excel中的重复项
List<ErpProductDO> existingProducts = new ArrayList<>();
if (!uniqueNamesToCheck.isEmpty()) {
existingProducts = productMapper.selectListByNameIn(uniqueNamesToCheck);
}
Map<String, ErpProductDO> existingProductMap = existingProducts.stream()
.collect(Collectors.toMap(ErpProductDO::getName, item -> item));
// 5. 遍历导入数据
for (ErpProductImportExcelVO importProduct : importProducts) {
// 如果已在失败列表中,则跳过
if (respVO.getFailureNames().containsKey(importProduct.getName())) {
continue;
}
try {
// 5.1 先处理分类和单位ID确保它们被设置
ErpProductDO productDOForConversion = convertToProductDO(importProduct);
// 5.2 转换为保存对象
ProductSaveReqVO saveReqVO = BeanUtils.toBean(importProduct, ProductSaveReqVO.class);
// 5.3 设置saveReqVO中的ID值以通过验证
saveReqVO.setCategoryId(productDOForConversion.getCategoryId());
saveReqVO.setUnitId(productDOForConversion.getUnitId());
// 5.4 字段校验
ValidationUtils.validate(saveReqVO);
} catch (ConstraintViolationException ex) {
respVO.getFailureNames().put(importProduct.getName(), ex.getMessage());
continue;
}
// 5.2 检查是否已存在
ErpProductDO existProduct = existingProductMap.get(importProduct.getName());
if (existProduct == null) {
try {
// 5.3 创建新记录
ErpProductDO newProduct = convertToProductDO(importProduct);
// 设置创建时间为当前时间
newProduct.setCreateTime(LocalDateTime.now());
productMapper.insert(newProduct);
respVO.getCreateNames().add(importProduct.getName());
} catch (DuplicateKeyException e) {
// 处理并发插入或其他原因导致的重复键异常
respVO.getFailureNames().put(importProduct.getName(), "该产品已存在");
}
} else if (isUpdateSupport) {
// 5.4 更新已有记录
ErpProductDO updateProduct = convertToProductDO(importProduct);
updateProduct.setId(existProduct.getId());
// 保持原有创建时间不变
updateProduct.setCreateTime(existProduct.getCreateTime());
productMapper.updateById(updateProduct);
respVO.getUpdateNames().add(importProduct.getName());
} else {
// 5.5 已存在且不支持更新
respVO.getFailureNames().put(importProduct.getName(), "该产品已存在");
}
}
return respVO;
}
private ErpProductDO convertToProductDO(ErpProductImportExcelVO importProduct) {
ErpProductDO productDO = BeanUtils.toBean(importProduct, ErpProductDO.class);
// 直接设置分类ID为5
productDO.setCategoryId(5L);
// 设置固定子分类名称为"备件"
productDO.setSubCategoryName("备件");
// 查找对应的子分类ID
ErpProductCategoryDO subCategoryDO = productCategoryService.getProductCategoryByName("备件");
if (subCategoryDO != null) {
productDO.setSubCategoryId(subCategoryDO.getId());
}
// 处理单位名称
if (StrUtil.isNotBlank(importProduct.getUnitName())) {
ErpProductUnitDO unitDO = productUnitService.getProductUnitByName(importProduct.getUnitName());
if (unitDO != null) {
productDO.setUnitId(unitDO.getId());
}
}
return productDO;
}
}

@ -92,4 +92,11 @@ public interface ErpProductUnitService {
*/
List<ErpProductUnitDO> getProductUnitList();
/**
*
*
* @param name
* @return
*/
ErpProductUnitDO getProductUnitByName(String name);
}

@ -125,4 +125,8 @@ public class ErpProductUnitServiceImpl implements ErpProductUnitService {
return productUnitMapper.selectList();
}
@Override
public ErpProductUnitDO getProductUnitByName(String name) {
return productUnitMapper.selectByName(name);
}
}

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute;
import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO;
import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper;
import io.swagger.v3.oas.annotations.Parameters;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@ -30,6 +32,7 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO;
import cn.iocoder.yudao.module.iot.service.devicemodelattribute.DeviceModelAttributeService;
import org.springframework.web.multipart.MultipartFile;
@Tag(name = "管理后台 - 采集设备模型-点位管理")
@RestController
@ -39,6 +42,8 @@ public class DeviceModelAttributeController {
@Resource
private DeviceModelAttributeService deviceModelAttributeService;
@Resource
private DeviceModelAttributeMapper deviceModelAttributeMapper;
@PostMapping("/create")
@Operation(summary = "创建采集设备模型-点位管理")
@ -120,4 +125,59 @@ public class DeviceModelAttributeController {
List<Map<String, Object>> deviceModelAttribute = deviceModelAttributeService.operationAnalysisDetails(deviceId,modelId,collectionStartTime,collectionEndTime);
return success(deviceModelAttribute);
}
/////////////// 以下是新增的代码 ///////////////
@GetMapping("/get-import-template")
@Operation(summary = "获取采集设备模型-点位管理导入模板")
@PreAuthorize("@ss.hasPermission('iot:device-model-attribute:import')")
@ApiAccessLog(operateType = EXPORT)
public void getImportTemplate(HttpServletResponse response) throws IOException {
// 准备模板数据
List<DeviceModelAttributeImportExcelVO> list = new ArrayList<>();
list.add(DeviceModelAttributeImportExcelVO.builder()
.attributeCode("test")
.attributeName("温度")
// .attributeType("1")
.typeName("报警信息")
.dataType("int64")
.address("AI0")
.dataUnit("s")
.ratio(1.0)
.remark("温度传感器")
.build());
list.add(DeviceModelAttributeImportExcelVO.builder()
.attributeCode("humidity_001")
.attributeName("湿度")
// .attributeType("2")
.typeName("能源参数")
.dataType("int64")
.address("AI1")
.dataUnit("s")
.ratio(1.0)
.remark("湿度传感器")
.build());
// 输出
ExcelUtils.write(response, "采集设备模型-点位管理导入模板.xls", "数据", DeviceModelAttributeImportExcelVO.class, list);
}
@PostMapping("/import")
@Operation(summary = "导入设备采集点位") // 对应 User 的 "导入用户"
@Parameters({
@Parameter(name = "file", description = "Excel 文件", required = true),
@Parameter(name = "deviceModelId", description = "设备型号ID", required = true, example = "1024"),
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
}) // 完全复用你的 Parameters 注解格式
@PreAuthorize("@ss.hasPermission('system:device-model-attribute:import')") // 对齐 User 的权限码风格
public CommonResult<DeviceModelAttributeImportRespVO> importExcel(
@RequestParam("file") MultipartFile file,
@RequestParam("deviceModelId") Long deviceModelId,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
// 1. 读取 Excel 数据 - 完全复用你的 ExcelUtils.read 调用方式
List<DeviceModelAttributeImportExcelVO> list = ExcelUtils.read(file, DeviceModelAttributeImportExcelVO.class);
// 2. 调用 Service 并返回结果 - 完全复用你的 success 包装方式
return success(deviceModelAttributeService.importAttributeList(list, deviceModelId, updateSupport));
}
}

@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo;
//import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
//import cn.iocoder.yudao.module.system.enums.DictTypeConstants; // 沿用你项目中的字典枚举
import com.alibaba.excel.annotation.ExcelIgnore;
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;
/**
* Excel VO
* UserImportExcelVO
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 完全复用你的配置,避免导入问题
public class DeviceModelAttributeImportExcelVO {
@ExcelProperty("点位编码") // 对应 User 的 "登录名称"
private String attributeCode;
@ExcelProperty("点位名称") // 对应 User 的 "用户名称"
private String attributeName;
// 点位类型不在Excel中显示
@ExcelIgnore
private String attributeType;
@ExcelProperty("点位类型名称")
private String typeName;
@ExcelProperty(value = "数据类型") // 对应 User 的 "用户性别"
// , converter = DictConvert.class
// @DictFormat("device_data_type") // 替换为你项目中数据类型的字典编码,对齐 User 的 DictFormat 用法
private String dataType;
@ExcelProperty("寄存器地址")
private String address;
@ExcelProperty("单位") // 对应 User 的 "用户邮箱"
private String dataUnit;
@ExcelProperty("倍率")
private Double ratio;
@ExcelProperty("备注")
private String remark;
// 设备型号ID不在Excel中显示由前端传入
@ExcelIgnore
private Long deviceModelId;
}

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* - Response VO
* UserImportRespVO
*/
@Schema(description = "管理后台 - 设备采集点位导入 Response VO")
@Data
@Builder
public class DeviceModelAttributeImportRespVO {
@Schema(description = "创建成功的点位编码数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> createCodes; // 对应 User 的 createUsernames替换为点位唯一标识编码
@Schema(description = "更新成功的点位编码数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> updateCodes; // 对应 User 的 updateUsernames替换为点位唯一标识编码
@Schema(description = "导入失败的点位集合key 为点位编码value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, String> failureCodes; // 对应 User 的 failureUsernames替换为点位唯一标识编码
}

@ -27,7 +27,7 @@ public class DeviceModelAttributeRespVO {
@Schema(description = "点位类型", example = "1")
@ExcelProperty("点位类型")
private Long attributeType;
private String attributeType;
@Schema(description = "类型名称", example = "1")
@ExcelProperty("类型名称")

@ -65,4 +65,8 @@ public interface DeviceModelAttributeMapper extends BaseMapperX<DeviceModelAttri
}
default DeviceModelAttributeDO selectByAttributeCode(String attributeCode) {
return selectOne(DeviceModelAttributeDO::getAttributeCode, attributeCode);
}
}

@ -55,4 +55,12 @@ public interface DeviceModelAttributeService {
List<Map<String, Object>> operationAnalysisDetails(Long deviceId, Long modelId,String collectionStartTime, String collectionEndTime);
List<DeviceModelAttributeDO> getDeviceModelAttributeList(Long id);
/**
*
* UserService importUserList
*/
DeviceModelAttributeImportRespVO importAttributeList(List<DeviceModelAttributeImportExcelVO> importAttributes, Long deviceModelId, boolean isUpdateSupport);
}

@ -1,7 +1,11 @@
package cn.iocoder.yudao.module.iot.service.devicemodelattribute;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO;
import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper;
import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.iot.service.device.TDengineService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
@ -10,6 +14,8 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.ConstraintViolationException;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
@ -36,7 +42,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
@Service
@Validated
@Slf4j
public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeService {
public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeService {
@Resource
private DeviceModelAttributeMapper deviceModelAttributeMapper;
@ -218,4 +224,110 @@ public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeServ
}
@Override
@Transactional(rollbackFor = Exception.class) // 完全复用你的事务注解,异常回滚所有导入
public DeviceModelAttributeImportRespVO importAttributeList(List<DeviceModelAttributeImportExcelVO> importAttributes, Long deviceModelId, boolean isUpdateSupport) {
// 1.1 参数校验 - 完全复刻 User 的空列表校验逻辑
if (CollUtil.isEmpty(importAttributes)) {
throw exception(DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS);
}
// 1.2 校验设备型号ID是否存在
if (deviceModelId == null) {
throw exception(DEVICE_MODEL_NOT_EXISTS);
}
// 2. 构建点位类型名称到ID的映射
List<DeviceAttributeTypeDO> attributeTypes = deviceAttributeTypeMapper.selectList();
Map<String, String> typeNameToIdMap = attributeTypes.stream()
.collect(Collectors.toMap(DeviceAttributeTypeDO::getName, type -> String.valueOf(type.getId())));
// 3. 初始化返回 VO - 完全复刻 User 的 builder 方式,使用 LinkedHashMap 保持顺序
DeviceModelAttributeImportRespVO respVO = DeviceModelAttributeImportRespVO.builder()
.createCodes(new ArrayList<>())
.updateCodes(new ArrayList<>())
.failureCodes(new LinkedHashMap<>())
.build();
// 4. 遍历,逐个创建 or 更新 - 完全复刻 User 的 forEach 逻辑
importAttributes.forEach(importAttribute -> {
// 4.1 设置设备型号ID
importAttribute.setDeviceModelId(deviceModelId);
// 4.2 处理点位类型转换
String typeName = importAttribute.getTypeName();
if (StringUtils.isBlank(typeName)) {
respVO.getFailureCodes().put(importAttribute.getAttributeCode(), "点位类型名称不能为空");
return;
}
String attributeTypeId = typeNameToIdMap.get(typeName);
if (StringUtils.isBlank(attributeTypeId)) {
respVO.getFailureCodes().put(importAttribute.getAttributeCode(), "点位类型名称无效: " + typeName);
return;
}
// 设置点位类型ID
importAttribute.setAttributeType(attributeTypeId);
// 4.3.1 校验字段是否符合要求 - 完全复刻 User 的 ValidationUtils.validate 调用
try {
// 注意:如果有 DeviceModelAttributeSaveReqVO替换为该 VO无则直接校验 importAttribute
ValidationUtils.validate(BeanUtils.toBean(importAttribute, DeviceModelAttributeSaveReqVO.class));
} catch (ConstraintViolationException ex) {
respVO.getFailureCodes().put(importAttribute.getAttributeCode(), ex.getMessage());
return;
}
// 4.3.2 业务规则校验 - 完全复刻 User 的 validateUserForCreateOrUpdate 逻辑
try {
validateAttributeForCreateOrUpdate(deviceModelId, importAttribute.getAttributeCode());
} catch (ServiceException ex) {
respVO.getFailureCodes().put(importAttribute.getAttributeCode(), ex.getMessage());
return;
}
// 4.4.1 判断如果不存在,进行插入 - 完全复刻 User 的 selectByUsername 逻辑
DeviceModelAttributeDO existAttribute = deviceModelAttributeMapper.selectByAttributeCode(importAttribute.getAttributeCode());
if (existAttribute == null) {
// 创建DO对象并设置点位类型名称
DeviceModelAttributeDO deviceModelAttribute = BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class);
deviceModelAttribute.setTypeName(typeName); // 设置点位类型名称
deviceModelAttributeMapper.insert(deviceModelAttribute);
respVO.getCreateCodes().add(importAttribute.getAttributeCode());
return;
}
// 4.4.2 如果存在,判断是否允许更新 - 完全复刻 User 的更新逻辑
if (!isUpdateSupport) {
respVO.getFailureCodes().put(importAttribute.getAttributeCode(), DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS.getMsg());
return;
}
// 创建更新对象并设置点位类型名称
DeviceModelAttributeDO updateAttribute = BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class);
updateAttribute.setId(existAttribute.getId()); // 复用已有数据的 ID
updateAttribute.setTypeName(typeName); // 设置点位类型名称
deviceModelAttributeMapper.updateById(updateAttribute);
respVO.getUpdateCodes().add(importAttribute.getAttributeCode());
});
return respVO;
}
/**
* /
* User validateUserForCreateOrUpdate
*/
private void validateAttributeForCreateOrUpdate(Long deviceModelId, String code) {
// 1. 校验设备型号是否存在(可根据你的业务补充,对应 User 的部门校验)
if (deviceModelId == null) {
throw exception(ErrorCodeConstants.DEVICE_MODEL_NOT_EXISTS); // 替换为你项目中的设备型号不存在错误码
}
// 2. 可补充其他业务校验(如数据类型合法性、单位长度等)
}
}

@ -32,6 +32,9 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.mes.controller.admin.criticalcomponent.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.criticalcomponent.CriticalComponentDO;
import cn.iocoder.yudao.module.mes.service.criticalcomponent.CriticalComponentService;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Parameter;
@Tag(name = "管理后台 - 设备关键件")
@RestController
@ -128,4 +131,43 @@ public class CriticalComponentController {
ExcelUtils.write(response, fileName, "数据", CriticalComponentExcelVO.class,list);
}
@GetMapping("/get-import-template")
@Operation(summary = "获取设备关键件导入模板")
@PreAuthorize("@ss.hasPermission('mes:critical-component:import')")
@ApiAccessLog(operateType = EXPORT)
public void getImportTemplate(HttpServletResponse response) throws IOException {
// 准备模板数据
List<CriticalComponentImportExcelVO> list = new ArrayList<>();
list.add(CriticalComponentImportExcelVO.builder()
.code("TEST001")
.name("电机")
.description("主驱动电机")
.remark("西门子品牌")
.build());
list.add(CriticalComponentImportExcelVO.builder()
.code("TEST002")
.name("传感器")
.description("温度传感器")
.remark("欧姆龙品牌")
.build());
// 输出
ExcelUtils.write(response, "设备关键件导入模板.xls", "数据", CriticalComponentImportExcelVO.class, list);
}
@PostMapping("/import")
@Operation(summary = "导入设备关键件")
@Parameters({
@Parameter(name = "file", description = "Excel 文件", required = true),
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
})
@PreAuthorize("@ss.hasPermission('mes:critical-component:import')")
public CommonResult<CriticalComponentImportRespVO> importExcel(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
// 1. 读取 Excel 数据
List<CriticalComponentImportExcelVO> list = ExcelUtils.read(file, CriticalComponentImportExcelVO.class);
// 2. 调用 Service 并返回结果
return success(criticalComponentService.importCriticalComponentList(list, updateSupport));
}
}

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.mes.controller.admin.criticalcomponent.vo;
import com.alibaba.excel.annotation.ExcelIgnore;
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.LocalDateTime;
/**
* Excel VO
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false)
public class CriticalComponentImportExcelVO {
@ExcelProperty("编码")
private String code;
@ExcelProperty("名称")
private String name;
@ExcelProperty("描述")
private String description;
@ExcelProperty("备注")
private String remark;
// 创建时间不在Excel中显示自动生成
@ExcelIgnore
private LocalDateTime createTime;
}

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.mes.controller.admin.criticalcomponent.vo;
import lombok.Builder;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Excel VO
*/
@Data
@Builder
public class CriticalComponentImportRespVO {
/**
*
*/
private List<String> createCodes;
/**
*
*/
private List<String> updateCodes;
/**
* key: value:
*/
private Map<String, String> failureCodes;
public CriticalComponentImportRespVO() {
this.createCodes = new ArrayList<>();
this.updateCodes = new ArrayList<>();
this.failureCodes = new LinkedHashMap<>();
}
public CriticalComponentImportRespVO(List<String> createCodes, List<String> updateCodes, Map<String, String> failureCodes) {
this.createCodes = createCodes;
this.updateCodes = updateCodes;
this.failureCodes = failureCodes;
}
}

@ -6,6 +6,7 @@ import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.criticalcomponent.CriticalComponentDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO;
import com.alibaba.excel.util.StringUtils;
@ -43,4 +44,8 @@ public interface CriticalComponentMapper extends BaseMapperX<CriticalComponentDO
return selectPage(reqVO, criticalComponentDOLambdaQueryWrapperX);
}
default CriticalComponentDO selectByCode(String code) {
return selectOne(CriticalComponentDO::getCode, code);
}
}

@ -56,4 +56,9 @@ public interface CriticalComponentService {
List<CriticalComponentExcelVO> exportDeviceComponent(Long id);
/**
*
*/
CriticalComponentImportRespVO importCriticalComponentList(List<CriticalComponentImportExcelVO> importComponents, boolean isUpdateSupport);
}

@ -1,17 +1,24 @@
package cn.iocoder.yudao.module.mes.service.criticalcomponent;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.repairtems.RepairTemsDO;
import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.repairtems.RepairTemsMapper;
import cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants;
import com.alibaba.excel.util.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.ConstraintViolationException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@ -197,4 +204,56 @@ public class CriticalComponentServiceImpl implements CriticalComponentService {
return BeanUtils.toBean(componentList,CriticalComponentExcelVO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public CriticalComponentImportRespVO importCriticalComponentList(List<CriticalComponentImportExcelVO> importComponents, boolean isUpdateSupport) {
// 1. 参数校验
if (CollUtil.isEmpty(importComponents)) {
throw exception(ErrorCodeConstants.CRITICAL_COMPONENT_NOT_EXISTS);
}
// 2. 初始化返回结果
CriticalComponentImportRespVO respVO = CriticalComponentImportRespVO.builder()
.createCodes(new ArrayList<>())
.updateCodes(new ArrayList<>())
.failureCodes(new LinkedHashMap<>())
.build();
// 3. 遍历导入数据
importComponents.forEach(importComponent -> {
try {
// 3.1 字段校验
ValidationUtils.validate(BeanUtils.toBean(importComponent, CriticalComponentSaveReqVO.class));
} catch (ConstraintViolationException ex) {
respVO.getFailureCodes().put(importComponent.getCode(), ex.getMessage());
return;
}
// 3.2 检查是否已存在
CriticalComponentDO existComponent = criticalComponentMapper.selectByCode(importComponent.getCode());
if (existComponent == null) {
// 3.3 创建新记录
CriticalComponentDO newComponent = BeanUtils.toBean(importComponent, CriticalComponentDO.class);
// 设置创建时间为当前时间
newComponent.setCreateTime(LocalDateTime.now());
criticalComponentMapper.insert(newComponent);
respVO.getCreateCodes().add(importComponent.getCode());
} else if (isUpdateSupport) {
// 3.4 更新已有记录
CriticalComponentDO updateComponent = BeanUtils.toBean(importComponent, CriticalComponentDO.class);
updateComponent.setId(existComponent.getId());
// 保持原有创建时间不变
updateComponent.setCreateTime(existComponent.getCreateTime());
criticalComponentMapper.updateById(updateComponent);
respVO.getUpdateCodes().add(importComponent.getCode());
} else {
// 3.5 已存在且不支持更新
respVO.getFailureCodes().put(importComponent.getCode(), "该关键件已存在");
}
});
return respVO;
}
}
Loading…
Cancel
Save