yuan
2026-04-24 b4a9e12e00b78e1aef8acda070434de9ffd0d66a
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
@@ -1,558 +1,289 @@
//package com.ruoyi.production.service.impl;
//
//import com.alibaba.fastjson2.JSONArray;
//import com.alibaba.fastjson2.JSONObject;
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.baomidou.mybatisplus.core.metadata.IPage;
//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.common.exception.ServiceException;
//import com.ruoyi.common.exception.base.BaseException;
//import com.ruoyi.common.utils.StringUtils;
//import com.ruoyi.common.utils.bean.BeanUtils;
//import com.ruoyi.common.utils.poi.ExcelUtil;
//import com.ruoyi.production.bean.dto.ProductionPlanDto;
//import com.ruoyi.production.bean.dto.ProductionPlanImportDto;
//import com.ruoyi.production.bean.vo.ProductionPlanVo;
//import com.ruoyi.production.mapper.ProductionPlanMapper;
//import com.ruoyi.production.pojo.ProductionPlan;
//import com.ruoyi.production.service.ProductionPlanService;
//import lombok.RequiredArgsConstructor;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//import org.springframework.web.multipart.MultipartFile;
//
//import jakarta.servlet.http.HttpServletResponse;
//import java.math.BigDecimal;
//import java.time.Instant;
//import java.time.LocalDateTime;
//import java.time.ZoneId;
//import java.util.*;
//import java.util.concurrent.locks.ReentrantLock;
//import java.util.stream.Collectors;
//
///**
// * <p>
// * 生产计划表 服务实现类
// * </p>
// *
// * @author 芯导软件(江苏)有限公司
// * @since 2026-04-21 02:11:10
// */
//@Service
//@RequiredArgsConstructor
//public class ProductionPlanServiceImpl extends ServiceImpl<ProductionPlanMapper, ProductionPlan> implements ProductionPlanService {
//
//    private ProductionPlanMapper productionPlanMapper;
//
//
//    /**
//     * 同步锁,确保手动和定时任务不同时执行
//     */
//    private final ReentrantLock syncLock = new ReentrantLock();
//
//    @Override
//    public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) {
//
//        return productionPlanMapper.selectPage(page, null);
//    }
//
//    /**
//     * 页面手动同步
//     */
//    @Override
//    public void loadProdData() {
//        syncProdData(1);
//    }
//
//    /**
//     * 定时任务同步
//     */
//    @Override
//    public void syncProdDataJob() {
//        syncProdData(2);
//    }
//
//    /**
//     * 合并生产计划
//     */
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean combine(ProductionPlanDto productionPlanDto) {
//        if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
//            return false;
//        }
//
//        //  查询主生产计划
//        List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(productionPlanDto.getIds());
//
//        if (plans == null || plans.isEmpty()) {
//            throw new ServiceException("下发失败,生产计划不存在");
//        }
//
//        //  校验是否存在不同的产品名称
//        String firstProductName = plans.get(0).getProductName();
//        if (plans.stream().anyMatch(p -> p.getProductName() == null || !p.getProductName().equals(firstProductName))) {
//            throw new BaseException("合并失败,存在不同的产品名称");
//        }
//
//        // 校验是否存在不同的产品规格
//        String firstProductSpec = plans.get(0).getModel();
//        if (plans.stream().anyMatch(p -> p.getModel() == null || !p.getModel().equals(firstProductSpec))) {
//            throw new BaseException("合并失败,存在不同的产品规格");
//        }
//
//        // 叠加剩余方数
//        BigDecimal totalRemainingVolume = plans.stream()
//                .map(ProductionPlan::getRemainingVolume)
//                .filter(Objects::nonNull)
//                .reduce(BigDecimal.ZERO, BigDecimal::add);
//        // 判断下发数量是否大于等于剩余方数
//        if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalRemainingVolume) > 0) {
//            throw new BaseException("操作失败,下发数量不能大于剩余方数");
//        }
//
//        // 创建生产订单
//        ProductOrder productOrder = new ProductOrder();
//        productOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity());
//        productOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
//        productOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
//        productOrder.setStrength(productionPlanDto.getStrength());
//        productOrder.setProductMaterialSkuId(plans.get(0).getProductMaterialSkuId());
//
//        Long orderId = productOrderService.insertProductOrder(productOrder);
//
//        //  当下发的产品为砌块或板材,就拉取BOM子集与工艺路线子集数据存入到附表中
//        if ("砌块".equals(productionPlanDto.getProductName())) {
//            productOrder.setRouteId(productionOrderAppendixService.populateBlocks(orderId, productionPlanDto));
//        }
//        if ("板材".equals(productionPlanDto.getProductName())) {
//            productOrder.setRouteId(productionOrderAppendixService.populatePlates(orderId, productionPlanDto));
//        }
//        //  更新绑定的工艺路线
//        productOrderService.updateById(productOrder);
//
//        // 根据下发数量,从第一个生产计划开始分配方数
//        BigDecimal assignedVolume = BigDecimal.ZERO;
//        for (ProductionPlan plan : plans) {
//            BigDecimal volume = plan.getVolume();
//            if (volume == null) {
//                continue;
//            }
//            // 计算剩余方数
//            BigDecimal remainingVolume = plan.getRemainingVolume();
//            if (remainingVolume.compareTo(BigDecimal.ZERO) <= 0) {
//                continue;
//            }
//
//            ProductOrderPlan productOrderPlan = new ProductOrderPlan();
//            productOrderPlan.setProductOrderId(productOrder.getId());
//            productOrderPlan.setProductionPlanId(plan.getId());
//
//            if (assignedVolume.add(remainingVolume).compareTo(productionPlanDto.getTotalAssignedQuantity()) >= 0) {
//                // 最后一个计划,分配剩余方数
//                BigDecimal lastRemainingVolume = productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume);
//                BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO).add(lastRemainingVolume);
//                plan.setAssignedQuantity(assignedQuantity);
//                plan.setStatus(assignedQuantity.compareTo(plan.getVolume()) >= 0 ? 2 : 1);
//                productOrderPlan.setAssignedQuantity(lastRemainingVolume);
//                productionPlanMapper.updateById(plan);
//                productOrderPlanMapper.insert(productOrderPlan);
//                break;
//            }
//
//            // 分配当前计划方数
//            BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO).add(remainingVolume);
//            plan.setAssignedQuantity(assignedQuantity);
//            plan.setStatus(assignedQuantity.compareTo(plan.getVolume()) >= 0 ? 2 : 1);
//            productOrderPlan.setAssignedQuantity(remainingVolume);
//            // 更新生产计划
//            productionPlanMapper.updateById(plan);
//            // 创建关联关系
//            productOrderPlanMapper.insert(productOrderPlan);
//            assignedVolume = assignedVolume.add(remainingVolume);
//        }
//
//        for (ProductionPlan plan : plans) {
//            BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO);
//            BigDecimal volume = Optional.ofNullable(plan.getVolume()).orElse(BigDecimal.ZERO);
//            if (assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
//                plan.setStatus(0);
//            } else if (assignedQuantity.compareTo(volume) >= 0) {
//                plan.setStatus(2);
//            } else {
//                plan.setStatus(1);
//            }
//            productionPlanMapper.updateById(plan);
//        }
//        return true;
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean add(ProductionPlanDto productionPlanDto) {
//        if (StringUtils.isEmpty(productionPlanDto.getApplyNo())) {
//            throw new ServiceException("新增失败,申请单编号不能为空");
//        }
//        Long count = productionPlanMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery()
//                .eq(ProductionPlan::getApplyNo, productionPlanDto.getApplyNo()));
//        if (count > 0) {
//            throw new ServiceException("新增失败,申请单编号 " + productionPlanDto.getApplyNo() + " 已存在");
//        }
//        productionPlanDto.setDataSourceType(DataSourceTypeEnum.MANUAL.getCode());
//        productionPlanDto.setStatus(0);
//        productionPlanMapper.insert(productionPlanDto);
//        return true;
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean update(ProductionPlanDto productionPlanDto) {
//        if (productionPlanDto == null || productionPlanDto.getId() == null) {
//            throw new ServiceException("编辑失败,数据不能为空");
//        }
//        ProductionPlan productionPlan = getById(productionPlanDto.getId());
//        if (productionPlan == null) {
//            throw new ServiceException("编辑失败,主生产计划不存在");
//        }
//
//        if (StringUtils.isNotEmpty(productionPlanDto.getApplyNo())
//                && !productionPlanDto.getApplyNo().equals(productionPlan.getApplyNo())) {
//
//            Long count = productionPlanMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery()
//                    .eq(ProductionPlan::getApplyNo, productionPlanDto.getApplyNo())
//                    .ne(ProductionPlan::getId, productionPlanDto.getId())); // 排除自身
//
//            if (count > 0) {
//                throw new ServiceException("编辑失败,申请单编号 " + productionPlanDto.getApplyNo() + " 已被占用");
//            }
//        }
//        // 已下发状态,不能编辑
//        if (productionPlan.getStatus() != 0) {
//            throw new BaseException("编辑失败,该生产计划已下发或部分下发状态,禁止编辑");
//        }
//
//        // 查询是否有关联订单
//        boolean hasProductOrderPlan = productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery()
//                        .eq(ProductOrderPlan::getProductionPlanId, productionPlanDto.getId()))
//                .stream().anyMatch(p -> p.getProductOrderId() != null);
//
//        if (hasProductOrderPlan) {
//            if (productionPlanDto.getVolume().compareTo(productionPlan.getVolume()) < 0) {
//                throw new BaseException("方数不能递减");
//            }
//        }
//
//        return productionPlanMapper.updateById(productionPlanDto) > 0;
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean delete(List<Long> ids) {
//        // 如果存在已下发的计划,则不能删除
//        if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids)).stream().anyMatch(p -> p.getStatus() == 1 || p.getStatus() == 2)) {
//            throw new BaseException("删除失败,存在已下发或部分下发的计划");
//        }
//        // 如果有关联订单,则不能删除
//        if (productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery().in(ProductOrderPlan::getProductionPlanId, ids)).stream().anyMatch(p -> p.getProductOrderId() != null)) {
//            throw new BaseException("删除失败,存在关联订单");
//        }
//
//        return productionPlanMapper.deleteBatchIds(ids) > 0;
//    }
//
//    /**
//     * 同步数据
//     */
//    @Transactional(rollbackFor = Exception.class)
//    public void syncProdData(Integer dataSyncType) {
//        if (!syncLock.tryLock()) {
//            log.warn("同步正在进行中,本次 {} 同步请求被跳过", dataSyncType == 1 ? "手动同步" : "定时任务同步");
//            return;
//        }
//
//        try {
//            JSONArray searchConditions = new JSONArray();
//            JSONObject condition = new JSONObject();
//            condition.put("key", "processApprovedResult");
//            JSONArray valueArray = new JSONArray();
//            valueArray.add("agree");
//
//            condition.put("value", valueArray);
//            condition.put("type", "ARRAY");
//            condition.put("operator", "in");
//            condition.put("componentName", "SelectField");
//            searchConditions.add(condition);
//
//            String searchFieldJson = searchConditions.toJSONString();
//
//            JSONArray dataArr = AliDingUtils.getFormDataList(aliDingConfig, aliDingConfig.getProducePlanFormUuid(), searchFieldJson, this, ProductionPlan::getFormModifiedTime);
//
//            if (dataArr.isEmpty()) {
//                log.info("没有更多新数据需要同步");
//                return;
//            }
//
//            //  解析并保存数据
//            List<ProductionPlan> list = parseProductionPlans(dataArr, dataSyncType, dataArr.size());
//            if (!list.isEmpty()) {
//                //  处理更新或新增
//                int affected = processSaveOrUpdate(list);
//                log.info("数据同步完成,共同步 {} 条数据", affected);
//            }
//
//        } catch (Exception e) {
//            log.error("同步生产计划异常", e);
//        } finally {
//            // 释放锁
//            syncLock.unlock();
//        }
//    }
//
//    private List<ProductionPlan> parseProductionPlans(JSONArray dataArr, Integer dataSyncType, Integer totalCount) {
//        List<ProductionPlan> list = new ArrayList<>();
//        LocalDateTime now = LocalDateTime.now();
//
//        for (int i = 0; i < dataArr.size(); i++) {
//            JSONObject item = dataArr.getJSONObject(i);
//            String formInstanceId = item.getString("formInstanceId");
//            String serialNo = item.getString("serialNo");
//
//            JSONObject originator = item.getJSONObject("originator");
//            String originatorName = originator != null && originator.containsKey("userName")
//                    ? originator.getJSONObject("userName").getString("nameInChinese") : "未知";
//
//            JSONObject formData = item.getJSONObject("formData");
//            JSONArray tableArr = formData.getJSONArray("tableField_l7fytfcn");
//            if (tableArr == null || tableArr.isEmpty()) {
//                continue;
//            }
//
//            for (int j = 0; j < tableArr.size(); j++) {
//                JSONObject row = tableArr.getJSONObject(j);
//                ProductionPlan plan = new ProductionPlan();
//
//                plan.setFormInstanceId(formInstanceId);
//                plan.setSerialNo(serialNo);
//                plan.setApplyNo(formData.getString("textField_l7fytfco"));
//                plan.setCustomerName(formData.getString("textField_lbkozohg"));
//
//                String materialCode = row.getString("textField_l9xo62q5");
//                // 根据物料编码查询物料信息表,关联物料ID
//                if (StringUtils.isNotEmpty(materialCode)) {
//                    LambdaQueryWrapper<ProductMaterialSku> skuQueryWrapper = new LambdaQueryWrapper<>();
//                    skuQueryWrapper.eq(ProductMaterialSku::getMaterialCode, materialCode);
//                    ProductMaterialSku sku = productMaterialSkuService.getOne(skuQueryWrapper);
//                    if (sku != null) {
//                        plan.setProductMaterialSkuId(sku.getId());
//                    }
//                }
//
//                plan.setLength(row.getInteger("numberField_lb7lgatg_value"));
//                plan.setWidth(row.getInteger("numberField_lb7lgath_value"));
//                plan.setHeight(row.getInteger("numberField_lb7lgati_value"));
//                plan.setQuantity(row.getInteger("numberField_lb7lgatj_value"));
//                plan.setVolume(row.getBigDecimal("numberField_l7fytfd3_value"));
//                plan.setStrength(row.getString("radioField_m9urarr2_id"));
//
//                JSONArray dateArr = row.getJSONArray("cascadeDateField_lfxqqluw");
//                if (dateArr != null && dateArr.size() == 2) {
//                    try {
//                        long start = Long.parseLong(dateArr.getString(0));
//                        long end = Long.parseLong(dateArr.getString(1));
//
//                        Date startDate = Date.from(Instant.ofEpochMilli(start)
//                                .atZone(ZoneId.systemDefault())
//                                .toLocalDate()
//                                .atStartOfDay(ZoneId.systemDefault())
//                                .toInstant());
//                        Date endDate = Date.from(Instant.ofEpochMilli(end)
//                                .atZone(ZoneId.systemDefault())
//                                .toLocalDate()
//                                .atStartOfDay(ZoneId.systemDefault())
//                                .toInstant());
//
//                        plan.setStartDate(startDate);
//                        plan.setEndDate(endDate);
//                    } catch (Exception e) {
//                        log.warn("解析日期失败: {}", dateArr);
//                    }
//                }
//
//                plan.setSubmitter(originatorName);
//                plan.setSubmitOrg("宁夏中创绿能实业集团有限公司");
//                plan.setRemarkOne(formData.getString("textareaField_l7fytfcy"));
//                plan.setRemarkTwo(formData.getString("textField_l7fytfcx"));
//                plan.setCreatorName(originatorName);
//
//                JSONObject modifyUser = item.getJSONObject("modifyUser");
//                if (modifyUser != null && modifyUser.containsKey("userName")) {
//                    plan.setModifierName(modifyUser.getJSONObject("userName").getString("nameInChinese"));
//                }
//
//                plan.setFormCreatedTime(AliDingUtils.parseUtcTime(item.getString("createdTimeGMT")));
//                plan.setFormModifiedTime(AliDingUtils.parseUtcTime(item.getString("modifiedTimeGMT")));
//                plan.setDataSourceType(DataSourceTypeEnum.DING_TALK.getCode());
//                plan.setCreateTime(now);
//                plan.setUpdateTime(now);
//                plan.setTotalCount(totalCount);
//
//                list.add(plan);
//            }
//        }
//        return list;
//    }
//
//    private int processSaveOrUpdate(List<ProductionPlan> list) {
//        if (list == null || list.isEmpty()) {
//            return 0;
//        }
//        int affected = 0;
//
//        //  去重 formInstanceId
//        Set<String> formIds = list.stream()
//                .map(ProductionPlan::getFormInstanceId)
//                .collect(Collectors.toSet());
//
//        //  查询数据库已有数据
//        List<ProductionPlan> existList = this.list(new LambdaQueryWrapper<ProductionPlan>().in(ProductionPlan::getFormInstanceId, formIds));
//
//        //  Map (formInstanceId + materialCode)
//        Map<String, ProductionPlan> existMap = new HashMap<>();
//        for (ProductionPlan p : existList) {
//            String key = p.getFormInstanceId() + "_" + p.getProductMaterialSkuId();
//            existMap.put(key, p);
//        }
//
//        //  遍历同步数据
//        for (ProductionPlan plan : list) {
//            String key = plan.getFormInstanceId() + "_" + plan.getProductMaterialSkuId();
//            ProductionPlan exist = existMap.get(key);
//            if (exist == null) {
//                // 新增
//                this.save(plan);
//                affected++;
//                log.info("新增数据 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getProductMaterialSkuId());
//            } else {
//                // 判断是否需要更新
//                if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(plan.getFormModifiedTime())) {
//                    plan.setId(exist.getId());
//                    plan.setCreateTime(exist.getCreateTime());
//                    this.updateById(plan);
//                    affected++;
//                    log.info("更新数据 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getProductMaterialSkuId());
//                }
//            }
//        }
//        return affected;
//    }
//
//    @Override
//    public List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query) {
//        return baseMapper.selectSummaryByProductType(query);
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void importProdData(MultipartFile file) {
//        if (file == null || file.isEmpty()) {
//            throw new ServiceException("导入数据不能为空");
//        }
//        ExcelUtil<ProductionPlanImportDto> excelUtil = new ExcelUtil<>(ProductionPlanImportDto.class);
//        List<ProductionPlanImportDto> list;
//        try {
//            list = excelUtil.importExcel(file.getInputStream());
//        } catch (Exception e) {
//            log.error("生产需求Excel导入失败", e);
//            throw new ServiceException("Excel解析失败");
//        }
//
//        if (list == null || list.isEmpty()) {
//            throw new ServiceException("Excel没有数据");
//        }
//
//        Set<String> applyNos = new HashSet<>();
//        Set<String> materialCodes = new HashSet<>();
//        for (int i = 0; i < list.size(); i++) {
//            ProductionPlanImportDto dto = list.get(i);
//            String applyNo = dto.getApplyNo();
//            String materialCode = dto.getMaterialCode();
//
//            if (StringUtils.isEmpty(applyNo)) {
//                throw new ServiceException("导入失败:第 " + (i + 2) + " 行申请单编号不能为空");
//            }
//            if (!applyNos.add(applyNo)) {
//                throw new ServiceException("导入失败:Excel 中存在重复的申请单编号: " + applyNo);
//            }
//            if (StringUtils.isEmpty(materialCode)) {
//                throw new ServiceException("导入失败:第 " + (i + 2) + " 行物料编码不能为空");
//            }
//
//            String strength = dto.getStrength();
//            if (StringUtils.isNotEmpty(strength)) {
//                if (!"A3.5".equals(strength) && !"A5.0".equals(strength)) {
//                    throw new ServiceException("导入失败:第 " + (i + 2) + " 行强度只能是 A3.5 或 A5.0");
//                }
//            }
//
//            materialCodes.add(materialCode);
//        }
//
//        //  申请单编号是否已存在
//        Long existApplyNoCount = baseMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery()
//                .in(ProductionPlan::getApplyNo, applyNos));
//        if (existApplyNoCount > 0) {
//            List<String> existApplyNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery()
//                            .in(ProductionPlan::getApplyNo, applyNos))
//                    .stream().map(ProductionPlan::getApplyNo).collect(Collectors.toList());
//            throw new ServiceException("导入失败,申请单编号已存在: " + String.join(", ", existApplyNos));
//        }
//
//        Map<String, Long> skuMap = productMaterialSkuService.list(Wrappers.<ProductMaterialSku>lambdaQuery()
//                        .in(ProductMaterialSku::getMaterialCode, materialCodes))
//                .stream().collect(Collectors.toMap(ProductMaterialSku::getMaterialCode, ProductMaterialSku::getId, (k1, k2) -> k1));
//
//        List<String> missingCodes = materialCodes.stream()
//                .filter(code -> !skuMap.containsKey(code))
//                .collect(Collectors.toList());
//        if (!missingCodes.isEmpty()) {
//            throw new ServiceException("导入失败,以下物料编码不存在: " + String.join(", ", missingCodes));
//        }
//
//        LocalDateTime now = LocalDateTime.now();
//        List<ProductionPlan> entityList = list.stream().map(dto -> {
//            ProductionPlan entity = new ProductionPlan();
//            BeanUtils.copyProperties(dto, entity);
//            entity.setProductMaterialSkuId(skuMap.get(dto.getMaterialCode()));
//            entity.setAssignedQuantity(BigDecimal.ZERO);
//            entity.setDataSourceType(DataSourceTypeEnum.MANUAL.getCode());
//            entity.setStatus(0);
//            entity.setCreateTime(now);
//            entity.setUpdateTime(now);
//            return entity;
//        }).collect(Collectors.toList());
//
//        this.saveBatch(entityList);
//    }
//
//    @Override
//    public void exportProdData(HttpServletResponse response, List<Long> ids) {
//        List<ProductionPlan> list;
//        if (ids != null && !ids.isEmpty()) {
//            list = baseMapper.selectBatchIds(ids);
//        } else {
//            list = baseMapper.selectList(null);
//        }
//
//        List<ProductionPlanImportDto> exportList = new ArrayList<>();
//        for (ProductionPlan entity : list) {
//            ProductionPlanImportDto dto = new ProductionPlanImportDto();
//            BeanUtils.copyProperties(entity, dto);
//            exportList.add(dto);
//        }
//        ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class);
//        util.exportExcel(response, exportList, "销售生产需求数据");
//    }
//
//}
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
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.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.production.bean.dto.ProductionPlanDto;
import com.ruoyi.production.bean.dto.ProductionPlanImportDto;
import com.ruoyi.production.bean.vo.ProductionPlanVo;
import com.ruoyi.production.mapper.ProductionOrderMapper;
import com.ruoyi.production.mapper.ProductionPlanMapper;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionPlan;
import com.ruoyi.production.service.ProductionOrderService;
import com.ruoyi.production.service.ProductionPlanService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ProductionPlanServiceImpl extends ServiceImpl<ProductionPlanMapper, ProductionPlan> implements ProductionPlanService {
    private static final int PLAN_STATUS_WAIT = 0;
    private static final int PLAN_STATUS_PARTIAL = 1;
    private static final int PLAN_STATUS_ISSUED = 2;
    private final ProductionPlanMapper productionPlanMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderService productionOrderService;
    @Override
    public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) {
        return productionPlanMapper.listPage(page, productionPlanDto);
    }
    /**
     * 合并生产计划并下发生产订单。
     * 业务约束:
     * 1. 仅允许同一产品型号的计划合并;
     * 2. 已下发或部分下发的计划禁止再次合并;
     * 3. 下发数量不能大于所选计划需求总量;
     * 4. 订单创建统一复用 ProductionOrderService.saveProductionOrder,确保工艺/BOM/领料主单等后续逻辑一致。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean combine(ProductionPlanDto productionPlanDto) {
        if (productionPlanDto == null || productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
            return false;
        }
        List<Long> planIds = productionPlanDto.getIds().stream()
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (planIds.isEmpty()) {
            throw new ServiceException("下发失败,未选择生产计划");
        }
        List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(planIds);
        if (plans == null || plans.isEmpty() || plans.size() != planIds.size()) {
            throw new ServiceException("下发失败,生产计划不存在或已被删除");
        }
        ProductionPlanDto firstPlan = plans.getFirst();
        if (firstPlan.getProductModelId() == null) {
            throw new ServiceException("下发失败,生产计划缺少产品型号");
        }
        boolean hasDifferentModel = plans.stream()
                .anyMatch(item -> !Objects.equals(item.getProductModelId(), firstPlan.getProductModelId()));
        if (hasDifferentModel) {
            throw new BaseException("合并失败,所选生产计划的产品型号不一致");
        }
        boolean hasIssuedPlan = plans.stream()
                .anyMatch(item -> item.getStatus() != null && item.getStatus() != PLAN_STATUS_WAIT);
        if (hasIssuedPlan) {
            throw new BaseException("合并失败,所选生产计划存在已下发或部分下发数据");
        }
        BigDecimal totalRequiredQuantity = plans.stream()
                .map(ProductionPlan::getQtyRequired)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (totalRequiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("下发失败,所选生产计划需求总量必须大于0");
        }
        BigDecimal assignedQuantity = productionPlanDto.getTotalAssignedQuantity();
        if (assignedQuantity == null || assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("下发失败,下发数量必须大于0");
        }
        if (assignedQuantity.compareTo(totalRequiredQuantity) > 0) {
            throw new ServiceException("下发失败,下发数量不能大于计划需求总量");
        }
        ProductionOrder productionOrder = new ProductionOrder();
        productionOrder.setProductionPlanIds(formatPlanIds(planIds));
        productionOrder.setProductModelId(firstPlan.getProductModelId());
        productionOrder.setQuantity(assignedQuantity);
        productionOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
        boolean saved = productionOrderService.saveProductionOrder(productionOrder);
        if (!saved) {
            throw new ServiceException("下发失败,生产订单保存失败");
        }
        int targetStatus = assignedQuantity.compareTo(totalRequiredQuantity) < 0 ? PLAN_STATUS_PARTIAL : PLAN_STATUS_ISSUED;
        List<ProductionPlan> updates = planIds.stream().map(id -> {
            ProductionPlan update = new ProductionPlan();
            update.setId(id);
            update.setStatus(targetStatus);
            return update;
        }).collect(Collectors.toList());
        if (!updates.isEmpty()) {
            this.updateBatchById(updates);
        }
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(ProductionPlanDto dto) {
        if (StringUtils.isBlank(dto.getMpsNo())) {
            dto.setMpsNo(generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        }else checkMpsNoUnique(dto.getMpsNo(), null);
        dto.setStatus(PLAN_STATUS_WAIT);
        dto.setSource("内部");
        return productionPlanMapper.insert(dto) > 0;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(ProductionPlanDto dto) {
        if (dto == null || dto.getId() == null) {
            throw new ServiceException("编辑失败,数据不能为空");
        }
        ProductionPlan old = getById(dto.getId());
        if (old == null) {
            throw new ServiceException("编辑失败,生产计划不存在");
        }
        if (old.getStatus() != PLAN_STATUS_WAIT) {
            throw new BaseException("编辑失败,该生产计划已下发或部分下发,禁止编辑");
        }
        if (StringUtils.isNotBlank(dto.getMpsNo()) && !dto.getMpsNo().equals(old.getMpsNo())) {
            checkMpsNoUnique(dto.getMpsNo(), dto.getId());
        }
        return productionPlanMapper.updateById(dto) > 0;
    }
    private void checkMpsNoUnique(String mpsNo, Long excludeId) {
        LambdaQueryWrapper<ProductionPlan> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ProductionPlan::getMpsNo, mpsNo);
        if (excludeId != null) {
            wrapper.ne(ProductionPlan::getId, excludeId);
        }
        if (productionPlanMapper.selectCount(wrapper) > 0) {
            throw new ServiceException("生产计划号 " + mpsNo + " 已存在");
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(List<Long> ids) {
        if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids))
                .stream()
                .anyMatch(p -> p.getStatus() == PLAN_STATUS_PARTIAL || p.getStatus() == PLAN_STATUS_ISSUED)) {
            throw new BaseException("删除失败,存在已下发或部分下发的计划");
        }
        if (productionOrderMapper.selectList(Wrappers.<ProductionOrder>lambdaQuery().in(ProductionOrder::getProductionPlanIds, ids))
                .stream()
                .anyMatch(p -> p.getId() != null)) {
            throw new BaseException("删除失败,存在关联生产订单");
        }
        return productionPlanMapper.deleteBatchIds(ids) > 0;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importProdData(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new ServiceException("导入数据不能为空");
        }
        ExcelUtil<ProductionPlanImportDto> excelUtil = new ExcelUtil<>(ProductionPlanImportDto.class);
        List<ProductionPlanImportDto> list;
        try {
            list = excelUtil.importExcel(file.getInputStream());
        } catch (Exception e) {
            throw new ServiceException("Excel解析失败");
        }
        if (list == null || list.isEmpty()) {
            throw new ServiceException("Excel没有数据");
        }
        Set<String> mpsNos = new HashSet<>();
        for (int i = 0; i < list.size(); i++) {
            ProductionPlanImportDto dto = list.get(i);
            String mpsNo = dto.getMpsNo();
            if (StringUtils.isEmpty(mpsNo)) {
                generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
            }
            if (!mpsNos.add(mpsNo)) {
                throw new ServiceException("导入失败:Excel 中存在重复的申请单编号 " + mpsNo);
            }
        }
        Long existApplyNoCount = baseMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery()
                .in(ProductionPlan::getMpsNo, mpsNos));
        if (existApplyNoCount > 0) {
            List<String> existMpsNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery()
                            .in(ProductionPlan::getMpsNo, mpsNos))
                    .stream()
                    .map(ProductionPlan::getMpsNo)
                    .collect(Collectors.toList());
            throw new ServiceException("导入失败,生产计划号已存在: " + String.join(", ", existMpsNos));
        }
        LocalDateTime now = LocalDateTime.now();
        List<ProductionPlan> entityList = list.stream().map(dto -> {
            ProductionPlan entity = new ProductionPlan();
            BeanUtils.copyProperties(dto, entity);
            entity.setStatus(PLAN_STATUS_WAIT);
            entity.setSource("内部");
            entity.setCreateTime(now);
            entity.setUpdateTime(now);
            return entity;
        }).collect(Collectors.toList());
        this.saveBatch(entityList);
    }
    @Override
    public void exportProdData(HttpServletResponse response, List<Long> ids) {
        List<ProductionPlanDto> list = productionPlanMapper.selectWithMaterialByIds(ids);
        List<ProductionPlanImportDto> exportList = new ArrayList<>();
        for (ProductionPlanDto entity : list) {
            ProductionPlanImportDto dto = new ProductionPlanImportDto();
            BeanUtils.copyProperties(entity, dto);
            exportList.add(dto);
        }
        ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class);
        util.exportExcel(response, exportList, "主生产计划");
    }
    private String formatPlanIds(List<Long> planIds) {
        return planIds.stream()
                .filter(Objects::nonNull)
                .distinct()
                .map(String::valueOf)
                .collect(Collectors.joining(",", "[", "]"));
    }
    private String generateNextPlanNo(String datePrefix) {
        QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("mps_no", "JH" + datePrefix);
        queryWrapper.orderByDesc("mps_no");
        queryWrapper.last("LIMIT 1");
        ProductionPlan latestPlan = productionPlanMapper.selectOne(queryWrapper);
        int sequence = 1;
        if (latestPlan != null && latestPlan.getMpsNo() != null && !latestPlan.getMpsNo().isEmpty()) {
            String sequenceStr = latestPlan.getMpsNo().substring(("JH" + datePrefix).length());
            try {
                sequence = Integer.parseInt(sequenceStr) + 1;
            } catch (NumberFormatException e) {
                sequence = 1;
            }
        }
        return "JH" + datePrefix + String.format("%04d", sequence);
    }
}