# EcnManagerImpl.java

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

    import com.fa.sesa.exception.Assert;
    import com.fa.sesa.exception.Errors;
    import com.fa.sesa.exception.SystemIllegalArgumentException;
    import com.fa.sesa.threadlocal.LocalContext;
    import com.mycim.framework.jdbc.Page;
    import com.mycim.framework.utils.lang.StringUtils;
    import com.mycim.framework.utils.lang.math.NumberUtils;
    import com.mycim.framework.utils.lang.time.DateUtils;
    import com.mycim.server.base.manager.NamedObjectManager;
    import com.mycim.server.base.manager.TransactionLogManager;
    import com.mycim.server.ecn.dao.EcnDAO;
    import com.mycim.server.edc.manager.EcnManager;
    import com.mycim.valueobject.MessageIdList;
    import com.mycim.valueobject.ObjectList;
    import com.mycim.valueobject.bas.NamedObject;
    import com.mycim.valueobject.bas.TransactionLog;
    import com.mycim.valueobject.consts.TransactionNames;
    import com.mycim.valueobject.consts.VersionStatus;
    import com.mycim.valueobject.prp.Ecn;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.sql.Timestamp;
    import java.text.DecimalFormat;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;

    /**
     * ecn
     *
     * @author Johnson.Wang
     * @version 6.0.0
     * @date 2019/8/31
     **/
    @Service
    @Transactional
    public class EcnManagerImpl implements EcnManager {
        private static final String DATE_FORMAT_YYYY_MM_DD = "yyyyMMdd";

        private static final String SUFFIX_PATTERN = "0000";

        private static final String SUFFIX_PLACEHOLDER = "____";

        private static final Integer ECN_ID_LENGTH = 15;

        private static final Integer PREFIX_LENGTH = 11;

        private static final Integer MAX_SEQUENCE = 9999;

        private static final Integer MAX_UPGRADE_COUNT = 99999;

        @Autowired
        private EcnDAO ecnDAO;

        @Autowired
        private NamedObjectManager namedObjectManager;

        @Autowired
        private TransactionLogManager transactionLogManager;

        @Override
        public Ecn generateUnFrozenEcn(Long userRrn, String type) {
            return generateUnFrozenEcn(userRrn, type, type);
        }

        @Override
        public Ecn generateUnFrozenEcn(Long requestBy, String type, String subType) {
            return generateEcn(requestBy, type, subType, VersionStatus.UNFROZEN_KEY);
        }

        @Override
        public Ecn generateActiveEcn(Long requestBy, String type, String subType) {
            return generateEcn(requestBy, type, subType, VersionStatus.ACTIVE_KEY);
        }

        @Override
        public Ecn generateActiveEcn(Long userRrn, String type) {
            return generateActiveEcn(userRrn, type, type);
        }

        @Override
        public Ecn getEcn(String ecnId) {
            return getEcn(namedObjectManager.getNamedObjectRrn(ecnId, LocalContext.getFacilityRrn(), ObjectList.ECN_KEY));
        }

        @Override
        public Ecn getEcn(long ecnRrn) {
            NamedObject namedObject = namedObjectManager.getNamedObject(ecnRrn);
            if (namedObject == null) {
                return null;
            }
            Ecn ecn = ecnDAO.getEcn(ecnRrn);
            ecn.setRequestById(namedObjectManager.getNamedObjectId(ecn.getRequestBy()));
            ecn.copyNamedObject(namedObject);

            if (ecn.getObjectType() == null) {
                ecn.setObjects(ecnDAO.getEcnObjectVersions(ecnRrn));
            } else {
                if (ecn.getObjectType().equalsIgnoreCase(ObjectList.CONTEXT_KEY)) {
                    ecn.setObjects(ecnDAO.getEcnContextValues(ecnRrn));
                } else {
                    ecn.setObjects(ecnDAO.getEcnObjectVersions(ecnRrn));
                }
            }
            return ecn;
        }

        @Override
        public void updateEcnStatus(String status, long ecnRrn) {
            updateEcnStatus(status, status, ecnRrn);
        }

        @Override
        public void updateEcnStatus(String status, String comments, long ecnRrn) {
            Ecn ecn = getEcn(ecnRrn);
            Assert.state(ecn != null,
                         Errors.create().key(MessageIdList.ECN_MISSING).content("The ECN is not found").build());
            // 1. 更改ecn状态
            ecnDAO.updateEcnStatus(status, comments, ecnRrn);

            // 2. 更改ecn关联对象状态
            if (ObjectList.CONTEXT_KEY.equals(ecn.getObjectType())) {
                ecnDAO.updateStatusForContext(status, ecnRrn);
            } else {
                ecnDAO.updateStatusForObjectVersion(status, ecnRrn);
            }
        }

        @Override
        public long insertEcn(Ecn ecn) {
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(ecn.getTransPerformedby(), ecn.getTransId());
            long transRrn = transactionLog.getTransRrn();
            long instanceRrn;
            try {
                instanceRrn = namedObjectManager.insertNamedObject(ecn, transRrn);
            } catch (SystemIllegalArgumentException e) {
                throw new SystemIllegalArgumentException(Errors.create().key(MessageIdList.ECN_MAX_UPGRADE_COUNT).content("The maximum number of daily update has been reached. Please try again tomorrow!").build());
            }
            // main or history table handle
            ecn.setInstanceRrn(instanceRrn);

            ecnDAO.insertEcn(ecn);
            ecnDAO.insertEcnHistory(transRrn, 1, transactionLog.getTransId(), ecn.getInstanceRrn(),
                                    ecn.getCreatedTimestamp());

            ecnDAO.insertEcnHistoryComment(ecn, transRrn);

            // mark transaction end flag
            transactionLogManager.markTransactionLog(transactionLog);
            return instanceRrn;
        }

        @Override
        public Ecn generateUnFrozenEcnForProcessPlan(long requestBy) {
            return generateUnFrozenEcn(requestBy, "PROCESSPLAN");
        }

        @Override
        public Ecn generateUnFrozenEcnForContext(long requestBy) {
            return generateUnFrozenEcn(requestBy, ObjectList.CONTEXT_KEY);
        }

        @Override
        public long getEcnSignOffRrn(String ecnType, String refNamedSpace, String tNamedSpace) {
            return ecnDAO.getEcnSignOffRrn(ecnType, refNamedSpace, tNamedSpace);
        }

        @Override
        public boolean isEcnControled(String object, String namedSpace) {
            return ecnDAO.isEcnControled(object, namedSpace);
        }

        @Override
        public Ecn generateActiveEcnForContext(long requestBy) {
            return generateActiveEcn(requestBy, ObjectList.CONTEXT_KEY);
        }

        @Override
        public Ecn activateEcn(Long ecnRrn, String userId) {
            Ecn ecn = getEcn(ecnRrn);
            ecn.setEcnStatus(VersionStatus.ACTIVE_KEY);
            ecn.setEffectiveDateFrom(getEffectiveDateFrom());
            ecn.setEffectiveDateTo(getEffectiveDateTo());
            ecn.setComments(TransactionNames.ACTIVE_KEY);
            ecn.setTransId(TransactionNames.ACTIVE_KEY);
            ecn.setTransPerformedby(userId);
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(ecn.getTransPerformedby(), ecn.getTransId());
            long transRrn = transactionLog.getTransRrn();

            // update ecn
            updateEcnForStatus(ecn);

            // make ecn transhisory
            ecnDAO.insertEcnHistory(transRrn, 1, transactionLog.getTransId(), ecn.getInstanceRrn(),
                                    transactionLog.getTransStartTimestamp());
            ecnDAO.insertEcnHistoryComment(ecn, transRrn);
            transactionLogManager.markTransactionLog(transactionLog);
            return ecn;
        }

        @Override
        public String generateECNID() {
            String ecnId;
            Calendar now = Calendar.getInstance();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            String today = sdf.format(now.getTime());

            String maxEcnNum = ecnDAO.getMaxEcnNo(today);
            int nextSeq = 1;
            if (StringUtils.isEmpty(maxEcnNum)) {
                ecnId = "F" + today + ".00001";
            } else {
                String maxStr = StringUtils.substringAfter(maxEcnNum, ".");
                nextSeq += Integer.parseInt(maxStr);
                Assert.isFalse(nextSeq > MAX_UPGRADE_COUNT, Errors.create().key(MessageIdList.ECN_MAX_UPGRADE_COUNT).content(
                        "The maximum number of daily update has been reached. Please try again tomorrow!").build());

                if (!NumberUtils.isParsable(maxStr)) {
                    ecnId = "F" + today + ".00001";
                } else {
                    int max = NumberUtils.toInt(maxStr) + 1;
                    String format = String.format("%05d", max);
                    ecnId = "F" + today + "." + format;
                }
            }
            return ecnId;
        }

        @Override
        public Ecn generateUnFrozenECNForRunCard(long requestBy, String type, String idType) {
            String ecnId = generateECNIDFromRunCard(idType);
            Ecn ecn = new Ecn(ecnId, namedObjectManager.getNamedSpace(LocalContext.getFacilityRrn(), ObjectList.ECN_KEY),
                              ObjectList.ECN_KEY);
            ecn.setObject(ObjectList.ECN_KEY);
            ecn.setTransId(TransactionNames.CREATE_KEY);
            ecn.setInstanceDesc("Automatic schema generation for split run card");

            ecn.setEffectiveDateFrom(getEffectiveDateFrom());
            ecn.setEffectiveDateTo(getEffectiveDateTo());
            ecn.setEcnStatus(VersionStatus.DRAFT_KEY);
            ecn.setObject(ObjectList.ECN_KEY);

            ecn.setObjectType(type);
            ecn.setObjectSubtype(type);
            ecn.setRequestBy(requestBy);

            ecn.setTransId(TransactionNames.CREATE_KEY);
            ecn.setTransPerformedby(LocalContext.getUserId());

            ecn.setInstanceRrn(insertEcn(ecn));
            return ecn;
        }

        @Override
        public Ecn generateUnFrozenEcnForProduct(long requestBy) {
            return generateUnFrozenEcn(requestBy, "PRODUCT");
        }

        @Override
        public void updateRunCardStatus(Long runCardRrn, Timestamp effectiveDateFrom, Timestamp effectiveDateTo,
                                        String status) {
            ecnDAO.updateRunCardStatus(runCardRrn, effectiveDateFrom, effectiveDateTo, status);
        }

        @Override
        public void insertEcnHistory(Long transRrn, Integer transSequence, String transId, Long ecnRrn) {
            ecnDAO.insertEcnHistory(transRrn, transSequence, transId, ecnRrn, new Timestamp(System.currentTimeMillis()));
        }

        @Override
        public Page getEcnHistoryPage(int pageNo, int pageSize, Long ecnRrn) {
            return ecnDAO.getEcnHistoryPage(pageNo, pageSize, ecnRrn);
        }

        public void updateEcnForStatus(Ecn ecn) {
            ecnDAO.updateEcnForStatus(ecn);
            if (ObjectList.CONTEXT_KEY.equals(ecn.getObjectType())) {
                ecnDAO.updateContextValueForEcn(ecn);
            } else {
                ecnDAO.updateObjectVersionForEcn(ecn);
            }
        }

        private Ecn generateEcn(Long requestBy, String type, String subType, String ecnStatus) {
            Ecn ecn = buildBaseEcnInfo();
            ecn.setObjectType(type);
            ecn.setObjectSubtype(subType);
            ecn.setRequestBy(requestBy);
            String userId = namedObjectManager.getInstanceId(requestBy);
            userId = StringUtils.isEmpty(userId) ? "SYSTEM" : userId;
            ecn.setTransPerformedby(userId);
            ecn.setEffectiveDateFrom(DateUtils.formatDate(new Date()));
            ecn.setEffectiveDateTo(getEffectiveDateTo());
            ecn.setEcnStatus(ecnStatus);
            ecn.setObject(ObjectList.ECN_KEY);
            ecn.setTransId(TransactionNames.CREATE_KEY);
            ecn.setInstanceRrn(insertEcn(ecn));
            return ecn;
        }

        private String generateECNIDFromRunCard(String idType) {
            StringBuilder prefix = buildPrefix(idType);

            String maxEcnId = ecnDAO.getMaxEcnIdFromHistory(prefix.toString() + SUFFIX_PLACEHOLDER);
            int nextSeq = 1;
            if (StringUtils.isNotEmpty(maxEcnId)) {
                nextSeq += substringForSeq(maxEcnId);
                Assert.isFalse(nextSeq > MAX_SEQUENCE, Errors.create().content(
                        "The generated serial number has reached the " + "maximum serial number of the day. Please " +
                                "apply again tomorrow.").build());
            }
            return prefix.append(new DecimalFormat(SUFFIX_PATTERN).format(nextSeq)).toString();
        }

        private int substringForSeq(String maxEcnId) {
            return Integer.parseInt(maxEcnId.substring(PREFIX_LENGTH, ECN_ID_LENGTH));
        }

        private StringBuilder buildPrefix(String idType) {
            Calendar now = Calendar.getInstance();
            String today = new SimpleDateFormat(DATE_FORMAT_YYYY_MM_DD).format(now.getTime());

            return new StringBuilder(ECN_ID_LENGTH).append(idType).append(today);
        }

        /**
         * 设置新ECN的基本信息
         */
        private Ecn buildBaseEcnInfo() {
            String ecnId = generateECNID();
            Ecn ecn = new Ecn(ecnId, namedObjectManager.getNamedSpace(LocalContext.getFacilityRrn(), ObjectList.ECN_KEY),
                              ObjectList.ECN_KEY);

            ecn.setInstanceDesc("Automatic schema generation");
            ecn.setObject(ObjectList.ECN_KEY);
            ecn.setTransId(TransactionNames.CREATE_KEY);

            return ecn;
        }

        /**
         * 生效时间: 当前时间
         *
         * @return
         */
        private String getEffectiveDateFrom() {
            return DateUtils.formatDate(new Date());
        }

        /**
         * 失效时间: 100年后时间
         *
         * @return
         */
        private String getEffectiveDateTo() {
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 100);
            return DateUtils.formatDate(cal.getTime());
        }

    }