src/main/java/com/ruoyi/production/service/impl/ProductProcessServiceImpl.java
@@ -1,14 +1,21 @@
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.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.pojo.Customer;
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.DictUtils;
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.ProductProcessDto;
import com.ruoyi.production.dto.ProductProcessImportDto;
import com.ruoyi.production.mapper.ProcessRouteItemMapper;
import com.ruoyi.production.mapper.ProductProcessMapper;
import com.ruoyi.production.mapper.ProductProcessRouteItemMapper;
@@ -16,22 +23,43 @@
import com.ruoyi.production.pojo.ProductProcess;
import com.ruoyi.production.pojo.ProductProcessRouteItem;
import com.ruoyi.production.service.ProductProcessService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ProductProcessServiceImpl extends ServiceImpl<ProductProcessMapper, ProductProcess> implements ProductProcessService {
public class ProductProcessServiceImpl extends ServiceImpl<ProductProcessMapper, ProductProcess>
        implements ProductProcessService {
    @Autowired
    private ProductProcessMapper productProcessMapper;
    @Autowired
    private ProcessRouteItemMapper processRouteItemMapper;
    @Autowired
    private ProductProcessRouteItemMapper productProcessRouteItemMapper;
    @Autowired
    private IProductModelService productModelService;
    @Autowired
    private IProductService productService;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
    public IPage<ProductProcessDto> listPage(Page page, ProductProcessDto productProcessDto) {
@@ -39,46 +67,222 @@
    }
    @Override
    public AjaxResult add(ProductProcessDto productProcessDto) {
    @Transactional(rollbackFor = Exception.class)
    public void add(ProductProcessDto productProcessDto) {
        if (ObjectUtils.isEmpty(productProcessDto.getName())) {
            throw new ServiceException("部件名称不能为空");
        }
        if (ObjectUtils.isNotEmpty(productProcessDto.getNo())) {
            long noCount = this.count(Wrappers.<ProductProcess>lambdaQuery().eq(ProductProcess::getNo, productProcessDto.getNo()));
            if (noCount > 0) {
                throw new ServiceException("工序编号已存在,不能重复");
            }
        }
        // 判断计划工时是否为空
        if (ObjectUtils.isEmpty(productProcessDto.getSalaryQuota())) {
            throw new ServiceException("新增失败,计划工时不能为空");
        }
        // 判断产品是否存在
//        if (ObjectUtils.isEmpty(productProcessDto.getProductModelId())) {
//            throw new ServiceException("新增失败,部件不能为空");
//        }
//        ProductModel productModel = productModelService.getById(productProcessDto.getProductModelId());
//        if (productModel == null) {
//            throw new ServiceException("新增失败,该部件不存在");
//        }
//        validateDuplicateTypeForSameProduct(productModel.getId(), productProcessDto.getType(), null);
        ProductProcess productProcess = new ProductProcess();
        BeanUtils.copyProperties(productProcessDto,productProcess);
        BeanUtils.copyProperties(productProcessDto, productProcess);
//        productProcess.setProductModelId(productModel.getId());
        validatePlanner(productProcessDto.getPlannerId(), productProcessDto.getPlannerName(), null);
        boolean save = productProcessMapper.insert(productProcess) > 0;
        if (save && ObjectUtils.isNull(productProcessDto.getNo())) {
            // 根据id生成no字段:GX + 8位数字(不足8位前面补0)
        if (save && ObjectUtils.isEmpty(productProcess.getNo())) {
            String no = "GX" + String.format("%08d", productProcess.getId());
            productProcess.setNo(no);
            productProcessMapper.updateById(productProcess);
            return AjaxResult.success();
        }
        return AjaxResult.success();
    }
    @Override
    public AjaxResult importData(MultipartFile file) {
        try {
            ExcelUtil<ProductProcess> util = new ExcelUtil<ProductProcess>(ProductProcess.class);
            List<ProductProcess> productProcessList = util.importExcel(file.getInputStream());
            if(CollectionUtils.isEmpty(productProcessList)){
                return AjaxResult.warn("模板错误或导入数据为空");
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductProcessDto productProcessDto) {
        if (ObjectUtils.isEmpty(productProcessDto.getId())) {
            throw new ServiceException("修改失败,工序ID不能为空");
        }
        if (ObjectUtils.isEmpty(productProcessDto.getName())) {
            throw new ServiceException("部件名称不能为空");
        }
        if (ObjectUtils.isNotEmpty(productProcessDto.getNo())) {
            long noCount = this.count(Wrappers.<ProductProcess>lambdaQuery()
                    .eq(ProductProcess::getNo, productProcessDto.getNo())
                    .ne(ProductProcess::getId, productProcessDto.getId()));
            if (noCount > 0) {
                throw new ServiceException("工序编号已存在,不能重复");
            }
            this.saveOrUpdateBatch(productProcessList);
            return AjaxResult.success(true);
        }catch (Exception e){
            e.printStackTrace();
            return AjaxResult.error("导入失败");
        }
        ProductProcess oldProductProcess = this.getById(productProcessDto.getId());
        if (oldProductProcess == null) {
            throw new ServiceException("修改失败,工序不存在");
        }
//        Long finalProductModelId = ObjectUtils.isNotEmpty(productProcessDto.getProductModelId())
//                ? productProcessDto.getProductModelId() : oldProductProcess.getProductModelId();
//        Integer finalType = ObjectUtils.isNotEmpty(productProcessDto.getType())
//                ? productProcessDto.getType() : oldProductProcess.getType();
//
        // 判断关联产品是否存在
//        ProductModel productModel = productModelService.getById(finalProductModelId);
//        if (productModel == null) {
//            throw new ServiceException("修改失败,关联部件不存在");
//        }
//        validateDuplicateTypeForSameProduct(productModel.getId(), finalType, productProcessDto.getId());
        // 校验计划人员
        validatePlanner(productProcessDto.getPlannerId(), productProcessDto.getPlannerName(), null);
        ProductProcess productProcess = new ProductProcess();
        BeanUtils.copyProperties(productProcessDto, productProcess);
        this.updateById(productProcess);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importData(MultipartFile file) {
        try {
            ExcelUtil<ProductProcessImportDto> util = new ExcelUtil<>(ProductProcessImportDto.class);
            List<ProductProcessImportDto> importList = util.importExcel(file.getInputStream());
            if (CollectionUtils.isEmpty(importList)) {
                throw new ServiceException("模板错误或导入数据为空");
            }
            List<ProductProcess> productProcessList = new ArrayList<>();
            for (int i = 0; i < importList.size(); i++) {
                ProductProcessImportDto importDto = importList.get(i);
                int rowNum = i + 2;
                SysUser sysUser = null;
                if (ObjectUtils.isEmpty(importDto)) {
                    throw new ServiceException("第" + rowNum + "行数据为空,请使用正确的模板进行导入");
                }
                if (ObjectUtils.isEmpty(importDto.getName())) {
                    throw new ServiceException("第" + rowNum + "行:部件名称不能为空");
                }
                if (ObjectUtils.isEmpty(importDto.getProductModel())) {
                    throw new ServiceException("第" + rowNum + "行:部件规格不能为空");
                }
                //  检验产品与类型是否存在
                Product product = productService.getOne(new LambdaQueryWrapper<Product>().eq(Product::getProductName, importDto.getName()));
                if (product == null) {
                    throw new ServiceException("第" + rowNum + "行: 部件【" + importDto.getName() + "】不存在");
                }
                ProductModel productModel = productModelService.getOne(new LambdaQueryWrapper<ProductModel>().eq(ProductModel::getProductId, product.getId()).eq(ProductModel::getModel, importDto.getProductModel()));
                if (ObjectUtils.isEmpty(productModel)) {
                    throw new ServiceException("第" + rowNum + "行:部件规格【" + importDto.getProductModel() + "】不存在");
                }
                if (ObjectUtils.isEmpty(importDto.getProductProcessType())) {
                    throw new ServiceException("第" + rowNum + "行:部件【" + importDto.getName() + "】的类型不能为空");
                }
                String dictValue = DictUtils.getDictValue("product_process_type", importDto.getProductProcessType());
                if (StringUtils.isEmpty(dictValue)) {
                    throw new ServiceException("第" + rowNum + "行:部件【" + importDto.getName() + "】的类型【"
                            + importDto.getProductProcessType() + "】不存在,请填写正确的类型:" + DictUtils.getDictLabels("product_process_type"));
                }
                // 检验计划工时
                if (importDto.getSalaryQuota() == null || importDto.getSalaryQuota().compareTo(BigDecimal.ZERO) < 0) {
                    throw new ServiceException("第" + rowNum + "行:计划工时不能为空与负数");
                }
                // 检验计划人员
                if (StringUtils.isNotEmpty(importDto.getPlannerName())) {
                    sysUser = sysUserMapper.selectUserByNickName(importDto.getPlannerName());
                    if (ObjectUtils.isEmpty(sysUser)) {
                        throw new ServiceException("第" + rowNum + "行:计划人员【" + importDto.getPlannerName() + "】不存在");
                    }
                }
                ProductProcess productProcess = new ProductProcess();
                BeanUtils.copyProperties(importDto, productProcess);
                productProcess.setProductModelId(productModel.getId());
                productProcess.setType(Integer.valueOf(dictValue));
                if (sysUser != null) {
                    productProcess.setPlannerId(sysUser.getUserId());
                    productProcess.setPlannerName(sysUser.getNickName());
                }
                productProcessList.add(productProcess);
            }
            saveOrUpdateBatch(productProcessList);
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            log.error("部件导入异常:{}", e.getMessage(), e);
            throw new ServiceException("部件导入异常:" + e.getMessage());
        }
    }
    /**
     * 校验计划人员是否存在
     *
     * @param plannerId   计划人员ID
     * @param plannerName 计划人员姓名
     * @param rowNum      行号
     */
    private void validatePlanner(Long plannerId, String plannerName, Integer rowNum) {
        String prefix = rowNum != null ? "第" + rowNum + "行:" : "";
        if (plannerId != null) {
            if (sysUserMapper.selectUserById(plannerId) == null) {
                throw new ServiceException(prefix + "计划人员ID【" + plannerId + "】不存在");
            }
        } else if (ObjectUtils.isNotEmpty(plannerName)) {
            if (sysUserMapper.selectUserByNickName(plannerName) == null) {
                throw new ServiceException(prefix + "计划人员姓名【" + plannerName + "】不存在");
            }
        }
    }
    /**
     * 校验同一产品下工序类型不能重复
     */
    private void validateDuplicateTypeForSameProduct(Long productModelId, Integer type, Long excludeId) {
        if (productModelId == null || type == null) {
            return;
        }
        ProductModel currentModel = productModelService.getById(productModelId);
        if (currentModel == null || currentModel.getProductId() == null) {
            return;
        }
        List<ProductModel> productModels = productModelService.list(
                Wrappers.<ProductModel>lambdaQuery().eq(ProductModel::getProductId, currentModel.getProductId()));
        if (CollectionUtils.isEmpty(productModels)) {
            return;
        }
        List<Long> productModelIds = productModels.stream().map(ProductModel::getId).collect(Collectors.toList());
        LambdaQueryWrapper<ProductProcess> queryWrapper = Wrappers.<ProductProcess>lambdaQuery()
                .in(ProductProcess::getProductModelId, productModelIds)
                .eq(ProductProcess::getType, type);
        if (excludeId != null) {
            queryWrapper.ne(ProductProcess::getId, excludeId);
        }
        if (this.count(queryWrapper) > 0) {
            throw new ServiceException("操作失败,同一产品不能存在重复的部件类型");
        }
    }
    @Override
    public String batchDelete(List<Integer> ids) {
        //查询是否生产中已经引用了这些工序
        List<ProcessRouteItem> processRouteItems = processRouteItemMapper.selectList(Wrappers.<ProcessRouteItem>lambdaQuery().in(ProcessRouteItem::getProcessId, ids));
        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().in(ProductProcessRouteItem::getProcessId, ids));
        if (!CollectionUtils.isEmpty(processRouteItems) || !CollectionUtils.isEmpty(productProcessRouteItems)){
            throw new RuntimeException("该工序已经被使用,无法删除");
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<Integer> ids) {
        // 查询是否生产中已经引用了这些工序
        List<ProcessRouteItem> processRouteItems = processRouteItemMapper
                .selectList(Wrappers.<ProcessRouteItem>lambdaQuery().in(ProcessRouteItem::getProcessId, ids));
        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(
                Wrappers.<ProductProcessRouteItem>lambdaQuery().in(ProductProcessRouteItem::getProcessId, ids));
        if (!CollectionUtils.isEmpty(processRouteItems) || !CollectionUtils.isEmpty(productProcessRouteItems)) {
            throw new ServiceException("该工序已经被使用,无法删除");
        }
        productProcessMapper.deleteBatchIds(ids);
        return null;
    }
}