# ProductAttributeItemManagerImpl.java

    package com.mycim.server.spec.manager.impl;

    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.collections.CollectionUtils;
    import com.mycim.framework.utils.lang.time.DateUtils;
    import com.mycim.server.base.manager.TransactionLogManager;
    import com.mycim.server.prp.manager.ProductVersionManager;
    import com.mycim.server.spec.dao.ProductAttributeItemDao;
    import com.mycim.server.spec.dao.ProductAttributeItemHistoryDao;
    import com.mycim.server.spec.manager.*;
    import com.mycim.valueobject.bas.TransactionLog;
    import com.mycim.valueobject.consts.VersionStatus;
    import com.mycim.valueobject.prp.Ecn;
    import com.mycim.valueobject.prp.ProcessSpecInfo;
    import com.mycim.valueobject.prp.ProcessSpecItem;
    import com.mycim.valueobject.prp.ProductAttributeFormDto;
    import com.mycim.valueobject.prp.ProductAttributeHistoryQueryDto;
    import com.mycim.valueobject.prp.ProductAttributeInfo;
    import com.mycim.valueobject.prp.ProductAttributeItem;
    import com.mycim.valueobject.prp.ProductSpecItem;
    import com.mycim.valueobject.prp.ProductVariableEnum;
    import com.mycim.valueobject.prp.ProductVersion;
    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.*;

    /**
     * Product Attribute
     *
     * @author Qiansheng.Wang
     * @since 2021-05-15
     */
    @Service
    @Transactional
    public class ProductAttributeItemManagerImpl implements ProductAttributeItemManager {

        @Autowired
        ProductAttributeItemDao productAttributeItemDao;

        @Autowired
        ProductAttributeItemHistoryDao productAttributeItemHistoryDao;

        @Autowired
        TransactionLogManager transactionLogManager;

        @Autowired
        ProductVersionManager productVersionManager;

        @Autowired
        ProductSpecInfoManager productSpecInfoManager;

        @Autowired
        ProcessSpecItemManager processSpecItemManager;

        @Autowired
        ProcessSpecInfoManager processSpecInfoManager;

        @Autowired
        ActivationAttributeManager activationAttributeManager;

        @Autowired
        ProductAttributeInfoManager productAttributeInfoManager;

        @Override
        public void insertProductAttributeItems(TransactionLog transactionLog,
                                                List<ProductAttributeItem> productAttributeItems) {
            if (CollectionUtils.isEmpty(productAttributeItems)) {
                return;
            }

            for (ProductAttributeItem productAttributeItem : productAttributeItems) {
                productAttributeItem.setStatus(VersionStatus.UNFROZEN_KEY);

                productAttributeItem.setEcnRrn(null);
                productAttributeItem.setEffectiveTime(null);
                productAttributeItem.setTerminatedTime(null);

                productAttributeItem.setCreatedUser(transactionLog.getTransPerformedBy());
                productAttributeItem.setCreatedTime(transactionLog.getTransStartTimestamp());

                productAttributeItem.setUpdatedUser(transactionLog.getTransPerformedBy());
                productAttributeItem.setUpdatedTime(transactionLog.getTransStartTimestamp());
            }

            productAttributeItemDao.insertProductAttributeItems(productAttributeItems);
            productAttributeItemHistoryDao.insertProductAttributeItemHistories(transactionLog, productAttributeItems);
        }

        @Override
        public void deleteProductAttributeItems(TransactionLog transactionLog,
                                                List<ProductAttributeItem> productAttributeItems) {
            if (CollectionUtils.isEmpty(productAttributeItems)) {
                return;
            }
            productAttributeItemDao.deleteProductAttributeItems(productAttributeItems);
            productAttributeItemHistoryDao.insertProductAttributeItemHistories(transactionLog, productAttributeItems);
        }

        @Override
        public void updateProductAttributeItems(TransactionLog transactionLog,
                                                List<ProductAttributeItem> productAttributeItems) {
            if (CollectionUtils.isEmpty(productAttributeItems)) {
                return;
            }

            for (ProductAttributeItem productAttributeItem : productAttributeItems) {
                productAttributeItem.setUpdatedUser(transactionLog.getTransPerformedBy());
                productAttributeItem.setUpdatedTime(transactionLog.getTransStartTimestamp());
            }

            productAttributeItemDao.updateProductAttributeItems(productAttributeItems);
            productAttributeItemHistoryDao.insertProductAttributeItemHistories(transactionLog, productAttributeItems);
        }

        @Override
        public void updateStatusOfProductAttributeItems(TransactionLog transactionLog,
                                                        List<ProductAttributeItem> productAttributeItems, Ecn ecn,
                                                        String targetStatus) {
            for (ProductAttributeItem attributeItem : productAttributeItems) {

                attributeItem.setStatus(targetStatus);
                attributeItem.setUpdatedTime(transactionLog.getTransStartTimestamp());
                attributeItem.setUpdatedUser(transactionLog.getTransPerformedBy());

                if (ecn != null) {
                    attributeItem.setEcnRrn(ecn.getInstanceRrn());
                    attributeItem.setEffectiveTime(DateUtils.stringToTimestamp(ecn.getEffectiveDateFrom()));
                    attributeItem.setTerminatedTime(DateUtils.stringToTimestamp(ecn.getEffectiveDateTo()));
                }

            }

            productAttributeItemDao.updateStatusOfProductAttributeItems(productAttributeItems);
            productAttributeItemHistoryDao.insertProductAttributeItemHistories(transactionLog, productAttributeItems);
        }

        @Override
        public void saveProductAttribute(TransactionLog transactionLog, ProcessSpecInfo processSpecInfo,
                                         List<ProcessSpecItem> processSpecItems, List<ProductVersion> allProductVersions) {
            saveProductAttribute(transactionLog, processSpecInfo, processSpecItems, allProductVersions, false);
        }

        @Override
        public void saveProductAttributeWithReset(TransactionLog transactionLog, ProcessSpecInfo processSpecInfo,
                                                  List<ProcessSpecItem> processSpecItems,
                                                  List<ProductVersion> allProductVersions) {
            saveProductAttribute(transactionLog, processSpecInfo, processSpecItems, allProductVersions, true);
        }

        private void saveProductAttribute(TransactionLog transactionLog, ProcessSpecInfo processSpecInfo,
                                          List<ProcessSpecItem> processSpecItems, List<ProductVersion> allProductVersions,
                                          boolean needReset) {
            // Build product attribute item templates
            List<ProductAttributeItem> productAttributeItemTemplates = buildProductAttributeItemTemplates(processSpecItems);

            // Build product attribute table infos
            List<ProductAttributeInfo> willSaveProductAttributeInfos = buildProductAttributeInfos(processSpecInfo,
                                                                                                  allProductVersions);

            // Get the highest active version of the spec item
            Integer highestActivatedProcessVersion = processSpecInfoManager
                    .getHighestActivatedProcessVersion(processSpecInfo.getProcessRrn());
            List<ProductSpecItem> highestActivatedProductSpecItems = activationAttributeManager
                    .getActiveProductSpec(processSpecInfo.getProcessRrn(), highestActivatedProcessVersion);

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

            List<ProductAttributeItem> productAttributeItemsInDb = getProductAttributeItems(processSpecInfo.getProcessRrn(),
                                                                                            processSpecInfo
                                                                                                    .getProcessVersion());
            // Build current product spec item and copy from the highest active version of the spec item
            for (ProductAttributeInfo productAttributeInfo : willSaveProductAttributeInfos) {
                willSaveProductAttributeItems
                        .addAll(buildProductAttributeItems(productAttributeItemTemplates, productAttributeInfo,
                                                           highestActivatedProcessVersion,
                                                           highestActivatedProductSpecItems));
            }

            if (CollectionUtils.isNotEmpty(willSaveProductAttributeInfos) &&
                    (CollectionUtils.isNotEmpty(willSaveProductAttributeItems) ||
                            CollectionUtils.isNotEmpty(productAttributeItemsInDb))) {
                productAttributeInfoManager
                        .saveProductAttributeInfos(transactionLog, processSpecInfo, willSaveProductAttributeInfos);

                saveProductAttributeItems(transactionLog, processSpecInfo, willSaveProductAttributeItems, needReset);

                //若535修改删除了所有变量则表示当前flow为全常量，将PROD_ATTRIBUTE_INFO表中对应数据删除
                List<ProductAttributeItem> productAttributeItems = getProductAttributeItems(processSpecInfo.getProcessRrn(),
                                                                                            processSpecInfo
                                                                                                    .getProcessVersion());
                if (CollectionUtils.isEmpty(productAttributeItems)) {
                     productAttributeInfoManager.deleteProductAttributeInfos(willSaveProductAttributeInfos);
                }
            }
        }

        private void saveProductAttributeItems(TransactionLog transactionLog, ProcessSpecInfo processSpecInfo,
                                               List<ProductAttributeItem> willSaveProductAttributeItems,
                                               boolean needReset) {
            Map<String, List<ProductAttributeItem>> productAttributeItemSaveMap = new HashMap<>();

            productAttributeItemSaveMap.put("insertItems", new ArrayList<>());
            productAttributeItemSaveMap.put("updateItems", new ArrayList<>());
            productAttributeItemSaveMap.put("deleteItems", new ArrayList<>());

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

            for (ProductAttributeItem item : willSaveProductAttributeItems) {
                if (!productAttributeItemsInDb.contains(item)) {
                    // insert
                    productAttributeItemSaveMap.get("insertItems").add(item);
                } else {
                    //update
                    productAttributeItemSaveMap.get("updateItems").add(item);
                }
            }

            if (needReset) {
                for (ProductAttributeItem item : productAttributeItemsInDb) {
                    if (!willSaveProductAttributeItems.contains(item)) {
                        // delete
                        productAttributeItemSaveMap.get("deleteItems").add(item);
                    }
                }
            }

            List<ProductAttributeItem> insertItems = productAttributeItemSaveMap.get("insertItems");
            List<ProductAttributeItem> deleteItems = productAttributeItemSaveMap.get("deleteItems");
            insertProductAttributeItems(transactionLog, insertItems);
            deleteProductAttributeItems(transactionLog, deleteItems);
            if (CollectionUtils.isNotEmpty(insertItems) || CollectionUtils.isNotEmpty(deleteItems)) {
                updateStatusOfProductAttributeItems(transactionLog, productAttributeItemSaveMap.get("updateItems"), null,
                                                    VersionStatus.UNFROZEN_KEY);
            }
        }

        private List<ProductAttributeItem> buildProductAttributeItems(
                List<ProductAttributeItem> productAttributeItemTemplates, ProductAttributeInfo productAttributeInfo,
                Integer latestActivatedProcessVersion, List<ProductSpecItem> latestActivatedProductSpecItems) {

            List<ProductAttributeItem> result = new ArrayList<>();
            for (ProductAttributeItem template : productAttributeItemTemplates) {
                ProductAttributeItem attribute = new ProductAttributeItem();

                BeanUtils.copyProperties(template, attribute);

                attribute.setProductRrn(productAttributeInfo.getProductRrn());
                attribute.setProductId(productAttributeInfo.getProductId());
                attribute.setProductVersion(productAttributeInfo.getProductVersion());

                ProductSpecItem productSpecItemTemp = new ProductSpecItem();

                BeanUtils.copyProperties(attribute, productSpecItemTemp);

                productSpecItemTemp.setProcessVersion(latestActivatedProcessVersion);

                int index = latestActivatedProductSpecItems.indexOf(productSpecItemTemp);

                if (index >= 0) {
                    productSpecItemTemp = latestActivatedProductSpecItems.get(index);

                    if (ProductVariableEnum.isRecipeVariable(attribute.getAttributeName())) {
                        attribute.setAttributeValue(productSpecItemTemp.getRecipeId());
                        attribute.setAttributeValueRrn(productSpecItemTemp.getRecipeRrn());
                    }
                    if (ProductVariableEnum.isReticleGroupVariable(attribute.getAttributeName())) {
                        attribute.setAttributeValue(productSpecItemTemp.getReticleFamilyId());
                        attribute.setAttributeValueRrn(productSpecItemTemp.getReticleFamilyRrn());
                    }
                    if (ProductVariableEnum.isEdcPlanVariable(attribute.getAttributeName())) {
                        attribute.setAttributeValue(productSpecItemTemp.getParameterSetId());
                        attribute.setAttributeValueRrn(productSpecItemTemp.getParameterSetRrn());
                    }
                }

                result.add(attribute);
            }

            return result;
        }

        private List<ProductAttributeInfo> buildProductAttributeInfos(ProcessSpecInfo processSpecInfo,
                                                                      List<ProductVersion> allProductVersions) {
            List<ProductAttributeInfo> willSaveProductAttributeInfos = new ArrayList<>();

            for (ProductVersion productVersion : allProductVersions) {
                ProductAttributeInfo info = new ProductAttributeInfo();

                info.setProductRrn(productVersion.getProductRrn());
                info.setProductId(productVersion.getProductId());
                info.setProductVersion(productVersion.getInstanceVersion());
                info.setProcessId(processSpecInfo.getProcessId());
                info.setProcessRrn(processSpecInfo.getProcessRrn());
                info.setProcessVersion(processSpecInfo.getProcessVersion());

                willSaveProductAttributeInfos.add(info);
            }
            return willSaveProductAttributeInfos;
        }

        /**
         * Build the {@code ProductAttributeDetailInfo} template to be added.
         *
         * @param processSpecItems saved process spec item list
         * @return List of templates constructed according to processSpecItems
         */
        private List<ProductAttributeItem> buildProductAttributeItemTemplates(List<ProcessSpecItem> processSpecItems) {
            List<ProductAttributeItem> productAttributeItems = new ArrayList<>();

            for (ProcessSpecItem processSpecItem : processSpecItems) {
                if (!BooleanUtils.toBoolean(processSpecItem.getVariableFlag())) {
                    continue;
                }

                addProductAttributeItems(productAttributeItems, processSpecItem, processSpecItem.getRecipeRrn(),
                                         processSpecItem.getRecipeId());

                addProductAttributeItems(productAttributeItems, processSpecItem, processSpecItem.getReticleFamilyRrn(),
                                         processSpecItem.getReticleFamilyId());

                addProductAttributeItems(productAttributeItems, processSpecItem, processSpecItem.getParameterSetRrn(),
                                         processSpecItem.getParameterSetId());
            }
            return productAttributeItems;
        }

        /**
         * Add the {@code ProductAttributeDetailInfo} to {@code productAttributeItems}
         *
         * @param productAttributeItems Target list
         * @param processSpecItem       Template data
         * @param attributeNameRrn      Instance RRN (like. Recipe RRN, Parameter Set RRN, Reticle Family RRN)
         * @param attributeName         Instance ID (like. Recipe ID, Parameter Set ID, Reticle Family ID)
         */
        private void addProductAttributeItems(List<ProductAttributeItem> productAttributeItems,
                                              ProcessSpecItem processSpecItem, Long attributeNameRrn,
                                              String attributeName) {
            if (attributeNameRrn != null && attributeNameRrn > 0 && ProductVariableEnum.isVariable(attributeName)) {
                ProductAttributeItem productAttributeItem = new ProductAttributeItem();

                BeanUtils.copyProperties(processSpecItem, productAttributeItem);

                productAttributeItem.setAttributeNameRrn(attributeNameRrn);
                productAttributeItem.setAttributeName(attributeName);

                productAttributeItems.add(productAttributeItem);
            }
        }

        @Override
        public List<ProductAttributeItem> getProductAttributeItems(Long processRrn, Integer processVersion) {
            ProductAttributeItem attributeItemQueryTemp = new ProductAttributeItem();
            attributeItemQueryTemp.setProcessRrn(processRrn);
            attributeItemQueryTemp.setProcessVersion(processVersion);
            return getProductAttributeItems(attributeItemQueryTemp);
        }

        @Override
        public List<ProductAttributeItem> getProductAttributeItems(Long processRrn, Integer processVersion, Long productRrn,
                                                                   Integer productVersion) {
            ProductAttributeItem attributeItemQueryTemp = new ProductAttributeItem();
            attributeItemQueryTemp.setProductRrn(productRrn);
            attributeItemQueryTemp.setProductVersion(productVersion);
            attributeItemQueryTemp.setProcessRrn(processRrn);
            attributeItemQueryTemp.setProcessVersion(processVersion);
            return getProductAttributeItems(attributeItemQueryTemp);
        }

        private List<ProductAttributeItem> getProductAttributeItems(ProductAttributeItem attributeItemQueryTemp) {
            List<ProductAttributeItem> items = productAttributeItemDao
                    .getProductAttributeItemsWithPrimaryKey(attributeItemQueryTemp);
            if (items == null) {
                items = new ArrayList<>();
            }
            return items;
        }

        @Override
        public List<ProductAttributeItem> queryProductAttributeItems(ProductAttributeFormDto queryInfo) {
            List<ProductAttributeItem> result = productAttributeItemDao.queryProductAttributeItems(queryInfo);
            if (CollectionUtils.isEmpty(result)) {
                result = Collections.emptyList();
            }
            return result;
        }

        @Override
        public Page queryProductAttributeHistories(ProductAttributeHistoryQueryDto historyQuery) {
            Page page = new Page(historyQuery.getPageNo(), historyQuery.getPageSize());

            return productAttributeItemHistoryDao.queryProductAttributeHistories(page, historyQuery);
        }

    }