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

import cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi;
import cn.axzo.oss.http.api.ServerFileServiceApi;
import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest;
import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse;
import cn.axzo.workflow.client.feign.bpmn.ProcessInstanceApi;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.log.LogApproveSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceBatchQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCarbonCopyDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCheckApproverDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceLogQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceMyPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.HistoricProcessInstanceSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ExtProcessLogVO;
import cn.axzo.workflow.common.model.response.bpmn.process.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceLogVO;
import cn.axzo.workflow.common.valid.group.ValidGroup;
import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd;
import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper;
import cn.axzo.workflow.core.service.BpmnProcessInstanceService;
import cn.axzo.workflow.core.service.BpmnProcessTaskService;
import cn.axzo.workflow.core.service.CategoryService;
import cn.axzo.workflow.core.service.ExtAxProcessLogService;
import cn.axzo.workflow.core.service.ExtAxProcessSignService;
import cn.axzo.workflow.core.service.ExtAxReModelService;
import cn.axzo.workflow.core.service.ExtAxReadRecordService;
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
import cn.axzo.workflow.server.common.annotation.RepeatSubmit;
import cn.axzo.workflow.server.common.util.RpcExternalUtil;
import cn.axzo.workflow.server.controller.web.BasicPopulateAvatarController;
import cn.azxo.framework.common.model.CommonResponse;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.BeanUtils;
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.Nullable;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_ID_NOT_IN_MODEL;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_READ_PARAM_ERROR;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_EXT_LOG_PARAM_ERROR;
import static cn.azxo.framework.common.model.CommonResponse.success;

/**
 * 流程实例相关控制器
 */
@Slf4j
@RequestMapping({"/web/v1/api/process/instance", "/api/process/instance"})
@RestController
@ErrorReporter
@Validated
public class BpmnProcessInstanceController extends BasicPopulateAvatarController implements ProcessInstanceApi {

    @Resource
    private BpmnProcessInstanceService bpmnProcessInstanceService;
    @Resource
    private BpmnProcessTaskService bpmnProcessTaskService;
    @Resource
    private ServerFileServiceApi serverFileServiceApi;
    @Resource
    private DocAnonymousDatabaseApi docAnonymousApi;
    @Resource
    private ExtAxReadRecordService readRecordService;
    @Resource
    private ExtAxModelDocMapper extAxModelDocMapper;
    @Resource
    private SpringProcessEngineConfiguration processEngineConfiguration;
    @Resource
    private ExtAxProcessSignService extAxProcessSignService;
    @Resource
    private ExtAxReModelService extAxReModelService;
    @Resource
    private ExtAxProcessLogService extAxProcessLogService;
    @Resource
    private CategoryService categoryService;

    /**
     * 超管查询所有流程实例
     *
     * @param dto
     * @return
     */
    @Operation(summary = "超管查询所有流程实例")
    @PostMapping("/page/all")
    @Override
    public CommonResponse<BpmPageResult<BpmnProcessInstanceAdminPageItemVO>> getAllProcessInstancePage(@Validated @RequestBody BpmnProcessInstanceAdminPageReqVO dto) {
        log.info("超管查询审批列表 getAllProcessInstancePage===>>>参数：{}", JSONUtil.toJsonStr(dto));
        return success(bpmnProcessInstanceService.getAdminProcessInstancePage(dto));
    }

    /**
     * 我发起的审批列表
     */
    @Operation(summary = "我发起的审批列表")
    @PostMapping("/page/my")
    @Override
    public CommonResponse<BpmPageResult<BpmnProcessInstancePageItemVO>> getMyProcessInstancePage(@Validated @RequestBody BpmnProcessInstanceMyPageReqVO dto) {
        log.info("我发起的审批列表 getOwnTaskPage===>>>参数：{}", JSONUtil.toJsonStr(dto));
        return success(bpmnProcessInstanceService.getMyProcessInstancePage(dto));
    }

    /**
     * 创建审批实例
     */
    @Operation(summary = "创建审批流程前，返回模型节点列表以及节点能否设置审批人")
    @PostMapping("/create/before")
    @Override
    @RepeatSubmit
    public CommonResponse<List<NodesByModelVO>> nodesBeforeCreateProcessInstance(@Validated @RequestBody BeforeProcessInstanceCreateDTO dto) {
        log.info("发起审核前，节点发起人自选nodesBeforeCreateProcessInstance===>>>参数：{}", JSONUtil.toJsonStr(dto));
        List<NodesByModelVO> nodes = bpmnProcessInstanceService.nodesBeforeCreateProcessInstance(dto);
        // 填充头像
        populateUsersAvatar(nodes.stream().flatMap(e -> e.getAssigners().stream().filter(Objects::nonNull)).collect(Collectors.toList()));
        return success(nodes);
    }

    /**
     * 创建审批实例
     */
    @Operation(summary = "创建流程实例")
    @PostMapping("/create")
    @Override
    @RepeatSubmit
    public CommonResponse<String> createProcessInstance(@Validated @RequestBody BpmnProcessInstanceCreateDTO dto) {
        log.info("发起审核createProcessInstance===>>>参数：{}", JSONUtil.toJsonStr(dto));
        // 填充名称头像
        populateUsersAvatar(dto.getInitiator());
        // 填充指定审批人名称头像
        if (!CollectionUtils.isEmpty(dto.getSpecifyAssignerMap())) {
            populateUsersAvatar(dto.getSpecifyAssignerMap().entrySet().stream().flatMap(e -> e.getValue().stream().filter(Objects::nonNull)).collect(Collectors.toList()));
        }
        // 填充签署人名称头像
        populateUsersAvatar(dto.getSignatories());
        dto.setAsync(false);
        return success(bpmnProcessInstanceService.createProcessInstance(dto));
    }

    /**
     * 撤回流程实例
     *
     * @param dto
     * @return
     */
    @Operation(summary = "撤回流程实例")
    @DeleteMapping("/cancel")
    @Override
    @RepeatSubmit
    public CommonResponse<Boolean> cancelProcessInstance(@Validated(ValidGroup.Update.class) @RequestBody BpmnProcessInstanceCancelDTO dto) {
        log.info("撤回审核cancelProcessInstant===>>>参数：{}", JSONUtil.toJsonStr(dto));
        populateUsersAvatar(dto.getInitiator());
        SuperBpmnProcessInstanceCancelDTO target = new SuperBpmnProcessInstanceCancelDTO();
        BeanUtils.copyProperties(dto, target);
        return success(bpmnProcessInstanceService.cancelProcessInstance(target));
    }

    @Operation(summary = "撤回流程实例(Super)")
    @DeleteMapping("/super/cancel")
    @RepeatSubmit
    @Override
    public CommonResponse<Boolean> superCancelProcessInstance(@Validated(ValidGroup.Insert.class) @RequestBody SuperBpmnProcessInstanceCancelDTO dto) {
        log.info("超级撤回审核 superCancelProcessInstant===>>>参数：{}", JSONUtil.toJsonStr(dto));
        populateUsersAvatar(dto.getInitiator());
        return success(bpmnProcessInstanceService.cancelProcessInstance(dto));
    }

    /**
     * 中止流程实例
     *
     * @param dto
     * @return
     */
    @Operation(summary = "中止流程实例")
    @Override
    @DeleteMapping("/abort")
    @RepeatSubmit
    public CommonResponse<Boolean> abortProcessInstance(@Validated @RequestBody BpmnProcessInstanceAbortDTO dto) {
        log.info("中止流程实例abortProcessInstant===>>>参数：{}", JSONUtil.toJsonStr(dto));
        populateUsersAvatar(dto.getAssigner());
        return success(bpmnProcessInstanceService.abortProcessInstance(dto));
    }

    /**
     * 批量中止流程实例
     *
     * @param dtos 请求参数
     * @return
     */
    @Operation(summary = "批量中止流程实例")
    @Override
    @DeleteMapping("/batch/abort")
    @RepeatSubmit
    public CommonResponse<BatchOperationResultVO> batchAbortProcessInstance(@Validated @RequestBody List<BpmnProcessInstanceAbortDTO> dtos) {
        log.info("批量中止流程实例abortProcessInstants===>>>参数：{}", JSONUtil.toJsonStr(dtos));
        return success(bpmnProcessInstanceService.batchAbortProcessInstance(dtos));
    }


    /**
     * 抄送流程
     *
     * @param dto
     * @return
     */
    @Operation(summary = "抄送流程")
    @RepeatSubmit
    @PostMapping("/carbon-copy")
    @Override
    public CommonResponse<Boolean> carbonCopyProcessInstance(@Validated @RequestBody BpmnProcessInstanceCarbonCopyDTO dto) {
        log.info("抄送流程实例carbonCopyProcessInstance===>>>参数：{}", JSONUtil.toJsonStr(dto));
        populateUsersAvatar(dto.getCopyAssigners());
        return success(bpmnProcessInstanceService.carbonCopyProcessInstance(dto));
    }

    /**
     * 获得流程实例
     *
     * @param dto {@link BpmnProcessInstanceQueryDTO} 可根据 Id,BusinessKey进行查询
     * @return 流程实例, 租户Id不必传
     */
    @Operation(summary = "获得流程实例")
    @GetMapping("/get")
    @Override
    public CommonResponse<BpmnProcessInstanceVO> getProcessInstanceVO(@Validated @RequestBody BpmnProcessInstanceQueryDTO dto) {
        log.info("获得历史的流程实例 getProcessInstanceVO===>>>参数：{}", JSONUtil.toJsonStr(dto));
        BpmnProcessInstanceVO result = bpmnProcessInstanceService.getProcessInstanceVO(dto);
        return success(result);
    }

    /**
     * 批量获得流程实例
     *
     * @param dto
     * @return
     */
    @Operation(summary = "批量获得流程实例")
    @GetMapping("/batch/get")
    @Override
    public CommonResponse<List<BpmnProcessInstanceVO>> getBatchProcessInstanceVO(@Validated @RequestBody BpmnProcessInstanceBatchQueryDTO dto) {
        log.info("批量获得历史的流程实例 getProcessInstanceVO===>>>参数：{}", JSONUtil.toJsonStr(dto));
        return success(bpmnProcessInstanceService.getBatchProcessInstanceVO(dto));
    }

    /**
     * 更新指定流程定义的版本的状态, 处于 suspended 状态的流程模型将不能再发起实例
     *
     * @param processDefinitionId 流程定义的编号
     * @param status              1, "active"; 2, "suspended"
     */
    @Operation(summary = "更新指定流程定义的版本的状态")
    @PutMapping("/status/update")
    @RepeatSubmit
    @Override
    public CommonResponse<Boolean> updateProcessStatus(@NotBlank(message = "流程定义 ID 不能为空") @RequestParam String processDefinitionId,
                                                       @NotNull(message = "状态不能为空") @RequestParam Integer status) {
        log.info("获得流程实例 updateProcessStatus===>>>参数：{},{}", processDefinitionId, status);
        Boolean result = bpmnProcessInstanceService.updateProcessStatus(processDefinitionId, status);
        return success(result);
    }

    /**
     * 获取审批流程实例的运行图
     *
     * @param processInstanceId 流程实例 ID
     * @param tenantId          租户 ID
     * @return
     */
    @Operation(summary = "获取审批流程实例的运行图")
    @GetMapping("/graphical")
    @Override
    public CommonResponse<ObjectNode> processInstanceGraphical(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
                                                               @Nullable @RequestParam(required = false) String tenantId) {
        return success(bpmnProcessInstanceService.getProcessInstanceGraphical(processInstanceId, tenantId));
    }

    /**
     * 获取指定实例的全节点执行顺序推算
     *
     * @param processInstanceId
     * @param tenantId
     * @return
     */
    @Operation(summary = "获取指定实例的全节点执行顺序推算")
    @GetMapping("/node/forecasting")
    @Override
    public CommonResponse<List<ProcessNodeDetailVO>> processInstanceNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
                                                                                 @Nullable String tenantId) {
        return success(bpmnProcessInstanceService.getProcessInstanceNodeForecast(processInstanceId, tenantId));
    }

    /**
     * 推断指定流程实例的过滤掉部分节点执行顺序
     *
     * @param processInstanceId
     * @param tenantId
     * @param allNode            如果为真时,相当于调用 {@link ProcessInstanceApi#processInstanceNodeForecast(String, String)} 方法,切会直接丢弃 nodeDefinitionKeys 参数
     *                           如果为假时,才结合 nodeDefinitionKeys 过滤掉传入的节点
     * @param nodeDefinitionKeys
     * @return
     */
    @Operation(summary = "推断指定流程实例的过滤掉部分节点执行顺序")
    @GetMapping("/node/filter/forecasting")
    @Override
    public CommonResponse<List<ProcessNodeDetailVO>> processInstanceFilterNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId,
                                                                                       @Nullable @RequestParam(required = false) String tenantId,
                                                                                       @RequestParam(required = false, defaultValue = "false") Boolean allNode,
                                                                                       @Nullable @RequestParam(required = false) List<String> nodeDefinitionKeys) {
        if (allNode) {
            return success(bpmnProcessInstanceService.getProcessInstanceNodeForecast(processInstanceId, tenantId));
        } else {
            return success(bpmnProcessInstanceService.getProcessInstanceNodeFilterForecast(processInstanceId, tenantId, nodeDefinitionKeys));
        }
    }

    /**
     * 获取指定流程实例的流程变量
     *
     * @param processInstanceId
     * @param tenantId
     * @return
     */
    @Operation(summary = "获取指定流程实例的流程变量")
    @GetMapping("/cooperation-org")
    @Override
    public CommonResponse<Map<String, Object>> getProcessVariables(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
                                                                   @Nullable String tenantId) {
        HistoricProcessInstance processInstance = bpmnProcessInstanceService.getProcessInstance(processInstanceId,
                tenantId, true);
        return success(processInstance.getProcessVariables());
    }

    /**
     * 枢智业务(审批台账专用)
     *
     * @return
     */
    @Operation(summary = "查询模型的租户集合")
    @GetMapping("/historic/page")
    public CommonResponse<BpmPageResult<HistoricProcessInstanceVO>> historicInstancePage(@RequestBody HistoricProcessInstanceSearchDTO dto) {
        return success(bpmnProcessInstanceService.historicProcessInstancePage(dto));
    }

    /**
     * 查询实例的租户集合
     *
     * @return
     */
    @Operation(summary = "查询实例的租户集合")
    @Override
    @GetMapping("/tenant/ids")
    public CommonResponse<List<String>> getTenantIds() {
        return success(bpmnProcessInstanceService.getTenantIds());
    }

    /**
     * 校验指定流程实例下,是否存在指定的审批人
     *
     * @return true 是在当前流程实例中,存在指定的审批人
     */
    @Operation(summary = "校验指定流程实例下,是否存在指定的审批人")
    @PostMapping("/check/approver")
    @Override
    public CommonResponse<Boolean> checkInstanceApprover(@Validated @RequestBody BpmnProcessInstanceCheckApproverDTO dto) {
        log.info("校验指定流程实例下,是否存在指定的审批人 checkInstanceApprover===>>>参数：{}", dto);
        return success(bpmnProcessInstanceService.checkInstanceApprover(dto));
    }

    /**
     * 获取指定流程实例的日志
     *
     * @param dto
     * @return
     */
    @Operation(summary = "获取指定流程实例的日志")
    @PostMapping("/logs")
    @Override
    public CommonResponse<BpmnProcessInstanceLogVO> getProcessInstanceLogs(@Validated @RequestBody BpmnProcessInstanceLogQueryDTO dto) {
        log.info("获取指定流程实例的日志 getProcessInstanceLog===>>>参数：{}", JSONUtil.toJsonStr(dto));
        BpmnProcessInstanceLogVO log = bpmnProcessInstanceService.getProcessInstanceLog(dto);
        parseSignatureUrl(log);
        resetPersonInfo(log);
        return success(log);
    }


    /**
     * 根据任务id查询任务状态，按钮详情
     *
     * @param taskButtonsSearchDTO 请求参数
     * @return
     */
    @Override
    @Operation(summary = "根据流程实例id,任务id查询任务状态，按钮详情")
    @PostMapping("/task/buttons/find")
    public CommonResponse<BpmnTaskButtonVo> findProcessSingleTaskButtons(@Validated @RequestBody BpmnTaskButtonSearchDTO taskButtonsSearchDTO) {
        return success(bpmnProcessInstanceService.findTaskButtons(taskButtonsSearchDTO));
    }

    private void resetPersonInfo(BpmnProcessInstanceLogVO log) {
        List<BpmnTaskDelegateAssigner> users = log.getTaskDetails().stream().flatMap(taskLog -> {
            Stream<BpmnTaskDelegateAssigner> assigner = Objects.nonNull(taskLog.getAssigneeSnapshot()) ? Stream.of(taskLog.getAssigneeSnapshot()) : Stream.empty();
            Stream<BpmnTaskDelegateAssigner> forecastAssignees = CollectionUtils.isEmpty(taskLog.getForecastAssignees()) ? Stream.empty() : taskLog.getForecastAssignees().stream();
            return Stream.concat(assigner, forecastAssignees);
        }).collect(Collectors.toList());
//        List<BpmnTaskDelegateAssigner> users = log.getTaskDetails().stream().map(BpmnTaskInstanceLogVO::getAssigneeSnapshot).filter(Objects::nonNull).filter(e -> NumberUtils.isNumber(e.getPersonId())).collect(Collectors.toList());
        users.add(log.getInitiator());
        populateUsersAvatar(users);
//        users.stream().collect(Collectors.toMap(BpmnTaskDelegateAssigner::getPersonId, Function.identity(), (s,t)->s))
//                .forEach();

    }

    private void parseSignatureUrl(BpmnProcessInstanceLogVO log) {
        List<String> signUrls = log.getTaskDetails().stream()
                .filter(i -> StringUtils.hasText(i.getTaskId()))
                .map(BpmnTaskInstanceLogVO::getSignatureUrl)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(signUrls)) {
            Map<String, String> ossUrlMap = ListUtils.emptyIfNull(getSignPrivateUrl(signUrls)).stream()
                    .collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, ApiSignUrlDownloadResponse::getSignUrl, (s, t) -> s));
            log.getTaskDetails().stream()
                    .filter(i -> StringUtils.hasText(i.getTaskId()))
                    .forEach(i -> i.setSignatureUrl(ossUrlMap.getOrDefault(i.getSignatureUrl(), null)));
        }
    }

    private List<ApiSignUrlDownloadResponse> getSignPrivateUrl(List<String> signUrls) {
        ApiSignUrlDownloadRequest request = new ApiSignUrlDownloadRequest();
        request.setFileKeys(signUrls);
        return RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(request), "批量获取手写签私有访问地址", request);
    }

    @Operation(summary = "更新指定流程表单最后一次编辑内容中指定 KEY 的全部数据")
    @PostMapping("/form/variable/update")
    @Override
    public CommonResponse<Boolean> updateInstanceFormVariables(@Validated @RequestBody FormVariablesUpdateDTO dto) {
        bpmnProcessInstanceService.overrideFormVariables(dto);
        return CommonResponse.success(true);
    }

    @Override
    @Operation(summary = "签署业务流程实例在审批待办中查询使用的文档列表")
    @PostMapping("/select/doc/list")
    public CommonResponse<List<DocPendingVO>> processInstanceSelectDocs(@Validated @RequestBody ProcessDocQueryDTO dto) {
        List<DocPendingVO> docs = bpmnProcessInstanceService.processInstanceSelectDocs(dto);
        if (CollectionUtils.isEmpty(docs)) {
            return CommonResponse.success(docs);
        }
        List<String> fileKeys = docs.stream().map(DocPendingVO::getFileKey).filter(StringUtils::hasText).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(fileKeys)) {
            return CommonResponse.success(docs);
        }
        ApiSignUrlDownloadRequest ossRequest = new ApiSignUrlDownloadRequest();
        ossRequest.setFileKeys(fileKeys);
        Map</*fileKey*/String, /*ossUrl*/ApiSignUrlDownloadResponse> ossUrlMap = RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(ossRequest), "批量获取文件 OSS 地址", ossRequest)
                .stream().collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, Function.identity(), (s, t) -> s));
        docs.forEach(i -> {
            if (StringUtils.hasText(i.getFileKey())) {
                ApiSignUrlDownloadResponse ossFileInfo = ossUrlMap.getOrDefault(i.getFileKey(), null);
                if (Objects.nonNull(ossFileInfo)) {
                    i.setOssUrl(ossFileInfo.getSignUrl());
                    i.setStorageSize(ossFileInfo.getStorageSize());
                }
            }
        });
        return success(docs);
    }

    @Override
    @Operation(summary = "获取审批人阅读状态")
    @PostMapping("/approver/read/status")
    public CommonResponse<List<SimpleDocDTO>> approverReadStatus(@Validated @RequestBody ApproverReadStatusDTO dto) {
        if (!StringUtils.hasText(dto.getAssigner().getPersonId())) {
            throw new WorkflowEngineException(PROCESS_DOC_READ_PARAM_ERROR);
        }
        return success(readRecordService.queryReadStatus(dto));
    }

    @Override
    @Operation(summary = "修改审批人关联文档阅读状态")
    @PostMapping("/approver/read/status/change")
    public CommonResponse<Boolean> approveReadStatusChange(@Validated @RequestBody ChangeApproverReadStatusDTO dto) {
        if (!StringUtils.hasText(dto.getAssigner().getPersonId())) {
            throw new WorkflowEngineException(PROCESS_DOC_READ_PARAM_ERROR);
        }
        CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
        List<DocBaseVO> relationDocs = commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(), true, extAxModelDocMapper, extAxReModelService));
        relationDocs.stream().filter(i -> Objects.equals(i.getId(), dto.getDocId()))
                .findAny().orElseThrow(() -> new WorkflowEngineException(PROCESS_DOC_ID_NOT_IN_MODEL));

        return success(readRecordService.changeReadStatus(dto));
    }

    @Override
    @Operation(summary = "获取签署业务流程最后替换的文档 fileKey 集合")
    @GetMapping("/final/docs")
    public CommonResponse<List<SignFileDTO>> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId) {
        return success(extAxProcessSignService.findByProcessInstanceId(processInstanceId).getFileArchive());
    }

    /**
     * 查询 ExtAxProcessLog 表中审批人的冗余信息
     *
     * @param dto
     * @return
     */
    @Operation(summary = "查询 ExtAxProcessLog 表中审批人的冗余信息")
    @GetMapping("/log/approve/ext")
    @Override
    public CommonResponse<List<ExtProcessLogVO>> getProcessLogByInstanceIdAndPersonId(@Validated @RequestBody LogApproveSearchDTO dto) {

        ExtAxProcessLog query = new ExtAxProcessLog();
        query.setProcessInstanceId(dto.getProcessInstanceId());
        if (StringUtils.hasText(dto.getTaskId())) {
            query.setTaskId(dto.getTaskId());
        } else {
            if (!NumberUtil.isLong(dto.getPersonId())) {
                throw new WorkflowEngineException(PROCESS_EXT_LOG_PARAM_ERROR);
            }
            query.setAssigneeId(Long.valueOf(dto.getPersonId()));
            query.setAssigneeTenantId(dto.getTenantId());
            query.setAssigneeOuId(Objects.isNull(dto.getOuId()) ? "" : dto.getOuId());
        }

        if (Objects.isNull(dto.getStatus())) {
            query.setStatus(BpmnProcessInstanceResultEnum.PROCESSING.getStatus());
        } else {
            query.setStatus(dto.getStatus().getStatus());
        }
        List<ExtAxProcessLog> extAxProcessLogs = extAxProcessLogService.genericQuery(query);
        return success(ListUtils.emptyIfNull(extAxProcessLogs).stream().map(i -> {
            ExtProcessLogVO logVO = new ExtProcessLogVO();
            logVO.setId(i.getId());
            logVO.setProcessInstanceId(i.getProcessInstanceId());
            logVO.setProcessTenantId(i.getTenantId());
            logVO.setActivityId(i.getActivityId());
            logVO.setActivityName(i.getActivityName());
            logVO.setTaskId(i.getTaskId());
            logVO.setAssigneePersonId(String.valueOf(i.getAssigneeId()));
            logVO.setAssigneeTenantId(i.getAssigneeTenantId());
            logVO.setAssigneeName(i.getAssigneeName());
            logVO.setAssigneeOuId(i.getAssigneeOuId());
            logVO.setStatus(BpmnProcessInstanceResultEnum.valueOfStatus(i.getStatus()));
            logVO.setOrgStructureSnapshotInfo(i.getExtra());
            return logVO;
        }).collect(Collectors.toList()));
    }
}
