package cc.smtweb.system.bpm.web.engine.flow;

import cc.smtweb.framework.core.common.AbstractEnum;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.EntityDao;
import cc.smtweb.framework.core.db.EntityHelper;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.session.UserSession;
import cc.smtweb.framework.core.util.CommUtil;
import cc.smtweb.framework.core.util.DateUtil;
import cc.smtweb.framework.core.util.NumberUtil;
import cc.smtweb.system.bpm.web.design.flow.FlowConst;
import cc.smtweb.system.bpm.web.design.flow.ModelProc;
import cc.smtweb.system.bpm.web.design.flow.ModelProcHelper;
import cc.smtweb.system.bpm.web.design.flow.define.Activity;
import cc.smtweb.system.bpm.web.design.flow.define.ProcInfo;
import cc.smtweb.system.bpm.web.engine.flow.entity.*;
import cc.smtweb.system.bpm.web.sys.user.dept.Dept;
import cc.smtweb.system.bpm.web.sys.user.dept.DeptCache;
import cc.smtweb.system.bpm.web.sys.user.party.Party;
import cc.smtweb.system.bpm.web.sys.user.party.PartyCache;
import cc.smtweb.system.bpm.web.sys.user.userGroup.UserGroupHelper;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;

import java.util.*;

/**
 * Created by Akmm at 2020/3/4 11:35
 * 单据流程实例组件
 */
@Data
public class FlowInstance {
    //会话信息
    private UserSession us;
    //流程定义
    private ModelProc procDef;
    //流程实例
    private ProcInst procInst;

    //活动任务
    private Task actInst;

    //流程执行变量
//    public Map<String, String> variables = new HashMap<>();

    //数据提供者
    private FlowProvider provider = new FlowProvider();

    private FlowListener listener;

    //提交驳回等之后的，属于我的新任务
    private Task nextMyTask;

    public static FlowInstance createById(UserSession us, FlowListener listener, long billId, long taskId) {
        FlowInstance flowInstance = new FlowInstance(listener, us);
        flowInstance.load(billId, taskId);
        return flowInstance;
    }

    public static FlowInstance createByBillType(UserSession us, FlowListener listener, long billType) {
        FlowInstance flowInstance = new FlowInstance(listener, us);
        flowInstance.create(billType);
        return flowInstance;
    }


    private FlowInstance(@NonNull FlowListener listener, UserSession us) {
        this.listener = listener;
        this.us = us;
    }

    //能否编辑
    public boolean canEdit() {
        //新增状态可以编辑
        if (procDef.isNew() || procInst.getStatu() == FlowConst.InstanceStatu.BEGIN.value)
            return true;
        return false;
    }


    /**
     * 流程条件
     *
     * @param expr
     * @return
     */
    public boolean execTrans(String expr) {
        if (StringUtils.isBlank(expr)) return true;
        SwMap params = new SwMap();
        params.putAll(procInst.getData());
        listener.setExprParam(params);
        Object o = NumberUtil.calcExprMapObject(expr, params);
        return o != null && (Boolean) o;
    }

    /**
     * 新建流程实例
     *
     * @param bill_type
     * @throws Exception
     */
    public void create(long bill_type) {
        Party loginParty = PartyCache.getInstance().get(us.getPartyId());
        if (EntityHelper.isEmpty(loginParty)) {
            throw new BizException("当前登录人员管理单位信息为空，请检查后保存！");
        }

        Dept loginDept = DeptCache.getInstance().get(us.getDeptId());
        if (EntityHelper.isEmpty(loginDept)) {
            loginDept = new Dept();
        }

        procDef = ModelProcHelper.getBillProc(bill_type, us.getUserId()); //根据单据分类获取流程定义
        if (EntityHelper.isEmpty(procDef)) {
//            throw new BizException("没有找到对应的流程定义，请到【系统设置】-【单据流程分配】为当前单据类型【" + BillTypeCache.getInstance().getName(bill_type) + "】分配工作流程！");
        }

        Activity start = ModelProcHelper.getFirstActivity(procDef.getEntityId());
        if (start == null) {
            throw new BizException("对应的流程没有定义有效的活动步骤！");
        }

        procInst = new ProcInst();
        procInst.init();
        procInst.setIsNew(true);
        procInst.setEntityId(DbEngine.getInstance().nextId());
        procInst.setPartyId(loginParty.getEntityId());
        procInst.setDeptId(loginDept.getEntityId());
        procInst.setBillType(bill_type);
        procInst.setMakeDate(DateUtil.nowDateTimeLong());
        procInst.setPrcId(procDef.getEntityId());
        procInst.setStartTime(DateUtil.nowDateTimeLong());
        procInst.setUserId(us.getUserId());
        procInst.setStatu(FlowConst.InstanceStatu.BEGIN.value);

        actInst = new Task();
        actInst.init();
        actInst.setEntityId(DbEngine.getInstance().nextId());
        actInst.setId(procInst.getEntityId());
        actInst.setPrcId(procDef.getEntityId());
        actInst.setActId(start.getId());
        actInst.setActName(start.getLabel());
        actInst.setHandler(us.getUserId());
        actInst.setStartTime(DateUtil.nowDateTimeLong());
        actInst.setStatu(FlowConst.ActivityStatu.HANDLE.value);
        actInst.setMake(true);
        actInst.setSign(false);
        actInst.setReject(false);
        actInst.setRetake(false);

        procInst.setTaskId(actInst.getEntityId());
        procInst.setActName(actInst.getActName());
    }

    /**
     * 办理
     *
     * @throws Exception
     */
    public void handle() {
        long user_id = us.getUserId();
        //校验权限，当前人员有权限干不，没权限直接抛异常
//        checkPermission();
        if (actInst.getStatu() != FlowConst.ActivityStatu.WAIT.value) {
            throw new BizException("当前任务非待办状态，不能办理失败！");
        }

        actInst.setHandler(user_id);
        actInst.setStartTime(DateUtil.nowDateTimeLong());
        actInst.setStatu(FlowConst.ActivityStatu.HANDLE.value);

        procInst.setTaskId(actInst.getEntityId());
        procInst.setActName(actInst.getActName());
        procInst.setStatu(FlowConst.InstanceStatu.RUNING.value);

        getTaskDao().updateEntity(actInst);
        getProcInstDao().updateEntity(procInst);

        updateComment(null, FlowConst.Button.HANDLE.value);
        buildBillLog(FlowConst.Button.HANDLE.value, "-", actInst, null);
        listener.afterHandle(actInst);
    }

    //从页面参数加载流程实例
    public void readFromPage(SwMap map) {
        procInst.readFromMap(map);
        actInst.setPriId(procInst.getId());
        actInst.setEntityId(procInst.getTaskId());
    }

    /**
     * 从数据库加载已有流程实例
     *
     * @param bill_id
     * @throws Exception
     */
    public void load(long bill_id, long task_id) {
        procInst = provider.findProcInst(bill_id);
        procDef = ModelProcHelper.getFromCache(procInst.getPrcId());
        actInst = null;
        task_id = getTaskId(task_id);
        if (task_id > 0L && actInst == null) {
            actInst = provider.findTask(task_id);
        }
    }

    //加载审批意见
    public List<Comment> loadComment() {
        return provider.findComment(procInst.getId());
    }

    /**
     * 当前任务处于待办时：
     * 上一步办理人进来，看上一个步骤，否则看当前步骤
     *
     * @param task_id
     * @return
     */
    private long getTaskId(long task_id) {
        if (task_id > 0L) return task_id;
        task_id = procInst.getTaskId();
        Task curTask = provider.findTask(task_id);
        if (EntityHelper.isEmpty(curTask)) throw new SwException("单据任务缺失（" + procInst.getId() + ":::" + task_id + "）！");
        actInst = curTask;
        if (curTask.getStatu() != FlowConst.ActivityStatu.WAIT.value) return task_id;
        //是自己的待办任务
        if (curTask.getStatu() == FlowConst.ActivityStatu.WAIT.value && FlowHelper.isMyWaitedTask(curTask.getId(), us.getUserId())) {
            return task_id;
        }

        //待办任务不是自己的，则去找前一个自己提交的任务
        List<Task> preActs = provider.findBeforeTasks(task_id);
        if (CommUtil.isEmpty(preActs)) return task_id;
        for (Task act : preActs) {
            if (act.getHandler() == us.getUserId()) {
                actInst = act;
                return act.getId();
            }
        }
        return task_id;
    }

    /**
     * 矫正默认任务及获取可选任务列表，供界面选择
     *
     * @return
     * @throws Exception
     */
    public List<Task> adjustTasks() {
        List<Task> listTask = new ArrayList<>();
        if (procInst.isNew() || procInst.getStatu() == FlowConst.InstanceStatu.DISUSE.value) {
            listTask.add(actInst);
            return listTask;
        }
        //获取当前单据活动任务列表及当前任务
//        act_inst = null;
        List<Task> list = provider.findCurTask(procInst.getId());
        //当前登录用户
        String sqlIn = "";
        for (Task act : list) {
            //如果办理人是当前用户，则置此任务为当前任务
            //我办理中的任务：非待办，且是我的任务
            if (act.getId() == actInst.getId()) {
                act = actInst;

                if (act.getStatu() == FlowConst.ActivityStatu.WAIT.value && FlowHelper.isMyWaitedTask(act.getId(), us.getUserId())) {
                    //我的待办任务，设下值，为后面的按钮判断减轻负担
                    act.setHandler(us.getUserId());
                }
            }
            //如果当前任务为待办理，那么要找其前置任务，作为活动任务，方便取回
            if (act.getStatu() == FlowConst.ActivityStatu.WAIT.value && !act.isReject() && !act.isRetake()) {
                sqlIn += "," + act.getEntityId();
            }
        }
        //找待办任务的前置任务，便于取回
        if (StringUtils.isNotEmpty(sqlIn)) {
            List<Task> list1 = provider.findBeforeTasks(sqlIn.substring(1));
            listTask.addAll(list1);
        }

        listTask.addAll(list);

        return listTask;
    }

    //启动流程
    public void save() {
        if (procInst.isNew()) {
            getProcInstDao().insertEntity(procInst);
            getTaskDao().insertEntity(actInst);
            getCommentDao().insertEntity(createComment(actInst));
            buildBillLog(FlowConst.Button.ADD.value, "-", null, null);
        } else {
            getProcInstDao().updateEntity(procInst);
            if (actInst != null) {
                getTaskDao().updateEntity(actInst);
            }
        }
    }

    /**
     * 删除流程
     *
     * @throws Exception
     */
    public void delete() {
        if (actInst != null && !actInst.isMake()) {
            throw new BizException("当前单据非制单状态，不允许删除！");
        }
        getProcInstDao().deleteEntity(procInst);
        getTaskDao().deleteEntity("where tsk_pri_id=?", procInst.getId());
        getCandidateDao().deleteEntity("where cnd_pri_id=?", procInst.getId());
        getCareDao().deleteEntity("where cr_pri_id=?", procInst.getId());
        getCommentDao().deleteEntity("where cmt_pri_id=?", procInst.getId());
        getSignDao().deleteEntity("where sgn_pri_id=?", procInst.getId());
        getTaskRelDao().deleteEntity("where trl_pri_id=?", procInst.getId());
    }

    /**
     * 返回页面前的设置
     */
    public void setPageRetExtra(SwMap ret) {
        procInst.setSelTaskId(actInst.getId());
        SwMap extra = ret.computeMapIfAbsent("extra", k -> new SwMap());

        List<Task> listTask = adjustTasks();
        //可选步骤
        List<SwMap> tasks = new ArrayList<>();

        for (Task act : listTask) {
            SwMap row = new SwMap();
            tasks.add(row);
            row.put("id", act.getId());
            row.put("label", act.getActName());
        }
        extra.put("tasks", tasks);

        //前置动作
        if (!actInst.isMake()) {
            //前置动作
            listTask = provider.findBeforeTasks(actInst.getId());
            if (CommUtil.isEmpty(listTask)) throw new SwException("未发现前置动作！");
            List<SwMap> bTasks = new ArrayList<>();
            boolean hasMake = false;
            for (Task act : listTask) {
                SwMap row = new SwMap();
                bTasks.add(row);
                row.put("id", act.getId());
                row.put("label", procDef.getProcInfo().getActNameById(act.getActId()));
                if (act.isMake()) {
                    row.put("type", 0);
                    hasMake = true;
                } else {
                    row.put("type", 1);
                }
            }
            if (!hasMake && procDef.getProcInfo().isRejectToMake()) {
                SwMap row = new SwMap();
                bTasks.add(0, row);
                Task act = provider.findMakeTask(procInst.getId());
                row.put("id", act.getId());
                row.put("label", procDef.getProcInfo().getActNameById(act.getActId()));
                row.put("type", 0);
            }
            extra.put("preTasks", bTasks);
        }

        List<SwMap> buttons = new ArrayList<>();
        resetButtons(buttons);
        extra.put("buttons", buttons);
    }

    private SwMap createButton(AbstractEnum.StrEnumBean type, boolean valid) {
        SwMap button = new SwMap();
        button.put("type", type.value);
        button.put("label", type.name);
        button.put("valid", valid);
        return button;
    }

    public void resetButtons(List<SwMap> buttons) {
        //按钮
        buttons.clear();
        long user_id = us.getUserId();
        //新增不要显示历史
        boolean isNew = procDef.isNew();
        boolean isFinish = procInst.getStatu() > FlowConst.InstanceStatu.RUNING.value;

        //能否新增，菜单有授权+单据有流程分配
        boolean canAdd = ModelProcHelper.getBillProc(procInst.getBillType(), user_id) != null;
        buttons.add(createButton(FlowConst.Button.ADD, canAdd));

        boolean canDel = !isFinish && actInst.isMake() && !isNew;
        buttons.add(createButton(FlowConst.Button.DEL, canDel));
        buttons.add(createButton(FlowConst.Button.DISUSE, !isNew && !isFinish && !actInst.isMake()));
        buttons.add(createButton(FlowConst.Button.LOG, !isNew));
        buttons.add(createButton(FlowConst.Button.WORD, !isNew));
        buttons.add(createButton(FlowConst.Button.EXCEL, !isNew));
        buttons.add(createButton(FlowConst.Button.SAVE, !isFinish && actInst.isMake() && actInst.getHandler() == user_id));

        boolean canHandle = !isFinish && actInst.getStatu() == FlowConst.ActivityStatu.WAIT.value && actInst.getHandler() == user_id;
        buttons.add(createButton(FlowConst.Button.HANDLE, canHandle));

        boolean canSubmit = !isFinish && actInst.getStatu() < FlowConst.ActivityStatu.SUBMIT.value && actInst.getHandler() == user_id;
        buttons.add(createButton(FlowConst.Button.SUBMIT, canSubmit));

        boolean canReject = !isFinish && !actInst.isMake() && actInst.getStatu() < FlowConst.ActivityStatu.SUBMIT.value && actInst.getHandler() == user_id;
        buttons.add(createButton(FlowConst.Button.REJECT, canReject));
        buttons.add(createButton(FlowConst.Button.INTERRUPT, canReject));

        //我已提交的任务，取回(前面已经过滤掉不能取回的任务：下一任务已办理)
        boolean canRetake = !isFinish && actInst.getStatu() == FlowConst.ActivityStatu.SUBMIT.value && actInst.getHandler() == user_id;
        buttons.add(createButton(FlowConst.Button.RETAKE, canRetake));
    }

    /**
     * 校验权限
     *
     * @throws Exception
     */
    private void checkPermission() {
        if (actInst == null) {
            throw new SwException("没有找到对应的任务步骤！");
        }
        long user_id = us.getUserId();
        //是我办理的单据，正常，返回
        if (actInst.getHandler() <= 0L || user_id == actInst.getHandler()) return;
        //不是我办理的单据，非制单状态，不允许他人操作
        if (!actInst.isMake()) {
            throw new BizException("非制单步骤，必须办理人本人操作！");
        }
    }

    /**
     * 中止，不通过
     */
    public void interrupt(String comment) {
        long user_id = us.getUserId();
        //校验权限，当前人员有权限干不，没权限直接抛异常
        checkPermission();
        if (actInst.getStatu() >= FlowConst.ActivityStatu.SUBMIT.value) {
            throw new BizException("当前任务已经提交，不能重复提交");
        }

        if (actInst.getStatu() == FlowConst.ActivityStatu.WAIT.value && !FlowHelper.isMyWaitedTask(actInst.getEntityId(), user_id)) {
            throw new BizException("您不是当前任务的办理人，不允许提交");
        }
        //校验完毕，接下来干活。
        List<Activity> new_acts = getProcDef().getProcInfo().findEndActivity();
        if (CommUtil.isEmpty(new_acts)) throw new SwException("该流程无结束节点！");

        //待新增任务
        List<Task> listInsertTask = new ArrayList<>();
        List<TaskRel> listTaskRel = new ArrayList<>();
        List<Comment> listTaskComment = new ArrayList<>();
        //待新增任务候选人
        List<Candidate> listInsertCand = new ArrayList<>();

        //只要一个就行
        buildTask(new_acts.get(0), listInsertTask, listInsertCand, listTaskRel, listTaskComment);

        actInst.setHandler(user_id);
        actInst.setStartTime(DateUtil.nowDateTimeLong());
        actInst.setStatu(FlowConst.ActivityStatu.INTERRUPT.value);
        actInst.setRetake(false);

        if (listInsertTask.size() > 0) {
            procInst.setTaskId(listInsertTask.get(0).getEntityId());
            procInst.setActName(listInsertTask.get(0).getActName());
        } else {
            procInst.setTaskId(actInst.getEntityId());
            procInst.setActName(actInst.getActName());
        }


        procInst.setStatu(FlowConst.InstanceStatu.INTERRUPT.value);
        procInst.setEndTime(DateUtil.nowDateTimeLong());

        //保存入库
        if (!listInsertTask.isEmpty()) {
            getTaskDao().batchInsertEntity(listInsertTask);
            getCandidateDao().batchInsertEntity(listInsertCand);
            getTaskRelDao().batchInsertEntity(listTaskRel);

            if (!listTaskComment.isEmpty()) {
                getCommentDao().batchInsertEntity(listTaskComment);
            }
        }
        getTaskDao().updateEntity(actInst);
        getProcInstDao().updateEntity(procInst);

        updateComment(comment, FlowConst.Button.INTERRUPT.value);
        buildBillLog(FlowConst.Button.INTERRUPT.value, comment, actInst, listInsertTask);
        listener.afterSubmit(actInst, listInsertTask);
    }


    /**
     * 提交
     *
     * @throws Exception
     */
    public void submit(String comment) {
        long user_id = us.getUserId();
        //校验权限，当前人员有权限干不，没权限直接抛异常
        checkPermission();
        if (actInst.getStatu() >= FlowConst.ActivityStatu.SUBMIT.value) {
            throw new BizException("当前任务已经完成，不能重复提交");
        }

        if (actInst.getStatu() == FlowConst.ActivityStatu.WAIT.value && !FlowHelper.isMyWaitedTask(actInst.getEntityId(), user_id)) {
            throw new BizException("您不是当前任务的办理人，不允许提交");
        }
        //校验完毕，接下来干活。
        List<Activity> new_acts = FlowHelper.getNextActivity(this);

        //待新增任务
        List<Task> listInsertTask = new ArrayList<>();
        List<TaskRel> listTaskRel = new ArrayList<>();
        List<Comment> listTaskComment = new ArrayList<>();
        //待新增任务候选人
        List<Candidate> listInsertCand = new ArrayList<>();

        for (Activity act : new_acts) {
            buildTask(act, listInsertTask, listInsertCand, listTaskRel, listTaskComment);
        }

        actInst.setHandler(user_id);
        actInst.setStartTime(DateUtil.nowDateTimeLong());
        actInst.setStatu(FlowConst.ActivityStatu.SUBMIT.value);
        actInst.setRetake(false);

        procInst.setTaskId(listInsertTask.get(0).getEntityId());
        procInst.setActName(listInsertTask.get(0).getActName());

        if (new_acts.size() == 1 && new_acts.get(0).getType() == FlowConst.ActivityType.END.value) {
            procInst.setStatu(FlowConst.InstanceStatu.FINISH.value);
            procInst.setEndTime(DateUtil.nowDateTimeLong());
        } else {
            procInst.setStatu(FlowConst.InstanceStatu.RUNING.value);
        }

        //保存入库
            getTaskDao().batchInsertEntity(listInsertTask);
            getCandidateDao().batchInsertEntity(listInsertCand);
            getTaskRelDao().batchInsertEntity(listTaskRel);

            if (!listTaskComment.isEmpty()) {
                getCommentDao().batchInsertEntity(listTaskComment);
            }

        getTaskDao().updateEntity(actInst);
        getProcInstDao().updateEntity(procInst);

        updateComment(comment, FlowConst.Button.SUBMIT.value);
        buildBillLog(FlowConst.Button.SUBMIT.value, comment, actInst, listInsertTask);
        listener.afterSubmit(actInst, listInsertTask);

        //设置当前步骤
        actInst = nextMyTask != null ? nextMyTask : listInsertTask.get(0);
    }

    /**
     * 作废
     *
     * @throws Exception
     */
    public void disuse(String comment) {
        long user_id = us.getUserId();
        //校验权限，当前人员有权限干不，没权限直接抛异常
        checkPermission();
        if (procInst.getStatu() == FlowConst.InstanceStatu.FINISH.value) {
            throw new BizException("当前单据已审批完成，不能作废单据");
        }

        if (actInst.getStatu() >= FlowConst.ActivityStatu.SUBMIT.value) {
            throw new BizException("当前任务已经完成，不能作废单据");
        }

        if (actInst.getStatu() == FlowConst.ActivityStatu.WAIT.value && !FlowHelper.isMyWaitedTask(actInst.getEntityId(), user_id)) {
            throw new BizException("您不是当前任务的办理人，不允许作废");
        }

        actInst.setHandler(user_id);
        actInst.setStartTime(DateUtil.nowDateTimeLong());
        actInst.setStatu(FlowConst.ActivityStatu.DISUSE.value);
        procInst.setStatu(FlowConst.InstanceStatu.DISUSE.value);
        procInst.setEndTime(DateUtil.nowDateTimeLong());

        getTaskDao().updateEntity(actInst);
        getProcInstDao().updateEntity(procInst);

        updateComment(comment, FlowConst.Button.DISUSE.value);
        buildBillLog(FlowConst.Button.DISUSE.value, comment, actInst, null);
        listener.afterDisuse(actInst);
    }

    /**
     * 提交创建任务
     *
     * @param act
     * @param listInsertTask
     * @param listInsertCand
     * @param listTaskRel
     * @throws Exception
     */
    private void buildTask(Activity act, List<Task> listInsertTask, List<Candidate> listInsertCand, List<TaskRel> listTaskRel, List<Comment> listTaskComment) {
        Task task = new Task();
        listInsertTask.add(task);
        task.init();
        task.setEntityId(DbEngine.getInstance().nextId());
        task.setPriId(procInst.getId());
        task.setPrcId(procDef.getId());
        task.setActId(act.getId());
        task.setActName(act.getLabel());
        task.setStartTime(DateUtil.nowDateTimeLong());
        task.setStatu(FlowConst.ActivityStatu.WAIT.value);
        task.setMake(false);
        task.setSign(false);
        task.setReject(false);
        task.setRetake(false);
        task.setAuto(false);

        TaskRel rel = new TaskRel();
        rel.setEntityId(DbEngine.getInstance().nextId());
        rel.setPriId(task.getPriId());
        rel.setDstTaskId(task.getId());
        rel.setSrcTaskId(actInst.getId());
        listTaskRel.add(rel);

        if (act.getType() != FlowConst.ActivityType.END.value) {
            Set<Long> listHander = new HashSet<>();

            long hanlder_group = 0L;
            int handler_range = 0;
            if (act.getHandler() != null) {
                hanlder_group = act.getHandler().getUserGroup();
                handler_range = act.getHandler().getFilter();
            }
            //获取所有候选人
            listHander = UserGroupHelper.getUserIdList(hanlder_group, procInst, handler_range, us);

            if (CommUtil.isEmpty(listHander)) {
                throw new BizException("没有符合条件的主办人！" + act.getLabel());
            }
            for (Long user_id : listHander) {
//                if (act.getHandler().getIgnore() && user_id == submit_user) continue;
                if (nextMyTask == null && user_id == us.getUserId()) {
                    nextMyTask = task;
                }
                Candidate cand = new Candidate();
                cand.init();
                cand.setEntityId(DbEngine.getInstance().nextId());
                cand.setPriId(task.getPriId());
                cand.setTskId(task.getId());
                cand.setUserId(user_id);

                listInsertCand.add(cand);
            }
            if (CommUtil.isEmpty(listInsertCand)) {
                throw new BizException("没有符合条件的主办人！" + act.getLabel());
            }

            //创建新的审批意见
            listTaskComment.add(createComment(task));
        }
    }


    /**
     * 取回
     *
     * @throws Exception
     */
    public void retake() {
        long user_id = us.getUserId();
        //校验权限，当前人员有权限干不，没权限直接抛异常
        checkPermission();
        if (actInst.getStatu() != FlowConst.ActivityStatu.SUBMIT.value) {
            throw new BizException("当前任务非提交状态，不能取回！");
        }
        Activity activityEntity = procDef.getProcInfo().findActivity(actInst.getActId());
        EntityDao<Task> taskDao = getTaskDao();

            List<Task> new_acts = provider.findAfterTasks(actInst.getId());

            for (Task act : new_acts) {
                if (act.getStatu() != FlowConst.ActivityStatu.WAIT.value) {
                    throw new BizException("后续任务[" + procDef.getProcInfo().getActNameById(act.getActId()) + "]已经办理，不能取回！");
                }
            }

            actInst.setHandler(user_id);
            actInst.setStartTime(DateUtil.nowDateTimeLong());
            actInst.setStatu(FlowConst.ActivityStatu.HANDLE.value);

            procInst.setTaskId(actInst.getEntityId());
            procInst.setActName(actInst.getActName());

            procInst.setStatu(FlowConst.InstanceStatu.RUNING.value);
            if (actInst.isMake()) procInst.setStatu(FlowConst.InstanceStatu.BEGIN.value);

            taskDao.updateEntity(actInst);
            taskDao.batchDelete(new_acts);
            getCommentDao().deleteEntity("where cmt_tsk_id in (" + EntityHelper.getSqlInIds(new_acts) + ")");
            getTaskRelDao().deleteEntity("where trl_src_task_id=?", actInst.getEntityId());
            getProcInstDao().updateEntity(procInst);

            updateComment(null, FlowConst.Button.RETAKE.value);
            buildBillLog(FlowConst.Button.RETAKE.value, "TODO", actInst, new_acts);
            listener.afterRetake(actInst, new_acts);
    }

    /**
     * 驳回
     *
     * @throws Exception
     */
    public void reject(String comment, String toTasks) {
        if (StringUtils.isEmpty(toTasks)) {
            throw new BizException("未指定驳回的目标步骤！");
        }
        String[] dstTasks = toTasks.split(",");
        if (dstTasks.length == 0) throw new BizException("未指定驳回的目标步骤！");

        long user_id = us.getUserId();
        //校验权限，当前人员有权限干不，没权限直接抛异常
        checkPermission();
        if (actInst.getStatu() >= FlowConst.ActivityStatu.SUBMIT.value) {
            throw new BizException("当前任务已经提交，不能驳回！");
        }

        if (actInst.getStatu() == FlowConst.ActivityStatu.WAIT.value && !FlowHelper.isMyWaitedTask(actInst.getEntityId(), user_id)) {
            throw new BizException("您不是当前任务的办理人，不允许驳回！");
        }

        //待新增任务
        List<Task> listInsertTask = new ArrayList<>();
        List<TaskRel> listTaskRel = new ArrayList<>();
        List<Comment> listTaskComment = new ArrayList<>();
        //待新增任务候选人
        List<Candidate> listInsertCand = new ArrayList<>();

        nextMyTask = null;
        //校验完毕，接下来干活。
        //并发考虑：找到此任务的前置任务，创建对应的新任务，原流程其他未办理任务删除，办理中及已提交的，置为
        for (String dstTaskId : dstTasks) {
            //rejectTo(comment, NumberUtil.getLongIgnoreErr(dstTaskId), listInsertTask, listTaskRel, listTaskComment, listInsertCand);
            long toTaskId = NumberUtil.getLongIgnoreErr(dstTaskId);
            Task toTask = provider.findTask(toTaskId);
            if (toTask == null) throw new SwException("没有找到驳回目标节点（" + toTaskId + "）！");

            buildRejectTask(toTask, listInsertTask, listInsertCand, listTaskRel, listTaskComment);
        }
        actInst.setStatu(FlowConst.ActivityStatu.REJECT.value);

        procInst.setTaskId(listInsertTask.get(0).getEntityId());
        procInst.setActName(listInsertTask.get(0).getActName());

        //先停止其他待办或办理中的任务
        FlowHelper.stopTask(procInst.getId(), actInst.getId());
        //保存入库

        getTaskDao().batchInsertEntity(listInsertTask);
        getCandidateDao().batchInsertEntity(listInsertCand);
        getTaskRelDao().batchInsertEntity(listTaskRel);

        if (!listTaskComment.isEmpty()) {
            getCommentDao().batchInsertEntity(listTaskComment);
        }

        getTaskDao().updateEntity(actInst);
        getProcInstDao().updateEntity(procInst);

        updateComment(comment, FlowConst.Button.REJECT.value);
        buildBillLog(FlowConst.Button.SUBMIT.value, comment, actInst, listInsertTask);
        listener.afterSubmit(actInst, listInsertTask);

        actInst = nextMyTask != null ? nextMyTask: listInsertTask.get(0);
    }

    private void buildRejectTask(Task oldTask, List<Task> listInsertTask, List<Candidate> listInsertCand, List<TaskRel> listTaskRel, List<Comment> listTaskComment) {
        Task task = new Task();
        listInsertTask.add(task);
        task.getData().putAll(oldTask.getData());
        task.setEntityId(DbEngine.getInstance().nextId());

        task.setStatu(FlowConst.ActivityStatu.WAIT.value);
        task.setReject(true);

        TaskRel rel = new TaskRel();
        rel.setEntityId(DbEngine.getInstance().nextId());
        rel.setPriId(task.getPriId());
        rel.setDstTaskId(task.getId());
        rel.setSrcTaskId(actInst.getId());
        listTaskRel.add(rel);

        Candidate cand = new Candidate();
        cand.init();
        cand.setEntityId(DbEngine.getInstance().nextId());
        cand.setPriId(task.getPriId());
        cand.setTskId(task.getId());
        cand.setUserId(oldTask.getHandler());
        if (nextMyTask == null && task.getHandler() == us.getUserId()) {
            nextMyTask = task;
        }
        listInsertCand.add(cand);

        //创建新的审批意见
        listTaskComment.add(createComment(task));
    }

    /**
     * 状态变更，构建日志
     */
    protected void buildBillLog(final String flow_opt, String comment, Task srcTask, List<Task> dstTasks) {
        ProcInfo procInfo = procDef.getProcInfo();

        FlowLog flowLog = new FlowLog();
        flowLog.setComment(comment);
        flowLog.setOpt(flow_opt);

        flowLog.setPriId(procInst.getId());
        flowLog.setTime(DateUtil.nowDateTimeLong());

        flowLog.setUsrId(us.getUserId());
        flowLog.setId(DbEngine.getInstance().nextId());

        String src = null, dst = null;
        StringBuilder sbDst = null;
        if (srcTask != null) {
            src = procInfo.getActNameById(srcTask.getActId());
            flowLog.setActName(src);
            flowLog.setActId(srcTask.getId());
        }
        if (!CommUtil.isEmpty(dstTasks)) {
            sbDst = new StringBuilder();
            for (Task a : dstTasks) {
                sbDst.append("/").append(procInfo.getActNameById(a.getActId()));
            }
            dst = sbDst.substring(1);
        }
        if (FlowConst.Button.RETAKE.value.equals(flow_opt)) {
            flowLog.setInfo(dst + "->" + src);
        } else if (dst != null) {
            flowLog.setInfo(src + "->" + dst);
        } else if (src != null) {
            flowLog.setInfo(src);
        } else {
            flowLog.setInfo("-");
        }
        DbEngine.getInstance().findDao(FlowLog.class).insertEntity(flowLog);
    }

    /**
     * 构建新的审批信息，制单或首次保存、提交创建新任务时创建
     */
    protected Comment createComment(Task task) {
        ProcInfo procInfo = procDef.getProcInfo();

        Comment bean = new Comment();
        bean.setId(DbEngine.getInstance().nextId());
        bean.setPriId(procInst.getId());
        bean.setTskId(task.getId());
        bean.setActId(task.getActId());
        bean.setActName(procInfo.getActNameById(task.getActId()));
        bean.setStartTime(DateUtil.nowDateTimeLong());
        bean.setStatu(FlowConst.ActivityStatu.WAIT.value);

        return bean;
    }

    private EntityDao<ProcInst> getProcInstDao() {
        return DbEngine.getInstance().findDao(ProcInst.class);
    }

    private EntityDao<Task> getTaskDao() {
        return DbEngine.getInstance().findDao(Task.class);
    }

    private EntityDao<Candidate> getCandidateDao() {
        return DbEngine.getInstance().findDao(Candidate.class);
    }

    private EntityDao<Care> getCareDao() {
        return DbEngine.getInstance().findDao(Care.class);
    }

    private EntityDao<Comment> getCommentDao() {
        return DbEngine.getInstance().findDao(Comment.class);
    }

    private EntityDao<Sign> getSignDao() {
        return DbEngine.getInstance().findDao(Sign.class);
    }

    private EntityDao<TaskRel> getTaskRelDao() {
        return DbEngine.getInstance().findDao(TaskRel.class);
    }

    interface ICommentWorker {
        void work(Comment bean, String comment, long userId);
    }

    private static Map<String, ICommentWorker> mapComment;

    static {
        mapComment = new HashMap<>();
        mapComment.put(FlowConst.Button.HANDLE.value, (bean, comment, userId) -> {
            bean.setStatu(FlowConst.ActivityStatu.HANDLE.value);
            bean.setHandler(userId);
            bean.setHandlerTime(DateUtil.nowDateTimeLong());
        });
        mapComment.put(FlowConst.Button.SUBMIT.value, (bean, comment, userId) -> {
            checkHandler(bean, userId);
            bean.setStatu(FlowConst.ActivityStatu.SUBMIT.value);
            bean.setEndTime(DateUtil.nowDateTimeLong());
            bean.setComment(comment);
        });
        mapComment.put(FlowConst.Button.RETAKE.value, (bean, comment, userId) -> {
            checkHandler(bean, userId);

            bean.setStatu(FlowConst.ActivityStatu.HANDLE.value);
            bean.setHandlerTime(DateUtil.nowDateTimeLong());
            bean.setEndTime(0L);
        });
        mapComment.put(FlowConst.Button.REJECT.value, (bean, comment, userId) -> {
            checkHandler(bean, userId);

            bean.setStatu(FlowConst.ActivityStatu.REJECT.value);
            bean.setHandlerTime(DateUtil.nowDateTimeLong());
            bean.setEndTime(DateUtil.nowDateTimeLong());
            bean.setComment(comment);
        });
        mapComment.put(FlowConst.Button.DISUSE.value, (bean, comment, userId) -> {
            checkHandler(bean, userId);
            bean.setStatu(FlowConst.ActivityStatu.DISUSE.value);
            bean.setEndTime(DateUtil.nowDateTimeLong());
            bean.setComment(comment);
        });
        mapComment.put(FlowConst.Button.INTERRUPT.value, (bean, comment, userId) -> {
            checkHandler(bean, userId);
            bean.setStatu(FlowConst.ActivityStatu.INTERRUPT.value);
            bean.setEndTime(DateUtil.nowDateTimeLong());
            bean.setComment(comment);
        });
    }

    private static void checkHandler(Comment bean, long userId) {
        if (bean.getHandlerTime() <= 0L) {
            bean.setHandler(userId);
            bean.setHandlerTime(DateUtil.nowDateTimeLong());
        }
    }

    protected void updateComment(String comment, String optType) {
        Comment bean = provider.findTaskComment(actInst.getId());
        if (bean == null) throw new SwException("没有找到对应的记录（comment）！");
        ICommentWorker worker = mapComment.get(optType);
        if (worker == null) throw new SwException("该类型（" + optType + "）没有实现审批意见！");
        worker.work(bean, comment, us.getUserId());
        getCommentDao().updateEntity(bean);
    }

    //获取流程定义信息
    public ModelProc getProcDef() {
        return procDef;
    }

    //获取指定的任务定义信息
    public Activity findActivity(String actId) {
        return procDef.getProcInfo().findActivity(actId);
    }
}
