liyong
2 天以前 79054eef527c991f9a84ab3bc3fd2603a5521eb1
Merge remote-tracking branch 'origin/dev_New' into dev_New
已添加1个文件
已修改12个文件
356 ■■■■■ 文件已修改
src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductBomController.java 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductStructureController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/BomImportDto.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductStructureMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProcessRoute.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductProcessRoute.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductStructure.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductBomService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductStructureService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductStructureServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceMaintenanceMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java
@@ -27,13 +27,13 @@
    private String maintenancePlanTimeReq;
    @ApiModelProperty("计划保养日期")
    private LocalDateTime maintenancePlanTime;
    private String maintenancePlanTime;
    @ApiModelProperty("实际保养人")
    private String maintenanceActuallyName;
    @ApiModelProperty("实际保养日期")
    private LocalDateTime maintenanceActuallyTime;
    private String maintenanceActuallyTime;
    @ApiModelProperty("实际保养日期")
    private String maintenanceActuallyTimeReq;
src/main/java/com/ruoyi/production/controller/ProductBomController.java
@@ -7,16 +7,23 @@
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.dto.ProductBomDto;
import com.ruoyi.production.dto.ProductProcessDto;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.*;
import com.ruoyi.production.pojo.ProcessRoute;
import com.ruoyi.production.pojo.ProductBom;
import com.ruoyi.production.pojo.ProductProcessRoute;
import com.ruoyi.production.pojo.ProductStructure;
import com.ruoyi.production.service.ProcessRouteService;
import com.ruoyi.production.service.ProductBomService;
import com.ruoyi.production.service.ProductProcessRouteService;
import com.ruoyi.production.service.ProductStructureService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -55,7 +62,7 @@
    @ApiModelProperty("新增BOM")
    @PostMapping("/add")
    @Log(title = "新增", businessType = BusinessType.INSERT)
    public AjaxResult add( @RequestBody ProductBom productBom) {
    public AjaxResult add(@RequestBody ProductBom productBom) {
        return productBomService.add(productBom);
    }
@@ -72,14 +79,14 @@
    public AjaxResult batchDelete(@RequestBody List<Integer> ids) {
        List<ProcessRoute> list = processRouteService.list(Wrappers.<ProcessRoute>lambdaQuery().in(ProcessRoute::getBomId, ids));
        List<ProductProcessRoute> list2 = productProcessRouteService.list(Wrappers.<ProductProcessRoute>lambdaQuery().in(ProductProcessRoute::getBomId, ids));
        if (list.size()>0 || list2.size()>0){
        if (list.size() > 0 || list2.size() > 0) {
            return AjaxResult.error("该BOM已经存在对应的工艺路线,无法进行删除");
        }
        if(CollectionUtils.isEmpty(ids)){
        if (CollectionUtils.isEmpty(ids)) {
            return AjaxResult.error("请选择至少一条数据");
        }
        //删除bom子表
        productStructureService.remove(Wrappers.<ProductStructure>lambdaQuery().in(ProductStructure::getBomId,ids));
        productStructureService.remove(Wrappers.<ProductStructure>lambdaQuery().in(ProductStructure::getBomId, ids));
        return AjaxResult.success(productBomService.removeBatchByIds(ids));
    }
@@ -91,4 +98,18 @@
        return AjaxResult.success(productBoms);
    }
    @PostMapping("uploadBom")
    @Log(title = "根据Excel导入BOM", businessType = BusinessType.IMPORT)
    @ApiOperation("根据Excel导入BOM")
    public AjaxResult uploadBom(@RequestParam("file") MultipartFile file) {
        return productBomService.uploadBom(file);
    }
    @PostMapping("exportBom")
    @Log(title = "导出BOM文件", businessType = BusinessType.EXPORT)
    @ApiOperation("导出BOM文件")
    public void exportBom(HttpServletResponse response, @RequestParam Integer bomId) {
        productBomService.exportBom(response, bomId);
    }
}
src/main/java/com/ruoyi/production/controller/ProductStructureController.java
@@ -34,7 +34,7 @@
    @ApiOperation("BOM查看子集详情")
    @GetMapping("/listBybomId/{bomId}")
    public R listBybomId( @PathVariable("bomId") Long bomId){
    public R listBybomId( @PathVariable("bomId") Integer bomId){
        return R.ok(productStructureService.listBybomId(bomId));
    }
}
src/main/java/com/ruoyi/production/dto/BomImportDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.ruoyi.production.dto;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class BomImportDto {
    @Excel(name = "父项产品编号")
    private String parentCode;
    @Excel(name = "父项产品名称")
    private String parentName;
    @Excel(name = "父项产品规格")
    private String parentSpec;
    @Excel(name = "子项产品编号")
    private String childCode;
    @Excel(name = "子项产品名称")
    private String childName;
    @Excel(name = "子项产品规格")
    private String childSpec;
    @Excel(name = "单位用量")
    private BigDecimal unitQty;
    @Excel(name = "投料工序")
    private String process;
    @Excel(name = "备注")
    private String remark;
}
src/main/java/com/ruoyi/production/mapper/ProductStructureMapper.java
@@ -13,7 +13,7 @@
@Mapper
public interface ProductStructureMapper  extends BaseMapper<ProductStructure> {
    List<ProductStructureDto> listBybomId(@Param("bomId") Long bomId);
    List<ProductStructureDto> listBybomId(@Param("bomId") Integer bomId);
    List<ProductStructureDto> listBybomAndProcess(@Param("bomId") Long bomId, @Param("processId") Long processId);
    List<ProductStructureDto> listBybomAndProcess(@Param("bomId") Integer bomId, @Param("processId") Long processId);
}
src/main/java/com/ruoyi/production/pojo/ProcessRoute.java
@@ -40,5 +40,5 @@
    private String processRouteCode;
    @ApiModelProperty(value = "BOM的ID")
    private Long bomId;
    private Integer bomId;
}
src/main/java/com/ruoyi/production/pojo/ProductProcessRoute.java
@@ -51,7 +51,7 @@
    private LocalDateTime updateTime;
    @ApiModelProperty("关联bom的id")
    private Long bomId;
    private Integer bomId;
    @ApiModelProperty("工艺路线编码")
    private String processRouteCode;
src/main/java/com/ruoyi/production/pojo/ProductStructure.java
@@ -50,7 +50,7 @@
    /**
     * å…³è”BOMid
     */
    private Long bomId;
    private Integer bomId;
    /**
     * çˆ¶èŠ‚ç‚¹ID
src/main/java/com/ruoyi/production/service/ProductBomService.java
@@ -2,11 +2,13 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.dto.ProductBomDto;
import com.ruoyi.production.dto.ProductProcessDto;
import com.ruoyi.production.pojo.ProductBom;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
 * <p>
@@ -21,4 +23,8 @@
    IPage<ProductBomDto> listPage(Page page, ProductBomDto productBomDto);
    AjaxResult add(ProductBom productBom);
    AjaxResult uploadBom(MultipartFile file);
    void exportBom(HttpServletResponse response, Integer bomId);
}
src/main/java/com/ruoyi/production/service/ProductStructureService.java
@@ -12,6 +12,6 @@
    Boolean addProductStructureDto(ProductStructureDto productStructureDto);
    List<ProductStructureDto> listBybomId(Long bomId);
    List<ProductStructureDto> listBybomId(Integer bomId);
}
src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
@@ -1,23 +1,36 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.service.IProductModelService;
import com.ruoyi.basic.service.IProductService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.dto.BomImportDto;
import com.ruoyi.production.dto.ProductBomDto;
import com.ruoyi.production.dto.ProductStructureDto;
import com.ruoyi.production.mapper.ProductBomMapper;
import com.ruoyi.production.mapper.ProductStructureMapper;
import com.ruoyi.production.pojo.ProductBom;
import com.ruoyi.production.pojo.ProductProcess;
import com.ruoyi.production.pojo.ProductStructure;
import com.ruoyi.production.service.ProductBomService;
import com.ruoyi.production.service.ProductProcessService;
import com.ruoyi.production.service.ProductStructureService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -31,13 +44,20 @@
public class ProductBomServiceImpl extends ServiceImpl<ProductBomMapper, ProductBom> implements ProductBomService {
    @Autowired
    private IProductService productService;
    @Autowired
    private ProductBomMapper productBomMapper;
    @Autowired
    private IProductModelService productModelService;
    @Autowired
    private ProductStructureMapper productStructureMapper;
    private ProductStructureService productStructureService;
    @Autowired
    private ProductProcessService productProcessService;
    @Override
    public IPage<ProductBomDto> listPage(Page page, ProductBomDto productBomDto) {
@@ -49,7 +69,6 @@
    public AjaxResult add(ProductBom productBom) {
        boolean save = productBomMapper.insert(productBom) > 0;
        if (save) {
            // æ ¹æ®id生成no字段:GX + 8位数字(不足8位前面补0)
            String no = "BM." + String.format("%05d", productBom.getId());
            productBom.setBomNo(no);
            productBomMapper.updateById(productBom);
@@ -69,12 +88,231 @@
            productStructure.setProductModelId(productBom.getProductModelId());
            productStructure.setUnit(productModel.getUnit());
            productStructure.setUnitQuantity(BigDecimal.valueOf(1));
            productStructure.setBomId(Long.valueOf(productBom.getId()));
            productStructure.setBomId(productBom.getId());
            productStructureMapper.insert(productStructure);
            productStructureService.save(productStructure);
            return AjaxResult.success();
        }
        return AjaxResult.error();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult uploadBom(MultipartFile file) {
        ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class);
        List<BomImportDto> list;
        try {
            list = util.importExcel(file.getInputStream());
        } catch (Exception e) {
            return AjaxResult.error("Excel解析失败");
        }
        if (list == null || list.isEmpty()) return AjaxResult.error("数据为空");
        //  å¤„理工序
        list.forEach(dto -> {
            dto.setParentName(clean(dto.getParentName()));
            dto.setParentSpec(clean(dto.getParentSpec()));
            dto.setChildName(clean(dto.getChildName()));
            dto.setChildSpec(clean(dto.getChildSpec()));
        });
        handleProcess(list);
        Map<String, Long> processMap = productProcessService.list().stream()
                .collect(Collectors.toMap(ProductProcess::getName, ProductProcess::getId, (k1, k2) -> k1));
        //  åˆ›å»º BOM æ•°æ®
        BomImportDto first = list.get(0);
        ProductModel rootModel = findModel(first.getParentName(), first.getParentSpec());
        ProductBom bom = new ProductBom();
        bom.setProductModelId(rootModel.getId());
        bom.setVersion("1.0");
        productBomMapper.insert(bom);
        bom.setBomNo("BM." + String.format("%05d", bom.getId()));
        productBomMapper.updateById(bom);
        // è®°å½•已经插入结构的节点:Key = "名称+规格", Value = structure_id
        Map<String, Long> treePathMap = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            BomImportDto dto = list.get(i);
            String parentKey = dto.getParentName() + "|" + dto.getParentSpec();
            String childKey = dto.getChildName() + "|" + dto.getChildSpec();
            //处理根节点,第一行且子项为空
            if (i == 0 && StringUtils.isBlank(dto.getChildName())) {
                ProductStructure rootNode = new ProductStructure();
                rootNode.setBomId(bom.getId());
                rootNode.setParentId(null); // é¡¶å±‚没有父节点
                rootNode.setProductModelId(rootModel.getId());
                rootNode.setUnitQuantity(BigDecimal.ONE);
                rootNode.setUnit(rootModel.getUnit());
                productStructureService.save(rootNode);
                treePathMap.put(parentKey, rootNode.getId());
                continue;
            }
            //  å¤„理子层级节点
            //  æ‰¾åˆ°çˆ¶èŠ‚ç‚¹åœ¨æ•°æ®åº“é‡Œçš„ ID
            Long parentStructureId = treePathMap.get(parentKey);
            if (parentStructureId == null) {
                // å¦‚æžœ Map é‡Œæ‰¾ä¸åˆ°ï¼Œè¯´æ˜Ž Excel é¡ºåºä¹±äº†æˆ–者数据有误
                throw new ServiceException("导入失败: çˆ¶é¡¹[" + dto.getParentName() + "]必须在其子项之前定义");
            }
            //  èŽ·å–å­é¡¹æ¨¡åž‹ä¿¡æ¯
            ProductModel childModel = findModel(dto.getChildName(), dto.getChildSpec());
            //  æ’入结构表
            ProductStructure node = new ProductStructure();
            node.setBomId(bom.getId());
            node.setParentId(parentStructureId); // çˆ¶èŠ‚ç‚¹ID
            node.setProductModelId(childModel.getId());
            node.setUnitQuantity(dto.getUnitQty());
            node.setUnit(childModel.getUnit());
            if (processMap.containsKey(dto.getProcess())) {
                node.setProcessId(processMap.get(dto.getProcess()));
            }
            productStructureService.save(node);
            //  æŠŠå½“前子项记录到 Map,作为以后更深层级的父项查找依据
            //  åŒä¸€çˆ¶é¡¹ä¸‹çš„同名子项不需要重复记录
            treePathMap.put(childKey, node.getId());
        }
        return AjaxResult.success("BOM导入成功");
    }
    @Override
    public void exportBom(HttpServletResponse response, Integer bomId) {
        if (bomId == null) {
            return;
        }
        List<ProductStructureDto> treeData = productStructureService.listBybomId(bomId);
        if (treeData == null || treeData.isEmpty()) {
            return;
        }
        //  å°†æ ‘形结构扁平化 ä½¿ç”¨ BFS算法 å¯¼å‡º,按层级顺序
        List<BomImportDto> exportList = new ArrayList<>();
        // Map<ID, Node> idMap ç”¨äºŽæŸ¥æ‰¾çˆ¶èŠ‚ç‚¹
        Map<Long, ProductStructureDto> idMap = new HashMap<>();
        populateMap(treeData, idMap);
        //  treeData çš„第一个是根节点
        for (ProductStructureDto root : treeData) {
            //  æ·»åŠ æ ¹èŠ‚ç‚¹
            BomImportDto rootRow = new BomImportDto();
            rootRow.setParentName(root.getProductName());
            rootRow.setParentSpec(root.getModel());
            rootRow.setUnitQty(root.getUnitQuantity());
            rootRow.setRemark("");
            exportList.add(rootRow);
            //  BFS éåކ-队列
            Queue<ProductStructureDto> queue = new LinkedList<>();
            if (root.getChildren() != null) {
                queue.addAll(root.getChildren());
            }
            while (!queue.isEmpty()) {
                ProductStructureDto child = queue.poll();
                // æŸ¥æ‰¾çˆ¶èŠ‚ç‚¹
                ProductStructureDto parent = idMap.get(child.getParentId());
                if (parent == null) {
                    // é™¤äº†æœ€å¤–层节点,其他节点的父类肯定是不会为空的
                    continue;
                }
                BomImportDto row = new BomImportDto();
                // çˆ¶ç±»ä¿¡æ¯
                row.setParentName(parent.getProductName());
                row.setParentSpec(parent.getModel());
                // å­ç±»ä¿¡æ¯
                row.setChildName(child.getProductName());
                row.setChildSpec(child.getModel());
                row.setUnitQty(child.getUnitQuantity());
                row.setProcess(child.getProcessName());
                exportList.add(row);
                //  å°†å­èŠ‚ç‚¹çš„å­èŠ‚ç‚¹åŠ å…¥é˜Ÿåˆ—-下一层
                if (child.getChildren() != null && !child.getChildren().isEmpty()) {
                    queue.addAll(child.getChildren());
                }
            }
        }
        ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class);
        util.exportExcel(response, exportList, "BOM结构导出");
    }
    private void populateMap(List<ProductStructureDto> nodes, Map<Long, ProductStructureDto> map) {
        if (nodes == null || nodes.isEmpty()) {
            return;
        }
        for (ProductStructureDto node : nodes) {
            map.put(node.getId(), node);
            populateMap(node.getChildren(), map);
        }
    }
    private ProductModel findModel(String name, String spec) {
        Product product = productService.getOne(new LambdaQueryWrapper<Product>()
                .eq(Product::getProductName, name).last("limit 1"));
        if (product == null) throw new ServiceException("产品未维护:" + name);
        ProductModel model = productModelService.getOne(new LambdaQueryWrapper<ProductModel>()
                .eq(ProductModel::getProductId, product.getId())
                .eq(ProductModel::getModel, spec).last("limit 1"));
        if (model == null) throw new ServiceException("规格未维护:" + name + "[" + spec + "]");
        return model;
    }
    private void handleProcess(List<BomImportDto> list) {
        Set<String> processNames = list.stream()
                .map(BomImportDto::getProcess)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());
        if (processNames.isEmpty()) {
            return;
        }
        List<ProductProcess> exists = productProcessService.list(
                new LambdaQueryWrapper<ProductProcess>().in(ProductProcess::getName, processNames)
        );
        Set<String> existNames = exists.stream()
                .map(ProductProcess::getName)
                .collect(Collectors.toSet());
        List<ProductProcess> needSave = processNames.stream()
                .filter(n -> !existNames.contains(n))
                .map(n -> {
                    ProductProcess p = new ProductProcess();
                    p.setName(n);
                    return p;
                })
                .collect(Collectors.toList());
        if (!needSave.isEmpty()) {
            productProcessService.saveBatch(needSave);
            needSave.forEach(p -> p.setNo("GX" + String.format("%08d", p.getId())));
            productProcessService.updateBatchById(needSave);
        }
    }
    private String clean(String s) {
        if (s == null) return null;
        return s.replaceAll("[\\u00A0\\u3000]", "").trim();
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductStructureServiceImpl.java
@@ -27,7 +27,7 @@
    @Transactional
    public Boolean addProductStructureDto(ProductStructureDto dto) {
        Long bomId = dto.getBomId();
        Integer bomId = dto.getBomId();
        //  å°†æ ‘扁平化
        List<ProductStructureDto> flatDtoList = new ArrayList<>();
@@ -130,7 +130,7 @@
    @Override
    public List<ProductStructureDto> listBybomId(Long bomId) {
    public List<ProductStructureDto> listBybomId(Integer bomId) {
        List<ProductStructureDto> list = productStructureMapper.listBybomId(bomId);
        Map<Long, ProductStructureDto> map = new HashMap<>();
src/main/resources/mapper/device/DeviceMaintenanceMapper.xml
@@ -44,6 +44,10 @@
            <if test="deviceMaintenanceDto.maintenanceActuallyTimeReq != null">
                and dm.maintenance_actually_time like concat('%',#{deviceMaintenanceDto.maintenanceActuallyTimeReq},'%')
            </if>
            <if test="deviceMaintenanceDto.maintenanceActuallyTime != null">
                and dm.maintenance_actually_time >= str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d')
                and dm.maintenance_actually_time &lt; date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day)
            </if>
        </where>
    </select>
    <select id="detailById" resultType="com.ruoyi.device.dto.DeviceMaintenanceDto">