# ProductAttributeServiceImpl.java

    package com.mycim.server.spec.service;

    import com.alipay.sofa.runtime.api.annotation.SofaService;
    import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;
    import com.fa.sesa.exception.Assert;
    import com.fa.sesa.exception.Errors;
    import com.fa.sesa.i18n.I18nUtils;
    import com.fa.sesa.threadlocal.LocalContext;
    import com.mycim.framework.jdbc.Page;
    import com.mycim.framework.utils.beans.BeanUtils;
    import com.mycim.framework.utils.lang.BooleanUtils;
    import com.mycim.framework.utils.lang.StringUtils;
    import com.mycim.framework.utils.lang.collections.CollectionUtils;
    import com.mycim.server.base.manager.NamedObjectManager;
    import com.mycim.server.base.manager.TransactionLogManager;
    import com.mycim.server.edc.manager.EcnManager;
    import com.mycim.server.edc.manager.ParameterSetManager;
    import com.mycim.server.prp.manager.ProcessManager;
    import com.mycim.server.prp.manager.ProductProcessManager;
    import com.mycim.server.prp.manager.ProductVersionManager;
    import com.mycim.server.prp.manager.TimeLimitSetUpManager;
    import com.mycim.server.rcp.manager.RecipeManager;
    import com.mycim.server.reticle.manager.ReticleFamilyManager;
    import com.mycim.server.spec.manager.*;
    import com.mycim.utils.FieldValidateUtils;
    import com.mycim.valueobject.MessageIdList;
    import com.mycim.valueobject.ObjectList;
    import com.mycim.valueobject.bas.TransactionLog;
    import com.mycim.valueobject.consts.ObjectStatus;
    import com.mycim.valueobject.consts.TransactionNames;
    import com.mycim.valueobject.consts.VersionStatus;
    import com.mycim.valueobject.prp.*;
    import org.apache.commons.lang3.math.NumberUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.lang.Override;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * Product Attribute Table
     *
     * @author Qiansheng.Wang
     * @version 1.0
     * @since 2021-05-15
     */
    @Service
    @Transactional
    @SofaService(interfaceType = ProductAttributeService.class, bindings = {@SofaServiceBinding(bindingType = "bolt")})
    public class ProductAttributeServiceImpl implements ProductAttributeService {

        @Autowired
        NamedObjectManager namedObjectManager;

        @Autowired
        TransactionLogManager transactionLogManager;

        @Autowired
        EcnManager ecnManager;

        @Autowired
        ProductAttributeInfoManager productAttributeInfoManager;

        @Autowired
        ProductAttributeItemManager productAttributeItemManager;

        @Autowired
        ProductSpecInfoManager productSpecInfoManager;

        @Autowired
        ProcessSpecInfoManager processSpecInfoManager;

        @Autowired
        ProcessSpecItemManager processSpecItemManager;

        @Autowired
        AttributeSetupManager attributeSetupManager;

        @Autowired
        SpecManager specManager;

        @Autowired
        ProcessManager processManager;

        @Autowired
        ProductVersionManager productVersionManager;

        @Autowired
        ProductProcessManager productProcessManager;

        @Autowired
        RecipeManager recipeManager;

        @Autowired
        ReticleFamilyManager reticleFamilyManager;

        @Autowired
        ParameterSetManager parameterSetManager;

        @Autowired
        TimeLimitSetUpManager timeLimitSetupManager;

        @Override
        public List<ProductAttributeItemDto> queryProductAttributeItems(ProductAttributeFormDto productAttributeForm) {
            List<ProductAttributeItemDto> result = new ArrayList<>();

            for (ProductAttributeItem temp : productAttributeItemManager.queryProductAttributeItems(productAttributeForm)) {
                ProductAttributeItemDto productAttributeItem = new ProductAttributeItemDto();

                BeanUtils.copyProperties(temp, productAttributeItem);

                result.add(productAttributeItem);
            }

            result.sort((o1, o2) -> {

                if (o1.getAttributeNameNumber() == o2.getAttributeNameNumber()) {
                    return o1.getFlowSeq().compareTo(o2.getFlowSeq());
                } else {
                    return o1.getAttributeNameNumber() - o2.getAttributeNameNumber();
                }

            });

            return result;
        }

        @Override
        public void saveProductAttributeDetailInfo(ProductAttributeFormDto productAttributeForm) {

            Assert.isFalse(CollectionUtils.isEmpty(productAttributeForm.getAttributeItems()),
                           Errors.create().content("no data to save").build());

            List<ProductAttributeItem> willSaveProductAttributes = new ArrayList<>();

            buildWillSaveProductAttributeItems(productAttributeForm.getAttributeItems(), willSaveProductAttributes);

            List<ProductAttributeItem> productAttributeItemsInDb = productAttributeItemManager.getProductAttributeItems(
                    productAttributeForm.getProcessRrn(), productAttributeForm.getProcessVersion());

            for (Iterator<ProductAttributeItem> iterator = willSaveProductAttributes.iterator(); iterator.hasNext(); ) {
                ProductAttributeItem productAttributeItem = iterator.next();

                int index = productAttributeItemsInDb.indexOf(productAttributeItem);

                if (index >= 0) {
                    ProductAttributeItem temp = productAttributeItemsInDb.get(index);

                    if (VersionStatus.ACTIVE_KEY.equals(temp.getStatus())) {
                        iterator.remove();
                    }
                }
            }

            Assert.state(CollectionUtils.isNotEmpty(willSaveProductAttributes),
                           Errors.create().content("No data can be saved.").build());

            for (ProductAttributeItem willSaveProductAttribute: willSaveProductAttributes) {
                //错误 #45489 536 特殊逻辑不允许创建带有变量的 recipe
                //筛选出变量recipe
                if(StringUtils.startsWith(willSaveProductAttribute.getAttributeName(),ProductVariableEnum.RECIPE_VARIABLE_PREFIX.getValue())){
                    if(StringUtils.isNotEmpty(willSaveProductAttribute.getAttributeValue())){//对非变量recipe 进行检查是否合法
                        String msg = FieldValidateUtils.validateRecipeIdFor536(willSaveProductAttribute.getAttributeValue());
                        Assert.state(StringUtils.isEmpty(msg),Errors.create().content(msg).build());
                    }
                }
            }
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.SAVE_KEY);
            transactionLog.setTransSequence(0L);

            productAttributeItemManager.updateProductAttributeItems(transactionLog, willSaveProductAttributes);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void freezeProductAttributeDetailInfo(ProductAttributeFormDto productAttributeForm) {

            checkAndBuildQueryProductAttributeForm(productAttributeForm);

            List<ProductAttributeInfo> willDoFreezeInfoList = getDoActionProductAttributeInfos(productAttributeForm,
                                                                                               VersionStatus.UNFROZEN_KEY);

            List<ProductAttributeItem> willDoFreezeItemList = getDoActionProductAttributeItems(willDoFreezeInfoList);

            Assert.isFalse(CollectionUtils.isEmpty(willDoFreezeInfoList) || CollectionUtils.isEmpty(willDoFreezeItemList),
                           Errors.create().content("No data can be frozen.").build());

            checkIfProductAttributeCanDoFreeze(willDoFreezeItemList);

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.FROZEN_KEY);

            updateStatusOfProductAttribute(transactionLog, null, willDoFreezeInfoList, willDoFreezeItemList,
                                           VersionStatus.FROZEN_KEY);

            handleProductVersionStatus(willDoFreezeInfoList, VersionStatus.FROZEN_KEY);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void unfreezeProductAttributeDetailInfo(ProductAttributeFormDto productAttributeForm) {

            checkAndBuildQueryProductAttributeForm(productAttributeForm);

            List<ProductAttributeInfo> willDoUnfreezeInfoList = getDoActionProductAttributeInfos(productAttributeForm,
                                                                                                 VersionStatus.FROZEN_KEY);

            List<ProductAttributeItem> willDoUnfreezeItemList = getDoActionProductAttributeItems(willDoUnfreezeInfoList);

            Assert.isFalse(
                    CollectionUtils.isEmpty(willDoUnfreezeInfoList) || CollectionUtils.isEmpty(willDoUnfreezeItemList),
                    Errors.create().content("No data can be unfreeze.").build());

            checkIfProductAttributeCanDoUnfreeze(willDoUnfreezeItemList);

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.UNFROZEN_KEY);

            updateStatusOfProductAttribute(transactionLog, null, willDoUnfreezeInfoList, willDoUnfreezeItemList,
                                           VersionStatus.UNFROZEN_KEY);

            handleProductVersionStatus(willDoUnfreezeInfoList, VersionStatus.UNFROZEN_KEY);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void activateProductAttributeDetailInfo(ProductAttributeFormDto productAttributeForm) {

            checkAndBuildQueryProductAttributeForm(productAttributeForm);

            Long processRrn = productAttributeForm.getProcessRrn();
            Integer processVersion = productAttributeForm.getProcessVersion();

            checkIfProcessSpecStatusIsActive(processRrn, processVersion);

            List<ProductAttributeInfo> willDoActivateInfoList = getDoActionProductAttributeInfos(productAttributeForm,
                                                                                                 VersionStatus.FROZEN_KEY);

            List<ProductAttributeItem> willDoActivateItemList = getDoActionProductAttributeItems(willDoActivateInfoList);

            Assert.isFalse(
                    CollectionUtils.isEmpty(willDoActivateInfoList) || CollectionUtils.isEmpty(willDoActivateItemList),
                    Errors.create().content("No data can be active.").build());

            checkIfProductAttributeCanDoActivate(willDoActivateItemList);

            checkIfHasUnfrozenTimeLimit(productAttributeForm.getProductRrns(), productAttributeForm.getProcessId(),
                                        productAttributeForm.getProcessVersion(), productAttributeForm.getProcessRrn());
            //check Multipath 是否已设定
            Assert.isFalse(
                    specManager.checkMultipath(productAttributeForm.getProcessId(), productAttributeForm.getProcessRrn(),
                                               productAttributeForm.getProcessVersion(),
                                               productAttributeForm.getProductIds()),
                    Errors.create().key(MessageIdList.MULTIPATH_PROCESS_SET)
                          .content("Multipath is not set for this process!").build());

            Ecn ecn = ecnManager.generateActiveEcnForContext(LocalContext.getUserRrn());

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.ACTIVE_KEY);

            updateStatusOfProductAttribute(transactionLog, ecn, willDoActivateInfoList, willDoActivateItemList,
                                           VersionStatus.ACTIVE_KEY);

            attributeSetupManager.saveProductAttributeToContextValue(LocalContext.getFacilityRrn(), ecn,
                                                                     willDoActivateItemList);

            handleProductVersionStatus(willDoActivateInfoList, VersionStatus.ACTIVE_KEY);

            activateProduct(transactionLog, processRrn, processVersion, willDoActivateInfoList);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public Map<String, Object> checkImportProductSpecItems(List<ProductAttributeItemDto> importList) {
            long facilityRrn = LocalContext.getFacilityRrn();

            Map<String, Object> result = new HashMap<>();
            for (ProductAttributeItemDto detail : importList) {
                StringBuilder rowErrorMsg = new StringBuilder();

                if (ProductVariableEnum.isRecipeVariable(detail.getAttributeName()) &&
                        StringUtils.isNotEmpty(detail.getAttributeValue())) {

                    detail.setAttributeValueRrn(
                            namedObjectManager.getNamedObjectRrn(detail.getAttributeValue(), facilityRrn,
                                                                 ObjectList.RECIPE_KEY));
                    if (detail.getAttributeValueRrn() == null || detail.getAttributeValueRrn() <= 0) {
                        rowErrorMsg.append("Recipe: ").append(detail.getAttributeValue()).append(" dose not existed.<br>");
                    } else if (recipeManager.isMainRecipe(detail.getAttributeValueRrn()) != detail.getAttributeValueRrn()) {
                        rowErrorMsg.append("Recipe: ").append(detail.getAttributeValue()).append(" is chamber recipe!<br>");
                    }
                }

                if (ProductVariableEnum.isReticleGroupVariable(detail.getAttributeName()) &&
                        StringUtils.isNotEmpty(detail.getAttributeValue())) {

                    detail.setAttributeValueRrn(
                            namedObjectManager.getNamedObjectRrn(detail.getAttributeValue(), facilityRrn,
                                                                 ObjectList.RETICLEFAMILY_KEY));
                    if (detail.getAttributeValueRrn() == null || detail.getAttributeValueRrn() <= 0) {
                        rowErrorMsg.append("Reticle Group: ").append(detail.getAttributeValue())
                                   .append(" does not existed!<br>");
                    }
                }

                if (ProductVariableEnum.isEdcPlanVariable(detail.getAttributeName()) &&
                        StringUtils.isNotEmpty(detail.getAttributeValue())) {

                    detail.setAttributeValueRrn(
                            namedObjectManager.getNamedObjectRrn(detail.getAttributeValue(), facilityRrn,
                                                                 ObjectList.PARAMETERSET_KEY));
                    if (detail.getAttributeValueRrn() == null || detail.getAttributeValueRrn() <= 0) {
                        rowErrorMsg.append("EDC Plan: ").append(detail.getAttributeValue())
                                   .append(" does not existed!<br>");
                    }
                }

                Assert.isTrue(StringUtils.isEmpty(rowErrorMsg.toString()),
                              Errors.create().content(rowErrorMsg.toString()).build());

                result.put(detail.getProductId() + "#$#" + detail.getProductVersion() + "#$#" + detail.getProcessVersion() +
                                   "#$#" + detail.getFlowSeq() + "#$#" + detail.getAttributeName(), detail);
            }

            return result;
        }

        @Override
        public void addProductVersion(ProductAttributeFormDto productAttributeForm) {
            Long processRrn = getProcessRrnOfProducts(productAttributeForm.getProductRrns());

            Assert.isTrue(processRrn.equals(productAttributeForm.getProcessRrn()),
                          Errors.create().content("Process and product has not relation!").build());

            List<ProductVersion> productVersions = getAllAddedProductVersionsOfProducts(
                    productAttributeForm.getProductRrns());

            Integer processVersion = processManager.getLastProcessVersion(processRrn);

            Assert.isFalse(processRrn <= 0, Errors.create().content("Current process has no version info! ").build());

            ProcessSpecInfo processSpecInfo = processSpecInfoManager.getProcessSpecInfoByPrimaryKey(processRrn,
                                                                                                    processVersion);

            Assert.isFalse(processSpecInfo == null || !BooleanUtils.toBoolean(processSpecInfo.getActiveFlag()),
                           Errors.create().content("process are not activate in process spec!").build());

            List<ProductAttributeItem> willSaveProductAttributeItems = new ArrayList<>();
            List<ProductAttributeInfo> willSaveProductAttributeInfos = new ArrayList<>();
            for (ProductVersion productVersion : productVersions) {
                long copyFromProductRrn = productVersion.getProductRrn();
                int copyFromProductVersion = productVersion.getProductVer() - 1;

                ProductAttributeInfo productAttributeInfo = productAttributeInfoManager.getProductAttributeInfos(processRrn,
                                                                                                                 processVersion,
                                                                                                                 copyFromProductRrn,
                                                                                                                 copyFromProductVersion);

                productAttributeInfo.setProductRrn(productVersion.getProductRrn());
                productAttributeInfo.setProductId(productVersion.getProductId());
                productAttributeInfo.setProductVersion(productVersion.getProductVer());

                willSaveProductAttributeInfos.add(productAttributeInfo);

                List<ProductAttributeItem> productAttributeItems = productAttributeItemManager.getProductAttributeItems(
                        processRrn, processVersion, copyFromProductRrn, copyFromProductVersion);

                for (ProductAttributeItem productAttributeItem : productAttributeItems) {
                    productAttributeItem.setProductRrn(productVersion.getProductRrn());
                    productAttributeItem.setProductId(productVersion.getProductId());
                    productAttributeItem.setProductVersion(productVersion.getProductVer());

                    willSaveProductAttributeItems.add(productAttributeItem);
                }
            }

            TransactionLog transactionLog = transactionLogManager.startTransactionLog(LocalContext.getUserId(),
                                                                                      TransactionNames.SAVE_KEY);

            productAttributeInfoManager.insertProductAttributeInfos(transactionLog, willSaveProductAttributeInfos);
            productAttributeItemManager.insertProductAttributeItems(transactionLog, willSaveProductAttributeItems);

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public Page queryProductAttributeHistories(ProductAttributeHistoryQueryDto historyQuery) {
            return productAttributeItemManager.queryProductAttributeHistories(historyQuery);
        }

        private void buildWillSaveProductAttributeItems(List<ProductAttributeItemDto> source,
                                                        List<ProductAttributeItem> target) {
            StringBuilder errorMsg = new StringBuilder();

            long facilityRrn = LocalContext.getFacilityRrn();

            int rowNum = 1;
            for (ProductAttributeItemDto sourceObj : source) {

                ProductAttributeItem targetObj = new ProductAttributeItem();

                BeanUtils.copyProperties(sourceObj, targetObj);

                StringBuilder rowErrorMsg = new StringBuilder();

                if (StringUtils.isNotEmptyTrim(targetObj.getProductId())) {
                    targetObj.setProductRrn(namedObjectManager.getNamedObjectRrn(targetObj.getProductId(), facilityRrn,
                                                                                 ObjectList.PRODUCT_KEY));
                    if (targetObj.getProductRrn() == null || targetObj.getProductRrn() <= 0) {
                        rowErrorMsg.append("Product: ").append(targetObj.getProductId()).append(" does not existed!<br>");
                    }
                } else {
                    rowErrorMsg.append("Product ID cannot be empty!<br>");
                }

                if (targetObj.getProductVersion() == null || targetObj.getProductVersion() <= 0) {
                    rowErrorMsg.append("Product Version cannot be empty!<br>");
                }

                targetObj.setStatus(VersionStatus.UNFROZEN_KEY);
                targetObj.setAttributeValue(StringUtils.trimToUpperCase(targetObj.getAttributeValue()));

                if (ProductVariableEnum.isRecipeVariable(targetObj.getAttributeName())) {

                    if (StringUtils.isNotBlank(targetObj.getAttributeValue())) {
                        targetObj.setAttributeValueRrn(
                                namedObjectManager.getNamedObjectRrn(targetObj.getAttributeValue(), facilityRrn,
                                                                     ObjectList.RECIPE_KEY));
                        if (ProductVariableEnum.isRecipeVariable(targetObj.getAttributeValue())) {
                            rowErrorMsg.append("Recipe: ").append(targetObj.getAttributeValue())
                                       .append(" is not available");
                        } else if (targetObj.getAttributeValueRrn() == null || targetObj.getAttributeValueRrn() <= 0) {
                            rowErrorMsg.append(recipeManager.checkRecipeAndCreate(targetObj.getAttributeValue()));
                            targetObj.setAttributeValueRrn(
                                    namedObjectManager.getNamedObjectRrn(targetObj.getAttributeValue(), facilityRrn,
                                                                         ObjectList.RECIPE_KEY));
                        } else if (recipeManager.isMainRecipe(targetObj.getAttributeValueRrn()) !=
                                targetObj.getAttributeValueRrn()) {
                            rowErrorMsg.append("Recipe: ").append(targetObj.getAttributeValue())
                                       .append(" is chamber recipe!<br>");
                        }
                    } else {
                        targetObj.setAttributeValue("");
                    }
                }

                if (ProductVariableEnum.isReticleGroupVariable(targetObj.getAttributeName()) &&
                        StringUtils.isNotEmpty(targetObj.getAttributeValue())) {

                    targetObj.setAttributeValueRrn(
                            namedObjectManager.getNamedObjectRrn(targetObj.getAttributeValue(), facilityRrn,
                                                                 ObjectList.RETICLEFAMILY_KEY));
                    if (targetObj.getAttributeValueRrn() == null || targetObj.getAttributeValueRrn() <= 0) {
                        rowErrorMsg.append("Reticle Group: ").append(targetObj.getAttributeValue())
                                   .append(" does not existed!<br>");
                    }
                }

                if (ProductVariableEnum.isEdcPlanVariable(targetObj.getAttributeName()) &&
                        StringUtils.isNotEmpty(targetObj.getAttributeValue())) {

                    targetObj.setAttributeValueRrn(
                            namedObjectManager.getNamedObjectRrn(targetObj.getAttributeValue(), facilityRrn,
                                                                 ObjectList.PARAMETERSET_KEY));
                    if (targetObj.getAttributeValueRrn() == null || targetObj.getAttributeValueRrn() <= 0) {
                        rowErrorMsg.append("EDC Plan: ").append(targetObj.getAttributeValue())
                                   .append(" does not existed!<br>");
                    }
                }

                if (rowErrorMsg.length() > 0) {
                    errorMsg.append(" Row Num: ").append(rowNum).append("<br>").append(rowErrorMsg);
                }

                target.add(targetObj);
                rowNum++;
            }

            Assert.isFalse(errorMsg.length() > 0, Errors.create().content(errorMsg.toString()).build());
        }

        private void checkIfProductAttributeCanDoFreeze(List<ProductAttributeItem> willDoFreezeList) {
            StringBuilder errorMsg = new StringBuilder();
            willDoFreezeList.forEach(obj -> {
                Assert.isFalse(StringUtils.equals(obj.getStatus(), VersionStatus.ACTIVE_KEY),
                               Errors.create().content("Product : {} Version :{} has already Active, cannot freeze!")
                                     .args(obj.getProductId(), obj.getProductVersion()).build());

                if (StringUtils.isEmpty(obj.getAttributeValue())) {
                    errorMsg.append(" Flow Seq: ").append(obj.getFlowSeq()).append(" AttributeName:")
                            .append(obj.getAttributeName());
                    errorMsg.append(" attributeValue cannot be empty.<br>");
                }
            });
            Assert.isFalse(errorMsg.length() > 0, Errors.create().content(errorMsg.toString()).build());
        }

        private void checkIfProductAttributeCanDoUnfreeze(List<ProductAttributeItem> willDoUnfreezeList) {
            willDoUnfreezeList.forEach(obj -> Assert.isFalse(StringUtils.equals(obj.getStatus(), VersionStatus.ACTIVE_KEY),
                                                             Errors.create().content(
                                                                           "Product : {} Version :{} has already Active, " +
                                                                                   "cannot" + " unfrozen!")
                                                                   .args(obj.getProductId(), obj.getProductVersion())
                                                                   .build()));
        }

        private void updateStatusOfProductAttribute(TransactionLog transactionLog, Ecn ecn,
                                                    List<ProductAttributeInfo> willDoActivateInfoList,
                                                    List<ProductAttributeItem> willDoActivateItemList,
                                                    String targetStatus) {
            productAttributeInfoManager.updateStatusOfProductAttributeInfos(transactionLog, willDoActivateInfoList,
                                                                            targetStatus);

            productAttributeItemManager.updateStatusOfProductAttributeItems(transactionLog, willDoActivateItemList, ecn,
                                                                            targetStatus);
        }

        private void activateProduct(TransactionLog transactionLog, Long processRrn, Integer processVersion,
                                     List<ProductAttributeInfo> willDoActivateInfoList) {
            //536 active 更新ProductSpecInfos为active，仅当前选择的product和product_version
            productSpecInfoManager.activateProductSpecInfos(transactionLog, willDoActivateInfoList);

            specManager.activateProduct(transactionLog, processRrn, processVersion);
        }

        private void checkIfProcessSpecStatusIsActive(Long processRrn, Integer processVer) {
            ProcessSpecInfo processSpecInfo = processSpecInfoManager.getProcessSpecInfoByPrimaryKey(processRrn, processVer);

            Assert.isFalse(processSpecInfo == null || !StringUtils.equalsIgnoreCase(processSpecInfo.getCurrentStatus(),
                                                                                    ObjectStatus.ACTIVE_KEY),
                           Errors.create().content("Process spec table should be activated first!").build());
        }

        private void checkIfProductAttributeCanDoActivate(List<ProductAttributeItem> willDoActivateList) {
            StringBuilder errorMsg = new StringBuilder();
            willDoActivateList.forEach(obj -> {
                Assert.isFalse(StringUtils.equals(obj.getStatus(), VersionStatus.UNFROZEN_KEY),
                               Errors.create().content("Product : {} Version :{} unfrozen, please freeze first!")
                                     .args(obj.getProductId(), obj.getProductVersion()).build());

                if (StringUtils.isEmpty(obj.getAttributeValue())) {
                    errorMsg.append(" Flow Seq: ").append(obj.getFlowSeq()).append(" AttributeName:")
                            .append(obj.getAttributeName());
                    errorMsg.append(" attributeValue cannot be empty.<br>");
                }
            });
            Assert.isFalse(errorMsg.length() > 0, Errors.create().content(errorMsg.toString()).build());
        }

        private void checkAndBuildQueryProductAttributeForm(ProductAttributeFormDto productAttributeForm) {
            Assert.isFalse(StringUtils.isEmpty(productAttributeForm.getProcessId()),
                           Errors.create().content(MessageIdList.PROCESS_EMPTY_ID).build());

            long processRrn = namedObjectManager.getNamedObjectRrn(productAttributeForm.getProcessId(),
                                                                   LocalContext.getFacilityRrn(), ObjectList.WFL_KEY);

            Assert.isFalse(processRrn <= 0, Errors.create().key(MessageIdList.PROCESS_PROCESS_MISSING).build());

            int processVersion =
                    productAttributeForm.getProcessVersion() != null ? productAttributeForm.getProcessVersion() : 0;

            processManager.checkIfProcessVersionIsValid(processRrn, processVersion);

            Assert.isFalse(
                    productAttributeForm.getProcessVersion() == null || productAttributeForm.getProcessVersion() <= 0,
                    Errors.create().content("Invalid process version!").build());

            productAttributeForm.setProcessRrn(processRrn);
            productAttributeForm.setProcessVersion(processVersion);
        }

        private void handleProductVersionStatus(List<ProductAttributeInfo> productAttributeInfos, String targetStatus) {

            Set<String> add = new HashSet<>();

            List<ProductVersion> productVersions = new ArrayList<>();
            for (ProductAttributeInfo attributeInfo : productAttributeInfos) {
                ProductVersion productVersion = new ProductVersion();

                productVersion.setInstanceRrn(attributeInfo.getProductRrn());
                productVersion.setInstanceVersion(attributeInfo.getProductVersion());

                if (add.add(attributeInfo.getProductRrn() + "||" + attributeInfo.getProductVersion())) {
                    productVersions.add(productVersion);
                }
            }

            if (StringUtils.equals(targetStatus, VersionStatus.ACTIVE_KEY)) {
                productVersionManager.activeProductVersion(productVersions);
            } else if (StringUtils.equals(targetStatus, VersionStatus.FROZEN_KEY)) {
                productVersionManager.frozenProductVersion(productVersions);
            } else if (StringUtils.equals(targetStatus, VersionStatus.UNFROZEN_KEY)) {
                productVersionManager.unFrozenProductVersion(productVersions);
            }
        }

        private List<ProductAttributeItem> getDoActionProductAttributeItems(
                List<ProductAttributeInfo> willDoActionInfoList) {
            List<ProductAttributeItem> result = new ArrayList<>();

            for (ProductAttributeInfo productAttributeInfo : willDoActionInfoList) {
                result.addAll(productAttributeItemManager.getProductAttributeItems(productAttributeInfo.getProcessRrn(),
                                                                                   productAttributeInfo.getProcessVersion(),
                                                                                   productAttributeInfo.getProductRrn(),
                                                                                   productAttributeInfo.getProductVersion()));
            }
            return result;
        }

        private List<ProductAttributeInfo> getDoActionProductAttributeInfos(ProductAttributeFormDto productAttributeForm,
                                                                            String status) {

            List<ProductAttributeInfo> result = new ArrayList<>();

            Long processRrn = productAttributeForm.getProcessRrn();
            Integer processVersion = productAttributeForm.getProcessVersion();

            if (CollectionUtils.isNotEmpty(productAttributeForm.getProductRrns())) {

                if (productAttributeForm.getProductRrns().size() == 1) {
                    Long productRrn = productAttributeForm.getProductRrns().iterator().next();

                    if (CollectionUtils.isNotEmpty(productAttributeForm.getProductVersions())) {
                        // 如选择了 Product，且选择了 Product Version 则获取选择的 Product 及 Product Version 的属性信息
                        result.addAll(getDoActionProductAttributeInfoList(processRrn, processVersion, productRrn,
                                                                          productAttributeForm.getProductVersions(),
                                                                          status));
                    } else {
                        // 如选择了 Product，但没有选择 Product Version 则获取 Product 及对应的所有 Product Version 的属性信息
                        result.addAll(getDoActionProductAttributeInfoList(processRrn, processVersion, productRrn, status));
                    }
                } else {
                    // 如选择了多个 Product，则获取每个 Product 及对应的所有 Product Version 的属性信息
                    for (Long productRrn : productAttributeForm.getProductRrns()) {
                        result.addAll(getDoActionProductAttributeInfoList(processRrn, processVersion, productRrn, status));
                    }
                }

            } else {
                // 如没有选择 Product，则获取所有 Product 及对应的所有 Product Version 的属性信息
                result.addAll(productAttributeInfoManager.getProductAttributeInfosByProcess(processRrn, processVersion));
            }
            return result;
        }

        private List<ProductAttributeInfo> getDoActionProductAttributeInfoList(long processRrn, int processVersion,
                                                                               long productRrn,
                                                                               List<Integer> productVersions,
                                                                               String status) {
            return productVersions.stream()
                                  .map(productVersion -> productAttributeInfoManager.getProductAttributeInfos(processRrn,
                                                                                                              processVersion,
                                                                                                              productRrn,
                                                                                                              productVersion))
                                  .filter(productAttributeInfo -> productAttributeInfo != null &&
                                          !BooleanUtils.toBoolean(productAttributeInfo.getActiveFlag()) &&
                                          StringUtils.equals(status, productAttributeInfo.getCurrentStatus()))
                                  .collect(Collectors.toList());
        }

        private List<ProductAttributeInfo> getDoActionProductAttributeInfoList(long processRrn, int processVersion,
                                                                               long productRrn, String status) {
            return productAttributeInfoManager.getProductAttributeInfosByProcess(processRrn, processVersion, productRrn)
                                              .stream().filter(productAttributeInfo -> productAttributeInfo != null &&
                            !BooleanUtils.toBoolean(productAttributeInfo.getActiveFlag()) &&
                            StringUtils.equals(status, productAttributeInfo.getCurrentStatus()))
                                              .collect(Collectors.toList());
        }

        private List<ProductVersion> getAllAddedProductVersionsOfProducts(List<Long> productRrns) {
            List<ProductVersion> productVersions = new ArrayList<>();

            for (Long productRrn : productRrns) {
                List<ProductVersion> allProductVersions = productVersionManager.getAllVersions(productRrn);
                if (CollectionUtils.isEmpty(allProductVersions)) {
                    productVersions.add(productVersionManager.addProductVersion(productRrn, 0));
                } else {
                    ProductVersion productVersion = allProductVersions.iterator().next();

                    Assert.isTrue(StringUtils.equalsIgnoreCase(productVersion.getVersionStatus(), VersionStatus.ACTIVE_KEY),
                                  Errors.create().content("{} {} inActive can not addProductVersion")
                                        .args(namedObjectManager.getInstanceId(productRrn), productVersion.getProductVer())
                                        .build());

                    productVersions.add(
                            productVersionManager.addProductVersion(productRrn, productVersion.getInstanceVersion()));
                }
            }
            return productVersions;
        }

        private Long getProcessRrnOfProducts(List<Long> productRrns) {
            Set<Long> processRrns = new HashSet<>();
            for (Long productRrn : productRrns) {
                List<ProductProcess> productProcesses = productProcessManager.getProductProcesses(productRrn);
                if (CollectionUtils.isEmpty(productProcesses)) {
                    continue;
                }

                for (ProductProcess productProcess : productProcesses) {
                    processRrns.add(productProcess.getProcessRrn());
                }
            }

            Assert.isFalse(processRrns.size() == 0,
                           Errors.create().content("This product has not belong any process").build());

            Assert.isFalse(processRrns.size() > 1, Errors.create().content("This product has belong some process").build());

            return processRrns.iterator().next();
        }

        private void checkIfHasUnfrozenTimeLimit(List<Long> productRrns, String processId, Integer processVersion,
                                                 Long processRrn) {
            Page page = new Page(1, Integer.MAX_VALUE);
            /**
             * 从TIMELIMIT_SETUP_TMP表能查询出数据，当前流程版本下有未激活的Q-Time
             * 因为当Q-Time激活时，会删除TIMELIMIT_SETUP_TMP表中对应的数据
             */
            page = timeLimitSetupManager.getTimeLimitSetupForTemp(page, StringUtils.EMPTY, processId, processVersion);
            List<TimeLimitSetup> results = (List<TimeLimitSetup>) page.getResults();
            if (CollectionUtils.isEmpty(results) || CollectionUtils.isEmpty(productRrns) ||
                    processRrn <= NumberUtils.LONG_ZERO) {
                return;
            }
            for (Long productRrn : productRrns) {
                //获取只包含当前product以及只有未挂product的q-time设定
                List<TimeLimitSetup> collect = results.stream().filter(t -> (productRrn.equals(t.getStartProductRrn()) ||
                                                              NumberUtils.LONG_ZERO.equals(t.getStartProductRrn())) && processRrn.equals(t.getStartProcessRrn()))
                                                      .collect(Collectors.toList());

                Assert.isFalse(CollectionUtils.isNotEmpty(collect),
                               Errors.create().key(MessageIdList.PROCESS_SPEC_TIMELIMIT_INACTIVE)
                                     .content("The process has inactive Q-Time and cannot be activated.").build());
            }

        }

    }