package com.microservices.zone.project.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.PageUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.html.EscapeUtil;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.datascope.annotation.DataScope;
import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.utils.DictUtils;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.domain.SysDictData;
import com.microservices.zone.common.service.IZoneAsyncService;
import com.microservices.zone.detail.domain.ZoneDetail;
import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.project.domain.ZoneProject;
import com.microservices.zone.project.domain.vo.*;
import com.microservices.zone.project.mapper.ZoneProjectMapper;
import com.microservices.zone.project.service.IZoneProjectService;
import com.microservices.zone.project.service.IZoneProjectTypeService;
import com.microservices.zone.specialProject.service.IZoneSpecialProjectRelevancyService;
import com.microservices.zone.utils.ZoneConstants;
import com.microservices.zone.utils.ZoneGitLinkRequestUrl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

import static com.microservices.common.core.utils.PageUtils.startPage;

/**
 * 特色专区项目聚合Service业务层处理
 *
 * @author otto
 * @date 2023-05-11
 */
@Service
public class ZoneProjectServiceImpl implements IZoneProjectService {
    private static final Logger logger = LoggerFactory.getLogger(ZoneProjectServiceImpl.class);
    @Autowired
    private ZoneProjectMapper zoneProjectMapper;
    @Autowired
    private IZoneDetailService zoneDetailService;
    @Autowired
    private IZoneProjectTypeService zoneProjectTypeService;
    @Autowired
    private GitLinkRequestHelper gitLinkRequestHelper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IZoneAsyncService zoneAsyncService;
    @Autowired
    @Lazy
    private IZoneSpecialProjectRelevancyService zoneSpecialProjectRelevancyService;

    /**
     * 查询特色专区项目聚合
     *
     * @param id 特色专区项目聚合主键
     * @return 特色专区项目聚合
     */
    @Override
    public ZoneProjectDataVo selectZoneProjectDataById(Long id) {
        ZoneProject zoneProject = zoneProjectMapper.selectZoneProjectById(id);
        if (zoneProject == null) {
            throw new ServiceException("该项目聚合不存在(项目聚合Id[" + id + "])");
        }
        zoneDetailService.checkZoneDataScope(zoneProject.getZoneId());
        JSONObject gitLinkProject = getGitLinkProjectById(zoneProject.getGitlinkProjectId());
        return zoneProjectToZoneProjectDataVo(zoneProject, gitLinkProject);
    }

    private ZoneProject selectZoneProjectById(Long id) {
        ZoneProject zoneProject = zoneProjectMapper.selectZoneProjectById(id);
        if (zoneProject == null) {
            throw new ServiceException("该项目聚合不存在(项目聚合Id[" + id + "])");
        }
        zoneDetailService.checkZoneDataScope(zoneProject.getZoneId());
        return zoneProject;
    }

    private JSONObject getGitLinkProjectById(Long gitLinkProjectId) {
        JSONObject result = gitLinkRequestHelper.doGet(
                ZoneGitLinkRequestUrl.PROJECTS("?ids=" + gitLinkProjectId)
        );
        JSONArray gitLinkProjectList = result.getJSONArray(ZoneConstants.GITLINK_RESPONSE_PROJECTS_KEY);
        if (gitLinkProjectList.isEmpty()) {
            throw new ServiceException("该项目聚合对应的GitLink项目不存在(GitLink项目Id[" + gitLinkProjectId + "])");
        }
        return gitLinkProjectList.getJSONObject(0);
    }

    /**
     * 查询特色专区项目聚合列表
     *
     * @param zoneProjectSearchVo 特色专区项目聚合
     * @param isOpen              是否前台访问
     * @return 特色专区项目聚合
     */
    @Override
    public GenericsTableDataInfo<ZoneProjectDataVo> selectZoneProjectList(ZoneProjectSearchVo zoneProjectSearchVo, boolean isOpen) {
        if (isOpen && zoneProjectSearchVo.getZoneId() == null) {
            throw new ServiceException("查询项目列表时,请指定专区Id");
        }
        zoneDetailService.selectZoneDetailById(zoneProjectSearchVo.getZoneId());
        startPage();
        zoneProjectSearchVo.setIsOpen(isOpen);
        List<ZoneProject> zoneProjectList = zoneProjectMapper.selectZoneProjectList(zoneProjectSearchVo);
        return PageUtils.toListPage(zoneProjectList, this::toZoneProjectDataVoList);
    }

    private List<ZoneProjectDataVo> toZoneProjectDataVoList(List<ZoneProject> zoneProjectList) {
        List<ZoneProjectDataVo> zoneProjectDataVoList = new ArrayList<>();
        // 原方案为遍历项目聚合再逐一从GitLink获取对应项目详情，此方案延时过高，调整为根据项目Id批量获取GitLink项目信息
        List<String> gitLinkProjectIdList = zoneProjectList
                .stream()
                .map(x -> String.valueOf(x.getGitlinkProjectId()))
                .collect(Collectors.toList());
        JSONObject result = gitLinkRequestHelper.doGet(
                ZoneGitLinkRequestUrl.PROJECTS("?ids=" + String.join(ZoneConstants.ARRAY_SPLIT_SYMBOLS, gitLinkProjectIdList))
        );
        List<JSONObject> gitLinkProjectList = result.getList(ZoneConstants.GITLINK_RESPONSE_PROJECTS_KEY, JSONObject.class);
        List<String> resultGitLinkProjectIdList = gitLinkProjectList
                .stream()
                .map(x -> x.getString("id"))
                .collect(Collectors.toList());

        //检查查询的GitLink项目Id列表和需查询的项目Id列表是否一致，不一致需要考虑是否该项目在GitLink中设置为私有或者删除了，此时需要清理该项目
        gitLinkProjectIdList.removeAll(resultGitLinkProjectIdList);
        if (!gitLinkProjectIdList.isEmpty()) {
            for (String gitLinkProjectId : gitLinkProjectIdList) {
                Long projectId = zoneProjectList
                        .stream()
                        .filter(x -> gitLinkProjectId.equals(String.valueOf(x.getGitlinkProjectId())))
                        .map(ZoneProject::getId)
                        .findFirst().orElse(null);
                if (projectId == null) {
                    throw new ServiceException("该GitLink项目对应的项目聚合不存在(GitLink项目Id[" + gitLinkProjectId + "])");
                }
                logger.info("GitLink中未查找到对应项目(GitLink项目Id为：{}),对该项目进行删除(项目Id为：{})"
                        , gitLinkProjectId
                        , projectId);
                deleteZoneProjectById(projectId);
                zoneProjectList = zoneProjectList.stream().filter(x -> !x.getId().equals(projectId)).collect(Collectors.toList());
            }
        }

        for (ZoneProject zoneProject : zoneProjectList) {
            JSONObject gitLinkProject = gitLinkProjectList
                    .stream()
                    .filter(x -> zoneProject.getGitlinkProjectId().equals(x.getLong("id")))
                    .findFirst()
                    .orElse(null);
            if (gitLinkProject == null) {
                throw new ServiceException("该项目聚合对应的GitLink项目不存在(项目Id[" + zoneProject.getId() + "])");
            }
            JSONObject author = gitLinkProject.getJSONObject("author");
            if (author != null) {
                String name = author.getString("name") + "/" + gitLinkProject.getString("name");
                String fullName = author.getString("login") + "/" + gitLinkProject.getString("identifier");
                if (!zoneProject.getName().equals(name) || !zoneProject.getFullName().equals(fullName)) {
                    zoneProject.setName(name);
                    zoneProject.setFullName(fullName);
                    zoneProjectMapper.updateZoneProject(zoneProject);
                }
            }
            zoneProjectDataVoList.add(zoneProjectToZoneProjectDataVo(zoneProject, gitLinkProject));
        }
        return zoneProjectDataVoList;
    }

    String[] getGitlinkProjectKeys(Long zoneId) {
        ZoneDetail zoneDetail = zoneDetailService.selectZoneDetailById(zoneId);
        String gitlinkProjectKeys = zoneDetail.getGitlinkProjectKeys();
        if (StringUtils.isEmpty(gitlinkProjectKeys)) {
            return new String[0];
        }
        return gitlinkProjectKeys.split(",");
    }

    private void updateProjectNameAndFullName(JSONObject gitLinkProject, ZoneProject zoneProject) {
        JSONObject author = gitLinkProject.getJSONObject("author");
        if (author != null) {
            String name = author.getString("name") + "/" + gitLinkProject.getString("name");
            String updateName = null;
            String updateFullName = null;
            if (StringUtils.isNotEmpty(name) && !name.equals(zoneProject.getName())) {
                updateName = name;
            }
            String fullName = author.getString("login") + "/" + gitLinkProject.getString("identifier");
            if (StringUtils.isNotEmpty(fullName) && !fullName.equals(zoneProject.getFullName())) {
                updateFullName = fullName;
            }
            if (updateName != null && updateFullName != null) {
                zoneProjectMapper.updateZoneProjectNameAndFullName(zoneProject.getZoneId()
                        , updateName
                        , updateFullName,
                        DateUtils.getNowDate());
            }
        }
    }

    private ZoneProjectDataVo zoneProjectToZoneProjectDataVo(ZoneProject zoneProject, JSONObject gitLinkProject) {
        //每次从GitLink查询项目时自动更新项目名称和项目标识
        updateProjectNameAndFullName(gitLinkProject, zoneProject);
        ZoneProjectDataVo zoneProjectDataVo = zoneProject.toZoneProjectDataVo();
        HashMap<String, String> projectKeyName = new HashMap<>();
        JSONObject projectPropertiesVo = new JSONObject();
        //项目名称、头像、描述为一定展示的字段
        projectPropertiesVo.put("repositoryId", gitLinkProject.getLongValue("id"));
        String authorImageUrl;
        JSONObject author = gitLinkProject.getJSONObject("author");
        if (author != null) {
            String authorImagePath = author.getString("image_url");
            authorImageUrl = EscapeUtil.removeExtraSlashOfUrl(String.format("%s/%s", gitLinkRequestHelper.gitLinkUrl, authorImagePath));
            projectPropertiesVo.put("authorImageUrl", authorImageUrl);
            projectKeyName.put("authorImageUrl", "用户头像");
            projectPropertiesVo.put("name", author.getString("name") + "/" + gitLinkProject.getString("name"));
            projectKeyName.put("name", "项目名称");
            projectPropertiesVo.put("fullName", author.getString("login") + "/" + gitLinkProject.getString("identifier"));
            projectKeyName.put("fullName", "项目完整地址");
        }
        String description = gitLinkProject.getString("description");
        projectPropertiesVo.put("description", description);
        projectKeyName.put("description", "项目简介");

        String[] projectKeys = getGitlinkProjectKeys(zoneProject.getZoneId());
        //当专区设置的项目需展示的Key时通过Key获取值，否则默认获取相关值
        List<SysDictData> sysDictDataList = DictUtils.getDictCache(CacheConstants.SYS_DICT_GITLINK_PROJECT_PROPERTIES);
        if (sysDictDataList != null) {
            for (String projectKey : projectKeys) {
                SysDictData sysDictData = sysDictDataList.stream().filter(x -> projectKey.equals(x.getDictValue())).findFirst().orElse(null);
                if (sysDictData == null) {
                    continue;
                }
                projectKeyName.put(sysDictData.getDictValue(), sysDictData.getDictLabel());
                switch (sysDictData.getDictValue()) {
                    case "topics": {
                        //项目标签
                        String topicsStr = "";
                        JSONArray topicsArray = gitLinkProject.getJSONArray("topics");
                        if (topicsArray != null) {
                            List<String> topics = new ArrayList<>();
                            for (int i = 0; i < topicsArray.size(); i++) {
                                topics.add(topicsArray.getJSONObject(i).getString("name"));
                            }
                            topicsStr = String.join(ZoneConstants.ARRAY_SPLIT_SYMBOLS, topics);
                        }
                        projectPropertiesVo.put("topics", topicsStr);
                        break;
                    }
                    case "praises_count": {
                        //点赞数
                        Long praisesCount = gitLinkProject.getLong("praises_count");
                        projectPropertiesVo.put("praisesCount", praisesCount);
                        break;
                    }
                    case "forked_count": {
                        //复刻数
                        Long forkedCount = gitLinkProject.getLong("forked_count");
                        projectPropertiesVo.put("forkedCount", forkedCount);
                        break;
                    }
                    case "language": {
                        //项目语言
                        String language = "";
                        JSONObject languageObj = gitLinkProject.getJSONObject("language");
                        if (languageObj != null) {
                            language = languageObj.getString("name");
                        }
                        projectPropertiesVo.put("language", language);
                        break;
                    }
                    case "time_ago": {
                        //更新时间
                        String timeAgo = gitLinkProject.getString("time_ago");
                        projectPropertiesVo.put("timeAgo", timeAgo);
                        break;
                    }
                    default: {
                        logger.error("{}该项目属性当前不支持展示", sysDictData.getDictLabel());
                    }
                }
            }
        }

        projectPropertiesVo.put("projectKeyName", projectKeyName);
        if (gitLinkProject.containsKey("has_dataset")) {
            zoneProjectDataVo.setHasDataset(gitLinkProject.getBoolean("has_dataset"));
        } else {
            zoneProjectDataVo.setHasDataset(false);
        }
        zoneProjectDataVo.setProjectProperties(projectPropertiesVo);
        zoneProjectDataVo.setProjectURL(EscapeUtil.removeExtraSlashOfUrl(String.format("%s/%s", gitLinkRequestHelper.gitLinkUrl, projectPropertiesVo.get("fullName"))));
        return zoneProjectDataVo;
    }

    private JSONArray autoGetProjectProperties(String[] projectKeys, JSONObject gitLinkProject) {
        JSONArray projectPropertiesList = new JSONArray();

        List<SysDictData> dictDataList = DictUtils.getDictCache(CacheConstants.SYS_DICT_GITLINK_PROJECT_PROPERTIES);
        if (dictDataList != null) {
            for (String key : projectKeys) {
                SysDictData sysDictData = dictDataList.stream().filter(x -> x.getDictValue().equals(key)).findFirst().orElse(null);
                if (sysDictData == null) {
                    throw new ServiceException("该GitLink项目属性不存在(属性Key[" + key + "])");
                }
                if (checkGitLinkProjectContainKey(gitLinkProject, key)) {
                    String value = "";
                    //默认通过符号“.”区分是否为对象取值，例如作者返回值为对象，此时key若为author.name，那么此处逻辑为从author中获取name的值
                    String[] keyPoints = key.split(ZoneConstants.PROJECT_PROPERTIES_POINT_KEY);
                    if (keyPoints.length == 1) {
                        value = gitLinkProject.getString(keyPoints[0]);
                    }
                    if (keyPoints.length > 1) {
                        if (ZoneConstants.PROJECT_PROPERTIES_ARRAY_KEY.equals(keyPoints[0])) {
                            JSONArray jsonArray = gitLinkProject.getJSONArray(keyPoints[1]);
                            List<String> valueArray = new ArrayList<>();
                            for (int i = 0; i < jsonArray.size(); i++) {
                                valueArray.add(recursivelyGetValue(jsonArray.getJSONObject(i), Arrays.copyOfRange(keyPoints, 2, keyPoints.length)));
                            }
                            value = String.join(ZoneConstants.ARRAY_SPLIT_SYMBOLS, valueArray);
                        } else {
                            value = recursivelyGetValue(gitLinkProject, keyPoints);
                        }
                    }

                    projectPropertiesList.add(new ProjectPropertiesMap(sysDictData.getDictLabel(), value));
                }
            }
        }
        return projectPropertiesList;
    }

    private boolean checkGitLinkProjectContainKey(JSONObject gitLinkProject, String key) {
        //默认通过符号“.”区分是否为对象取值，例如作者返回值为对象，此时key若为author.name，那么此处逻辑为从author中获取name的值
        String[] keyPoints = key.split(ZoneConstants.PROJECT_PROPERTIES_POINT_KEY);
        if (keyPoints.length == 1) {
            return gitLinkProject.containsKey(keyPoints[0]);
        }
        if (keyPoints.length > 1) {
            if (ZoneConstants.PROJECT_PROPERTIES_ARRAY_KEY.equals(keyPoints[0])) {
                JSONArray jsonArray = gitLinkProject.getJSONArray(keyPoints[1]);
                if (jsonArray == null) {
                    return false;
                } else if (!jsonArray.isEmpty()) {
                    return StringUtils.isNotEmpty(recursivelyGetValue(jsonArray.getJSONObject(0), Arrays.copyOfRange(keyPoints, 2, keyPoints.length)));
                }
                return true;
            } else {
                return StringUtils.isNotEmpty(recursivelyGetValue(gitLinkProject, keyPoints));
            }
        }
        return false;
    }

    private String recursivelyGetValue(JSONObject jsonObject, String[] keyPoints) {
        for (int i = 0; i < keyPoints.length; i++) {
            String key = keyPoints[i];
            if (i == keyPoints.length - 1) {
                return jsonObject.getString(key);
            }
            jsonObject = jsonObject.getJSONObject(key);
        }
        return null;
    }

    /**
     * 新增特色专区项目聚合
     *
     * @param zoneProjectInputVo 特色专区项目聚合
     * @return 结果
     */
    @Override
    public int insertZoneProject(ZoneProjectInputVo zoneProjectInputVo) {
        ZoneDetail zoneDetail = zoneDetailService.selectZoneDetailById(zoneProjectInputVo.getZoneId());
        zoneProjectTypeService.selectZoneProjectTypeByIdAndZoneId(zoneProjectInputVo.getProjectTypeId(), zoneProjectInputVo.getZoneId());
        JSONObject gitLinkProject = getGitLinkProjectById(zoneProjectInputVo.getGitlinkProjectId());
        ZoneProject zoneProject = zoneProjectInputVo.toZoneProject();
        zoneProject.setCreateTime(DateUtils.getNowDate());
        zoneProject.setDeptId(zoneDetail.getDeptId());
        zoneProject.setDeptId(zoneDetail.getDeptId());
        JSONObject author = gitLinkProject.getJSONObject("author");
        if (author != null) {
            zoneProject.setName(author.getString("name") + "/" + gitLinkProject.getString("name"));
            zoneProject.setFullName(author.getString("login") + "/" + gitLinkProject.getString("identifier"));
        }
        //设置排序值为当前最大的排序值+1
        Integer maxSort = zoneProjectMapper.selectMaxSortByZoneId(zoneProjectInputVo.getZoneId());
        if (maxSort == null) {
            maxSort = 1;
        } else {
            maxSort++;
        }
        zoneProject.setSort(maxSort);
        int success = zoneProjectMapper.insertZoneProject(zoneProject);
        zoneAsyncService.setProjectGitlinkInfoCache(zoneProject);
        //当首页推荐文章数量不为0时，新增文章应该设置为首页推荐后一位
        Integer homePageCount = selectHomepageCount(zoneProject.getZoneId());
        if (homePageCount > 0) {
            Integer sort = setSortByOrder(zoneProject, homePageCount + 1, ZoneConstants.FALSE);
            zoneProject.setSort(sort);
            zoneProjectMapper.updateZoneProject(zoneProject);
        }
        return success;
    }

    /**
     * 修改特色专区项目聚合
     *
     * @param id                  项目聚合主键
     * @param zoneProjectUpdateVo 特色专区项目聚合
     * @return 结果
     */
    @Override
    public int updateZoneProject(Long id, ZoneProjectUpdateVo zoneProjectUpdateVo) {
        ZoneProject oldZoneProject = selectZoneProjectById(id);
        if (zoneProjectUpdateVo.getProjectTypeId() != null) {
            zoneProjectTypeService.selectZoneProjectTypeById(zoneProjectUpdateVo.getProjectTypeId());
        }
        ZoneProject zoneProject = zoneProjectUpdateVo.toZoneProject(id);
        zoneProject.setUpdateTime(DateUtils.getNowDate());
        zoneProject.setUpdateBy(SecurityUtils.getUsername());
        if (zoneProjectUpdateVo.getOrder() != null) {
            Integer setSort = setSortByOrder(oldZoneProject, zoneProjectUpdateVo.getOrder(), oldZoneProject.getIsHomepage());
            zoneProject.setSort(setSort);
        }
        // 切换项目所在栏目时需更新活跃度排名数据
        if (!oldZoneProject.getProjectTypeId().equals(zoneProjectUpdateVo.getProjectTypeId())) {
            zoneAsyncService.clearZoneProjectSortByScoreCache(zoneProject);
            zoneAsyncService.setProjectGitlinkInfoCache(zoneProject);
        }
        return zoneProjectMapper.updateZoneProject(zoneProject);
    }

    private Integer setSortByOrder(ZoneProject data, Integer order, String isHomePage) {
        int setSort;
        Long zoneId = data.getZoneId();
        Long id = data.getId();
        String name = data.getName();
        Integer maxCount = zoneProjectMapper.selectCountByZoneId(zoneId);
        //检查用户输入顺序是否合法
        if (order < 1 || order > maxCount) {
            throw new ServiceException(String.format("顺序最小为1，最大为%d(名称[%s])", maxCount, name));
        }
        if (isHomePage != null) {
            Integer homePageCount = selectHomepageCount(zoneId);
            if (ZoneConstants.FALSE.equals(isHomePage)) {
                //设置非首页推荐的文章顺序时，顺序需要大于首页推荐数量
                if (order <= homePageCount) {
                    throw new ServiceException(String.format("当前首页推荐数量为[%d]，设置非首页推荐的顺序时，顺序需小于该数量(名称[%s])", homePageCount, data.getName()));
                }
            } else {
                //设置首页推荐的文章排序时，顺序最大值不能大于首页推荐数量
                if (order > homePageCount) {
                    throw new ServiceException(String.format("当前首页推荐数量为[%d]，设置首页推荐的顺序时，顺序最大值不能大于首页推荐数量(名称[%s])", homePageCount, data.getName()));
                }
            }

        }
        //获取所在行
        ZoneProject theOrderContainSelf = zoneProjectMapper.selectDataByOrderAndZoneId(data, order, true);
        //当指定序号为当前数据时，不修改排序
        if (theOrderContainSelf != null && theOrderContainSelf.getId().equals(id)) {
            return null;
        }

        if (order == 1 || order.equals(maxCount)) {
            int minSort = zoneProjectMapper.selectMinSortByZoneId(zoneId);
            int maxSort = zoneProjectMapper.selectMaxSortByZoneId(zoneId);
            //当用户输入顺序为1时，数据顺序调整为最小值-1
            if (order == 1) {
                setSort = (maxSort + 1);
            } else {
                //当用户输入顺序为列表总数时，数据顺序调整为最大值+1
                setSort = (minSort - 1);
            }
        } else {
            //获取指定序号的数据(不包含当前数据)
            ZoneProject theOrderData = zoneProjectMapper.selectDataByOrderAndZoneId(
                    data, order, false);
            if (theOrderData == null) {
                throw new ServiceException(String.format("指定顺序不存在(名称[%s]，顺序[%d])", name, order));
            }
            //计算用户上一个序号对应的排序值(不包含当前数据)
            ZoneProject theLastOrderData = zoneProjectMapper.selectDataByOrderAndZoneId(
                    data, order - 1, false);
            Integer theLastSort = theLastOrderData.getSort();
            if (theOrderData.getSort() - theLastSort > 1) {
                setSort = theLastSort - 1;
            } else {
                setSort = theOrderData.getSort();
                //需要将小于于等于该sort的所有数据项sort-1
                Integer updateCount = zoneProjectMapper.batchUpdateSortDecrease(zoneId, setSort);
                logger.info("批量减少小于指定排序的排序值，共计更新{}条", updateCount);
            }
        }
        return setSort;
    }

    /**
     * 批量删除特色专区项目聚合
     *
     * @param ids 需要删除的特色专区项目聚合主键
     * @return 结果
     */
    @Override
    public int deleteZoneProjectByIds(Long[] ids) {
        int success = 0;
        for (Long id : ids) {
            success += deleteZoneProjectById(id);
        }
        return success;
    }

    /**
     * 删除特色专区项目聚合信息
     *
     * @param id 特色专区项目聚合主键
     * @return 结果
     */
    @Override
    public int deleteZoneProjectById(Long id) {
        ZoneProject zoneProject = selectZoneProjectById(id);
        zoneAsyncService.clearZoneProjectSortByScoreCache(zoneProject);
        zoneSpecialProjectRelevancyService.deleteZoneSpecialProjectRelevancyByProjectId(id);
        return zoneProjectMapper.deleteZoneProjectById(id);
    }

    @Override
    public List<ProjectLanguageVo> selectGitlinkProjectLanguages() {
        return gitLinkRequestHelper.doGetToObjectList(ZoneGitLinkRequestUrl.PROJECT_LANGUAGES(), ProjectLanguageVo.class);
    }

    @Override
    public GenericsTableDataInfo<JSONObject> selectGitlinkProjectList(Long zoneId, GitLinkProjectSearchVo gitLinkProjectSearchVo) {

        JSONObject result = gitLinkRequestHelper.doGet(
                ZoneGitLinkRequestUrl.PROJECTS(gitLinkProjectSearchVo.genQueryString())
        );
        List<JSONObject> list = result.getList(ZoneConstants.GITLINK_RESPONSE_PROJECTS_KEY, JSONObject.class);
        if (list == null) {
            throw new ServiceException("Gitlink项目列表获取失败");
        }
        ZoneProjectSearchVo zoneProjectSearchVo = new ZoneProjectSearchVo();
        zoneProjectSearchVo.setZoneId(zoneId);
        List<ZoneProject> zoneProjectList = zoneProjectMapper.selectZoneProjectList(zoneProjectSearchVo);
        List<JSONObject> gitlinkProjectList = new ArrayList<>();
        for (JSONObject gitlinkProject : list) {
            if (zoneId != null) {
                boolean isChoose = zoneProjectList.stream().anyMatch(x -> x.getGitlinkProjectId().equals(gitlinkProject.getLong("id")));
                if (isChoose) {
                    gitlinkProject.put("isChoose", true);
                } else {
                    gitlinkProject.put("isChoose", false);
                }
            }
            gitlinkProjectList.add(gitlinkProject);
        }
        Long size = result.getLong(ZoneConstants.GITLINK_RESPONSE_TOTAL_COUNT_KEY);
        return PageUtils.toPage(gitlinkProjectList, size);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertBatchZoneProject(List<ZoneProjectInputVo> zoneProjectInputVoList) {
        int success = 0;
        for (ZoneProjectInputVo zoneProjectInputVo : zoneProjectInputVoList) {
            Long zoneId = zoneProjectInputVo.getZoneId();
            zoneDetailService.checkZoneDataScope(zoneId);
            Long gitlinkProjectId = zoneProjectInputVo.getGitlinkProjectId();
            ZoneProject zoneProject = zoneProjectMapper
                    .selectZoneProjectByGitLinkProjectIdAndZoneId(gitlinkProjectId, zoneId);
            if (zoneProject != null) {
                throw new ServiceException("该GitLink项目已存在于专区中，不可重复添加(专区Id[" + zoneId + "],GitLink项目Id[" + gitlinkProjectId + "])");
            }
            success += insertZoneProject(zoneProjectInputVo);
        }
        return success;
    }

    @Override
    @DataScope(deptAlias = "zp")
    public Integer selectZoneProjectListCount(ZoneProjectSearchVo zoneProjectSearchVo) {
        return zoneProjectMapper.selectZoneProjectListCount(zoneProjectSearchVo);
    }

    @Override
    public int updateZoneProjectHomePageShow(Long id) {
        ZoneProject zoneProject = selectZoneProjectById(id);
        //检查项目原本是否首页展示，若原本未展示将设置为展示，若设置了首页最大展示数量此刻需判断是否已达到最大值
        if (ZoneConstants.TRUE.equals(zoneProject.getIsHomepage())) {
            //取消首页推荐时，需将顺序设置为首页推荐后一位,因为需要取消一位，所以设置的顺序应该跟首页推荐数量一致
            Integer homepageCount = selectHomepageCount(zoneProject.getZoneId());
            Integer sort = setSortByOrder(zoneProject, homepageCount, null);
            zoneProject.setSort(sort);
            zoneProject.setIsHomepage(ZoneConstants.FALSE);
        } else {
            ZoneDetail zoneDetail = zoneDetailService.selectZoneDetailById(zoneProject.getZoneId());
            Long homepageProjectSize = zoneDetail.getHomepageProjectSize();
            if (homepageProjectSize != null && homepageProjectSize > 0) {
                ZoneProjectSearchVo zoneProjectSearchVo = new ZoneProjectSearchVo();
                zoneProjectSearchVo.setZoneId(zoneDetail.getId());
                zoneProjectSearchVo.setIsHomepage(ZoneConstants.TRUE);
                List<ZoneProject> zoneProjectList = zoneProjectMapper.selectZoneProjectList(zoneProjectSearchVo);
                if (zoneProjectList.size() >= homepageProjectSize) {
                    throw new ServiceException("该专区首页最多展示" + homepageProjectSize + "个项目(专区名称[" + zoneDetail.getName() + "])");
                }
            }
            //首页推荐时，需将顺序设置为1
            Integer sort = setSortByOrder(zoneProject, 1, null);
            zoneProject.setSort(sort);
            zoneProject.setIsHomepage(ZoneConstants.TRUE);
        }
        return zoneProjectMapper.updateZoneProject(zoneProject);
    }

    @Override
    public Integer selectHomepageCount(Long zoneId) {
        return zoneProjectMapper.selectHomepageCount(zoneId);
    }

    @Override
    public List<JSONObject> selectZoneProjectByScoreList(ZoneProjectSearchVo zoneProjectSearchVo, boolean isOpen) {
        if (zoneProjectSearchVo.getProjectTypeId() == null) {
            throw new ServiceException("查询项目聚合列表（活跃度排名）时请指定项目类型");
        }
        if (isOpen && zoneProjectSearchVo.getZoneId() == null) {
            throw new ServiceException("查询项目列表时,请指定专区Id");
        }
        zoneDetailService.selectZoneDetailById(zoneProjectSearchVo.getZoneId());

        // 获取专区下活跃度列表
        Date now = DateUtils.getNowDate();
        String lastWeekMonday = DateUtils.mondayOfLastWeek(now, 1);
        String lastWeekKey = CacheConstants.getZoneProjectSortByScore(lastWeekMonday, zoneProjectSearchVo.getZoneId(), zoneProjectSearchVo.getProjectTypeId());
        String secondLastWeekMonday = DateUtils.mondayOfLastWeek(now, 2);
        String secondLastWeekKey = CacheConstants.getZoneProjectSortByScore(secondLastWeekMonday, zoneProjectSearchVo.getZoneId(), zoneProjectSearchVo.getProjectTypeId());
        Set<Long> gitlinkProjectIdSet = redisService.getSortSetBySize(lastWeekKey);

        zoneProjectSearchVo.setIsOpen(isOpen);
        List<ZoneProject> zoneProjectList = zoneProjectMapper.selectZoneProjectList(zoneProjectSearchVo);
        List<ZoneProject> zoneProjectAndScoreList = new ArrayList<>();

        for (Long gitlinkProjectId : gitlinkProjectIdSet) {
            zoneProjectList.stream().filter(x -> gitlinkProjectId.equals(x.getGitlinkProjectId())).findFirst().ifPresent(zoneProjectAndScoreList::add);
        }

        List<JSONObject> zoneProjectJSONList = new ArrayList<>();


        List<ZoneProjectDataVo> zoneProjectDataVoList = toZoneProjectDataVoList(zoneProjectAndScoreList);
        for (ZoneProjectDataVo zoneProjectDataVo : zoneProjectDataVoList) {
            Long gitlinkProjectId = zoneProjectDataVo.getGitlinkProjectId();
            JSONObject jsonObject = JSONObject.from(zoneProjectDataVo);
            try {
                ProjectGitlinkInfoVo projectGitlinkInfoVo = redisService.getCacheObject(CacheConstants.getZoneProjectGitlinkInfo(gitlinkProjectId));
                Long lastWeekRank = redisService.getSortSetRank(lastWeekKey, gitlinkProjectId);
                Long secondLastWeekRank = redisService.getSortSetRank(secondLastWeekKey, gitlinkProjectId);
                projectGitlinkInfoVo.setScoreChange(secondLastWeekRank - lastWeekRank);
                projectGitlinkInfoVo.setContributorsList(null);
                jsonObject.put("extra", projectGitlinkInfoVo);
            } catch (Exception e) {
                logger.error("获取项目gitlink信息失败", e);
            }
            zoneProjectJSONList.add(jsonObject);
        }
        return zoneProjectJSONList;
    }
}
