content: package com.ctrip.framework.apollo.portal.service;
  
  import com.google.common.base.Objects;
  import com.google.gson.Gson;
  
  import com.ctrip.framework.apollo.common.constants.GsonType;
  import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
  import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
  import com.ctrip.framework.apollo.core.enums.Env;
  import com.ctrip.framework.apollo.core.utils.StringUtils;
  import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
  import com.ctrip.framework.apollo.portal.constant.TracerEventType;
  import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
  import com.ctrip.framework.apollo.portal.entity.bo.KVEntity;
  import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
  import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO;
  import com.ctrip.framework.apollo.portal.enums.ChangeType;
  import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
  import com.ctrip.framework.apollo.tracer.Tracer;
  
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Service;
  import org.springframework.util.CollectionUtils;
  
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  @Service
  public class ReleaseService {
  
    private static final Gson gson = new Gson();
  
    @Autowired
    private UserInfoHolder userInfoHolder;
    @Autowired
    private AdminServiceAPI.ReleaseAPI releaseAPI;
  
    public ReleaseDTO publish(NamespaceReleaseModel model) {
      Env env = model.getEnv();
      boolean isEmergencyPublish = model.isEmergencyPublish();
      String appId = model.getAppId();
      String clusterName = model.getClusterName();
      String namespaceName = model.getNamespaceName();
      String releaseBy = StringUtils.isEmpty(model.getReleasedBy()) ?
                         userInfoHolder.getUser().getUserId() : model.getReleasedBy();
  
      ReleaseDTO releaseDTO = releaseAPI.createRelease(appId, env, clusterName, namespaceName,
                                                       model.getReleaseTitle(), model.getReleaseComment(),
                                                       releaseBy, isEmergencyPublish);
  
      Tracer.logEvent(TracerEventType.RELEASE_NAMESPACE,
                      String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
  
      return releaseDTO;
    }
  
    public ReleaseDTO updateAndPublish(String appId, Env env, String clusterName, String namespaceName,
                                       String releaseTitle, String releaseComment, String branchName,
                                       boolean isEmergencyPublish, boolean deleteBranch, ItemChangeSets changeSets) {
  
      return releaseAPI.updateAndPublish(appId, env, clusterName, namespaceName, releaseTitle, releaseComment, branchName,
                                         isEmergencyPublish, deleteBranch, changeSets);
    }
  
    public List<ReleaseBO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
                                           int size) {
      List<ReleaseDTO> releaseDTOs = releaseAPI.findAllReleases(appId, env, clusterName, namespaceName, page, size);
  
      if (CollectionUtils.isEmpty(releaseDTOs)) {
        return Collections.emptyList();
      }
  
      List<ReleaseBO> releases = new LinkedList<>();
      for (ReleaseDTO releaseDTO : releaseDTOs) {
        ReleaseBO release = new ReleaseBO();
        release.setBaseInfo(releaseDTO);
  
        Set<KVEntity> kvEntities = new LinkedHashSet<>();
        Map<String, String> configurations = gson.fromJson(releaseDTO.getConfigurations(), GsonType.CONFIG);
        Set<Map.Entry<String, String>> entries = configurations.entrySet();
        for (Map.Entry<String, String> entry : entries) {
          kvEntities.add(new KVEntity(entry.getKey(), entry.getValue()));
        }
        release.setItems(kvEntities);
        //为了减少数据量
        releaseDTO.setConfigurations("");
        releases.add(release);
      }
  
      return releases;
    }
  
    public List<ReleaseDTO> findActiveReleases(String appId, Env env, String clusterName, String namespaceName, int page,
                                               int size) {
      return releaseAPI.findActiveReleases(appId, env, clusterName, namespaceName, page, size);
    }
  
    public ReleaseDTO findReleaseById(Env env, long releaseId) {
      Set<Long> releaseIds = new HashSet<>(1);
      releaseIds.add(releaseId);
      List<ReleaseDTO> releases = findReleaseByIds(env, releaseIds);
      if (CollectionUtils.isEmpty(releases)) {
        return null;
      } else {
        return releases.get(0);
      }
  
    }
  
    public List<ReleaseDTO> findReleaseByIds(Env env, Set<Long> releaseIds) {
      return releaseAPI.findReleaseByIds(env, releaseIds);
    }
  
    public ReleaseDTO loadLatestRelease(String appId, Env env, String clusterName, String namespaceName) {
      return releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
    }
  
    public void rollback(Env env, long releaseId) {
      releaseAPI.rollback(env, releaseId, userInfoHolder.getUser().getUserId());
    }
  
    public ReleaseCompareResult compare(Env env, long baseReleaseId, long toCompareReleaseId) {
  
      ReleaseDTO baseRelease = null;
      ReleaseDTO toCompareRelease = null;
      if (baseReleaseId != 0) {
        baseRelease = releaseAPI.loadRelease(env, baseReleaseId);
      }
  
      if (toCompareReleaseId != 0) {
        toCompareRelease = releaseAPI.loadRelease(env, toCompareReleaseId);
      }
  
      return compare(baseRelease, toCompareRelease);
    }
  
    public ReleaseCompareResult compare(ReleaseDTO baseRelease, ReleaseDTO toCompareRelease) {
      Map<String, String> baseReleaseConfiguration = baseRelease == null ? new HashMap<>() :
                                                     gson.fromJson(baseRelease.getConfigurations(), GsonType.CONFIG);
      Map<String, String> toCompareReleaseConfiguration = toCompareRelease == null ? new HashMap<>() :
                                                          gson.fromJson(toCompareRelease.getConfigurations(),
                                                                        GsonType.CONFIG);
  
      ReleaseCompareResult compareResult = new ReleaseCompareResult();
  
      //added and modified in firstRelease
      for (Map.Entry<String, String> entry : baseReleaseConfiguration.entrySet()) {
        String key = entry.getKey();
        String firstValue = entry.getValue();
        String secondValue = toCompareReleaseConfiguration.get(key);
        //added
        if (secondValue == null) {
          compareResult.addEntityPair(ChangeType.DELETED, new KVEntity(key, firstValue),
                                      new KVEntity(key, null));
        } else if (!Objects.equal(firstValue, secondValue)) {
          compareResult.addEntityPair(ChangeType.MODIFIED, new KVEntity(key, firstValue),
                                      new KVEntity(key, secondValue));
        }
  
      }
  
      //deleted in firstRelease
      for (Map.Entry<String, String> entry : toCompareReleaseConfiguration.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        if (baseReleaseConfiguration.get(key) == null) {
          compareResult
              .addEntityPair(ChangeType.ADDED, new KVEntity(key, ""), new KVEntity(key, value));
        }
  
      }
  
      return compareResult;
    }
  }
  