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());
        }

    }

}