package cn.iocoder.yudao.module.bpm.service.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmOAReimbursementPrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAImprestDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReimbursementDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAImprestService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAProcureServiceImpl;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAReimbursementService;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;

/**
 * 流程实例 Service 实现类
 *
 * ProcessDefinition & ProcessInstance & Execution & Task 的关系：
 *     1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" />
 *
 * HistoricProcessInstance & ProcessInstance 的关系：
 *     1. <a href=" https://my.oschina.net/843294669/blog/71902" />
 *
 * 简单来说，前者 = 历史 + 运行中的流程实例，后者仅是运行中的流程实例
 *

 */
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
    @Resource
    private TaskService engineTaskService;

    @Resource
    private BpmTaskAssignRuleMapper taskRuleMapper;

    @Resource
    private BpmUserGroupService userGroupService;

    @Resource
    private RuntimeService runtimeService;
    @Resource
    private BpmProcessInstanceExtMapper processInstanceExtMapper;
    @Resource
    @Lazy // 解决循环依赖
    private BpmTaskService taskService;
    @Resource
    private BpmProcessDefinitionService processDefinitionService;
    @Resource
    private HistoryService historyService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private DeptApi deptApi;
    @Resource
    private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
    @Resource
    @Lazy // 解决循环依赖
    private BpmMessageService messageService;
    @Override
    public ProcessInstance getProcessInstance(String id) {
        return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
    }

    @Override
    public List<ProcessInstance> getProcessInstances(Set<String> ids) {
        return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
    }

    @Override
    public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
                                                                                 BpmProcessInstanceMyPageReqVO pageReqVO) {
        // 通过 BpmProcessInstanceExtDO 表，先查询到对应的分页
        PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return new PageResult<>(pageResult.getTotal());
        }

        // 获得流程 Task Map
        List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);

        List<String> ids = taskMap.values().stream()
                .flatMap(Collection::stream)
                .map(Task::getAssignee)
                .collect(Collectors.toList());

        Iterator<String> iterator = ids.iterator();
        while (iterator.hasNext()) {
            String id = iterator.next();
            if (id == null) {
                iterator.remove();
            }
        }

        List<Long> longIds = ids.stream()
                .map(Long::valueOf)
                .collect(Collectors.toList());
//        获得 User Map
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(longIds);

        // 转换返回
        return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
        // 获得流程定义
        ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
        // 发起流程
        return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
    }

    @Override
    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
        // 获得流程定义
        ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
        // 发起流程
        return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
    }

    @Override
    public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
        // 获得流程实例
        HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
        if (processInstance == null) {
            return null;
        }
        BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
        Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);

        // 获得流程定义
        ProcessDefinition processDefinition = processDefinitionService
                .getProcessDefinition(processInstance.getProcessDefinitionId());
        Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
        BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
                processInstance.getProcessDefinitionId());
        Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
        String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());

        // 获得 User
        AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
        DeptRespDTO dept = null;
        if (startUser != null) {
            dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
        }

        // 拼接结果
        return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
                processDefinition, processDefinitionExt, bpmnXml, startUser, dept);
    }

    @Override
    public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
        // 校验流程实例存在
        ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
        }
        // 只能取消自己的
        if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
        }

        // 通过删除流程实例，实现流程实例的取消,
        // 删除流程实例，正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
        deleteProcessInstance(cancelReqVO.getId(),
                BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
    }

    /**
     * 获得历史的流程实例
     *
     * @param id 流程实例的编号
     * @return 历史的流程实例
     */
    @Override
    public HistoricProcessInstance getHistoricProcessInstance(String id) {
        return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
    }

    @Override
    public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
        return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
    }

    @Override
    public void createProcessInstanceExt(ProcessInstance instance) {
        // 获得流程定义
        ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
        // 插入 BpmProcessInstanceExtDO 对象
        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
                .setProcessInstanceId(instance.getId())
                .setProcessDefinitionId(definition.getId())
                .setName(instance.getProcessDefinitionName())
                .setStartUserId(Long.valueOf(instance.getStartUserId()))
                .setCategory(definition.getCategory())
                .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());

        processInstanceExtMapper.insert(instanceExtDO);
    }

    @Override
    public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
        // 判断是否为 Reject 不通过。如果是，则不进行更新.
        // 因为，updateProcessInstanceExtReject 方法，已经进行更新了
        if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) {
            return;
        }

        // 需要主动查询，因为 instance 只有 id 属性
        // 另外，此时如果去查询 ProcessInstance 的话，字段是不全的，所以去查询了 HistoricProcessInstance
        HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
        // 更新拓展表
        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
                .setProcessInstanceId(event.getProcessInstanceId())
                .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime，所以这里设置
                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
                .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);

        // 发送流程实例的状态事件
        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
    }

    @Override
    public void updateProcessInstanceExtComplete(ProcessInstance instance) {
        // 需要主动查询，因为 instance 只有 id 属性
        // 另外，此时如果去查询 ProcessInstance 的话，字段是不全的，所以去查询了 HistoricProcessInstance
        HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
        // 更新拓展表
        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
                .setProcessInstanceId(instance.getProcessInstanceId())
                .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime，所以这里设置
                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全，说明审批通过
        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);


        Map<String, Object> processVariables = runtimeService.getVariables(instance.getProcessInstanceId());
        String reason = (String)processVariables.get("approve_reason") ;
        // 发送流程被通过的消息
        messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance,reason));

        // 发送流程实例的状态事件
        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProcessInstanceExtReject(String id, String reason) {
        // 需要主动查询，因为 instance 只有 id 属性
        ProcessInstance processInstance = getProcessInstance(id);
        // 删除流程实例，以实现驳回任务时，取消整个审批流程
        deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));

        // 更新 status + result
        // 注意，不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消，进而调用 updateProcessInstanceExtCancel 方法，
        // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL，显然和 result 不一定是一致的
        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
                .setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);

        // 发送流程被不通过的消息
        messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));

        // 发送流程实例的状态事件
        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
    }

    private void deleteProcessInstance(String id, String reason) {
        runtimeService.deleteProcessInstance(id, reason);
    }

    private String createProcessInstance0(Long userId, ProcessDefinition definition,
                                          Map<String, Object> variables, String businessKey) {
        // 校验流程定义
        if (definition == null) {
            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
        }
        if (definition.isSuspended()) {
            throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
        }

        // 创建流程实例
        ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionId(definition.getId())
                .businessKey(businessKey)
                .name(definition.getName().trim())
                .variables(variables)
                .start();

        // 设置流程名字
        runtimeService.setProcessInstanceName(instance.getId(), definition.getName());

        // 补全流程实例的拓展表
        processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
                .setFormVariables(variables));

        /** 创建流程后，添加抄送人 End    add by yj 2024.1.4 */
        processCCToUsers(definition,instance) ;

        /** 通过自己发起的流程 */
//        approveSelfTask(instance.getId()) ;

        return instance.getId();
    }

    private void approveSelfTask(String processInstanceId ) {
        List<Task> tasks =engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list() ;
        if( tasks != null && tasks.size() > 0) {
            Task task = tasks.get(0) ;
            String assigneeId = task.getAssignee();
            //如果当前登陆用户是审批人，那么自动审批通过
            if( assigneeId.equals( SecurityFrameworkUtils.getLoginUserId().toString() )) {
                BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO() ;
                reqVO.setId(task.getId()) ;
                reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
                taskService.approveTask(getLoginUserId(), reqVO);
            }
        }
    }

    /**
     * 获得抄送我的流程实例的分页
     *
     * @param userId 用户编号
     * @param pageReqVO 分页请求
     * @return 流程实例的分页
     */
    public PageResult<BpmProcessInstancePageItemRespVO> getMyCCProcessInstancePage(Long userId,
                                                                                 BpmProcessInstanceMyPageReqVO pageReqVO) {
        // 通过 BpmProcessInstanceExtDO 表，先查询到对应的分页
        PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return new PageResult<>(pageResult.getTotal());
        }

        // 获得流程 Task Map
        List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
        // 转换返回
        return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap,null);

    }

    public List<BpmProcessInstanceStatisticsRespVO> getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO){
        pageReqVO = getUserids(pageReqVO) ;
        return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO) ;
    }

    public List<BpmProcessInstanceStatisticsRespVO> getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO){
        pageReqVO = getUserids(pageReqVO) ;
        return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO) ;
    }

    public PageResult<BpmProcessInstancePageItemRespVO> getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
        // 通过 BpmProcessInstanceExtDO 表，先查询到对应的分页
        PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return new PageResult<>(pageResult.getTotal());
        }

        // 获得流程 Task Map
        List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);

        List<String> ids = taskMap.values().stream()
                .flatMap(Collection::stream)
                .map(Task::getAssignee)
                .collect(Collectors.toList());

        Iterator<String> iterator = ids.iterator();
        while (iterator.hasNext()) {
            String id = iterator.next();
            if (id == null) {
                iterator.remove();
            }
        }

        List<Long> longIds = ids.stream()
                .map(Long::valueOf)
                .collect(Collectors.toList());
        //获得  审批人  User Map
        Map<Long, AdminUserRespDTO> assigneeUserMap = adminUserApi.getUserMap(longIds);

        //获得  发起人  User Map
        List<BpmProcessInstanceExtDO> bpieDOs = pageResult.getList() ;
        longIds = new ArrayList<>() ;
        for (BpmProcessInstanceExtDO bpieDO: bpieDOs) {
            longIds.add(bpieDO.getStartUserId()) ;
        }
        Map<Long, AdminUserRespDTO> startUserMap = adminUserApi.getUserMap(longIds);

        // 转换返回
        return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap,startUserMap);

    }

    @Resource
    PermissionApi permissionApi;

    /**
     * 根据数据权限，查询关联操作的用户IDS
     * @param pageReqVO
     * @return
     * @param <T>
     */
    private <T> T getUserids(T pageReqVO)  {
        try {
            Class<T> clazz = (Class<T>) pageReqVO.getClass();
            Field idField = clazz.getDeclaredField("userIds");
            idField.setAccessible(true); // 设置可访问性

            Long[] userIds = null;
            Long userId = WebFrameworkUtils.getLoginUserId();
            DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
            //查询全部
            if (deptDataPermission.getAll()) {
                //idField.set(pageReqVO, null); // 设置属性值
                return pageReqVO;
            }

            // 情况二，即不能查看部门，又不能查看自己，则说明 100% 无权限
            if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
                    && Boolean.FALSE.equals(deptDataPermission.getSelf())) {
                //设置成0，一个不存在的用户Id，就查询不到数据了。
                userIds = new Long[]{0L};
                idField.set(pageReqVO, userIds);
                return pageReqVO;
            }

            //情况三 至查询自己
            if (deptDataPermission.getSelf()) {
                userIds = new Long[]{userId};
                idField.set(pageReqVO, userIds);
                return pageReqVO;
            }

            Set<Long> deptIds = deptDataPermission.getDeptIds();
            //查询部门关联的用户Id
            List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
            List<Long> tempList = new ArrayList<>();
            for (AdminUserRespDTO user : users) {
                Long id = user.getId();
                tempList.add(id);
            }
            tempList.add(userId);
            userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
            idField.set(pageReqVO, userIds);
            return pageReqVO;
        }catch (Exception exception){
            exception.printStackTrace();
            throw exception(BPM_SYSTEM_BUG);
        }
    }

    /**
     * /创建流程后，添加抄送人 Begin    add by yj 2024.1.4
     *  在设计流程的时候，需要添加一个任务块  名字必须叫Activity_cc  分配权限的时候，需要选择用户组。
     *
     * @param definition
     * @param instance
     */
    private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
        //获取bpm_task_assign_reule （Bpm 任务规则表）的流程中有没有配置抄送节点 固定抄送名称为：Activity_cc
        String processDefinitionId = definition.getId() ;
        List<BpmTaskAssignRuleDO> rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);

        for(BpmTaskAssignRuleDO rule :rules ){
            String key = rule.getTaskDefinitionKey() ;  //任务名称
            Integer type = rule.getType() ;
            if( !key.isEmpty() &&  key.equals(BpmConstants.CC_NAME) && type == 40 ) {
                StringBuffer str = new StringBuffer() ;
                Set<Long> options = rule.getOptions() ;
                List<Long> list = new ArrayList(options);
                for(Long groupId : list) {
                    //需要根据这个groupId，查询这个组中的用户id
                    BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
                    Set<Long> userIds = userGroup.getMemberUserIds() ;
                    List<Long> userIdList = new ArrayList(userIds);
                    for(Long user_id  : userIdList) {
                        str.append("[").append(user_id).append("]") ;
                    }
                }

                //流程是采购计划时
                if (definition.getKey().equals(BpmOAProcureServiceImpl.PROCESS_KEY)) {

                    AdminUserRespDTO userRespDTO = adminUserApi.getUser(Long.valueOf(instance.getStartUserId())).getCheckedData();
                    DeptRespDTO deptRespDTO = deptApi.getDept(userRespDTO.getDeptId()).getCheckedData();

                    //发起人部门为 生产部及以下时
                    if (deptRespDTO.getFlag().contains("130")) {

                        //添加 供应部抄送
                        BpmUserGroupDO userGroup = userGroupService.getUserGroup(121L);
                        Set<Long> userIds = userGroup.getMemberUserIds() ;
                        List<Long> userIdList = new ArrayList(userIds);
                        for(Long user_id  : userIdList) {
                            str.append("[").append(user_id).append("]") ;
                        }
                    }
                }

                String ccids = str.toString() ;
                //根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
                BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
                        .setCcids(ccids) .setProcessInstanceId(instance.getProcessInstanceId());
                processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
                break ;
            }
        }
    }


    @Resource
    private BpmTaskExtMapper taskExtMapper;

    @Override
    public List<BpmProcessFinishStatisticsRespVO> getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {

        //按审核人分组查询，统计已完成流程数量及平均耗时间
        List<BpmProcessFinishStatisticsRespVO> respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
        if( respVOS == null || respVOS.size() == 0) {
            return null ;
        }

        List<Long> idList = respVOS.stream()
                .map(BpmProcessFinishStatisticsRespVO::getUserId)
                .collect(Collectors.toList());

        //根据userId,查询UserMap
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(idList) ;
        Long[] idsArray = new Long[userMap.size()];
        // 使用 map 的 keySet() 方法获取键集合
        Set<Long> keys = userMap.keySet();
        // 遍历键集合，将每个键添加到数组中
        int index = 0;
        for (Long key : keys) {
            idsArray[index++] = key;
        }
        pageReqVO.setUserIds(idsArray) ;
        //按审核人分组查询，未审批完成的流程数量
        List<BpmProcessFinishStatisticsRespVO> bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount( pageReqVO ) ;

        //按审核人分组查询，未完成的记录数
        Map<Long, Integer> unFinfishCountCountMap  = new HashMap<>();
        for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
            unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
        }

        respVOS.forEach(respVO -> respVO.setName( userMap.get(respVO.getUserId()).getNickname()));
        respVOS.forEach(respVO -> respVO.setUnFinfishCount( unFinfishCountCountMap.get(respVO.getUserId())));

        //获取排行榜第一记录的耗时，作为基础数据
        double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
        int baseNumber = (int)num; // 再通过类型转换操作符将其转换为整数

        DecimalFormat df = new DecimalFormat("#.00");
        respVOS.forEach(respVO -> {
            //格式化流程完成率
            float finfishCount = (float) respVO.getFinfishCount() ;
            float unFinfishCount = respVO.getUnFinfishCount() == null ? 0: respVO.getUnFinfishCount();
            float all = finfishCount +  unFinfishCount ;
            float result = finfishCount / all;
            float rate = result * 100 ;
            respVO.setCompletionRate(df.format(rate)+"%") ;
            double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
            int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入，并转换为int类型
            respVO.setUserTime(roundedValue+"") ;

            //格式化进度百度比， 参照最高的数据进行百分比显示
            double percentage = ((int)Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
            respVO.setPercentage((int) Math.round(percentage)) ;
            //设置未完成
            respVO.setUnFinfishCount( Integer.valueOf((int)unFinfishCount)) ;
        });
        return respVOS ;
    }

    @Override
    public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
        return processInstanceExtMapper.selectByProcessInstanceId(id);
    }

    @Resource
    private FileApi fileApi;

    @Resource
    @Lazy // 解决循环依赖
    private BpmOAReimbursementService reimbursementService;

    @Resource
    @Lazy // 解决循环依赖
    private BpmOAImprestService imprestService;

    @Override
    public BpmProcessInstancePrintDataRespVO getOAReportPrintData(BpmProcessInstancePrintDataReqVO reqVO) {

        BpmProcessInstancePrintDataRespVO bpmProcessInstancePrintDataRespVO = new BpmProcessInstancePrintDataRespVO() ;
        String key = reqVO.getKey() ;                               //流程标识
        String processInstanceId = reqVO.getId() ;                  //流程实例ID
        bpmProcessInstancePrintDataRespVO.setId(processInstanceId) ;
        bpmProcessInstancePrintDataRespVO.setKey(key) ;

        BpmProcessInstanceRespVO bpmProcessInstance = getProcessInstanceVO(processInstanceId) ;
        Long businessKey = Long.valueOf(bpmProcessInstance.getBusinessKey());  //流程业务表-主键ID
        if("oa_reimbursement".equals(key)) { //报销流程
            BpmOAReimbursementDO reimbursement = reimbursementService.getReimbursement(businessKey);
            BpmOAReimbursementRespVO bpmOAReimbursementRespVO = reimbursementService.convert(reimbursement); //报销业务数据
            List<BpmTaskRespVO> taskRespVOList = taskService.getTaskListByProcessInstanceId(processInstanceId) ;           //报销审核人数据
            //给User添加签名地址
            taskRespVOList.forEach(taskRespVO ->
                    taskRespVO.getAssigneeUser().setSignURL(
                            fileApi.getUserSignImgPath(
                               taskRespVO.getAssigneeUser().getId()
                            ).getData()
                    )
            );

            //备用金信息查询
            BpmOAImprestDO bpmOAImprestDO = imprestService.getImprestByReimbursementId(reimbursement.getId());
            if (bpmOAImprestDO != null) {

                //设置备用金 金额
                bpmOAReimbursementRespVO.setAmount(bpmOAImprestDO.getAmount());
            }

            //获取流程抄送用户编号
            BpmProcessInstanceExtDO processInstanceExtDO = getProcessInstanceDO(processInstanceId);

            //获取流程抄送用户信息
            List<Long> ccUserIds = new ArrayList<>();
            if (processInstanceExtDO.getCcids() != null && !processInstanceExtDO.getCcids().isEmpty()) {

                Pattern pattern = Pattern.compile("\\[(\\d+)]");
                Matcher matcher = pattern.matcher(processInstanceExtDO.getCcids());
                while (matcher.find()) {

                    ccUserIds.add(Long.parseLong(matcher.group(1)));
                }
            }
            List<AdminUserRespDTO> userRespDTOS = adminUserApi.getUserList(ccUserIds).getCheckedData();

            //获取抄送用户部门信息
            Map<Long, DeptRespDTO> deptMapDTO = deptApi.getDeptMap(convertSet(userRespDTOS, AdminUserRespDTO::getDeptId));

            //拼接数据
            List<BpmOAReimbursementPrintDataRespVO.CCUser> ccUsers = userRespDTOS.stream().map(user -> {
                BpmOAReimbursementPrintDataRespVO.CCUser cc = new BpmOAReimbursementPrintDataRespVO.CCUser();
                cc.setCcUserId(user.getId());
                cc.setCcUserName(user.getNickname());
                cc.setCcDeptName(deptMapDTO.get(user.getDeptId()).getName());
                return cc;
            }).collect(Collectors.toList());

            BpmOAReimbursementPrintDataRespVO reimbursementPrintData = new BpmOAReimbursementPrintDataRespVO() ;
            reimbursementPrintData.setBpmOAReimbursementRespVO(bpmOAReimbursementRespVO) ;
            reimbursementPrintData.setProcessTasks(taskRespVOList);
            reimbursementPrintData.setCcUsers(ccUsers);

            bpmProcessInstancePrintDataRespVO.setBpmOAReimbursementPrintDataRespVO(reimbursementPrintData) ;
        }
        return bpmProcessInstancePrintDataRespVO ;
    }
}
