/**
 * Copyright &copy; 2021-2026 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
 */
package com.jeeplus.flowable.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jeeplus.centerservice.service.ccpm.flow.FlowRequest;
import com.jeeplus.centerservice.utils.ConvertServiceUtil;
import com.jeeplus.centerservice.utils.Global;
import com.jeeplus.centerservice.utils.RestTemplateService;
import com.jeeplus.common.TokenProvider;
import com.jeeplus.common.constant.CacheNames;
import com.jeeplus.common.redis.RedisUtils;
import com.jeeplus.extension.domain.NodeSetting;
import com.jeeplus.extension.service.NodeSettingService;
import com.jeeplus.flowable.model.ActRuTaskInfo;
import com.jeeplus.flowable.model.Flow;
import com.jeeplus.flowable.model.MyNoticeList;
import com.jeeplus.flowable.model.TaskComment;
import com.jeeplus.flowable.service.FlowTaskService;
import com.jeeplus.flowable.service.MyNoticeService;
import com.jeeplus.flowable.vo.HisTaskVo;
import com.jeeplus.flowable.vo.ProcessVo;
import com.jeeplus.mail.feign.IMailApi;
import com.jeeplus.sys.feign.IUserApi;
import com.jeeplus.sys.service.dto.UserDTO;
import com.jeeplus.weChat.util.HttpUtils;
import com.jeeplus.weChat.util.SendMessageUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 流程个人任务相关Controller
 *
 * @author jeeplus
 * @version 2021-09-03
 */
@RestController
@RequestMapping("/flowable/task")
@Log4j2
public class FlowableTaskController {

    @Autowired
    private MyNoticeService noticeServicel;

    @Autowired
    private FlowTaskService flowTaskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;


    @Autowired
    private TaskService taskService;

    @Autowired
    private NodeSettingService nodeSettingService;

    @Autowired
    private IUserApi userApi;

    @Autowired
    private IMailApi mailApi;

    @Resource
    private RestTemplateService restTemplateService;

    @Resource
    private FlowRequest flowRequest;
    @Autowired
    private RedisUtils redisUtils;

    @Value("${wechat.projecturl}")
    private String projectUrl;

    @GetMapping("todo")
    public ResponseEntity todoListData(Page<ProcessVo> page, Flow flow, String type) throws Exception {
//        page = flowTaskService.todoList ( page, flow, type );
//        return ResponseEntity.ok ( page );
        //获取当前登陆人的信息
        UserDTO currentUserDTO = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        String newCode = null;

        //判定如果当前登陆人是否可以查看其他服务的待办信息
        if ("1".equals(currentUserDTO.getOtherServiceFlag())) {
                // redis查询是否成功连接ccpm
                newCode = tyrUpdateCode(flow,"ccpm");
                if ("1".equals(newCode)) {
                    // 中台数据查询
                    List<Map<String, Object>> cpa = queryTodoListCenter(page, flow, "cpa", Global.getConfig("INQUIRE_STATUS"), type);
                    if (cpa.size() > 0) {
                        List<ProcessVo> list = JSON.parseArray(JSON.toJSONString(cpa), ProcessVo.class);
                        List<ProcessVo> processVos = ConvertServiceUtil.convertProcessVoList(list);
                        // 将整合后的结果排序后分页
                        Page sortAndPaging = ConvertServiceUtil.getSortAndPaging(page, processVos);
                        return ResponseEntity.ok(sortAndPaging);
                    } else {
                        // 如果中台没有返回任何数据，就去查询本系统分页待办数据
                        Page<ProcessVo> pageList = flowTaskService.todoList(page, flow, type, currentUserDTO.getId());
                        return ResponseEntity.ok(pageList);
                    }
                }else {
                    tyrUpdateCode(flow,"ccpm");
                    return todoListData(page,flow,type);
                }
        } else {
            // 查询本系统分页待办数据
            Page<ProcessVo> pageList = flowTaskService.todoList(page, flow, type, currentUserDTO.getId());
            return ResponseEntity.ok(pageList);
        }
    }

    private static final int MAX_RETRY_ATTEMPTS = 5; //最大自动连接次数

    public String tyrUpdateCode(Flow flow,String belongProject){
        String code =(String) RedisUtils.getInstance().get(CacheNames.CCPM_IS_CONNECT, "ccpm");
        UserDTO currentUserDTO = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        //code不为空时说明已经存在,则去判断上次是否连接成功
        if (StringUtils.isNotBlank(code)){
            if (!"1".equals(code)){
                //初始次数
                AtomicInteger attempt = new AtomicInteger(0);
                while (attempt.get() < MAX_RETRY_ATTEMPTS) {
                        Object response = flowRequest.getNotifyList(flow, belongProject, currentUserDTO);
                        if (Objects.nonNull(response)) {
                            RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "1");
                            return "1";
                        }
                        // 连接失败，增加尝试次数
                        attempt.incrementAndGet();
                        System.err.println("数据台连接失败，正在重试...（尝试次数：" + attempt.get() + "/" + MAX_RETRY_ATTEMPTS + ")");
                }
                triggerScheduledTaskToCheckConnection(flow, belongProject);
            }else {
                code =(String) RedisUtils.getInstance().get(CacheNames.CCPM_IS_CONNECT, "ccpm");
            }
        }else {
            //为空则说明第一次连接,进行存值
            Object response = flowRequest.getNotifyList(flow, belongProject, currentUserDTO);
            if (Objects.nonNull(response)) {
                RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "1");
                return "1";
            }else {
                RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "0");
            }
        }

        return code;
    }

    /**
     * 每隔五秒进行尝试连接ccpm
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void triggerScheduledTaskToCheckConnection(Flow flow,String belongProject){
        //获取redis中存的值
        String code =(String) RedisUtils.getInstance().get(CacheNames.CCPM_IS_CONNECT, "ccpm");

        UserDTO currentUserDTO = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        //判定如果当前登陆人是否可以查看其他服务的待办信息
        if ("1".equals(currentUserDTO.getOtherServiceFlag())) {
            if (StringUtils.isNotBlank(code)) {
                if (!"1".equals(code)) {
                    Object response = flowRequest.getNotifyList(flow, belongProject, currentUserDTO);
                    if (Objects.nonNull(response)) {
                        RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "1");
                    }
                }
            } else {
                //为空则说明第一次连接,进行存值
                Object response = flowRequest.getNotifyList(flow, belongProject, currentUserDTO);
                if (Objects.nonNull(response)) {
                    RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "1");
                } else {
                    RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "0");
                }
            }
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        System.out.println("定时查询:"+ sdf.format(new Date()));
    }

    /**
     * 查询待办信息后分发给所属系统
     *
     * @param flow
     * @param belongProject 所属系统
     * @param inquireStatus 各服务引用状态 =》 （若包含，则进行获取该服务的流程信息）
     * @return
     * @throws Exception
     */
    @GetMapping("queryTodoListCenter")
    public List<Map<String, Object>> queryTodoListCenter(Page<ProcessVo> page, Flow flow, String belongProject, String inquireStatus, String type) throws Exception {
        List<Map<String, Object>> result = new ArrayList<>();
        List<ProcessVo> processVoList = new ArrayList<>();
        // 任务数量（线程池的线程数量、计数器的值）
        int taskVolume = 2;
        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(taskVolume);
        // 创建单次计数器（事实上就是创建多个锁，每次.countDown()会释放一把锁，使用.await()就是等待全部锁被释放，所以这个计数器是线程安全的）
        CountDownLatch countDownLatch = new CountDownLatch(taskVolume);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        UserDTO currentUserDTO = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        // cpa线程任务
        Future<?> cpa = executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("查询cpa待办开始" + sdf.format(new Date()));
                try {
                    // cpa系统待办数据查询
                    if (inquireStatus.contains("cpa") || ("cpa").equals(belongProject)) {
                        Page<ProcessVo> pageList = flowTaskService.todoList(page, flow, type, currentUserDTO.getId());
                        pageList.getRecords().stream().forEach(item -> {
                            item.setBelongProject("cpa");
                            // 查询审核人
                            if (Objects.nonNull(item.getTask()) && StringUtils.isNotBlank(item.getTask().getAssignee())) {
                                UserDTO userDTO = SpringUtil.getBean(IUserApi.class).getById(item.getTask().getAssignee());
                                if (Objects.nonNull(userDTO) && StringUtils.isNotBlank(userDTO.getName())) {
                                    item.getTask().setAssignee(userDTO.getName());
                                }
                            }
                        });
                        List<ProcessVo> processVos = pageList.getRecords();
                        processVoList.addAll(processVos);
                    }
                } catch (Exception e) {
                    System.out.println("cpa系统待办查询失败");
                    e.printStackTrace();
                }
                // 当前线程执行完毕，计数器 -1
                countDownLatch.countDown();
                System.out.println("查询cpa待办结束" + sdf.format(new Date()));
            }
        });
        // ccpm线程任务
        Future<?> ccpm = executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("查询ccpm待办开始" + sdf.format(new Date()));
                try {
                    // ccpm系统待办数据查询
                    if (inquireStatus.contains("ccpm") || ("ccpm").equals(belongProject)) {
                        // 访问ccpm
                        Object response = flowRequest.getNotifyList(flow, belongProject, currentUserDTO);
                        if (Objects.nonNull(response)) {
                            RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "1");
                            List<Map<String, Object>> res = (List) JSONObject.parseArray(JSON.toJSONString(response));
                            if (("ccpm").equals(belongProject)) { // 如果是ccpm系统查询，则直接返回
                                result.addAll(res);
                            } else { // 如果是其他系统查询，则将结果格式化后返回
                                if (CollectionUtil.isNotEmpty(res)) {
                                    List<ProcessVo> processVos = JSON.parseArray(JSON.toJSONString(res), ProcessVo.class);
                                    processVoList.addAll(processVos);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    System.out.println("ccpm系统待办查询失败");
                    RedisUtils.getInstance().set(CacheNames.CCPM_IS_CONNECT, "ccpm", "0");
                    e.printStackTrace();
                }
                // 当前线程执行完毕，计数器 -1
                countDownLatch.countDown();
                System.out.println("查询ccpm待办结束" + sdf.format(new Date()));
            }
        });
        // 阻塞 等待线程池任务执行完后再执行后面代码
        // 设置最大等待时间 5秒
        countDownLatch.await(5, TimeUnit.SECONDS);
        // 关闭线程池
        executorService.shutdown();
        // 根据不同系统返回不同字段值
        if ("cpa".equals(belongProject)) {
            result.addAll((List) JSON.parseArray(JSON.toJSONString(processVoList)));
        } else if ("ccpm".equals(belongProject)) {
            result.addAll(ConvertServiceUtil.copyNotifyListToCcpm(processVoList));
        }
        return result;
    }

    @GetMapping("allTodo")
    public ResponseEntity allTodo(Flow flow, String type) {
        UserDTO currentUserDTO = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        Page<ProcessVo> pageList = flowTaskService.todoList(new Page<>(1, -1), flow, type, currentUserDTO.getId());
        pageList.getRecords().stream().forEach(item -> {
            // 查询审核人
            if (Objects.nonNull(item.getTask()) && StringUtils.isNotBlank(item.getTask().getAssignee())) {
                UserDTO userDTO = SpringUtil.getBean(IUserApi.class).getById(item.getTask().getAssignee());
                if (Objects.nonNull(userDTO) && StringUtils.isNotBlank(userDTO.getName())) {
                    item.getTask().setAssignee(userDTO.getName());
                }
            }
        });
        return ResponseEntity.ok(pageList.getRecords());
    }

    /**
     * 获取已办任务
     *
     * @return
     */
    @GetMapping("historic")
    public ResponseEntity historicListData(Page<HisTaskVo> page, Flow flow, String type) {
        page = flowTaskService.historicList(page, flow, type);
        return ResponseEntity.ok(page);
    }

    /**
     * 获取我的申请列表
     *
     * @return
     */
    @GetMapping("myApplyed")
    public ResponseEntity myApplyedListData(Page<ProcessVo> page, Flow flow, String type) throws Exception {
        page = flowTaskService.getMyStartedProcIns(userApi.getByToken(TokenProvider.getCurrentToken()), page, flow, type);
        return ResponseEntity.ok(page);
    }


    /**
     * 获取任务流转历史列表
     */
    @GetMapping("historicTaskList")
    public ResponseEntity historicTaskList(Flow flow) throws Exception {
        List<Flow> historicTaskList = flowTaskService.historicTaskList(flow.getProcInsId());
        return ResponseEntity.ok(historicTaskList);
    }


    /**
     * 获取流程表单
     */
    @GetMapping("getTaskDef")
    public ResponseEntity getTaskDef(Flow flow) {
        // 获取流程XML上的表单KEY
        String formKey = flowTaskService.getFormKey(flow.getProcDefId(), flow.getTaskDefKey());

        NodeSetting typeNode = nodeSettingService.queryByKey(flow.getProcDefKey(), flow.getTaskDefKey(), "formType");
        NodeSetting ReadOnlyNode = nodeSettingService.queryByKey(flow.getProcDefKey(), flow.getTaskDefKey(), "formReadOnly");
        String formType = "1";
        boolean formReadOnly = false;
        if (typeNode != null) {
            formType = typeNode.getValue();
            formReadOnly = "true".equals(ReadOnlyNode.getValue());
        } else {
            if (StringUtils.isBlank(formKey)) {
                formType = "1";
            } else if (formKey.indexOf("/") >= 0) {
                formType = "2";
            }
        }

        // 获取流程实例对象
        if (flow.getProcInsId() != null) {
            if (flowTaskService.getProcIns(flow.getProcInsId()) != null) {
                flow.setProcIns(flowTaskService.getProcIns(flow.getProcInsId()));
            } else {
                flow.setFinishedProcIns(flowTaskService.getFinishedProcIns(flow.getProcInsId()));
            }
        }

        flow.setFormUrl(formKey);
        flow.setFormReadOnly(formReadOnly);
        flow.setFormType(formType);
        return ResponseEntity.ok(flow);
    }


    /**
     * 启动流程
     */
    @PostMapping("start")
    public ResponseEntity start(@RequestBody Flow flow) throws UnsupportedEncodingException {
        String procInsId = flowTaskService.startProcess(flow.getProcDefKey(), flow.getBusinessTable(), flow.getBusinessId(), flow.getTitle(), flow.getRecordType(), flow.getProcDefId(), flow.getDays());

        //指定下一步处理人
        if (StringUtils.isNotBlank(flow.getAssignee())) {
            Task task = taskService.createTaskQuery().processInstanceId(procInsId).active().singleResult();
            if (task != null) {
                String[] split = flow.getAssignee().split(",");
                if (split.length == 1) {
                    taskService.setAssignee(task.getId(), flow.getAssignee());
                } else {
                    for (String i : split) {
                        taskService.addCandidateUser(task.getId(), i);
                    }
                }
            }
        }
        sendMsgToOpenIds(procInsId);
        return ResponseEntity.ok(procInsId);
    }

    /**
     * 启动流程
     */
    @PostMapping("startForFen")
    public String startForFen(@RequestBody Map<String, String> map) {

        String procDefKey = map.get("procDefKey");
        String procDefId = map.get("procDefId");
        String businessTable = map.get("businessTable");
        String businessId = map.get("businessId");
        String title = map.get("title");
        String recordType = map.get("recordType");
        String assignee = map.get("assignee");
        String userId = map.get("userId");

        if (StrUtil.isBlank(procDefKey) && StrUtil.isNotBlank(procDefId)) {
            procDefKey = StrUtil.split(procDefId, ":").get(0);
        }

        String procInsId = flowTaskService.startProcessForFen(procDefKey, businessTable, businessId, title, recordType, procDefId, userId);

        //指定下一步处理人
        if (StringUtils.isNotBlank(assignee)) {
            Task task = taskService.createTaskQuery().processInstanceId(procInsId).active().singleResult();
            if (task != null) {
                String[] split = assignee.split(",");
                if (split.length == 1) {
                    taskService.setAssignee(task.getId(), assignee);
                } else {
                    for (String i : split) {
                        taskService.addCandidateUser(task.getId(), i);
                    }
                }
            }
        }
        return procInsId;
    }

    /**
     * 启动流程
     */
    @PostMapping("startForFenNew")
    public Map<String, String> startForFenNew(@RequestBody Map<String, Map<String, String>> map) {

        Map<String, String> map1 = new HashMap();

        for (Map.Entry<String, Map<String, String>> entry : map.entrySet()) {
            String key = entry.getKey();
            if (key.equals("底稿")) {
                String procInsId = getProcInsId(entry);
                map1.put("底稿", procInsId);
            } else if (key.equals("电脑")) {
                String procInsId = getProcInsId(entry);
                map1.put("电脑", procInsId);
            } else if (key.equals("应收账款")) {
                String procInsId = getProcInsId(entry);
                map1.put("应收账款", procInsId);
            } else if (key.equals("社保欠款")) {
                String procInsId = getProcInsId(entry);
                map1.put("社保欠款", procInsId);
            } else if (key.equals("备用金")) {
                String procInsId = getProcInsId(entry);
                map1.put("备用金", procInsId);
            } else if (key.equals("实习")) {
                String procInsId = getProcInsId(entry);
                map1.put("实习", procInsId);
            } else if (key.equals("入职")) {
                String procInsId = getProcInsId(entry);
                map1.put("入职", procInsId);
            } else if (key.equals("完善个人信息")) {
                String procInsId = getProcInsId(entry);
                map1.put("完善个人信息", procInsId);
            } else if (key.equals("员工信息审批")) {
                String procInsId = getProcInsId(entry);
                map1.put("员工信息审批", procInsId);
            }else if (key.equals("折扣结账")) {
                String procInsId = getProcInsId(entry);
                map1.put("折扣结账", procInsId);
            }
        }
        return map1;
    }

    public String getProcInsId(Map.Entry<String, Map<String, String>> entry) {
        Map<String, String> valueMap = entry.getValue();
        String procDefKey = valueMap.get("procDefKey");
        String procDefId = valueMap.get("procDefId");
        String businessTable = valueMap.get("businessTable");
        String businessId = valueMap.get("businessId");
        String title = valueMap.get("title");
        String recordType = valueMap.get("recordType");
        String assignee = valueMap.get("assignee");
        String userId = valueMap.get("userId");
        if (StrUtil.isBlank(procDefKey) && StrUtil.isNotBlank(procDefId)) {
            procDefKey = StrUtil.split(procDefId, ":").get(0);
        }

        String procInsId = flowTaskService.startProcessForFen(procDefKey, businessTable, businessId, title, recordType, procDefId, userId);

        //指定下一步处理人
        if (StringUtils.isNotBlank(assignee)) {
            Task task = taskService.createTaskQuery().processInstanceId(procInsId).active().singleResult();
            if (task != null) {
                String[] split = assignee.split(",");
                if (split.length == 1) {
                    taskService.setAssignee(task.getId(), assignee);
                } else {
                    for (String i : split) {
                        taskService.addCandidateUser(task.getId(), i);
                    }
                }
            }
        }
        return procInsId;
    }

    /**
     * 设置下一节点审核人
     */
    @PostMapping("updateAssignee")
    public String updateAssignee(@RequestBody Map<String, String> map) {

        String procInsId = map.get("procInsId");
        String assignee = map.get("assignee");

        //指定下一步处理人
        if (StringUtils.isNotBlank(assignee)) {
            Task task = taskService.createTaskQuery().processInstanceId(procInsId).active().singleResult();
            if (task != null) {
                String[] split = assignee.split(",");
                if (split.length == 1) {
                    taskService.setAssignee(task.getId(), assignee);
                } else {
                    for (String i : split) {
                        taskService.addCandidateUser(task.getId(), i);
                    }
                }
            }
        }
        return procInsId;
    }

    /**
     * 签收任务
     */
    @PostMapping("claim")
    public ResponseEntity claim(@RequestBody Flow flow) {
        String userId = userApi.getByToken(TokenProvider.getCurrentToken()).getId();//ObjectUtils.toString(UserUtils.getUser().getId());
        flowTaskService.claim(flow.getTaskId(), userId);
        return ResponseEntity.ok("签收成功!");
    }

    /**
     * 完成任务
     */
    @PostMapping(value = "complete")
    public ResponseEntity complete(@RequestBody Flow flow) {
        flowTaskService.complete(flow, flow.getVars().getVariableMap());
        return ResponseEntity.ok("完成任务!");
    }

    /**
     * 读取流程历史数据，用于渲染流程图
     */
    @GetMapping("getFlowChart")
    public Map getFlowChart(String processInstanceId) throws Exception {
        return flowTaskService.getDiagram(processInstanceId);
    }

    /**
     * 删除任务
     *
     * @param taskId 流程实例ID
     * @param reason 删除原因
     */
    @DeleteMapping("deleteTask")
    public ResponseEntity deleteTask(String taskId, String reason) {
        if (StringUtils.isBlank(reason)) {
            return ResponseEntity.badRequest().body("请填写删除原因");
        } else {
            flowTaskService.deleteTask(taskId, reason);
            return ResponseEntity.ok("删除任务成功，任务ID=" + taskId);
        }
    }

    /**
     * 加签
     */
    @PostMapping("addSignTask")
    public ResponseEntity addSignTask(String taskId, String userIds, String comment, Boolean flag) throws Exception {
        flowTaskService.addSignTask(taskId, Arrays.asList(userIds.split(",")), comment, flag);
        return ResponseEntity.ok("加签成功!");
    }

    ;


    /**
     * 审批
     *
     * @param flow
     */
    @PostMapping("audit")
    public ResponseEntity auditTask(HttpServletRequest request, Flow flow) throws UnsupportedEncodingException {
        Map<String, Object> vars = Maps.newHashMap();
        Map<String, String[]> map = request.getParameterMap();
        for (Map.Entry<String, String[]> entry : map.entrySet()) {
            if (entry.getKey().startsWith("vars.")) {
                String key = entry.getKey().substring(5);
                String value = entry.getValue()[0];
                if("agree".equals(key)){
                    if ("true".equals(value) || "false".equals(value)) {
                        vars.put(key, Boolean.valueOf(value).booleanValue());
                    } else {
                        vars.put(key, value);
                    }
                }

            }
        }

        flowTaskService.auditSave(flow, vars);
        //指定下一步处理人
        if (StringUtils.isNotBlank(flow.getAssignee())) {
            Task task = taskService.createTaskQuery().processInstanceId(flow.getProcInsId()).active().singleResult();
            if (task != null) {
                taskService.setAssignee(task.getId(), flow.getAssignee());
            }
        }
        sendMsgToOpenIds(flow.getProcInsId());
        return ResponseEntity.ok(flow.getProcInsId());
    }

    /**
     * 提交批量审核任务。接口只负责接收任务并立即返回，具体审核在后台线程执行。
     */
    @PostMapping("batch-audit-jobs")
    public ResponseEntity batchAudit(HttpServletRequest request, @RequestBody Map<String, Object> body) {
        try {
            Map<String, String> result = flowTaskService.submitBatchAudit(
                    body,
                    TokenProvider.getCurrentToken(),
                    request.getHeader("domain")
            );
            return ResponseEntity.status(HttpStatus.ACCEPTED).body(result);
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }

    /**
     * 查询批量审核进度。
     */
    @GetMapping("batch-audit-jobs/{jobId}")
    public ResponseEntity batchAuditProgress(@PathVariable String jobId) {
        Map<String, Object> progress = flowTaskService.getBatchAuditProgress(jobId);
        if (progress == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(progress);
    }

    @GetMapping("getTaskAuditUsersForApp")
    public List<String> getTaskAuditUsersForApp(String procInsId) {
        TaskQuery todoTaskQuery = taskService.createTaskQuery()
                .active()
                .processInstanceId(procInsId) // 添加过滤条件
                .includeProcessVariables()
                .orderByTaskCreateTime()
                .desc();

        List<Task> list = todoTaskQuery.list();

        List<String> users = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            users.addAll(flowTaskService.getTaskAuditUsers(list.get(i).getId()));
        }

        return users;
    }

    // 向审核人发送 公众号通知
    public void sendMsgToOpenIds(String procInsId) throws UnsupportedEncodingException {

        TaskQuery todoTaskQuery = taskService.createTaskQuery()
                .active()
                .processInstanceId(procInsId) // 添加过滤条件
                .includeProcessVariables()
                .orderByTaskCreateTime()
                .desc();

        StringBuilder allUsersStringBuilder = new StringBuilder();
        List<Task> list = todoTaskQuery.list();
        for (int i = 0; i < list.size(); i++) {
            List<String> users = flowTaskService.getTaskAuditUsers(list.get(i).getId());
            // 根据得到的审核人id，查询出对应的openId
            for (String user : users) {
                if (allUsersStringBuilder.length() > 0) {
                    allUsersStringBuilder.append(","); // 添加逗号作为分隔符
                }
                allUsersStringBuilder.append(user);
            }
            String userIds = allUsersStringBuilder.toString();

            String openIds = SpringUtil.getBean(IUserApi.class).getOpenIdsByIds(userIds);

            String[] openIdArray = openIds.split(",");
            List<String> openIdsList = Arrays.asList(openIdArray);

            Flow flow = new Flow();
            String procDefId = list.get(i).getProcessDefinitionId();
            String taskDefKey = list.get(i).getTaskDefinitionKey();
//            String procInsIdFromTask = list.get(i).getProcessInstanceId();
            String procDefKey = SpringUtil.getBean(RepositoryService.class).createProcessDefinitionQuery()
                    .processDefinitionId(procDefId)
                    .singleResult()
                    .getKey();

            flow.setProcDefId(procDefId);
            flow.setTaskDefKey(taskDefKey);
            flow.setProcInsId(procInsId);
            flow.setProcDefKey(procDefKey);
            flow = getFlowInfo(flow);
            //如果下一节点审核人数量大于0，则进行发送审核微信公众号通知
            if(openIdsList.size()>0){
                SendMessageUtil.sendMessage(list.get(i), openIdsList, flow, projectUrl);
            }

        }
    }

    public Flow getFlowInfo(Flow flow){
        // 获取流程XML上的表单KEY
        String formKey = flowTaskService.getFormKey(flow.getProcDefId(), flow.getTaskDefKey());

        NodeSetting typeNode = nodeSettingService.queryByKey(flow.getProcDefKey(), flow.getTaskDefKey(), "formType");
        NodeSetting ReadOnlyNode = nodeSettingService.queryByKey(flow.getProcDefKey(), flow.getTaskDefKey(), "formReadOnly");
        String formType = "1";
        boolean formReadOnly = false;
        if (typeNode != null) {
            formType = typeNode.getValue();
            formReadOnly = "true".equals(ReadOnlyNode.getValue());
        } else {
            if (StringUtils.isBlank(formKey)) {
                formType = "1";
            } else if (formKey.indexOf("/") >= 0) {
                formType = "2";
            }
        }

        // 获取流程实例对象
        if (flow.getProcInsId() != null) {
            if (flowTaskService.getProcIns(flow.getProcInsId()) != null) {
                flow.setProcIns(flowTaskService.getProcIns(flow.getProcInsId()));
            } else {
                flow.setFinishedProcIns(flowTaskService.getFinishedProcIns(flow.getProcInsId()));
            }
        }

        flow.setFormUrl(formKey);
        flow.setFormReadOnly(formReadOnly);
        flow.setFormType(formType);
        return flow;
    }

    /**
     * 取回已经执行的任务，只有在下一任务节点未执行或者未签收时才能取回
     */
    @RequestMapping("callback")
    public ResponseEntity callback(@Param("preTaskId") String preTaskId,
                                   @Param("currentTaskId") String currentTaskId,
                                   @Param("processInstanceId") String processInstanceId,
                                   @Param("preTaskDefKey") String preTaskDefKey,
                                   @Param("currentTaskDefKey") String currentTaskDefKey) {
        try {
            // 取得流程实例
            ProcessInstance instance = runtimeService
                    .createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            if (instance == null) {
                return ResponseEntity.badRequest().body("流程已经结束");
            }

            //在已办任务列表中清除该任务信息
            historyService.deleteHistoricTaskInstance(preTaskId);

            List currTasks = Lists.newArrayList();
            currTasks.add(currentTaskDefKey);
            //回退到上一节点
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(instance.getId())
                    .moveActivityIdsToSingleActivityId(currTasks, preTaskDefKey).changeState();
            historyService.deleteHistoricTaskInstance(currentTaskId);
            return ResponseEntity.ok("取回成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.badRequest().body("流程取回失败，未知错误.");
        }
    }


    /**
     * 委托任务
     *
     * @param taskId 任务ID
     */
    @RequestMapping("delegate")
    public ResponseEntity delegate(String taskId, String userId) {
        if (StringUtils.isBlank(taskId) || StringUtils.isBlank(userId)) {
            return ResponseEntity.badRequest().body("参数异常");
        }
        taskService.setOwner(taskId, userApi.getByToken(TokenProvider.getCurrentToken()).getId());// 委托人为任务的拥有者
        taskService.delegateTask(taskId, userId);
        return ResponseEntity.ok("委托成功");
    }

    /**
     * 取消签收任务
     *
     * @param taskId 任务ID
     */
    @RequestMapping("unclaim")
    public ResponseEntity unclaim(String taskId) {
        taskService.unclaim(taskId);
        return ResponseEntity.ok("取消签收成功");
    }

    /**
     * 转派任务
     *
     * @param taskId 任务ID
     * @param userId 接收人
     */
    @PutMapping("transfer")
    public ResponseEntity transferTask(String taskId, String userId) {
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(taskId)) {
            return ResponseEntity.badRequest().body("转派失败, 参数异常");
        }
        // 设置当前流程任务办理人
        Authentication.setAuthenticatedUserId(userApi.getByToken(TokenProvider.getCurrentToken()).getId());
        taskService.setAssignee(taskId, userId);
        return ResponseEntity.ok("转派成功!");
    }

    /**
     * 获取可退回任务节点
     *
     * @param taskId
     * @return
     */
    @RequestMapping(value = "/backNodes")
    public ResponseEntity backNodes(@RequestParam String taskId) {
        List<Flow> nodes = flowTaskService.getBackNodes(taskId);
        return ResponseEntity.ok(nodes);
    }

    /**
     * 驳回任务到指定节点
     */
    @RequestMapping(value = "/back")
    public ResponseEntity back(String backTaskDefKey, String taskId, TaskComment comment) {
        String procInsId = flowTaskService.backTask(backTaskDefKey, taskId, comment);
        return ResponseEntity.ok(procInsId);
    }

    /**
     * 催办任务
     *
     * @param taskId 任务ID
     */
    @PostMapping("urge")
    public ResponseEntity<String> urge(String taskId, String content) {
        List<UserDTO> assignList = Lists.newArrayList();
        taskService.getIdentityLinksForTask(taskId).forEach(identityLink -> {
            assignList.add(userApi.getById(identityLink.getUserId()));
        });

        mailApi.sendEmail(assignList, "你收到一条流程催办任务!", content + "<p><a href=\"/#/flowable/task/TodoList\" target=\"_blank\">点击处理</a><br></p>");
        return ResponseEntity.ok("催办成功!");
    }

    @GetMapping("getTaskAuditUsers")
    public List<String> getTaskAuditUsers(String taskId) {
        return flowTaskService.getTaskAuditUsers(taskId);
    }

    @GetMapping("getTaskAuditUsersByprocInstId")
    public  List<String> getTaskAuditUsersByprocInstId(String procInsId) {
        return flowTaskService.getTaskAuditUsersByprocInstId(procInsId);
    }




    @GetMapping("getCurrentTaskName")
    public String getCurrentTaskName(String processInstanceId) {
        return flowTaskService.getCurrentTaskName(processInstanceId);
    }

    @GetMapping("auditByProcInsIdAndFlag")
    public void auditByProcInsIdAndFlag(String procInsId, String flag, String assignee, String comm) throws Exception {
        flowTaskService.auditByProcInsIdAndFlag(procInsId, flag, assignee, comm);
    }


    @GetMapping("checkIsLastTask")
    public boolean checkIsLastTask(String procInsId) {
        return flowTaskService.checkIsLastTask(procInsId);
    }

    /**
     * 获取流程表单
     */
    @GetMapping("getTaskDefInfo")
    public ResponseEntity getTaskDefInfo(Flow flow) {
        if (StringUtils.isNotBlank(flow.getTaskId()) && StringUtils.isBlank(flow.getTaskName()) && StringUtils.isBlank(flow.getTaskDefKey())) {
            ActRuTaskInfo taskInfoByTaskId = flowTaskService.getTaskInfoByTaskId(flow.getTaskId());
            if (null != taskInfoByTaskId) {
                flow.setTaskId(taskInfoByTaskId.getId());
                flow.setProcInsId(taskInfoByTaskId.getProcInstId());
                flow.setTaskName(taskInfoByTaskId.getName());
                flow.setTaskDefKey(taskInfoByTaskId.getTaskDefKey());
            }
            //当前任务信息
            Task task = taskService.createTaskQuery().processInstanceId(flow.getProcInsId()).active().singleResult();
            if (null != task) {
                if (StringUtils.isNotBlank(task.getProcessDefinitionId())) {
                    flow.setProcDefId(task.getProcessDefinitionId());
                }
                if (null != task.getProcessVariables()) {
                    String title = (String) task.getProcessVariables().get("title");
                    if (StringUtils.isNotBlank(title)) {
                        flow.setTitle(title);
                    } else {
                        flow.setTitle("");
                    }
                }
            }

            Process process = SpringUtil.getBean(RepositoryService.class).getBpmnModel(taskInfoByTaskId.getProcDefId()).getMainProcess();
            if (null != process && StringUtils.isNotBlank(process.getId())) {
                flow.setProcDefKey(process.getId());
            }
        }
        // 获取流程XML上的表单KEY
        String formKey = flowTaskService.getFormKey(flow.getProcDefId(), flow.getTaskDefKey());

        NodeSetting typeNode = nodeSettingService.queryByKey(flow.getProcDefKey(), flow.getTaskDefKey(), "formType");
        NodeSetting ReadOnlyNode = nodeSettingService.queryByKey(flow.getProcDefKey(), flow.getTaskDefKey(), "formReadOnly");
        String formType = "1";
        boolean formReadOnly = false;
        if (typeNode != null) {
            formType = typeNode.getValue();
            formReadOnly = "true".equals(ReadOnlyNode.getValue());
        } else {
            if (StringUtils.isBlank(formKey)) {
                formType = "1";
            } else if (formKey.indexOf("/") >= 0) {
                formType = "2";
            }
        }

        // 获取流程实例对象
        if (flow.getProcInsId() != null) {
            if (flowTaskService.getProcIns(flow.getProcInsId()) != null) {
                flow.setProcIns(flowTaskService.getProcIns(flow.getProcInsId()));
            } else {
                flow.setFinishedProcIns(flowTaskService.getFinishedProcIns(flow.getProcInsId()));
            }
        }

        flow.setFormUrl(formKey);
        flow.setFormReadOnly(formReadOnly);
        flow.setFormType(formType);
        return ResponseEntity.ok(flow);
    }

    /**
     * 根据procInsId获取当前流程节点名称
     *
     * @param procInsId
     * @return
     */
    @GetMapping("/getTaskNameByProcInsId")
    public ResponseEntity<String> getTaskNameByProcInsId(@RequestParam("procInsId") String procInsId) {
        String res = getTaskName(procInsId);
        return ResponseEntity.ok(res);
    }

    /**
     * 根据procInsId获取当前流程节点名称
     *
     * @param procInsId
     * @return
     */
    public String getTaskName(String procInsId) {
        if (StringUtils.isNotBlank(procInsId)) {
            Task task = taskService.createTaskQuery().processInstanceId(procInsId).active().singleResult();
            if (task != null) {
                //获取流程发布Id信息
                String definitionId = runtimeService.createProcessInstanceQuery().processInstanceId(procInsId).singleResult().getProcessDefinitionId();
                //获取bpm对象
                BpmnModel bpmnModel = SpringUtil.getBean(RepositoryService.class).getBpmnModel(definitionId);
                //传节点定义key 获取当前节点
                FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
                if (ObjectUtil.isNotEmpty(flowNode)) {
                    if (StringUtils.isNotBlank(flowNode.getName())) {
                        return flowNode.getName();
                    }
                }
            }
        }
        return "";
    }

    @GetMapping("getTaskIdByprocInstId")
    public String getTaskIdByprocInstId(String procInsId) {
        return flowTaskService.getTaskIdByprocInstId(procInsId);
    }




    /**
     * 获取当前节点信息
     *
     * @param processInstanceId
     * @return
     */
    @GetMapping("getCurrentTask")
    public FlowNode getCurrentTask(String processInstanceId) {
        return flowTaskService.getCurrentTask(processInstanceId);
    }

    /**
     * 获取流转历史任务列表
     *
     * @param procInsId 流程实例
     */
    @PostMapping("historicTaskList2")
    public Map historicTaskList2(String procInsId) throws Exception {
        return flowTaskService.historicTaskList2(procInsId);
    }

    /**
     * 获取流转历史任务列表
     *
     * @param procInsId 流程实例
     */
    @PostMapping("historicTaskList3")
    public Map historicTaskList3(String procInsId) throws Exception {
        return flowTaskService.historicTaskList3(procInsId);
    }

    /**
     * 根据通知信息查询数据
     *
     * @param myNoticeList
     * @return
     */
    @GetMapping("getRepetitionCountBymyNotice")
    public MyNoticeList getRepetitionCountBymyNotice(MyNoticeList myNoticeList) {
        return noticeServicel.getRepetitionCountBymyNotice(myNoticeList);
    }

    /**
     * 根据通知信息查询数据
     *
     * @param myNoticeList
     * @return
     */
    @PostMapping("insertMyNotice")
    public void insertMyNotice(MyNoticeList myNoticeList) {
        noticeServicel.insertMyNotice(myNoticeList);
    }

    /**
     * 根据通知信息查询数据
     *
     * @param myNoticeList
     * @return
     */
    @PostMapping("updateMyNotice")
    public void updateMyNotice(MyNoticeList myNoticeList) {
        noticeServicel.updateMyNotice(myNoticeList);
    }

    /**
     * 保存审核意见
     *
     * @param flow
     */
    @PostMapping("auditSave")
    public void auditSave(Flow flow, Map vars) {
        flowTaskService.auditSave(flow, vars);

    }


    @ApiOperation(value = "新增")
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String add(@RequestBody Map<String, String> map) throws Exception {
        //taskId
        String taskId = map.get("taskId");
        //标题
        String title = map.get("title");
        //流程id
        String defId = map.get("defId");
        //流程名称
        String taskName = map.get("taskName");
        //创建人
        String createUser = map.get("createUser");
        //创建时间
        String createTime = map.get("createTime");
        //通知人名称
        String noticeName = map.get("noticeName");
        //通知人id
        String noticeId = map.get("noticeId");
        //创建人id
        String createById = map.get("createById");
        return noticeServicel.add(taskId, title, defId, taskName, createUser, createTime, noticeName, noticeId, createById);
    }

    @RequestMapping(value = "/taskDispose", method = RequestMethod.POST)
    public String taskDispose(@RequestBody Map<String, String> map) {
        String data = map.get("data");
        String currentToken = map.get("currentToken");
        return flowTaskService.taskDispose(data, currentToken);
    }

    @GetMapping("/list")
    @ApiOperation(value = "待办列表")
    public ResponseEntity noticeList(Page<MyNoticeList> page, String type, String title, String taskName) {
        Page<MyNoticeList> voPage = noticeServicel.noticeList(page, type, title, taskName);
        return ResponseEntity.ok(voPage);
    }

    @ApiOperation(value = "根据通知信息查询数据")
    @RequestMapping(value = "/insertMyNotice2", method = RequestMethod.POST)
    public void insertMyNotice2(@RequestBody Map<String, String> map) throws Exception {
        String myNoticeInfo = map.get("myNoticeInfo");
        MyNoticeList jsonObject = JSON.parseObject(myNoticeInfo, new TypeReference<MyNoticeList>() {
        });
        noticeServicel.insertMyNotice(jsonObject);
    }

    @ApiOperation(value = "根据通知信息查询数据")
    @RequestMapping(value = "/updateMyNotice2", method = RequestMethod.POST)
    public void updateMyNotice2(@RequestBody Map<String, String> map) throws Exception {
        String myNoticeInfo = map.get("myNoticeInfo");
        MyNoticeList jsonObject = JSON.parseObject(myNoticeInfo, new TypeReference<MyNoticeList>() {
        });
        noticeServicel.updateMyNotice(jsonObject);
    }

    @ApiOperation(value = "会计/评估,修改通知数据数据")
    @RequestMapping(value = "/updateTitleMyNotice2", method = RequestMethod.POST)
    public void updateTitleMyNotice2(@RequestBody Map<String, String> map) throws Exception {
        String myNoticeInfo = map.get("myNoticeInfo");
        MyNoticeList jsonObject = JSON.parseObject(myNoticeInfo, new TypeReference<MyNoticeList>() {
        });
        noticeServicel.updateTitleMyNotice2(jsonObject);
    }

    @ApiOperation(value = "根据通知信息查询数据")
    @RequestMapping(value = "/getRepetitionCountBymyNoticeTitle", method = RequestMethod.POST)
    public String getRepetitionCountBymyNoticeTitle(@RequestBody Map<String, String> map) throws Exception {
        String myNoticeInfo = map.get("myNoticeInfo");
        MyNoticeList jsonObject = JSON.parseObject(myNoticeInfo, new TypeReference<MyNoticeList>() {
        });
        MyNoticeList noticeList = noticeServicel.getRepetitionCountBymyNoticeTitle(jsonObject);
        return JSON.toJSONString(noticeList);
    }

    /**
     * 会计/评估超期收款时获取到的通知数量
     * @param map
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "根据通知信息查询数据")
    @RequestMapping(value = "/getCountBymyNoticeTitle", method = RequestMethod.POST)
    public String getCountBymyNoticeTitle(@RequestBody Map<String, String> map) throws Exception {
        String myNoticeInfo = map.get("myNoticeInfo");
        MyNoticeList jsonObject = JSON.parseObject(myNoticeInfo, new TypeReference<MyNoticeList>() {
        });
        MyNoticeList noticeList = noticeServicel.getCountBymyNoticeTitle(jsonObject);
        return JSON.toJSONString(noticeList);
    }

    /**
     * 根据procInsId获取当前流程的表单的提交审核日期（仅限报销流程）
     *
     * @param flow
     * @return
     */
    @GetMapping("/getApprovalTimeByProcInsId")
    public ResponseEntity<String> getApprovalTimeByProcInsId(Flow flow) {
        try {
            if (flow.getProcInsId() != null) {
                if (flowTaskService.getProcIns(flow.getProcInsId()) != null) {
                    flow.setProcIns(flowTaskService.getProcIns(flow.getProcInsId()));
                }
            }
            String res = flowTaskService.getApprovalTimeByProcInsId(flow);
            return ResponseEntity.ok(res);
        } catch (Exception e) {
            return  null;
        }
    }

    /**
     * 根据procInsId获取当前流程的表单的提交审核日期（仅限报销流程）
     *
     * @param flow
     * @return
     */
    @GetMapping("/getCreateTimeByProcInsId")
    public ResponseEntity<String> getCreateTimeByProcInsId(Flow flow) {
        try {
            if (flow.getProcInsId() != null) {
                if (flowTaskService.getProcIns(flow.getProcInsId()) != null) {
                    flow.setProcIns(flowTaskService.getProcIns(flow.getProcInsId()));
                }
            }
            String res = flowTaskService.getCreateTimeByProcInsId(flow);
            return ResponseEntity.ok(res);
        } catch (Exception e) {
            return  null;
        }
    }



}
