package cn.axzo.workflow.server.controller.web.bpmn;

import cn.axzo.basics.common.BeanMapper;
import cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi;
import cn.axzo.nanopart.doc.api.index.request.CopyNodeRequest;
import cn.axzo.oss.http.api.ServerFileServiceApi;
import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectRequest;
import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectResponse;
import cn.axzo.workflow.client.feign.bpmn.ProcessModelApi;
import cn.axzo.workflow.common.enums.FileTypeEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelUpdateDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocByIdDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCloneDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocResetDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocTenantQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO;
import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO;
import cn.axzo.workflow.common.model.request.bpmn.print.RestPrintTemplateConfigDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelDetailVO;
import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO;
import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO;
import cn.axzo.workflow.common.model.response.print.PrintModelDTO;
import cn.axzo.workflow.core.conf.CustomEventManager;
import cn.axzo.workflow.core.engine.cmd.CustomGetModelByDefinitionIdCmd;
import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd;
import cn.axzo.workflow.core.engine.event.DocChangeEventImpl;
import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc;
import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper;
import cn.axzo.workflow.core.service.AggregateModelService;
import cn.axzo.workflow.core.service.BpmnProcessDefinitionService;
import cn.axzo.workflow.core.service.BpmnProcessInstanceService;
import cn.axzo.workflow.core.service.BpmnProcessModelService;
import cn.axzo.workflow.core.service.ExtAxDocContentService;
import cn.axzo.workflow.core.service.ExtAxModelDocService;
import cn.axzo.workflow.core.service.ExtAxReModelService;
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
import cn.axzo.workflow.server.common.annotation.RepeatSubmit;
import cn.axzo.workflow.server.common.util.WpsUtil;
import cn.azxo.framework.common.model.CommonResponse;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.repository.Model;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_CLONE_ERROR;
import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_HIPRINT_ID_INVAILD;
import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_QUERY_ERROR;
import static cn.axzo.workflow.common.code.OtherRespCode.ILLEGAL_PARAM_ERROR;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID;
import static cn.axzo.workflow.common.enums.FileTypeEnum.EXCEL;
import static cn.axzo.workflow.common.enums.FileTypeEnum.WORD;
import static cn.azxo.framework.common.model.CommonResponse.success;

/**
 * 流程模型相关控制器
 */
@Tag(name = "运维管理 - 流程模型")
@Slf4j
@RequestMapping({"/web/v1/api/process/model", "/api/process/model"})
@RestController
@ErrorReporter
@Validated
public class BpmnProcessModelController implements ProcessModelApi {
    @Resource
    private BpmnProcessModelService bpmnProcessModelService;
    @Resource
    private BpmnProcessDefinitionService bpmnProcessDefinitionService;
    @Resource
    private AggregateModelService aggregateModelService;
    @Resource
    private ExtAxReModelService reModelService;
    @Resource
    private BpmnProcessInstanceService bpmnProcessInstanceService;
    @Resource
    private ExtAxModelDocService modelDocService;
    @Resource
    private ExtAxDocContentService docContentService;
    @Resource
    private DocAnonymousDatabaseApi docAnonymousApi;
    @Resource
    private ServerFileServiceApi fileServiceApi;
    @Resource
    private SpringProcessEngineConfiguration springProcessEngineConfiguration;
    @Resource
    private ExtAxModelDocMapper extAxModelDocMapper;
    @Resource
    private WpsUtil wpsUtil;
    @Resource
    private CustomEventManager eventPublisher;

    /**
     * 获取流程模型的查询结果
     */
    @Operation(summary = "流程模型列表")
    @GetMapping("/page")
    @Override
    public CommonResponse<BpmPageResult<BpmnModelDetailVO>> page(@Validated @RequestBody BpmnModelSearchDTO dto) {
        log.info("获取流程模型getModelPage===>>>参数：{}", JSONUtil.toJsonStr(dto));
        BpmPageResult<BpmnModelDetailVO> result = bpmnProcessModelService.getModelPage(dto);
        return success(result);
    }

    /**
     * 创建流程模型
     * return bpmnModelId的主键
     */
    @Operation(summary = "创建流程模型")
    @PostMapping("/create")
    @Override
    @RepeatSubmit
    public CommonResponse<String> create(@Validated @RequestBody BpmnModelCreateDTO dto) {
        log.info("创建流程createBpmModel===>>>参数：{}", JSONUtil.toJsonStr(dto));
        String bpmnModelId;
        if (Objects.isNull(dto.getFormJsonModel())) {
            // 无表单的模型
            bpmnModelId = bpmnProcessModelService.createBpmModel(dto);
        } else {
            bpmnModelId = aggregateModelService.createBpmnAndFormModel(dto);
        }

        CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
        List<DocBaseVO> commonDocBaseVos = commandExecutor.execute(new CustomGetModelDocsCmd(null, dto.getKey(), null, false, false, extAxModelDocMapper, reModelService));
        //通用模板的文档
        if (!CollectionUtils.isEmpty(commonDocBaseVos)) {
            //进行克隆
            commonDocBaseVos.forEach(cb -> this.singleCloneDoc(cb.getId(), DocModelInfo.builder()
                    .targetModelKey(dto.getKey())
                    .targetModelId(bpmnModelId)
                    .tenantId(dto.getTenantId())
                    .tag(cb.getTag())
                    .build()));
            eventPublisher.publishEvent(new DocChangeEventImpl(dto.getKey(), dto.getTenantId(), Collections.emptyList(), modelDocService.querySetting(dto.getKey(), NO_TENANT_ID)));
        }
        return success(bpmnModelId);
    }

    /**
     * 引擎内部测试接口，勿随意调用
     * 通过 Xml 创建模型
     *
     * @return
     */
    @Operation(summary = "通过 Xml 创建模型", hidden = true)
    @PostMapping("/xml/create")
    public CommonResponse<String> createWithXml() {
        return success(bpmnProcessModelService.createBpmModelWithXml(""));
    }

    /**
     * 通过模型 ID 获取模型
     */
    @Operation(summary = "通过模型ID查询指定流程模型")
    @GetMapping("/get")
    @Override
    public CommonResponse<BpmnModelDetailVO> getById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam String processModelId, @RequestParam(required = false) String tenantId) {
        log.info("获取模型getModelDetailById===>>>参数：{}", JSONUtil.toJsonStr(processModelId));
        BpmnModelDetailVO result = bpmnProcessModelService.getById(processModelId, tenantId);
        return success(result);
    }


    /**
     * 通过模型 KEY 获取模型
     */
    @Operation(summary = "通过模型KEY查询指定流程模型")
    @GetMapping("/getByKey")
    @Override
    public CommonResponse<BpmnModelDetailVO> getByKey(@NotBlank(message = "流程模型 KEY 不能为空") @RequestParam String processModelKey, @NotBlank(message = "租户不能为空") @RequestParam String tenantId) {
        log.info("获取模型getModelDetailByKey===>>>参数：{}", JSONUtil.toJsonStr(processModelKey));
        BpmnModelDetailVO result = bpmnProcessModelService.getByKey(processModelKey, tenantId);
        return success(result);
    }

    /**
     * 校验是否能更新模型即其关联的流程定义内容
     *
     * @param modelId 模型 ID
     * @return
     */
    @Operation(summary = "获取指定模型的扩展属性")
    @GetMapping("/ext")
    @Override
    public CommonResponse<BpmnModelExtVO> getModelExt(@NotBlank(message = "模型 ID 不能为空") @RequestParam(required =
            false) String modelId) {
        log.info("获取指定模型的扩展属性getModelExt===>>>参数：{}", JSONUtil.toJsonStr(modelId));
        return success(reModelService.getStatusByModelId(modelId));
    }

    /**
     * 修改流程信息
     */
    @Operation(summary = "更新流程模型")
    @PutMapping("/update")
    @Override
    public CommonResponse<String> update(@Validated @RequestBody BpmnModelUpdateDTO dto) {
        log.info("修改流程信息updateBpmModel===>>> 模型 ID: {}", dto.getProcessModelId());
        if (Objects.isNull(dto.getFormJsonModel())) {
            bpmnProcessModelService.updateBpmModel(dto);
        } else {
            aggregateModelService.updateBpmnAndFormModel(dto);
        }
        return success(dto.getProcessModelId());
    }


    /**
     * 通过模型 ID 部署模型
     *
     * @param modelTenantId  模型所属租户 ID
     * @param processModelId 模型 ID
     * @param operator       操作人信息
     * @return 部署完成的流程定义Id
     */
    @Operation(summary = "通过模型 ID 部署流程模型")
    @PostMapping("/deploy")
    @Override
    public CommonResponse<String> deployById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam(required = false) String processModelId, @RequestParam(required = false, defaultValue = "") String modelTenantId, @RequestParam(required = false) String operator) {
        log.info("部署模型deployBpmModelById===>>>参数：{}, 租户 ID: {}, operator: {}", JSONUtil.toJsonStr(processModelId),
                modelTenantId, operator);
        BpmnTaskDelegateAssigner assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class);
        String result = aggregateModelService.deployBpmnAndFormModel(processModelId, modelTenantId, assignee);
        return success(result);
    }


    /**
     * 通过模型 KEY 部署模型
     *
     * @param processModelKey 模型 Key
     * @param modelTenantId   模型所属租户 ID
     * @param operator        操作人信息
     * @return 部署完成的流程定义Id
     */
    @Operation(summary = "通过模型 KEY 部署流程模型")
    @PostMapping("/deployByKey")
    @Override
    public CommonResponse<String> deployByKey(@NotBlank(message = "流程模型 KEY 不能为空") @RequestParam(required = false) String processModelKey, @NotBlank(message = "租户不能为空") @RequestParam(required = false) String modelTenantId, @RequestParam(required = false) String operator) {
        log.info("部署模型deployBpmModelByKey===>>>参数：{}", JSONUtil.toJsonStr(processModelKey));
        BpmnTaskDelegateAssigner assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class);
        String result = bpmnProcessModelService.deployBpmModelByKey(processModelKey, modelTenantId, assignee);
        return success(result);
    }

    /**
     * 通过模型 ID 取消部署流程模型
     *
     * @param processModelId 模型 ID
     * @param tenantId       租户 ID
     * @param operator       操作人信息
     * @return
     */
    @Operation(summary = "通过模块 ID 取消部署流程模型")
    @PostMapping("/undeploy")
    @Override
    public CommonResponse<Void> unDeployById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam(required = false) String processModelId,
                                             @RequestParam(required = false, defaultValue = "") String tenantId,
                                             @RequestParam(required = false) String operator) {
        log.info("部署模型unDeployBpmModelById===>>>参数：{}", JSONUtil.toJsonStr(processModelId));
        BpmnTaskDelegateAssigner assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class);
        bpmnProcessModelService.unDeployBpmModelById(processModelId, tenantId, assignee);
        return success();
    }

    /**
     * 通过模型 ID 删除模型
     *
     * @param processModelId 模型 ID
     * @param tenantId       租户 ID
     */
    @Operation(summary = "删除指定模型 ID 的流程模型")
    @DeleteMapping("/delete")
    @Override
    public CommonResponse<Void> deleteById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam String processModelId,
                                           @RequestParam(required = false, defaultValue = "") String tenantId) {
        log.info("删除模型deleteBpmModel===>>>参数：{}", JSONUtil.toJsonStr(processModelId));
        bpmnProcessModelService.deleteBpmModelById(processModelId, tenantId);
        return success();
    }

    /**
     * 通过模型 KEY 删除模型
     *
     * @param processModelKey 模型 KEY
     * @param tenantId        租户 ID
     * @return
     */
    @Operation(summary = "删除指定模型 KEY 的流程模型")
    @DeleteMapping("/deleteByKey")
    @Override
    public CommonResponse<Void> deleteByKey(@NotBlank(message = "流程模型 KEY 不能为空") @RequestParam String processModelKey,
                                            @RequestParam(required = false, defaultValue = "") String tenantId) {
        log.info("删除模型deleteBpmModel===>>>参数：{}", JSONUtil.toJsonStr(processModelKey));
        bpmnProcessModelService.deleteBpmModelByKey(processModelKey, tenantId);
        return success();
    }

    /**
     * 通过模型 ID 修改模型状态
     *
     * @param modelId  模型 ID
     * @param status   状态
     * @param operator 操作人
     * @return
     */
    @Operation(summary = "修改模型状态")
    @PostMapping("/changeStatus")
    @Override
    public CommonResponse<Boolean> changeStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId,
                                                @NotNull(message = "状态不能为空") @RequestParam Integer status,
                                                @RequestParam(required = false) String operator) {
        log.info("修改模型状态changeStatus===>>>参数：{}", JSONUtil.toJsonStr(modelId));
        BpmnTaskDelegateAssigner assignee = null;
        if (StringUtils.hasText(operator)) {
            assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class);
        }
        bpmnProcessModelService.changeStatus(modelId, status, assignee);
        return success(true);
    }

    /**
     * 修改模型打印开关状态
     *
     * @param modelId
     * @param status
     * @param operator
     * @return
     */
    @Operation(summary = "修改模型打印开关状态")
    @PostMapping("/print/changeStatus")
    @Override
    public CommonResponse<Boolean> changePrintStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId,
                                                     @NotNull(message = "状态不能为空") @RequestParam Integer status,
                                                     @RequestParam(required = false) String operator) {
        log.info("修改模型打印开关状态changeStatus===>>>参数：{}", JSONUtil.toJsonStr(modelId));
        BpmnTaskDelegateAssigner assignee = null;
        if (StringUtils.hasText(operator)) {
            assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class);
        }
        bpmnProcessModelService.changePrintStatus(modelId, status, assignee);
        return success(true);
    }

    /**
     * 查询流程模型使用的分类列表
     *
     * @return
     */
    @Operation(summary = "查询流程模型使用的分类列表")
    @GetMapping("/category/ids")
    @Override
    public CommonResponse<List<String>> getModelCategoryList() {
        log.info("获取已经使用过的分类集合getModelCategoryList");
        return success(bpmnProcessModelService.getModelCategoryList());
    }

    /**
     * 查询模型的租户集合
     *
     * @return
     */
    @Operation(summary = "查询模型的租户集合")
    @GetMapping("/tenant/ids")
    @Override
    public CommonResponse<List<String>> getModelTenantIds() {
        log.info("查询模型的租户集合getTenantIds");
        return success(bpmnProcessModelService.getTenantIds());
    }

    /**
     * 获取打印模板配置内容
     *
     * @param dto
     * @return
     */
    @Operation(summary = "获取打印模板配置内容")
    @PostMapping("/print/template/config/query")
    @Override
    public CommonResponse<PrintModelDTO> getPrintTemplateConfig(@Validated @RequestBody PrintTemplateConfigQueryDTO dto) {
        log.info("获取打印模板配置内容");
        if (!StringUtils.hasText(dto.getModelId()) && !StringUtils.hasText(dto.getProcessInstanceId()) && !StringUtils.hasText(dto.getProcessDefinitionKey())) {
            if (!StringUtils.hasText(dto.getProcessDefinitionKey()) && !StringUtils.hasText(dto.getTenantId())) {
                throw new WorkflowEngineException(ILLEGAL_PARAM_ERROR, "processDefinitionKey 与 tenantId 不能同时为空");
            }
            throw new WorkflowEngineException(ILLEGAL_PARAM_ERROR, "模型 ID 、实例 ID 和业务 ID 不能同时为空");
        }
        if (!StringUtils.hasText(dto.getModelId()) && StringUtils.hasText(dto.getProcessInstanceId())) {
            dto.setModelId(bpmnProcessInstanceService.getModelIdByProcessInstanceId(dto.getProcessInstanceId()));
        }
        if (StringUtils.hasText(dto.getProcessDefinitionKey())) {
            BpmnProcessDefinitionVO definition = bpmnProcessDefinitionService.getActiveProcessDefinitionByKey(dto.getProcessDefinitionKey(), dto.getTenantId());
            CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
            Model model = commandExecutor.execute(new CustomGetModelByDefinitionIdCmd(definition.getId()));
            dto.setModelId(model.getId());
        }
        return success(bpmnProcessModelService.getPrintTemplateConfig(dto.getModelId()));
    }

    /**
     * 代运营重置打印模板
     *
     * @param dto
     * @return
     */
    @Operation(summary = "代运营重置打印模板")
    @PostMapping(value = "/print/template/config/reset")
    public CommonResponse<Boolean> resetPrintTemplateConfig(@Validated @RequestBody RestPrintTemplateConfigDTO dto) {
        String activeProcessDefinitionId = bpmnProcessDefinitionService.getActiveProcessDefinitionId(NO_TENANT_ID, dto.getProcessDefinitionKey());
        String modelId = bpmnProcessInstanceService.getModelIdByProcessDefinitionId(null, activeProcessDefinitionId);
        PrintModelDTO config = bpmnProcessModelService.getPrintTemplateConfig(modelId);
        bpmnProcessModelService.printTemplateConfig(PrintTemplateConfigUpsertDTO.builder()
                .modelId(dto.getModelId())
                .printFileName(config.getPrintFileName())
                .printTemplateName(config.getPrintTemplateName())
                .printTemplateConfig(StringUtils.hasText(config.getPrintTemplateConfig()) ? config.getPrintTemplateConfig() : "")
                .build());
        return success(true);
    }

    /**
     * 搜索文档列表
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "搜索文档列表")
    @PostMapping("/doc/page")
    public CommonResponse<BpmPageResult<DocBaseVO>> docPage(@Validated @RequestBody DocSearchDTO dto) {
        return success(modelDocService.docPage(dto));
    }

    /**
     * 获取指定 docIds 文档列表
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "获取指定 docIds 文档列表")
    @PostMapping("/doc/ids")
    public CommonResponse<List<DocBaseVO>> docByIds(@Validated @RequestBody DocByIdDTO dto) {
        return success(modelDocService.getIds(dto.getIds()));
    }

    /**
     * 根据业务 ID 获取模型文档列表，自动适配公共模板和代运营
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "根据业务 ID 获取模型文档列表，自动适配公共模板和代运营")
    @PostMapping(value = "/doc/list")
    public CommonResponse<List<DocBaseVO>> docList(@Validated @RequestBody DocQueryDTO dto) {
        if (!StringUtils.hasText(dto.getProcessInstanceId()) && (!StringUtils.hasText(dto.getProcessDefinitionKey()) && CollectionUtils.isEmpty(dto.getProcessDefinitionKeys()))) {
            throw new WorkflowEngineException(MODEL_FILE_QUERY_ERROR);
        }
        return success(modelDocService.docList(dto));
    }

    /**
     * 获取关联 HiPrint 类型文档模板内容
     *
     * @param fileRelationId
     * @return
     */
    @Override
    @Operation(summary = "获取关联 HiPrint 类型文档模板内容")
    @PostMapping(value = "/hi-print/content/get")
    public CommonResponse<String> getHiPrintContent(@RequestParam String fileRelationId) {
        if (!NumberUtil.isNumber(fileRelationId)) {
            throw new WorkflowEngineException(MODEL_FILE_HIPRINT_ID_INVAILD);
        }
        return success(docContentService.getContent(Long.valueOf(fileRelationId)));
    }

    /**
     * 添加关联文档
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "添加关联文档")
    @PutMapping(value = "/doc/create")
    public CommonResponse<Boolean> createDoc(@Validated @RequestBody DocCreateDTO dto) {
        return success(modelDocService.createDoc(dto, true) > 0);
    }

    /**
     * 修改关联文档
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "修改关联文档")
    @PostMapping(value = "/doc/update")
    public CommonResponse<Boolean> updateDoc(@Validated @RequestBody DocUpdateDTO dto) {
        return success(modelDocService.updateDoc(dto));
    }

    /**
     * 克隆关联文档
     *
     * @param docId
     * @return
     */
    @Override
    @Operation(summary = "克隆关联文档")
    @PostMapping(value = "/doc/clone")
    public CommonResponse<Boolean> cloneDoc(@RequestParam("id") Long docId) {
        return success(this.singleCloneDoc(docId, null));
    }

    /**
     * 单个复制文档模版
     *
     * @param docId        复制的模版id
     * @param docModelInfo 模版关联的审批业务模版信息
     * @return 是否成功
     */
    private Boolean singleCloneDoc(Long docId, DocModelInfo docModelInfo) {
        DocBaseVO originDoc = modelDocService.get(docId);
        ArrayList<FileTypeEnum> wpsSupported = Lists.newArrayList(WORD, EXCEL);
        if (wpsSupported.contains(originDoc.getFileType())) {
            // WPS 文档 clone
            CopyNodeRequest copy = new CopyNodeRequest();
            copy.setCode(originDoc.getFileRelationId());
            CommonResponse<String> response = docAnonymousApi.copy(copy);
            if (Objects.isNull(response) || !Objects.equals(response.getCode(), 200)) {
                throw new WorkflowEngineException(MODEL_FILE_CLONE_ERROR, Objects.isNull(response) ? "网络异常" : response.getMsg());
            }
            DocCreateDTO newDoc = BeanMapper.copyBean(originDoc, DocCreateDTO.class);
            newDoc.setFileRelationId(response.getData());
            newDoc.setTag("");
            if (docModelInfo != null) {
                newDoc.setModelId(docModelInfo.getTargetModelId());
                newDoc.setModelKey(docModelInfo.getTargetModelKey());
                newDoc.setTenantId(docModelInfo.getTenantId());
                newDoc.setTag(docModelInfo.getTag());
            }
            return modelDocService.createDoc(newDoc, false) > 0;
        }
        if (Objects.equals(originDoc.getFileType(), FileTypeEnum.PDF)) {
            // PDF 文档 clone
            CommonResponse<ServerFileBatchCopyObjectResponse> response = fileServiceApi.batchCopyObject(ServerFileBatchCopyObjectRequest.builder()
                    .copyObjects(Sets.newHashSet(ServerFileBatchCopyObjectRequest.ServerFileCopyObjectRequest.builder()
                            .srcFileKey(originDoc.getFileRelationId())
                            .build()))
                    .build());
            if (Objects.isNull(response) || Objects.equals(response.getCode(), 200)) {
                throw new WorkflowEngineException(MODEL_FILE_CLONE_ERROR, Objects.isNull(response) ? "网络异常" : response.getMsg());
            }
            DocCreateDTO newDoc = BeanMapper.copyBean(originDoc, DocCreateDTO.class);
            newDoc.setFileRelationId(response.getData().getResponses().get(0).getSrcFileKey());
            newDoc.setTag("");
            if (docModelInfo != null) {
                newDoc.setModelId(docModelInfo.getTargetModelId());
                newDoc.setModelKey(docModelInfo.getTargetModelKey());
                newDoc.setTenantId(docModelInfo.getTenantId());
                newDoc.setTag(docModelInfo.getTag());
            }
            return modelDocService.createDoc(newDoc, false) > 0;
        }
        return modelDocService.cloneDoc(DocCloneDTO.builder()
                .docId(docId)
                .targetTenantId(docModelInfo.getTenantId())
                .targetModelId(docModelInfo.getTargetModelId())
                .targetFileTag(docModelInfo.getTag())
                .build()) > 0;
    }

    /**
     * 删除指定文档
     *
     * @param docId
     * @return
     */
    @Override
    @Operation(summary = "删除指定文档")
    @DeleteMapping(value = "/doc/delete")
    public CommonResponse<Boolean> deleteDoc(@RequestParam("id") Long docId) {
        Boolean result = modelDocService.deleteDoc(docId);
        if (Objects.equals(Boolean.TRUE, result)) {
            DocBaseVO doc = modelDocService.get(docId, true);
            if (Objects.equals(WORD, doc.getFileType()) || Objects.equals(EXCEL, doc.getFileType())) {
                wpsUtil.deleteWpsFile(doc.getFileRelationId());
            }
        }
        return success(result);
    }

    /**
     * 关联文档配置排序
     *
     * @param dto
     * @return
     */
    @Operation(summary = "关联文档配置排序")
    @PostMapping(value = "/doc/order")
    @Override
    public CommonResponse<Boolean> orderDoc(@Validated @RequestBody DocOrderDTO dto) {
        return success(modelDocService.orderDoc(dto));
    }

    /**
     * 设置关联文档状态
     *
     * @param dto
     * @return
     */
    @Operation(summary = "设置关联文档状态")
    @PostMapping(value = "/doc/status")
    public CommonResponse<Boolean> statusDoc(@Validated @RequestBody DocStatusDTO dto) {
        return success(modelDocService.statusDoc(dto));
    }

    /**
     * 设置关联文档的必选状态
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "设置关联文档的必选状态")
    @PostMapping(value = "/doc/require")
    public CommonResponse<Boolean> requireDoc(@Validated @RequestBody DocStatusDTO dto) {
        return success(modelDocService.requireDoc(dto));
    }

    /**
     * 特殊的查询设置过关联过文档的工作台 ID 集合
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "特殊的查询设置过关联过文档的工作台 ID 集合")
    @PostMapping(value = "/has/docs/tenantId")
    public CommonResponse<List<Long>> hasFilesTenantIds(@Validated @RequestBody DocTenantQueryDTO dto) {
        return success(modelDocService.querySetting(dto.getProcessDefinitionKey(), null)
                .stream().map(ExtAxModelDoc::getTenantId)
                .filter(StringUtils::hasText)
                .map(Long::parseLong)
                .distinct()
                .collect(Collectors.toList()));
    }

    /**
     * 重置模版关联文档
     *
     * @param dto
     * @return
     */
    @Override
    @Operation(summary = "重置模版关联文档")
    @PostMapping(value = "/doc/reset")
    public CommonResponse<Boolean> resetDoc(@Validated @RequestBody DocResetDTO dto) {
        DocSearchDTO docSearchDTO = new DocSearchDTO();
        docSearchDTO.setModelId(dto.getModelId());
        docSearchDTO.setTenantId(dto.getWorkspaceId());
        docSearchDTO.setPageNo(1);
        docSearchDTO.setPageSize(999);
        BpmPageResult<DocBaseVO> docBaseVOBpmPageResult = modelDocService.docPage(docSearchDTO);
        List<DocBaseVO> docBaseVOS = ListUtils.emptyIfNull(docBaseVOBpmPageResult.getList());
        //先删除
        if (!CollectionUtils.isEmpty(docBaseVOS)) {
            modelDocService.batchDeleteDoc(docBaseVOS.stream().map(DocBaseVO::getId).collect(Collectors.toList()));
        }

        CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
        List<DocBaseVO> commonDocBaseVos = commandExecutor.execute(new CustomGetModelDocsCmd(null,
                dto.getCategory(), null, false, false, extAxModelDocMapper, reModelService));
        //通用模板的文档
        if (!CollectionUtils.isEmpty(commonDocBaseVos)) {
            //进行克隆
            commonDocBaseVos.forEach(cb -> this.singleCloneDoc(cb.getId(), DocModelInfo.builder()
                    .targetModelKey(dto.getCategory())
                    .targetModelId(dto.getModelId())
                    .tenantId(dto.getWorkspaceId())
                    .tag(cb.getTag())
                    .build()));
        }
        return CommonResponse.success(Boolean.TRUE);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class DocModelInfo {
        private String targetModelId;
        private String targetModelKey;
        private String tenantId;
        private String tag;
    }
}
