package com.microservices.cms.doc.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.microservices.cms.doc.domain.*;
import com.microservices.cms.doc.domain.gitlink.EntryDto;
import com.microservices.cms.doc.domain.gitlink.FileEntryDto;
import com.microservices.cms.doc.mapper.CmsDocMapper;
import com.microservices.cms.doc.service.ICmsAsyncService;
import com.microservices.cms.doc.service.ICmsDocCommentService;
import com.microservices.cms.doc.service.ICmsDocService;
import com.microservices.cms.project.domain.CmsProject;
import com.microservices.cms.project.service.ICmsProjectService;
import com.microservices.cms.utils.CmsConstants;
import com.microservices.cms.utils.CmsGitLinkRequestUrl;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.constant.HttpStatus;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.enums.DocAndZoneOperation;
import com.microservices.common.core.enums.DocAuditStatus;
import com.microservices.common.core.enums.SystemRole;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.common.core.utils.html.EscapeUtil;
import com.microservices.common.httpClient.constant.GitLinkConstants;
import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.auth.AuthUtil;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteDeptService;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.RemoteZoneService;
import com.microservices.system.api.domain.SysUserDeptRole;
import com.microservices.system.api.model.LoginUser;
import com.microservices.system.api.utils.FeignUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.microservices.common.core.utils.sign.Base64.contentToBase64;

/**
 * 文章信息Service业务层处理
 *
 * @author otto
 * @date 2023-03-28
 */
@Service
public class CmsDocServiceImpl implements ICmsDocService {
    private static final Logger logger = LoggerFactory.getLogger(CmsDocServiceImpl.class);
    @Autowired
    private CmsDocMapper cmsDocMapper;
    @Autowired
    private RemoteZoneService remoteZoneService;
    @Autowired
    private RemoteUserService remoteUserService;
    @Autowired
    private ICmsProjectService cmsProjectService;
    @Autowired
    private GitLinkRequestHelper gitLinkRequestHelper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private ICmsAsyncService cmsAsyncService;
    @Autowired
    private RemoteDeptService remoteDeptService;
    @Autowired
    @Lazy
    private ICmsDocCommentService cmsDocCommentService;
    @Value("${cms.detailPath:/newdetail}")
    public String detailPath;

    /**
     * 查询文章信息
     *
     * @param id     文章信息主键
     * @param isOpen
     * @return 文章信息
     */
    @Override
    public CmsDocDetailDto selectCmsDocById(Long id, boolean isOpen, HttpServletRequest request) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
        if (cmsDoc == null || !canVisitNotPassedDoc(cmsDoc, isOpen, request)) {
            throw new ServiceException(HttpStatus.NOT_FOUND, "该文章不存在(文章Id[" + id + "])");
        }
        if (cmsDoc.getIsDir()) {
            throw new ServiceException("不能通过文章接口获取栏目详情(文章Id[" + id + "])");
        }

        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());

        String branchName = DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus()) ? CmsConstants.BRANCH_MASTER :  cmsDoc.getAuditTmpBranchName();
        FileEntryDto fileEntryDto = getFileEntryDto(cmsProject, cmsDoc.getFilePath(), branchName);
        EntryDto entryDto = fileEntryDto.getEntries();

        CmsDocDetailDto cmsDocDto = cmsDoc.copyToCmsDocDetailDto();
        CmsDoc cmsDir = cmsDocMapper.selectCmsDocDirByNameAndDeptId(cmsDoc.getDirName(), cmsDoc.getDeptId());
        cmsDocDto.setCmsDir(cmsDir);
        cmsDocDto.setContent(entryDto.getContent());
        if (cmsDoc.getPublishTime() != null && cmsDoc.getPublishTime().after(cmsDoc.getCreateTime())) {
            cmsDocDto.setIsFirstPublish(false);
        }
        if (isOpen) {
            updateVisits(cmsDocDto.getId());
        }
        return cmsDocDto;
    }

    @Override
    public CmsDoc selectBaseCmsDocById(Long id) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
        if (cmsDoc == null || cmsDoc.getIsDir()) {
            throw new ServiceException("该文章不存在(文章Id[%d])", id);
        }
        return cmsDoc;
    }

    private boolean canVisitNotPassedDoc(CmsDoc cmsDoc, boolean isOpen, HttpServletRequest request) {
        boolean canVisit = true;
        if (isOpen) {
            if (!DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus())) {
                canVisit = false;
                String gitLinkToken = AuthUtil.getGitLinkRequestToken(request);
                if (StringUtils.isNotEmpty(gitLinkToken)) {
                    String currentUserName = FeignUtils.getReturnData(
                            remoteUserService.getUserNameByGitLinkToken(gitLinkToken, SecurityConstants.INNER));
                    if (cmsDoc.getCreateBy() != null && cmsDoc.getCreateBy().equals(currentUserName)) {
                        canVisit = true;
                    }
                }
            }
        }
        return canVisit;
    }

    @Override
    public FileEntryDto getFileEntryDto(CmsProject cmsProject, String filePath, String branchName) {
        //todo：暂时仅考虑master分支
        JSONObject result = gitLinkRequestHelper.doGet(
                CmsGitLinkRequestUrl.GET_FILE_SUB_ENTRIES(
                        cmsProject,
                        filePath,
                        branchName));
        String Entries_Key = "entries";
        if (!result.containsKey(Entries_Key)) {
            throw new ServiceException("获取GitLink项目中的文件失败(项目名称[" + cmsProject.getName() + "])");
        }
        return result.toJavaObject(FileEntryDto.class);
    }

    @Override
    public List<CmsDocAllDto> selectCmsDocListByDept(Long deptId, CmsDocSearchVo cmsDocSearchVo) {
        cmsDocSearchVo.setDeptId(deptId);
        cmsDocSearchVo.setIsDir(false);
        List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearchVo);
        return cmsDocList.stream().map(CmsDoc::copyToCmsDocAllDto).collect(Collectors.toList());
    }

    @Override
    public int selectCmsDocCountByDept(Long deptId, CmsDocSearchVo cmsDocSearchVo) {
        cmsDocSearchVo.setDeptId(deptId);
        cmsDocSearchVo.setIsDir(false);
        return cmsDocMapper.selectCmsDocCount(cmsDocSearchVo);
    }

    @Override
    public List<EntryDto> getGitLinkFileByDir(CmsProject cmsProject, String dirName) {
        //获取GitLink仓库专栏目录下所有文件
        JSONObject result = gitLinkRequestHelper.doGet(
                CmsGitLinkRequestUrl.GET_DIR_SUB_ENTRIES(cmsProject, dirName, CmsConstants.BRANCH_MASTER));
        List<EntryDto> entryDtoList = getEntriesFromRequestResult(result, cmsProject, true);
        if (entryDtoList == null) {
            return null;
        }
        //仅获取类型为File的文件
        entryDtoList = entryDtoList
                .stream()
                .filter(entry -> CmsConstants.ENTRY_TYPE_FILE_KEY.equals(entry.getType()))
                .collect(Collectors.toList());
        return entryDtoList;
    }

    @Override
    public void updateCmsDir(Long id, CmsDirUpdateVo cmsDirUpdateVo) {
        CmsDoc oldCmsDir = selectCmsDir(id);
        checkCmsLockStatus(oldCmsDir.getDeptId());
        //设置栏目排序
        setCmsSort(cmsDirUpdateVo.getOrder(), oldCmsDir, false, null);
        updateCmsCommonDatabase(oldCmsDir, CmsConstants.BRANCH_MASTER);
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(oldCmsDir.getDeptId());
        CmsDoc newCmsDir = oldCmsDir.copyToRenameCmsDir(cmsDirUpdateVo.getName());

        if (StringUtils.isNotEmpty(newCmsDir.getName()) && !newCmsDir.getName().equals(oldCmsDir.getName())) {
            CmsDoc cmsDir = cmsDocMapper.selectCmsDocDirByNameAndDeptId(newCmsDir.getName(), oldCmsDir.getDeptId());
            if (cmsDir != null) {
                    throw new ServiceException("栏目名称已存在，不允许进行重命名(新栏目名称[" + newCmsDir.getName() + "])");
                }
            cmsAsyncService.asyncMoveFileToNewDir(cmsProject, oldCmsDir, newCmsDir);
            }
    }

    @Override
    public void syncAll() {
        CmsProject cmsProjectSearch = new CmsProject();
        List<CmsProject> cmsProjectList = cmsProjectService.selectCmsProjectList(cmsProjectSearch);
        for (CmsProject cmsProject : cmsProjectList) {
            cmsAsyncService.fullSyncDocData(cmsProject);
        }

    }

    @Override
    public Integer selectHomepageCountByDeptIdAndDirName(Long deptId, String dirName) {
        return cmsDocMapper.selectHomepageCountByDeptIdAndDirName(deptId, dirName);
    }

    @Override
    public List<CmsDocDetailDto> selectCmsDocDetailList(Long dirId) {
        List<CmsDocDetailDto> cmsDocDetailDtoList = new ArrayList<>();
        CmsDoc cmsDir = cmsDocMapper.selectCmsDocDirById(dirId);
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDir.getDeptId());
        CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
        cmsDocSearchVo.setDirName(cmsDir.getName());
        cmsDocSearchVo.setDeptId(cmsDir.getDeptId());
        cmsDocSearchVo.setIsDir(false);
        List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearchVo);
        List<CompletableFuture<CmsDocDetailDto>> futures = new ArrayList();
        for (CmsDoc cmsDoc : cmsDocList) {
            CompletableFuture<CmsDocDetailDto> future = CompletableFuture.supplyAsync(() -> {
                CmsDocDetailDto cmsDocDetailDto = cmsDoc.copyToCmsDocDetailDto();
                cmsDocDetailDto.setContent(getCmsDocFileContent(cmsProject, cmsDoc));
                return cmsDocDetailDto;
            });
            futures.add(future);
        }

        // 等待所有异步任务完成，并将结果添加到结果列表中
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
        allTasks.thenRun(() -> {
            for (CompletableFuture<CmsDocDetailDto> future : futures) {
                try {
                    cmsDocDetailDtoList.add(future.get());
                } catch (Exception e) {
                    // 处理异常情况
                    throw new ServiceException("异步获取文章详情时出错：" + e.getMessage());
                }
            }
        }).join();

        return cmsDocDetailDtoList;
    }

    @Override
    public String getDocDetailUrl(Long deptId, Long id) {
        return EscapeUtil.removeExtraSlashOfUrl(
                gitLinkRequestHelper.gitLinkUrl + "/zone" + "/" + getZoneKeyByDeptId(deptId) + "/" + detailPath + "/" + id
        );
    }

    /**
     * 查询文章信息列表
     *
     * @param dirId
     * @param cmsDocSearchVo
     * @return 文章信息
     */
    @Override
    public List<CmsDocBaseDto> selectCmsDocList(Long dirId, CmsDocSearchVo cmsDocSearchVo) {
        return selectCmsDocListByDatabase(dirId, cmsDocSearchVo);
    }

    private List<CmsDocBaseDto> selectCmsDocListByDatabase(Long dirId, CmsDocSearchVo cmsDocSearchVo) {
        CmsDoc cmsDocDir = selectCmsDir(dirId);

        cmsDocSearchVo.setDeptId(cmsDocDir.getDeptId());
        cmsDocSearchVo.setDirName(cmsDocDir.getName());
        List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearchVo);
        if (cmsDocList == null || cmsDocList.isEmpty()) {
            return new ArrayList<>();
        }
        return cmsDocList.stream().map(CmsDoc::copyToCmsDocBaseDto).collect(Collectors.toList());
    }

    /**
     * 检查专区资讯是否处于锁定状态，若处于则抛出异常
     *
     * @param deptId
     */
    private void checkCmsLockStatus(Long deptId) {
        JSONObject zoneJson = this.getZoneJSONByDeptId(deptId);
        String isLock = zoneJson.getString("isLockCms");
        if (CmsConstants.TRUE.equals(isLock)) {
            String name = zoneJson.getString("name");
            throw new ServiceException("当前专区资讯处于锁定状态，不允许修改(专区名称[%s])", name);
        }
    }

    /**
     * 新增文章信息
     *
     * @param dirId
     * @param cmsDocInputDto 文章信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer insertCmsDoc(Long dirId, CmsDocInputDto cmsDocInputDto) {
        //通过SHA获取栏目
        CmsDoc dirDoc = selectCmsDir(dirId);
        checkCmsLockStatus(dirDoc.getDeptId());
        CmsDoc cmsDoc = cmsDocInputDto.toCmsDoc(dirDoc);

        if (isNoAuditRequired(dirDoc.getDeptId()) || isCurrentUserZoneAdmin(dirDoc.getDeptId())) {
            //文章自动置为审核通过状态值
            cmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
            boolean success = insertCommonCmsDoc(cmsDoc,
                    "创建文章：" + StringUtils.truncateStringAndConcatenatingSpecifiedString(cmsDoc.getName(), 100, "..."),
                    cmsDocInputDto.getContent());
            return success ? cmsDoc.getId().intValue() : null;
        } else {
            //创建临时分支，创建文件，创建PR
            //文章待审核状态值
            cmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
            cmsDoc.setAuditType(DocAndZoneOperation.OPERATION_CREATE.getOperationCode());
            boolean success = insertCmsDocWithAudit(cmsDoc,
                    "创建文章：" + StringUtils.truncateStringAndConcatenatingSpecifiedString(cmsDoc.getName(), 100, "..."),
                    cmsDocInputDto.getContent());
            return success ? cmsDoc.getId().intValue() : null;
        }

    }

    private boolean isCurrentUserZoneAdmin(Long deptId) {
        boolean isZoneAdmin = false;
        List<SysUserDeptRole> sysUserDeptRoleList = FeignUtils.getReturnData(
                remoteDeptService.getSysUserDeptRoleListByDeptId(deptId, SecurityConstants.INNER));
        LoginUser currentUser = SecurityUtils.getLoginUser();
        if (sysUserDeptRoleList != null && !sysUserDeptRoleList.isEmpty()) {
            isZoneAdmin = sysUserDeptRoleList.stream().anyMatch(sysUserDeptRole -> sysUserDeptRole.getSysRole() != null
                    && sysUserDeptRole.getUserId().equals(currentUser.getUserId())
                    && sysUserDeptRole.getSysRole().getRoleKey().equals(SystemRole.ZONE_ADMIN.getRoleKey()));
        }
        return isZoneAdmin;
    }

    private boolean isNoAuditRequired(Long deptId) {
        JSONObject zoneJson = this.getZoneJSONByDeptId(deptId);
        String needAudit = zoneJson.getString("docNeedAudit");
        return "0".equals(needAudit);
    }

    @Override
    public String getCmsDocFileContent(CmsProject cmsProject, CmsDoc cmsDoc) {
        String branchName = CmsConstants.BRANCH_MASTER;
        if (!DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus())) {
            branchName = cmsDoc.getAuditTmpBranchName();
        }
        //当删除文章为文件夹时，判断该文件夹下是否存在文章，若存在需先删除所有文章
        JSONObject getSubEntriesResult = gitLinkRequestHelper.doGet(
                CmsGitLinkRequestUrl.GET_FILE_SUB_ENTRIES(
                        cmsProject,
                        cmsDoc.getFilePath(),
                        branchName));
        FileEntryDto fileEntryDto = getSubEntriesResult.toJavaObject(FileEntryDto.class);
        if (fileEntryDto == null || fileEntryDto.getEntries() == null) {
            throw new ServiceException("该文章不存在(文章名称[" + cmsDoc.getName() + "])");
        }
        EntryDto entryDto = fileEntryDto.getEntries();
        return entryDto.getContent();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean auditCmsDoc(Long docId, CmsDocAuditVo cmsDocAuditVo) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(docId);
        checkCmsLockStatus(cmsDoc.getDeptId());
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
        if (cmsDocAuditVo.getPass() == 1) {
            //同意合并请求
            JSONObject result = mergePullRequestByGitLink(cmsProject, cmsDoc);
            if (result.containsKey(GitLinkConstants.STATUS_KEY)) {
                //2.若请求GitLink合并请求操作返回正确的状态值，返回合并成功,更新数据库
                if (result.getInteger(GitLinkConstants.STATUS_KEY).equals(1)) {
                    if (cmsDoc.getAuditType().equals(DocAndZoneOperation.OPERATION_CREATE.getOperationCode())) {
                        handleCreatedCmsDocWithPass(cmsProject, cmsDoc);
                        return true;
                    }
                    if (cmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_UPDATE.getOperationCode())) {
                        handleUpdatedCmsDocWithPass(cmsProject, cmsDoc);
                        return true;
                    }
                    if (cmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_DELETE.getOperationCode())) {
                        handleDeletedCmsDocWithPass(cmsDoc, cmsProject);
                        return true;
                    }
                }
            }
        }
        if (cmsDocAuditVo.getPass() == 0) {
            //拒绝GitLink合并请求
            JSONObject result = refuseMergePullRequestByGitLink(cmsDoc, cmsProject);
            if (result.containsKey(GitLinkConstants.STATUS_KEY)) {
                //2.若拒绝GitLink合并请求操作返回正确的状态值,更新数据库
                if (result.getInteger(GitLinkConstants.STATUS_KEY).equals(1)) {
                    handleCmsDocWithNoPassByReedit(cmsDocAuditVo, cmsDoc, cmsProject);
                    return true;
                }
            }
        }
        return true;
    }

    @Override
    public Boolean isCmsDocEditable(CmsDocBaseDto cmsDocBaseDto) {
        boolean editable = false;
        //已审核通过文章，根据是否有存在待处理文章返回，存在false，不存在true
        if (DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDocBaseDto.getAuditStatus())) {
            editable = cmsDocMapper.hasSourceDoc(String.valueOf(cmsDocBaseDto.getId())) == 0;
            return editable;
        }
        SysUserDeptRole currentSysUserDeptRole = SecurityUtils.getUserIdentity();
        String currentUserName = currentSysUserDeptRole.getSysUser().getUserName();
        String currentUserRoleKey = currentSysUserDeptRole.getSysRole().getRoleKey();
        //审核未通过文章——审核不通过+可重新编辑,本人及专区管理员可编辑
        if (DocAuditStatus.AUDIT_FAIL.getAuditCode().equals(cmsDocBaseDto.getAuditStatus())) {
            editable = SystemRole.ZONE_OPERATOR.getRoleKey().equals(currentUserRoleKey)
                    || SystemRole.ZONE_ADMIN.getRoleKey().equals(currentUserRoleKey);
            return editable;
        }
        //待审核文章，可重复编辑
        if (DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(cmsDocBaseDto.getAuditStatus())) {
            editable = cmsDocBaseDto.getCreateBy().equals(currentUserName);
        }
        return editable;
    }

    private void handleCmsDocWithNoPassByReedit(CmsDocAuditVo cmsDocAuditVo, CmsDoc cmsDoc, CmsProject cmsProject) {
        //允许重新编辑
        if (cmsDocAuditVo.getReedit().equals(1)) {
            cmsDoc.setAuditStatus(DocAuditStatus.AUDIT_FAIL.getAuditCode());
            cmsDocMapper.updateCmsDoc(cmsDoc);
            return;
        }
        //不允许重新编辑
        if (cmsDocAuditVo.getReedit().equals(0)) {
            //删除待审核文章
            cmsDocMapper.deleteCmsDocById(cmsDoc.getId());
            //删除分支
            gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
            return;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleDeletedCmsDocWithPass(CmsDoc cmsDoc, CmsProject cmsProject) {
        long oldCmsDocId = Long.parseLong(cmsDoc.getAuditType().split("-")[1]);
        Long[] ids = {cmsDoc.getId(), oldCmsDocId};
        int success = deleteCmsDocDatabaseById(ids);
        if (success == ids.length) {
            //删除临时分支
            gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void handleUpdatedCmsDocWithPass(CmsProject cmsProject, CmsDoc cmsDoc) {
        Long newDocId = cmsDoc.getId();
        //同步更新人、发布人、发布人用户主页链接、发布人头像等信息
        cmsAsyncService.updateCmsPublishDataByGitLink(cmsProject, cmsDoc, CmsConstants.BRANCH_MASTER);
        cmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
        //用新文章数据更新旧文章数据
        Long oldCmsDocId = Long.parseLong(cmsDoc.getAuditType().split("-")[1]);
        CmsDoc oldCmsDoc = cmsDocMapper.selectCmsDocById(oldCmsDocId);
        cmsDoc.setId(oldCmsDoc.getId());
        cmsDocMapper.updateCmsDoc(cmsDoc);
        //删除用于审核的文章数据
        int success = cmsDocMapper.deleteCmsDocById(newDocId);
        if (success > 0) {
            //删除临时分支
            gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
        }

    }


    @Transactional(rollbackFor = Exception.class)
    public void handleCreatedCmsDocWithPass(CmsProject cmsProject, CmsDoc cmsDoc) {
        //同步更新人、发布人、发布人用户主页链接、发布人头像等信息
        cmsAsyncService.updateCmsPublishDataByGitLink(cmsProject, cmsDoc, CmsConstants.BRANCH_MASTER);
        cmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
        int success = cmsDocMapper.updateCmsDoc(cmsDoc);
        if (success > 0) {
            //删除临时分支
            gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
        }

        // 推送文章地址到搜索引擎
        cmsAsyncService.pushDocDataSearchEngine(cmsDoc);
    }

    private JSONObject mergePullRequestByGitLink(CmsProject cmsProject, CmsDoc cmsDoc) {
        String gitLinkPullRequestId = cmsDoc.getAuditPrNumber().split("-")[1];
        //1.调用gitlink合并请求通过接口
        JSONObject requestBody = new JSONObject();
        requestBody.put("title", "审核通过:" + cmsDoc.getFilePath());
        requestBody.put("id", gitLinkPullRequestId);
        requestBody.put("do", "merge");
        requestBody.put("project_id", cmsProject.getIdentifier());
        return gitLinkRequestHelper.doPost(
                CmsGitLinkRequestUrl.MERGE_PULL_REQUEST(cmsProject, gitLinkPullRequestId), requestBody);
    }

    private JSONObject refuseMergePullRequestByGitLink(CmsDoc cmsDoc, CmsProject cmsProject) {
        //1.调用gitlink拒绝合并请求接口
        return gitLinkRequestHelper.doPost(
                CmsGitLinkRequestUrl.REFUSE_MERGE_PULL_REQUEST(cmsProject, cmsDoc.getAuditPrNumber()), null);
    }

    public static String sha1(String data) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA1");
            md.update(data.getBytes());
            StringBuilder buf = new StringBuilder();
            byte[] bits = md.digest();
            for (int bit : bits) {
                int a = bit;
                if (a < 0) {
                    a += 256;
                }
                if (a < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(a));
            }
            return buf.toString();
        } catch (Exception e) {
            throw new ServiceException("计算字符串Sha出错");
        }
    }
    @Override
    public void setCmsSort(Integer order, CmsDoc oldCmsDoc, boolean isChangeOnDir, String isHomePage) {
        //调整文章/栏目顺序
        if (order != null) {
            Integer setSort;
            boolean isDir = oldCmsDoc.getIsDir();
            String dirName = oldCmsDoc.getDirName();
            String name = oldCmsDoc.getName();
            Long deptId = oldCmsDoc.getDeptId();
            CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
            cmsDocSearchVo.setDeptId(deptId);
            cmsDocSearchVo.setIsDir(isDir);
            cmsDocSearchVo.setAuditStatus(DocAuditStatus.AUDIT_ALL.getAuditCode());
            if (isChangeOnDir) {
                cmsDocSearchVo.setDirName(dirName);
            }
            Integer maxCount = cmsDocMapper.selectCmsDocCount(cmsDocSearchVo);
            //检查用户输入顺序是否合法
            if (order < 1 || order > maxCount) {
                throw new ServiceException(String.format("顺序最小为1，最大为%d(文章/栏目名称[%s])", maxCount, name));
            }
            Integer homePageCount = null;
            if (!oldCmsDoc.getIsDir() && isHomePage != null) {
                if (isChangeOnDir) {
                    homePageCount = selectHomepageCountByDeptIdAndDirName(deptId, dirName);
                } else {
                    homePageCount = selectHomepageCountByDeptIdAndDirName(deptId, null);
                }
                if (CmsConstants.FALSE.equals(isHomePage)) {
                    //设置非首页推荐的文章顺序时，顺序需要大于首页推荐数量
                    if (order <= homePageCount) {
                        throw new ServiceException(String.format("当前首页推荐数量为[%d]，设置非首页推荐的文章顺序时，文章顺序需小于该数量(文章名称[%s])", homePageCount, name));
                    }
                } else {
                    //设置首页推荐的文章排序时，顺序最大值不能大于首页推荐数量
                    if (order > homePageCount) {
                        throw new ServiceException(String.format("当前首页推荐数量为[%d]，设置首页推荐的文章排序时，顺序最大值不能大于首页推荐数量(文章名称[%s])", homePageCount, name));
                    }
                }

            }
            //获取文章/栏目所在行
            CmsDoc theOrderCmsContainSelf = cmsDocMapper.selectCmsByOrderAndDeptId(oldCmsDoc, order, isChangeOnDir, true);
            //当指定序号文章/栏目为当前文章/栏目时，不修改排序
            if (theOrderCmsContainSelf != null && theOrderCmsContainSelf.getId().equals(oldCmsDoc.getId())) {
                return;
            }
            boolean orderByFirst = true;
            //当在栏目下排序且当前栏目下首页推荐数量为0时，需检查全局下是否存在首页推荐的文章，若存在不能按照顺序值为1的逻辑来操作
            if (isChangeOnDir && homePageCount != null && homePageCount.equals(0)) {
                int allDirHomePageCount = selectHomepageCountByDeptIdAndDirName(deptId, null);
                if (allDirHomePageCount > 0) {
                    orderByFirst = false;
                }
            }
            if (orderByFirst && order == 1 || order.equals(maxCount)) {
                int minSort = cmsDocMapper.selectCmsMinSortByDeptId(deptId, isDir);
                int maxSort = cmsDocMapper.selectCmsMaxSortByDeptId(deptId, isDir);
                //当用户输入顺序为1时，文章顺序调整为最大值+1.栏目顺序调整为最小值-1
                if (order == 1) {
                    setSort = isDir ? (minSort - 1) : (maxSort + 1);
                } else {
                    //当用户输入顺序为列表总数时，文章顺序调整为最小值-1.栏目顺序调整为最大值+1
                    setSort = isDir ? (maxSort + 1) : (minSort - 1);
                }
            } else if (!isChangeOnDir) {
                {
                    //获取指定序号的文章/栏目(不包含当前文章/栏目)
                    CmsDoc theOrderCms = cmsDocMapper.selectCmsByOrderAndDeptId(oldCmsDoc, order, false, false);
                    if (theOrderCms == null) {
                        throw new ServiceException(String.format("指定顺序不存在(文章/栏目名称[%s]，顺序[%d])", name, order));
                    }
                    //计算用户上一个序号对应的排序值(不包含当前文章/栏目)
                    CmsDoc theLastOrderCms = cmsDocMapper.selectCmsByOrderAndDeptId(oldCmsDoc, order - 1, false, false);
                    Integer theLastSort = theLastOrderCms.getSort();
                    if (isDir) {
                        if (theOrderCms.getSort() - theLastSort > 1) {
                            setSort = theLastSort + 1;
                        } else {
                            setSort = theOrderCms.getSort();
                            //需要将大于等于该sort的所有数据项sort+1
                            Integer updateCount = cmsDocMapper.batchUpdateCmsSortIncreased(deptId, true, setSort);
                            logger.info("批量增加大于指定排序的排序值，共计更新{}条", updateCount);
                        }
                    } else {
                        if (theLastSort - theOrderCms.getSort() > 1) {
                            setSort = theLastSort - 1;
                        } else {
                            setSort = theOrderCms.getSort();
                            //需要将小于于等于该sort的所有数据项sort-1
                            Integer updateCount = cmsDocMapper.batchUpdateCmsSortDecrease(deptId, false, setSort);
                            logger.info("批量减少小于指定排序的排序值，共计更新{}条", updateCount);
                        }
                    }
                }
            } else {
                //获取指定序号的文章/栏目(不包含当前文章/栏目)
                CmsDoc theOrderCms = cmsDocMapper.selectCmsByOrderAndDeptId(oldCmsDoc, order, true, false);
                if (theOrderCms == null) {
                    throw new ServiceException(String.format("指定顺序不存在(文章/栏目名称[%s]，顺序[%d])", name, order));
                }
                setSort = theOrderCms.getSort();
                //需要将小于于等于该sort的所有数据项sort-1
                Integer updateCount = cmsDocMapper.batchUpdateCmsSortDecrease(deptId, false, setSort);
                logger.info("批量减少小于指定排序的排序值，共计更新{}条", updateCount);
            }
            oldCmsDoc.setSort(setSort);
        }
    }


    /**
     * 修改文章信息
     *
     * @param id
     * @param cmsDocDto 文章信息
     * @return 结果
     */
    @Override
    public boolean updateCmsDoc(Long id, CmsDocDto cmsDocDto) {
        CmsDoc oldCmsDoc = cmsDocMapper.selectCmsDocById(id);
        checkCmsLockStatus(oldCmsDoc.getDeptId());
        if (!hasRepoRelatedChanged(oldCmsDoc, cmsDocDto)) {
            return cmsDocMapper.updateCmsDoc(oldCmsDoc) > 0;
        }
        // 当文章名称发生更改时，判断文章名是否在当前栏目下已存在
        if (StringUtils.isNotEmpty(cmsDocDto.getName()) && !cmsDocDto.getName().equals(oldCmsDoc.getName())) {
            String dirName = oldCmsDoc.getDirName();
            if (cmsDocDto.getDirId() != null) {
                CmsDir cmsDir = selectCmsDirById(cmsDocDto.getDirId(), false);
                if (!cmsDir.getName().equals(dirName)) {
                    dirName = cmsDir.getName();
                }
            }
            checkDocName(cmsDocDto.getName(), oldCmsDoc.getDeptId(), dirName);
        }
        //用户为专区管理员或专区无审核机制时，直接更新文章及仓库主分支
        if (isCurrentUserZoneAdmin(oldCmsDoc.getDeptId()) || isNoAuditRequired(oldCmsDoc.getDeptId())) {
            //专区管理员重新编辑并提交审核未通过文章 或 重新编辑并提交待审核文章时专区不需审核机制
            if (!oldCmsDoc.getAuditStatus().equals(DocAuditStatus.AUDIT_PASS.getAuditCode())) {
                //文章审核类型：update-原文章id 取出原文章id直接更新，删除待审核更新类型文章
                if (oldCmsDoc.getAuditType() != null && oldCmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_UPDATE.getOperationCode())) {
                    Long originCmsDocId = Long.parseLong(oldCmsDoc.getAuditType().split("-")[1]);
                    CmsDoc originCmsDoc = cmsDocMapper.selectCmsDocById(originCmsDocId);
                    deleteTmpBranchIfBranchExist(oldCmsDoc);
                    BeanUtils.copyProperties(originCmsDoc,oldCmsDoc);
                    cmsDocMapper.deleteCmsDocById(id);
                } else
                    //文章审核类型：create 直接新增文章
                    if (oldCmsDoc.getAuditType() != null && oldCmsDoc.getAuditType().contains(DocAndZoneOperation.OPERATION_CREATE.getOperationCode())) {
                        deleteTmpBranchIfBranchExist(oldCmsDoc);
                        cmsDocMapper.deleteCmsDocById(id);
                        return directlyInsertAuditedCmsDocWhenZoneHasNoAuditRequired(cmsDocDto, oldCmsDoc);
                    }
            }
            String branchName = CmsConstants.BRANCH_MASTER;
            oldCmsDoc = cmsAsyncService.updateCmsDocDatabaseAndUpdateForGitLink(oldCmsDoc, cmsDocDto, branchName);
            oldCmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
            oldCmsDoc.setAuditTmpBranchName(null);
            oldCmsDoc.setKeywords(cmsDocDto.getKeywords());
            return updateCmsCommonDatabase(oldCmsDoc, CmsConstants.BRANCH_MASTER) > 0;
        } else {

            CmsDoc newDocWithAudit = new CmsDoc();
            //待审核、未通过文章重新编辑并提交
            if (DocAuditStatus.AUDIT_FAIL.getAuditCode().equals(oldCmsDoc.getAuditStatus())
                    || DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(oldCmsDoc.getAuditStatus())) {
                oldCmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
                newDocWithAudit = oldCmsDoc;
            } else
                //更新已发布文章
                if (oldCmsDoc.getAuditStatus().equals(DocAuditStatus.AUDIT_PASS.getAuditCode())) {
                //1.创建一条auditType="update-原文章id"，audit_status为2，其余字段与待更新文章一致的新文章数据
                newDocWithAudit = insertDocWithAudit(oldCmsDoc,
                        DocAndZoneOperation.OPERATION_UPDATE.getOperationCode() + "-" + id,
                        DocAuditStatus.TO_BE_AUDIT.getAuditCode());
            }
            cmsAsyncService.asyncUpdateBranchAndFileAndCreatePR(newDocWithAudit, cmsDocDto);
            return true;
        }

    }

    private boolean hasRepoRelatedChanged(CmsDoc oldCmsDoc, CmsDocDto cmsDocDto) {
        setCmsSort(cmsDocDto.getOrder(), oldCmsDoc, cmsDocDto.isChangeOnDir(), oldCmsDoc.getIsHomepage());
        //获取专区项目
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(oldCmsDoc.getDeptId());
        String content = cmsDocDto.getContent();
        //获取原文章内容
        String oldContent = getCmsDocFileContent(cmsProject, oldCmsDoc);
        boolean isChangeName = false;
        boolean isChangeDir = false;
        boolean isChangeContent = false;

        //检查文章是否需要更换栏目
        if (cmsDocDto.getDirId() != null) {
            CmsDoc newCmsDir = cmsDocMapper.selectCmsDocDirById(cmsDocDto.getDirId());
            if (newCmsDir == null) {
                throw new ServiceException("该栏目不存在(栏目Id[" + cmsDocDto.getDirId() + "])");
            }
            if (!newCmsDir.getName().equals(oldCmsDoc.getDirName())) {
                isChangeDir = true;
            }
        }

        if (StringUtils.isNotEmpty(cmsDocDto.getName())
                && !oldCmsDoc.getName().equals(cmsDocDto.getName())) {
            isChangeName = true;
        }

        if (content != null) {
            String newMd5 = DigestUtils.md5DigestAsHex(content.getBytes());
            String oldMd5 = null;
            if (StringUtils.isNotEmpty(oldContent)) {
                oldMd5 = DigestUtils.md5DigestAsHex(oldContent.getBytes());
            }
            if (!newMd5.equals(oldMd5)) {
                isChangeContent = true;
            }
        }

        oldCmsDoc.setKeywords(cmsDocDto.getKeywords());
        oldCmsDoc.setSummary(cmsDocDto.getSummary());
        oldCmsDoc.setHeadImg(cmsDocDto.getHeadImg());
        return isChangeName || isChangeContent || isChangeDir;
    }

    private void deleteTmpBranchIfBranchExist(CmsDoc cmsDoc) {
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
        try {
            gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
        } catch (ServiceException e) {
            //忽略分支不存在异常
            if(!"分支不存在！".equals(e.getMessage())) {
                throw new ServiceException(e.getMessage());
            }
        }

    }

    private boolean directlyInsertAuditedCmsDocWhenZoneHasNoAuditRequired(CmsDocDto cmsDocDto, CmsDoc oldCmsDoc) {
        CmsDoc dirDoc = selectCmsDir(cmsDocDto.getDirId());
        oldCmsDoc.setName(cmsDocDto.getName());
        String fileName = cmsDocDto.getName() + CmsConstants.MARKDOWN_SUFFIX;
        oldCmsDoc.setFileName(fileName);
        oldCmsDoc.setFilePath(dirDoc.getName() + CmsConstants.DIR_SEPARATOR + fileName);
        oldCmsDoc.setSummary(cmsDocDto.getSummary());
        oldCmsDoc.setDirName(dirDoc.getName());
        oldCmsDoc.setHeadImg(cmsDocDto.getHeadImg());
        oldCmsDoc.setDeptId(dirDoc.getDeptId());
        oldCmsDoc.setAuditStatus(DocAuditStatus.AUDIT_PASS.getAuditCode());
        oldCmsDoc.setSort(cmsDocDto.getOrder());
        oldCmsDoc.setAuditType(null);
        oldCmsDoc.setAuditPrNumber(null);
        oldCmsDoc.setAuditTmpBranchName(null);
        return insertCommonCmsDoc(oldCmsDoc,
                "创建文章：" + StringUtils.truncateStringAndConcatenatingSpecifiedString(oldCmsDoc.getName(), 100, "..."),
                cmsDocDto.getContent());
    }

    private CmsDoc insertDocWithAudit(CmsDoc oldCmsDoc, String auditType, String auditStatus) {
        oldCmsDoc.setId(null);
        oldCmsDoc.setAuditType(auditType);
        oldCmsDoc.setAuditStatus(auditStatus);
        oldCmsDoc.setCreateBy(SecurityUtils.getUsername());
        cmsDocMapper.insertCmsDoc(oldCmsDoc);
        return oldCmsDoc;
    }

    private CmsDoc insertDeletedDocWithAudit(CmsDoc oldCmsDoc, Long oldCmsDocId) {
        oldCmsDoc.setId(null);
        oldCmsDoc.setAuditType(DocAndZoneOperation.OPERATION_DELETE.getOperationCode() + "-" + oldCmsDocId);
        oldCmsDoc.setAuditStatus(DocAuditStatus.TO_BE_AUDIT.getAuditCode());
        oldCmsDoc.setCreateBy(SecurityUtils.getUsername());
        cmsDocMapper.insertCmsDoc(oldCmsDoc);
        return oldCmsDoc;
    }


    @Override
    public int updateCmsCommonDatabase(CmsDoc cmsDoc, String branchName) {
        cmsDoc.setUpdateTime(DateUtils.getNowDate());
        cmsDoc.setUpdateBy(SecurityUtils.getUsername());
        int success = cmsDocMapper.updateCmsDoc(cmsDoc);
        updateCmsPublishData(cmsDoc, branchName);
        return success;
    }

    /**
     * 批量删除文章信息
     *
     * @param ids 需要删除的文章信息主键
     * @return 结果
     */
    @Override
    public int deleteCmsDocByIds(Long[] ids) {
        int success = 0;
        for (Long id : ids) {
            success += deleteCmsDocById(id);
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertCmsDocDir(Long deptId, CmsDir cmsDir) {
        checkCmsLockStatus(deptId);
        CmsDoc cmsDoc = new CmsDoc();
        cmsDoc.setIsDir(true);
        cmsDoc.setFilePath(cmsDoc.getDirPath(cmsDir.getName()));
        cmsDoc.setDeptId(deptId);
        cmsDoc.setName(cmsDir.getName());
        return insertCommonCmsDoc(cmsDoc, "创建栏目：" + cmsDir.getName(), "");
    }

    @Override
    public List<EntryDto> getEntriesFromRequestResult(JSONObject result, CmsProject cmsProject, boolean isSort) {
        String Entries_Key = "entries";
        if (!result.containsKey(Entries_Key)) {
            throw new ServiceException("获取GitLink项目中的文件失败(项目名称[" + cmsProject.getName() + "])");
        }
        //将返回的文件（/目录）数据转换为实体对象，便于操作
        List<EntryDto> entryDtoList = result.getJSONArray(Entries_Key).toJavaList(EntryDto.class);
        if (isSort) {
            Collections.sort(entryDtoList);
        }

        return entryDtoList;
    }

    @Override
    public List<CmsDir> selectCmsDocDirList(Long deptId, boolean isOpen) {
        return selectCmsDocDirListByDatabase(deptId);
    }

    private List<CmsDir> selectCmsDocDirListByDatabase(Long deptId) {
        List<CmsDoc> cmdDirList = cmsDocMapper.selectCmsDirListByDeptId(deptId);
        if (cmdDirList == null || cmdDirList.isEmpty()) {
            return new ArrayList<>();
        }
        return cmdDirList.stream().map(CmsDoc::copyToCmsDir).collect(Collectors.toList());
    }

    private List<CmsDir> selectCmsDocDirListByGitLink(Long deptId) {
        List<CmsDir> cmsDirList = new ArrayList<>();
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(deptId);
        //暂时仅考虑主干分支
        //获取GitLink仓库下一级目录所有文件
        JSONObject result = gitLinkRequestHelper.doGet(CmsGitLinkRequestUrl.GET_ENTRIES(cmsProject, CmsConstants.BRANCH_MASTER));
        List<EntryDto> entryDtoList = getEntriesFromRequestResult(result, cmsProject, false);
        if (entryDtoList == null) {
            return cmsDirList;
        }
        //仅获取类型为Dir的文件
        entryDtoList = entryDtoList
                .stream()
                .filter(entry -> CmsConstants.ENTRY_TYPE_DIR_KEY.equals(entry.getType()))
                .collect(Collectors.toList());
        //获取所有DirName，统一进行数据库查找，减少重复的数据库操作，优化查询性能
        List<String> cmsDirNameList = entryDtoList.stream().map(EntryDto::getName).collect(Collectors.toList());
        List<CmsDoc> cmdDirList = new ArrayList<>();
        if (!cmsDirNameList.isEmpty()) {
            cmdDirList = cmsDocMapper.selectCmsDocDirByDeptIdAndInName(deptId, cmsDirNameList);
        }
        for (EntryDto entry : entryDtoList) {
            //检查数据库中是否存在该栏目
            CmsDoc cmsDir = cmdDirList.stream().filter(x -> entry.getName().equals(x.getName())).findFirst().orElse(null);
            if (cmsDir == null) {
                cmsAsyncService.syncCreateDir(entry.getName(), entry, cmsProject, true);
            }
            if (cmsDir != null) {
                cmsDirList.add(cmsDir.copyToCmsDir());
            }
        }
        //根据sort排序栏目
        Collections.sort(cmsDirList);
        return cmsDirList;
    }

    private CmsDoc selectCmsDir(Long dirId) {
        CmsDoc cmsDir = cmsDocMapper.selectCmsDocDirById(dirId);
        if (cmsDir == null) {
            throw new ServiceException("该栏目不存在(栏目Id[" + dirId + "])");
        }
        return cmsDir;
    }

    @Override
    public List<HotCmsDocDto> selectHotCmsDocList(Long dirId) {
        CmsDoc cmsDocInput = new CmsDoc();
        CmsDoc cmsDir = selectCmsDir(dirId);
        cmsDocInput.setDeptId(cmsDir.getDeptId());
        cmsDocInput.setIsDir(false);
        cmsDocInput.setDirName(cmsDir.getName());
        List<CmsDoc> cmsDocList = cmsDocMapper.selectHotCmsDocList(cmsDocInput);
        if (cmsDocList != null) {
            //todo:需检查该文章是否在GitLink中存在
            return cmsDocList.stream().map(x -> x.copyToHotCmsDocDto()).collect(Collectors.toList());
        }

        return new ArrayList<>();
    }

    @Override
    public boolean updateVisits(Long id) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
        if (cmsDoc == null || cmsDoc.getIsDir()) {
            throw new ServiceException("该文章不存在(文章Id[" + id + "])");
        }
        Long visits = 1L;
        if (cmsDoc.getVisits() != null) {
            visits = visits + cmsDoc.getVisits();
        }
        cmsDoc.setVisits(visits);
        return cmsDocMapper.updateCmsDoc(cmsDoc) > 0;
    }

    @Override
    public List<HotCmsDocDto> selectHotCmsDocListByDept(Long deptId) {
        CmsDoc cmsDocInput = new CmsDoc();
        cmsDocInput.setDeptId(deptId);
        cmsDocInput.setIsDir(false);
        List<CmsDoc> cmsDocList = cmsDocMapper.selectHotCmsDocList(cmsDocInput);
        if (cmsDocList != null) {
            //todo:需检查该文章是否在GitLink中存在
            return cmsDocList.stream().map(CmsDoc::copyToHotCmsDocDto).collect(Collectors.toList());
        }

        return new ArrayList<>();
    }

    @Override
    public List<CmsDocOverviewDto> selectCmsDocOverviewList(Long deptId, boolean isOpen) {
        List<CmsDocOverviewDto> cmsDocOverviewDtoList = new ArrayList<>();
        List<CmsDoc> cmsDirList = cmsDocMapper.selectCmsDirListByDeptId(deptId);
        for (CmsDoc cmsDir : cmsDirList) {
            CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
            cmsDocSearchVo.setIsOpen(isOpen);
            List<CmsDocBaseDto> cmsDocBaseDtoList = selectCmsDocList(cmsDir.getId(), cmsDocSearchVo);
            CmsDocOverviewDto cmsDocOverviewDto = new CmsDocOverviewDto();
            cmsDocOverviewDto.setId(cmsDir.getId());
            cmsDocOverviewDto.setName(cmsDir.getName());
            cmsDocOverviewDto.setSort(cmsDir.getSort());
            if (cmsDocBaseDtoList != null && !cmsDocBaseDtoList.isEmpty()) {
                int lastIndex = 5;
                if (lastIndex > cmsDocBaseDtoList.size()) {
                    lastIndex = cmsDocBaseDtoList.size();
                }
                cmsDocOverviewDto.setCmsDocList(cmsDocBaseDtoList.subList(0, lastIndex));
            }

            cmsDocOverviewDtoList.add(cmsDocOverviewDto);
        }
        Collections.sort(cmsDocOverviewDtoList);
        return cmsDocOverviewDtoList;
    }

    @Override
    public CmsDir selectCmsDirById(Long dirId, boolean isOpen) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocDirById(dirId);
        return cmsDoc.copyToCmsDir();
    }

    @Override
    public Long getDeptIdByZoneId(Long zoneId) {
        JSONObject zoneObj = getZoneJSONByZoneId(zoneId);
        return zoneObj.getLong("deptId");
    }

    private String getZoneKeyByZoneId(Long zoneId) {
        JSONObject zoneObj = getZoneJSONByZoneId(zoneId);
        return zoneObj.getString("key");
    }

    private String getZoneKeyByDeptId(Long deptId) {
        JSONObject zoneObj = getZoneJSONByDeptId(deptId);
        return zoneObj.getString("key");
    }

    private JSONObject getZoneJSONByZoneId(Long zoneId) {
        String zoneJSONOfZoneIdKey = CacheConstants.ZONE_JSON_OF_ZONE_ID_KEY + zoneId;
        if (redisService.hasKey(zoneJSONOfZoneIdKey)) {
            return redisService.getCacheObject(zoneJSONOfZoneIdKey);
        } else {
            String zoneJSON = FeignUtils.getReturnData(remoteZoneService.getZoneJSONByZoneId(zoneId, SecurityConstants.INNER));
            if (StringUtils.isEmpty(zoneJSON)) {
                throw new ServiceException("专区不存在，请联系管理员");
            }
            JSONObject zoneObj = JSONObject.parseObject(zoneJSON);
            if (zoneObj == null) {
                throw new ServiceException("专区不存在，请联系管理员");
            }
            redisService.setCacheObject(zoneJSONOfZoneIdKey, zoneObj);
            return zoneObj;
        }
    }

    @Override
    public JSONObject getZoneJSONByDeptId(Long deptId) {
        String zoneJSONOfDeptIdKey = CacheConstants.ZONE_JSON_OF_DEPT_ID_KEY + deptId;
        if (redisService.hasKey(zoneJSONOfDeptIdKey)) {
            return redisService.getCacheObject(zoneJSONOfDeptIdKey);
        } else {
            String zoneJSON = FeignUtils.getReturnData(remoteZoneService.getZoneJSONByDeptId(deptId, SecurityConstants.INNER));
            JSONObject zoneObj = JSONObject.parseObject(zoneJSON);
            redisService.setCacheObject(zoneJSONOfDeptIdKey, zoneObj);
            return zoneObj;
        }
    }

    @Override
    public int updateCmsDocHomePageShow(Long id) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
        if (cmsDoc == null || cmsDoc.getIsDir()) {
            throw new ServiceException("该文章不存在(文章Id[" + id + "])");
        }
        //检查项目原本是否首页展示，若原本未展示将设置为展示，若设置了首页最大展示数量此刻需判断是否已达到最大值
        if (CmsConstants.TRUE.equals(cmsDoc.getIsHomepage())) {
            //取消首页推荐时，需将顺序设置为首页推荐后一位,因为需要取消一位，所以设置的顺序应该跟首页推荐数量一致
            Integer homepageCount = selectHomepageCountByDeptIdAndDirName(cmsDoc.getDeptId(), null);
            setCmsSort(homepageCount, cmsDoc, false, null);
            cmsDoc.setIsHomepage(CmsConstants.FALSE);
        } else {
            Long homePageCmsSize = FeignUtils.getReturnData(
                    remoteZoneService.getZoneHomePageCmsSizeByDeptId(cmsDoc.getDeptId(), SecurityConstants.INNER)
            );
            if (homePageCmsSize != null && homePageCmsSize > 0) {
                CmsDocSearchVo cmsDocSearch = new CmsDocSearchVo();
                cmsDocSearch.setDeptId(cmsDoc.getDeptId());
                cmsDocSearch.setIsHomepage(CmsConstants.TRUE);
                List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearch);
                if (cmsDocList.size() >= homePageCmsSize) {
                    throw new ServiceException("该专区首页最多展示" + homePageCmsSize + "条新闻");
                }
            }
            //首页推荐时，需将顺序设置为1
            setCmsSort(1, cmsDoc, false, null);
            cmsDoc.setIsHomepage(CmsConstants.TRUE);
        }
        return cmsDocMapper.updateCmsDoc(cmsDoc);
    }

    @Override
    public Long getCmsCountByDeptId(Long deptId) {
        return cmsDocMapper.getCmsCountByDeptId(deptId);
    }

    @Override
    public List<CmsDocBaseDto> selectHomePageDocList(Long deptId) {
        CmsDocSearchVo cmsDocSearch = new CmsDocSearchVo();
        cmsDocSearch.setDeptId(deptId);
        cmsDocSearch.setIsHomepage(CmsConstants.TRUE);
        List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearch);
        List<CmsDocBaseDto> cmsDocBaseDtoList = new ArrayList<>();
        for (CmsDoc cmsDoc : cmsDocList) {
            CmsDocBaseDto cmsDocBaseDto = cmsDoc.copyToCmsDocBaseDto();
            cmsDocBaseDtoList.add(cmsDocBaseDto);
        }
        return cmsDocBaseDtoList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CmsDir initCmsDir(Long deptId, CmsDir cmsDirInput) {
        boolean isSuccess = insertCmsDocDir(deptId, cmsDirInput);
        if (isSuccess) {
            return cmsDocMapper.selectCmsDocDirByNameAndDeptId(cmsDirInput.getName(), deptId).copyToCmsDir();
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean initCmsDoc(Long dirId, CmsDocInputDto cmsDocInputDto) {

        Integer newCmsDocId = insertCmsDoc(dirId, cmsDocInputDto);
        if (newCmsDocId != null) {
            //初始化文章时，自动将文章设置为首页推荐
            CmsDoc cmsDir = cmsDocMapper.selectCmsDocDirById(dirId);
            CmsDoc cmsDoc = cmsDocMapper.selectCmsDocByNameAndDeptId(cmsDocInputDto.getName(), cmsDir.getDeptId(), cmsDir.getName());
            if (cmsDoc != null) {
                updateCmsDocHomePageShow(cmsDoc.getId());
            }
            return newCmsDocId > 0;
        }
        return false;
    }

    @Override
    public void syncDocData(Long deptId, String bodyStr) {
        cmsAsyncService.asyncDocData(deptId, bodyStr);
    }

    private boolean insertCommonCmsDoc(CmsDoc cmsDoc, String message, String content) {
        checkDocName(cmsDoc.getName(), cmsDoc.getDeptId(), cmsDoc.getDirName());
        createFileByGitLink(cmsDoc, message, content);
        boolean success = insertCmsDocDatabase(cmsDoc, content);
        // 推送文章地址到搜索引擎
        cmsAsyncService.pushDocDataSearchEngine(cmsDoc);
        return success;
    }

    private void checkDocName(String docName, Long deptId, String dirName) {
        //检查该文件是否已存在
        CmsDoc oldCmsDoc = cmsDocMapper.selectCmsDocByNameAndDeptId(docName, deptId, dirName);
        if (oldCmsDoc != null) {
            if (oldCmsDoc.getIsDir()) {
                throw new ServiceException("该栏目已存在(栏目名称[%s])", docName);
            } else {
                throw new ServiceException("该文章在当前栏目下已存在(文章名称[%s],栏目名称[%s])", docName, dirName);
            }

        }
    }

    private boolean insertCmsDocWithAudit(CmsDoc cmsDoc, String message, String content) {
        //检查该文件是否已存在
        CmsDoc oldCmsDoc = cmsDocMapper.selectCmsDocByNameAndDeptId(cmsDoc.getName(), cmsDoc.getDeptId(), cmsDoc.getDirName());
        if (oldCmsDoc != null) {
            throw new ServiceException("该文章已存在(文章名称[" + cmsDoc.getName() + "])");
        }
        boolean insertResult = insertCmsDocDatabase(cmsDoc, content);
        if (insertResult) {
            //异步处理创建临时分支、提交文件至临时分支、创建合并请求
            cmsAsyncService.asyncCreateBranchAndFileAndPR(cmsDoc, message, content);
        }
        return insertResult;
    }

    @Override
    public void createFileByGitLink(CmsDoc cmsDoc, String message, String content) {
        //2、检查该组织是否创建了专区项目
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
        //3、调用GitLink创建文件接口，创建文件夹以及文件夹下的.keep文件
        JSONObject requestBody = new JSONObject();
        requestBody.put("filepath", cmsDoc.getFilePath());
        //todo:暂时只考虑主干提交
        requestBody.put("branch", CmsConstants.BRANCH_MASTER);
        requestBody.put("content", contentToBase64(content));
        requestBody.put("message", message);
        JSONObject result = gitLinkRequestHelper.doPost(
                CmsGitLinkRequestUrl.CREATE_FILE(cmsProject),
                requestBody);
        cmsDoc.setFileName(result.getString("name"));
        cmsDoc.setFileSize(result.getLong("size"));
        cmsDoc.setFileSha(result.getString("sha"));
    }

    @Override
    public Long getNotAuditCmsCountByDeptId(Long deptId) {
        return cmsDocMapper.getNotAuditCmsCountByDeptId(deptId);
    }

    @Override
    public CmsDocBaseDto selectCmsDocBaseInfoById(Long id) {
        CmsDoc cmsDoc = selectBaseCmsDocById(id);
        return cmsDoc.copyToCmsDocBaseDto();
    }

    @Override
    public Long getCmsDocVisitsByDeptId(Long deptId) {
        return cmsDocMapper.getCmsDocVisitsByDeptId(deptId);
    }

    @Override
    public boolean insertCmsDocDatabase(CmsDoc cmsDoc, String content) {
        //设置排序值为当前最大的排序值+1
        Integer maxSort = cmsDocMapper.selectCmsMaxSortByDeptId(cmsDoc.getDeptId(), cmsDoc.getIsDir());
        if (maxSort == null) {
            maxSort = 1;
        } else {
            maxSort++;
        }
        cmsDoc.setSort(maxSort);
        if (StringUtils.isEmpty(cmsDoc.getCreateBy())) {
            cmsDoc.setCreateBy(SecurityUtils.getUsername());
        }

        cmsDoc.setCreateTime(DateUtils.getNowDate());
        cmsDoc.setUpdateTime(DateUtils.getNowDate());
        //5、创建该文件栏目
        boolean insertResult = cmsDocMapper.insertCmsDoc(cmsDoc) > 0;
        //文件已提交（sha值存在)，更新发布人信息
        if (StringUtils.isNotEmpty(cmsDoc.getFileSha())) {
            updateCmsPublishData(cmsDoc, CmsConstants.BRANCH_MASTER);
        }
        //若用户未输入摘要，则从正文中提取
        if (!cmsDoc.getIsDir() && StringUtils.isEmpty(cmsDoc.getSummary())) {
            cmsAsyncService.asyncSetSummary(cmsDoc.getId(), content);
        }
        if (!cmsDoc.getIsDir()) {
            //当首页推荐文章数量不为0时，新增文章应该设置为首页推荐后一位
            Integer homePageCount = selectHomepageCountByDeptIdAndDirName(cmsDoc.getDeptId(), null);
            if (homePageCount > 0) {
                setCmsSort(homePageCount + 1, cmsDoc, false, CmsConstants.FALSE);
                cmsDocMapper.updateCmsDoc(cmsDoc);
            }
        }
        return insertResult;
    }

    @Override
    public void updateCmsPublishData(CmsDoc cmsDoc, String branchName) {
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
        cmsAsyncService.updateCmsPublishDataByGitLink(cmsProject, cmsDoc, branchName);
    }

    @Override
    public int deleteCmsDocById(Long id) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
        if (cmsDoc == null) {
            throw new ServiceException("该文章不存在(文章Id[" + id + "])");
        }
        checkCmsLockStatus(cmsDoc.getDeptId());
        //2、获取专区项目
        CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());

        //删除待审核、审核未通过文章——删除临时分支，拒绝合并请求
        if (DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(cmsDoc.getAuditStatus())
                || DocAuditStatus.AUDIT_FAIL.getAuditCode().equals(cmsDoc.getAuditStatus())) {
            gitLinkRequestHelper.doDelete(CmsGitLinkRequestUrl.DELETE_BRANCH(cmsProject, cmsDoc.getAuditTmpBranchName()));
            cmsDocMapper.deleteCmsDocById(id);
            return 1;
        }
        if (isCurrentUserZoneAdmin(cmsDoc.getDeptId())  //当前用户为专区管理员
                || isNoAuditRequired(cmsDoc.getDeptId()) //当前专区文章发布无需审核机制
        ) {
            String branchName = CmsConstants.BRANCH_MASTER;
            boolean deleteResult = cmsAsyncService.deleteCmsDocForGitLink(cmsDoc, cmsProject, branchName);
            if (deleteResult) {
                return deleteCmsDocDatabaseById(cmsDoc.getId());
            }
        } else {
            //1.创建一条auditType="delete-原文章id",audit_status=2，其余字段与待删除文章一致的新文章数据
            CmsDoc newDocWithAudit = insertDeletedDocWithAudit(cmsDoc, id);
            cmsAsyncService.asyncCreateBranchAndDeleteFileAndCreatePR(newDocWithAudit, cmsProject);
            return 1;
        }
        throw new ServiceException("文件删除失败");
    }

    public int deleteCmsDocDatabaseById(Long id) {
        CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
        if (cmsDoc != null && !cmsDoc.getIsDir()) {
            // 检查文章是否存在评论Issue，若存在先删除Issue
            cmsDocCommentService.deleteCommentIssueByDocId(cmsDoc);
        }
        return cmsDocMapper.deleteCmsDocById(id);
    }

    public int deleteCmsDocDatabaseById(Long[] ids) {
        int success = 0;
        for (Long id : ids) {
            success += deleteCmsDocDatabaseById(id);
        }
        return success;
    }
}
