<?php

namespace application\modules\mobile\controllers;

use application\core\utils\ArrayUtil;
use application\core\utils\Attach;
use application\core\utils\Env;
use application\core\utils\Ibos;
use application\core\utils\StringUtil;
use application\modules\department\model\Department;
use application\modules\department\utils\Department as DepartmentUtil;
use application\modules\main\components\CommonAttach;
use application\modules\main\utils\Main;
use application\modules\main\utils\Main as MainUtil;
use application\modules\message\model\Notify;
use application\modules\message\model\NotifyMessage;
use application\modules\mobile\utils\Mobile;
use application\modules\user\model\User;
use application\modules\workflow\core\FlowConst;
use application\modules\workflow\core\FlowForm as ICFlowForm;
use application\modules\workflow\core\FlowFormViewer as ICFlowFormViewer;
use application\modules\workflow\core\FlowProcess as ICFlowProcess;
use application\modules\workflow\core\FlowRun as ICFlowRun;
use application\modules\workflow\core\FlowRunProcess as ICFlowRunProcess;
use application\modules\workflow\core\FlowType as ICFlowType;
use application\modules\workflow\model\FlowCategory;
use application\modules\workflow\model\FlowDataN;
use application\modules\workflow\model\FlowProcess;
use application\modules\workflow\model\FlowProcessTurn;
use application\modules\workflow\model\FlowRun;
use application\modules\workflow\model\FlowRunfeedback;
use application\modules\workflow\model\FlowRunProcess;
use application\modules\workflow\model\FlowType;
use application\modules\workflow\utils\Common;
use application\modules\workflow\utils\Common as WfCommonUtil;
use application\modules\workflow\utils\Handle;
use application\modules\workflow\utils\Handle as WfHandleUtil;
use application\modules\workflow\utils\Preview as WfPreviewUtil;
use application\modules\workflow\utils\WfNew as WfNewUtil;
use application\core\utils\Convert;
use CHtml;

class WorkController extends BaseController
{

    const TODO = '1,2';   // 待办标记
    const FORCE = 1;
    const UN_RECEIVE = 1; // 未接收
    const HANDLE = 2;   // 办理中
    const TRANS = '3,4';   // 已转交
    const DONE = 4; // 已办结
    const PRESET = 5;   // 自由流程预设步骤
    const DELAY = 6;   // 已延期
    const DEFAULT_PAGE_SIZE = 10; // 默认页面条数

    /**
     * 列表页专用属性
     * @var array
     */

    protected $_extraAttributes = array(
        'uid' => 0,
        'op' => '',
        'sort' => '',
        'type' => '',
        'runid' => '',
        'flowid' => '',
        'processid' => '',
        'flowprocess' => '',
        'sortText' => '',
        'key' => ''
    );

    /**
     * 检索类型 - 数据库标识 映射数组
     * @var array
     */
    protected $typeMapping = array(
        'todo' => self::TODO,
        'trans' => self::TRANS,
        'done' => self::DONE,
        'delay' => self::DELAY
    );

    public function actionIndex()
    {
        $param = array(
            'op' => $this->op,
            'type' => $this->type,
            'sort' => $this->sort,
        );
        $data = array_merge($param, $this->getListData());
        $this->ajaxReturn($data, Mobile::dataType());
    }

    /**
     * 处理列表数据
     * @return array
     */
    protected function getListData()
    {
        $fields = array(
            'frp.runid', 'frp.processid', 'frp.flowprocess',
            'frp.flag', 'frp.opflag', 'frp.processtime', 'ft.freeother',
            'ft.flowid', 'ft.name as typeName', 'ft.type', 'ft.listfieldstr',
            'fr.name as runName', 'fr.beginuser', 'fr.begintime', 'fr.endtime',
            'fr.focususer'
        );
        $offset = isset($_GET['offset']) ? intval($_GET['offset']) : 0;
        // 如果是分类视图并且检索类型是待办，要把未接收的工作也找出来作为气泡显示
        $flag = $this->typeMapping[$this->type];
        $condition = array(
            'and', 'fr.delflag = 0', 'frp.childrun = 0', sprintf('frp.uid = %d', $this->uid)
        );
        if ($flag == self::DONE) {
            $condition[] = "fr.endtime != '0'";
        } else {
            $condition[] = array('in', 'frp.flag', explode(',', $flag));
        }
        if ($flag == self::TRANS) {
            $condition[] = 'fr.endtime = 0';
        }
        $sort = 'frp.runid DESC';
        $group = '';
        if ($this->getIsOver()) {
            if ($this->type == 'trans') {
                $sort = 'frp.processtime DESC';
            } else {
                $sort = 'fr.endtime DESC';
            }
            $group = 'frp.runid';
        } elseif ($this->getIsTodo()) {
            $sort = 'frp.createtime DESC';
        } elseif ($this->getIsDelay()) {
            $sort = 'frp.flag DESC';
        }
        if ($this->sort == 'host') {
            $condition[] = 'frp.opflag = 1';
        } else if ($this->sort == 'sign') {
            $condition[] = 'frp.opflag = 0';
        } else if ($this->sort == 'rollback') {
            $condition[] = 'frp.processid != frp.flowprocess';
        }
        if ($this->flowid !== '') {
            $condition[] = 'fr.flowid = ' . $this->flowid;
        }
        $key = StringUtil::filterCleanHtml(Env::getRequest('keyword'));
        if ($key) {
            $condition[] = array('or', "fr.runid LIKE '%{$key}%'", "fr.name LIKE '%{$key}%'",);
        }
        $runProcess = Ibos::app()->db->createCommand()
            ->select($fields)
            ->from('{{flow_run_process}} frp')
            ->leftJoin('{{flow_run}} fr', 'frp.runid = fr.runid')
            ->leftJoin('{{flow_type}} ft', 'fr.flowid = ft.flowid')
            ->where($condition)
            ->order($sort)
            ->group($group)
            ->limit(self::DEFAULT_PAGE_SIZE)
            ->offset($offset)
            ->queryAll();
        if (!empty($runProcess)) {
            $runProcess = array_map(function ($v) {
                $v['runName'] = html_entity_decode($v['runName']);
                return $v;
            }, $runProcess);
        }
        if (count($runProcess) < self::DEFAULT_PAGE_SIZE) {
            $hasMore = false;
        } else {
            $hasMore = true;
        }
        if ($this->op == 'list') {
            return array_merge(array(
                'datas' => $runProcess,
                'hasMore' => $hasMore
            ), $this->handleList($runProcess, $flag));
        } else if ($this->op == 'category') {
            return $this->handleCategory($runProcess);
        }
    }

    /**
     * 当前页面是否已转交或已办结类型
     * @return boolean
     */
    protected function getIsOver()
    {
        return in_array($this->type, array('trans', 'done'));
    }

    /**
     * 当前页面是否待办类型
     * @return boolean
     */
    protected function getIsTodo()
    {
        return $this->type == 'todo';
    }

// -------------------以下来自formController-----------------------------

    /**
     * 当前页面是否延期类型
     * @return boolean
     */
    protected function getIsDelay()
    {
        return $this->type == 'delay';
    }

    // ---------------------以下主办所用到的方法--------------------

    /**
     * 处理列表视图显示
     * @param array $runProcess
     * @param string $flag
     * @return array
     */
    protected function handleList($runProcess, $flag)
    {
        $allProcess = FlowProcess::model()->fetchAllProcessSortByFlowId();
        foreach ($runProcess as &$run) {
            // 发起人
            //$run['user'] = User::model()->fetchByUid( $run['beginuser'] );
            // 如果查询类型为已办结及已完成，查找该运行实例实际运行的步骤信息
            if ($this->getIsOver()) {
                //-- 获取当前工作流最新一步骤的主办信息
                $rp = FlowRunProcess::model()->fetchCurrentNextRun($run['runid'], $this->uid, $flag);
                if (!empty($rp)) {
                    $run['processid'] = $rp['processid'];
                    $run['flowprocess'] = $rp['flowprocess'];
                    $run['opflag'] = $rp['opflag'];
                    $run['flag'] = $rp['flag'];
                }
            }

            if ($run['type'] == 1) {
                // 如果是固定流程，显示实际步骤的名字
                if (isset($allProcess[$run['flowid']][$run['flowprocess']]['name'])) {
                    $run['stepname'] = $allProcess[$run['flowid']][$run['flowprocess']]['name'];
                } else {
                    $run['stepname'] = Ibos::lang('Process steps already deleted');
                }
            } else {
                //如果是自由流程则显示当前是第几步骤
                $run['stepname'] = Ibos::lang('Stepth', '', array('{step}' => $run['processid']));
            }
            if ($this->type !== 'done') {
                $run['focus'] = StringUtil::findIn($this->uid, $run['focususer']);
            } else {
                if (!empty($run['endtime'])) {
                    $usedTime = $run['endtime'] - $run['begintime'];
                    $run['usedtime'] = WfCommonUtil::getTime($usedTime);
                }
            }

            // 页面可操作项判断
            $handleOpt = $this->getHandleOpt($run);
            $rollbackOpt = $this->getRollbackOpt($run);
            $turnOpt = $this->getTurnOpt($run);
            $endOpt = $this->getEndOpt($run);
            $delOpt = $this->getDelOpt($run);
            // 通用传递参数，url编码使之不明文可见
            $param = array(
                'runid' => $run['runid'],
                'flowid' => $run['flowid'],
                'processid' => $run['processid'],
                'flowprocess' => $run['flowprocess']
            );
            $run['key'] = WfCommonUtil::param($param);
            // foreach($runProcess as $k => $rp){
            // 	$runProcess[$k]['handleopt'] = $handleOpt;
            // 	$runProcess[$k]['rollbackopt'] = $rollbackOpt;
            // 	$runProcess[$k]['turnopt'] = $turnOpt;
            // 	$runProcess[$k]['endopt'] = $endOpt;
            // 	$runProcess[$k]['delopt'] = $delOpt;
            // }
        }
        return array('datas' => $runProcess);
    }

    /**
     * 获取办理类型操作权限 (主办or会签)
     * @param array $run 当前运行实例
     */
    public function getHandleOpt(&$run)
    {
        // 必须在待办页面才可进行下一判断
        if ($this->getIsTodo()) {
            if ($run['opflag'] == '1') {
                $run['host'] = true;
                return 'host';
            } else {
                $run['sign'] = true;
                return 'sign';
            }
        }
    }

    /**
     * 获取撤回操作权限
     * @param array $run 当前运行实例
     */
    public function getRollbackOpt(&$run)
    {
        // 必须：主办人 及 转交下一步状态 及 未结束 及 在结束类型页面 才可撤回
        if ($run['opflag'] && $run['flag'] == FlowConst::PRCS_TRANS && $run['endtime'] == 0 && $this->getIsOver()) {
            $run['rollback'] = true;
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取转交下一步操作权限
     * @param array $run 当前运行实例
     */
    public function getTurnOpt(&$run)
    {
        // 非办结类型及办理中状态才可进行下一判断
        if ($run['flag'] == FlowConst::PRCS_HANDLE && !$this->getIsOver()) {
            // 主办人才可转交
            if ($run['opflag'] == '1') {
                $run['turn'] = true;
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 获取结束流程操作权限
     * @param array $run 当前运行实例
     */
    public function getEndOpt(&$run)
    {
        // 非办结类型及办理中状态才可进行下一判断
        if ($run['flag'] == FlowConst::PRCS_HANDLE && !$this->getIsOver()) {
            // 非固定流程与主办人才可结束
            if ($run['type'] != 1 && $run['opflag'] == '1') {
                $run['end'] = true;
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 获取删除权限
     * @param array $run 当前运行实例
     */
    public function getDelOpt(&$run)
    {
        // 必须在流程第一步 及 未转交之前 或 拥有管理员权限的人才可删除
        if (($run['processid'] == '1' && $run['flag'] < FlowConst::PRCS_TRANS) || Ibos::app()->user->isadministrator) {
            $run['del'] = true;
            return true;
        } else {
            return false;
        }
    }

    public function actionFollow()
    {
        $offset = isset($_GET['offset']) ? intval($_GET['offset']) : 0;
        $fields = array(
            'frp.runid', 'frp.processid', 'frp.flowprocess',
            'frp.flag', 'frp.opflag', 'frp.processtime', 'ft.freeother',
            'ft.flowid', 'ft.name as typeName', 'ft.type', 'ft.listfieldstr',
            'fr.name as runName', 'fr.beginuser', 'fr.begintime', 'fr.endtime',
            'fr.focususer'
        );
        $flag = $this->typeMapping[$this->type];
        $sort = 'frp.runid DESC';
        $group = 'frp.runid';
        $condition = array(
            'and', 'fr.delflag = 0', 'frp.childrun = 0', sprintf('frp.uid = %d', $this->uid),
            sprintf("FIND_IN_SET(fr.focususer,'%s')", $this->uid)
        );
        $list = Ibos::app()->db->createCommand()
            ->select($fields)
            ->from('{{flow_run_process}} frp')
            ->leftJoin('{{flow_run}} fr', 'frp.runid = fr.runid')
            ->leftJoin('{{flow_type}} ft', 'fr.flowid = ft.flowid')
            ->where($condition)
            ->order($sort)
            ->group($group)
            ->limit(self::DEFAULT_PAGE_SIZE)
            ->offset($offset)
            ->queryAll();
        if (count($list) < self::DEFAULT_PAGE_SIZE) {
            $hasMore = false;
        } else {
            $hasMore = true;
        }
        $data = array_merge(
            array(
                'datas' => $list,
                'hasMore' => $hasMore
            ), $this->handleList($list, $flag)
        );
        $this->ajaxReturn($data, Mobile::dataType());
    }

    public function actionNew()
    {
        $data = array();
        $this->handleStartFlowList($data);
        $this->ajaxReturn($data, Mobile::dataType());
    }

    /**
     * 处理发起工作的列表数据
     * @param array $data
     */
    protected function handleStartFlowList(&$data)
    {
        $flowList = $commonlyFlowList = $sort = array();
        // 获取当前用户可用的工作流ID
        $enabledFlowIds = WfNewUtil::getEnabledFlowIdByUid($this->uid);
        $commonlyFlowIds = FlowRun::model()->fetchCommonlyUsedFlowId($this->uid);
        foreach (FlowType::model()->fetchAll(array('order' => 'sort,flowid')) as $flow) {
            $catId = $flow['catid'];
            $flowId = $flow['flowid'];
            if (!isset($flowList[$catId])) {
                $sort[$catId] = array();
                $cat = FlowCategory::model()->fetchByPk($catId);
                if ($cat) {
                    $sort[$catId] = $cat;
                }
            }
            // 使用状态过滤：无论有无权限，都不可见
            if ($flow['usestatus'] == 3) {
                continue;
            }
            // 使用状态过滤：有权限才可见
            $enabled = in_array($flowId, $enabledFlowIds);
            if (!$enabled && $flow['usestatus'] == 2) {
                continue;
            }
            // 使用状态过滤：可见但无权限者不可点击，赋予一个变量标识，交由前台控制交互
            $flow['enabled'] = $enabled;
            // 常用流程
            if (in_array($flowId, $commonlyFlowIds)) {
                $commonlyFlowList[] = $flow;
            }
            $flowList[$catId][] = $flow; //和网页端不一样，只需要数组
        }
        // 根据后台分类的排序对数组重新排序
        ksort($flowList, SORT_NUMERIC);
//		$data['flows'] = $flowList;
        $data['common'] = $commonlyFlowList;
        foreach ($sort as $key => &$cate) {
            if (isset($flowList[$key])) {
                $cate["flows"] = $flowList[$key];
                $cate["flowcount"] = count($flowList[$key]);
                $data['cate'][] = $cate;
            }
        }
    }

    /**
     * 表单办理
     *
     */
    public function actionForm()
    {
        $key = Env::getRequest('key');
        $type = Env::getRequest('type');
        if ($key) {
            $this->key = $key;
            $param = WfCommonUtil::param($key, 'DECODE');
            $this->_extraAttributes = $param;
            $this->runid = $param['runid'];
            $this->flowid = $param['flowid'];
            $this->processid = $param['processid'];
            $this->flowprocess = $param['flowprocess'];
        } else {
            $this->ajaxReturn("<script>alert('工作流数据错误，可能已转交或被回退')</script>", "EVAL");
        }
        // 流程实例
        $flow = new ICFlowType(intval($this->flowid));
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $data = array();
            // 只读控件
            $readOnly = $_POST['readonly'];
            // 隐藏控件
            $hidden = $_POST['hidden'];
            // 保存标志
            $saveflag = $_POST['saveflag']; //保存跳转标志
            // 会签意见附件
            $fbAttachmentId = $_POST['fbattachmentid'];
            // 公共附件
            $attachmentID = $_POST['attachmentid'];
            // 会签意见
            $content = isset($_POST['content']) ? StringUtil::filterCleanHtml($_POST['content']) : '';
            // 经办人标记
            $topflag = $_POST['topflag'];
            // 检查权限
            $this->checkRunAccess($this->runid, $this->processid, $this->createUrl('list/index'));
            // 如果是主办人
            if (FlowRunProcess::model()->getIsOp($this->uid, $this->runid, $this->processid)) {
                //手机端改写：只需要关注可写字段
                $enablefiledArr = explode(",", $_POST['enablefiled']);
                $flowname = isset($_POST['title']) ? CHtml::encode($_POST['title']) : '';
                $formData = array();
                $flowId = $this->flowid;
                $flow = new ICFlowType($flowId);
                $structure = $flow->form->parser->structure;
                foreach ($structure as $index => $item) {
                    if (!in_array("data_" . $item['itemid'], $enablefiledArr)) {
                        continue;
                    }
                    $value = isset($_POST[$index]) ? $_POST[$index] : '';
                    $formData[$index] = $value;
                }
                if (!empty($flowname)) {
                    $formData['name'] = $flowname;
                    FlowRun::model()->modify($this->runid, array('name' => $flowname));
                }
                $this->handleAttachComponent($formData);
                $formData && $this->handleImgComponent($formData);
                $formData && FlowDataN::model()->update($this->flowid, $this->runid, $formData);
            }
            // 会签意见处理部分，会签意见，手写签章，或上传了会签附件都视为提交了一条会签记录
            if (!empty($content) || !empty($fbAttachmentId)) {
                $fbData = array(
                    'runid' => $this->runid,
                    'processid' => $this->processid,
                    'flowprocess' => $this->flowprocess,
                    'uid' => $this->uid,
                    'content' => $content,
                    'attachmentid' => $fbAttachmentId,
                    'edittime' => TIMESTAMP,
                );
                FlowRunfeedback::model()->add($fbData);
                // 更新会签附件ID
                Attach::updateAttach($fbAttachmentId, $this->runid);
            }
            FlowRun::model()->modify($this->runid, array('attachmentid' => $attachmentID));
            Attach::updateAttach($attachmentID, $this->runid);
            if (!empty($attachmentID)) {
                $flowRun = FlowRun::model()->findByPk($this->runid);
                $flowRunStep = $flowRun->attachmentstep;
                $flowRunStepArray = !empty($flowRunStep) ? \CJSON::decode($flowRunStep) : array();
                foreach (explode(',', $attachmentID) as $aid) {
                    if (empty($flowRunStepArray['aid'])) {
                        $flowRunStepArray[$aid] = $this->flowprocess;
                    }
                }
                $flowRunStepString = \CJSON::encode($flowRunStepArray);
                $flowRun->attachmentstep = $flowRunStepString;
                $flowRun->save();
            }
            // 执行保存插件
            $plugin = FlowProcess::model()->fetchSavePlugin($this->flowid, $this->flowprocess);
            if (!empty($plugin)) {
                $pluginFile = './system/modules/workflow/plugins/save/' . $plugin;
                if (file_exists($pluginFile)) {
                    include_once($pluginFile);
                }
            }
            // 第一步发起，置改工作流保存状态为1
            if ('1' == $this->processid) {
                FlowRun::model()->modify($this->runid, array('saveflag' => FlowRun::SAVED));
            }
            switch ($saveflag) {
                case 'save':
                    MainUtil::setCookie('save_flag', 1, 300);
                    $this->redirect($this->createUrl('form/index', array('key' => $this->key)));
                    $this->ajaxReturn("<script>alert('保存成功')</script>", "EVAL");
                    break;
                case 'turn':
                    MainUtil::setCookie('turn_flag', 1, 300);
                    $this->redirect($this->createUrl('form/index', array('key' => $this->key)));
                    break;
                case 'end':
                case 'finish':
                    if ($saveflag == 'end') {
                        $param = array('opflag' => 1);
                    } else {
                        $param = array('topflag' => $topflag);
                    }
                    $this->redirect($this->createUrl('handle/complete', array_merge($param, array('key' => $this->key))));
                    break;
                default:
                    break;
            }
            // 如果是流程第一步
            if ($this->processid == 1) {
                // 设置工作流的发起人为自己
                FlowRun::model()->modify(
                    $this->runid, array('beginuser' => $this->uid, 'begintime' => TIMESTAMP)
                );
            }
        } else {
            $this->checkRunDel();
            // 查看工作流时不检查权限
            if ($type != 'view') {
                $this->checkIllegal();
            }
            $len = strlen($flow->autonum);
            for ($i = 0; $i < $flow->autolen - $len; $i++) {
                $flow->autonum = "0" . $flow->autonum;
            }
            // 运行步骤实例
            $runProcess = new ICFlowRunProcess($this->runid, $this->processid, $this->flowprocess, $this->uid);
            // 如果是固定流程，取步骤信息
            $checkitem = '';
            $attr = array();
            if ($flow->isFixed()) {
                // 步骤实例
                $process = new ICFlowProcess($this->flowid, $this->flowprocess);
                // 会签人不需要添加表单填写验证
                $attr = $process->toArray();
                if (!empty($attr) && $runProcess->opflag != 0) {
                    $checkitem = $process->checkitem;
                }
                if (!empty($attr) && $process->allowback > 0) {
                    $isAllowBack = true; //$this->isAllowBack( $runProcess->parent );
                }
            } else {
                $process = array();
            }
            // 运行实例
            $run = new ICFlowRun($this->runid);
            $hasOtherOPUser = FlowRunProcess::model()->getHasOtherOPUser(
                $this->runid, $this->processid, $this->flowprocess, $this->uid);
            // 如果当前步骤是子流程，查找出父流程的流程ID
            // if ( $run->pid !== 0 ) {
            // 	$parentFlowID = FlowRun::model()->fetchFlowIdByRunId( $run->pid );
            // }
            // 如果当前步骤状态为未接收，设置该步骤状态为办理中
            if ($runProcess->flag == self::UN_RECEIVE) {
                $this->setSelfToHandle($runProcess->id);
            }
            // 如果当前是主办人并且设置了先接收者为主办，把后续步骤更新为从办人
            if ($runProcess->topflag == 1 && $runProcess->opflag == 1) {
                FlowRunProcess::model()->updateTop(
                    $this->uid, $this->runid, $this->processid, $this->flowprocess
                );
            }
            // 如果设置了无主办人会签，要检查是否还有别的人没有会签，如果只剩下自己，那么就可以转交下一步
            if ($runProcess->topflag == 2) {
                if (!$hasOtherOPUser) {
                    $runProcess->opflag = 1;
                }
            }
            // 如果是流程第一步
            if ($this->processid == 1) {
                // 如果当前步骤是子流程且是第一步，更新工作流运行步骤表该步骤的办理状态为办理中
                if (!empty($run->parentrun)) {
                    $this->setParentToHandle($run->parentrun, $this->runid);
                }
            }

            // 如果是固定流程并设置了超时间隔的
            if ($flow->isFixed() && !empty($attr) && $process->timeout != 0) {
                // 如果该步骤未接收并且不是第一步，流程开始的时间为上一步的办结完的时间
                if ($runProcess->flag == self::UN_RECEIVE && $this->processid !== 1) {
                    $preProcess = $runProcess->parent;
                    $processBegin = FlowRunProcess::model()->fetchDeliverTime($this->runid, $preProcess);
                } else {
                    // 否则，为该步骤开始办理的时间
                    $processBegin = $runProcess->processtime ? $runProcess->processtime : TIMESTAMP;
                }
                $timeUsed = TIMESTAMP - $processBegin;
            }
            // 处理表单
            $flow->name = html_entity_decode($flow->name);
            $run->name = html_entity_decode($run->name);
            $viewer = new ICFlowFormViewer(
                array(
                    'flow' => $flow,
                    'form' => $flow->getForm(),
                    'run' => $run,
                    'process' => $process,
                    'rp' => $runProcess
                )
            );
            $data = array_merge(
                array(
                    'flow' => $flow->toArray(),
                    'run' => $run->toArray(),
                    'process' => !empty($process) ? $attr : $process,
                    'checkItem' => $checkitem,
                    'prcscache' => WfCommonUtil::loadProcessCache($this->flowid),
                    'rp' => $runProcess->toArray(),
                    'rpcache' => WfPreviewUtil::getViewFlowData($this->runid, $this->flowid, $this->uid, $remindUid),
                    'fbSigned' => $this->isFeedBackSigned(),
                    'allowBack' => isset($isAllowBack) ? $isAllowBack : false,
                    'timeUsed' => isset($timeUsed) ? $timeUsed : 0,
                    'uploadConfig' => Attach::getUploadConfig()
                ), $viewer->render(false, false, true) //手机端渲染数据
            );

            // 按可写,已写,空值 三种状态来划分控件. 手机端：
            $formdata = array(
                'enableArr' => '',
                'valueArr' => '',
                'emptyArr' => '',
            );
            $data['enablefiled'] = array();

            if (isset($data['model']['itemData']) && is_array($data['model']['itemData'])) {
                // 固定流程
                if ($flow->isFixed()) {
                    if (isset($data['prcscache'][$data['rp']['flowprocess']]['processitem'])) {
                        $enableFiled = explode(",", $data['prcscache'][$data['rp']['flowprocess']]['processitem']);
                    } else {
                        $enableFiled = array();
                    }
                    // 自由流程
                } elseif ($flow->isFree()) {
                    if (isset($data['rp']['freeitem'])) {
                        $enableFiled = explode(",", $data['rp']['freeitem']);
                    } else {
                        $enableFiled = array();
                    }
                }
                foreach ($data['model']['itemData'] as $k => $v) {
                    if (substr($k, 0, 5) != "data_") {
                        continue;
                    }

                    if (isset($data['model']['structure'][$k])) {
                        $structure = $data['model']['structure'][$k];

                        // 标签控件永远是可用状态
                        if ($structure['data-type'] == 'label') {
                            $formdata['valueArr'][] = $structure;
                            continue;
                        }

                        // 判断是否有效组件
                        if (!empty($structure['data-title'])) {
                            $structure['origin-value'] = $v;
                            $structure['value'] = $v;

                            // 签章控件的签章地址需要加上host
                            if ($structure['data-type'] == 'sign' && strtolower(ENGINE) != 'saas') {
                                $structure['value'] = Ibos::app()->request->getHostInfo()  . '/' . $v;
                            }
                            if (in_array($structure['data-title'], $enableFiled)) {
                                //记下可修改的页面
                                $data['enablefiled'][] = $k;
                                $structure['value'] = empty($data['model']['eleout'][$k]) ? '' : $data['model']['eleout'][$k];
                                $formdata['enableArr'][] = $structure;
                                continue;
                            }
                            if ($v != "") {
                                if ($structure['data-type'] == 'fileupload') {
                                    $structure['value'] = empty($data['model']['eleout'][$k]) ? '' : $data['model']['eleout'][$k];
                                    $formdata['valueArr'][] = $structure;
                                } elseif ($structure['data-type'] == 'imgupload') {
                                    $structure['value'] = empty($data['model']['eleout'][$k]) ? '' : $data['model']['eleout'][$k];
                                    $formdata['valueArr'][] = $structure;
                                } else {
                                    $formdata['valueArr'][] = $structure;
                                }
                                continue;
                            }
                            // 自由流程为可写字段未设置时，默认都可写
                            if ($flow->isFree() && empty($data['rp']['freeitem'])) {
                                $data['enablefiled'][] = $k;
                                $structure['value'] = $data['model']['eleout'][$k];
                                $formdata['enableArr'][] = $structure;
                            }
                            if ($flow->isFixed()) {
                                $formdata['emptyArr'][] = $structure;
                            }

                            $data['model']['structure'][$k] = $structure;
                        }
                    }
                }
            }

            $sortOfStructure = isset($data['model']['structure']) ? $data['model']['structure'] : 0;

            $data['enableArr'] = ArrayUtil::getValue($formdata, 'enableArr', array());
            $data['valueArr'] = ArrayUtil::getValue($formdata, 'valueArr', array());
            $data['emptyArr'] = ArrayUtil::getValue($formdata, 'emptyArr', array());

            // 按照表单结构排序
            $data['enableArr'] = $this->sortByStructure($sortOfStructure, $data['enableArr']);
            $data['valueArr'] = $this->sortByStructure($sortOfStructure, $data['valueArr']);
            $data['emptyArr'] = $this->sortByStructure($sortOfStructure, $data['emptyArr']);

            // 处理公共附件
            if (!empty($run->attachmentid)) {
                $attachPurv = $this->getAttachPriv($flow, $process, $runProcess);
                $down = $attachPurv['down'];
                $edit = $attachPurv['edit'];
                $del = $attachPurv['del'];
                // 暂时没用到
                // $read = $attachPurv['read'];
                // $print = $attachPurv['print'];
                $data['attachData'] = Attach::getAttach($run->attachmentid, $down, $down, $edit, $del);
            } else {
                $data['attachData'] = array();
            }

            $data['allowAttach'] = $this->isEnabledAttachment($flow, $run, $process, $runProcess);

            // 是否允许会签及读取会签意见信息
            if ($flow->isFixed() && !empty($attr) && $process->signlook == 0) {
                $data['allowFeedback'] = true;
                $data['feedback'] = WfHandleUtil::loadFeedback($flow->getID(), $run->getID(), $flow->type, $this->uid);
            } else {
                $data['allowFeedback'] = false;
            }

            // 如果回退选项为“允许回退到之前步骤”
            // 则将之前步骤列表输出
            if (!empty($process) && $process->allowback == '2') {
                $data['backlist'] = $this->getBackList($runProcess->flowprocess);
            }

            // 自由流程判断是否有预设步骤，如果没有，则可以结束当前流程
            if ($flow->isFree() && $runProcess->opflag == '1') {
                $hasDefault = FlowRunProcess::model()->getHasDefaultStep($this->runid, $this->processid);
                if (!$hasDefault) {
                    $data['defaultEnd'] = true;
                }
            }
            // 如果是自由流程及流程主办人选项设置为无主办人会签
            if ($flow->isFree() && $runProcess->topflag == '2') {
                // 检查是否还有其他经办人
                if (!$hasOtherOPUser) {
                    $data['otherEnd'] = true;
                }
            }
            //javescrip
            $data['script'] = $flow->form->script;
            NotifyMessage::model()->setReadByModule($this->uid, 'workflow');
            $this->ajaxReturn($data, Mobile::dataType());
            /////////////////////////////////////////////////////////////
        }
    }


    /**
     * 根据表单结构对数据进行排序
     * @param array $sortOfStructure
     * @param array $data
     */
    public function sortByStructure($sortOfStructure, $data)
    {
        $tempArray = array();
        if (is_array($sortOfStructure) && is_array($data)) {
            foreach ($sortOfStructure as $sortItem) {
                foreach ($data as $arrItem) {
                    if ($sortItem['itemid'] == $arrItem['itemid']) {
                        array_push($tempArray, $arrItem);
                    }
                }
            }
            return $tempArray;
        }
        return $data;
    }

    /**
     * 检查运行实例权限
     * @param integer $runId
     * @param string $jump
     */
    public function checkRunAccess($runId, $processId = 0, $jump = '')
    {
        $per = WfCommonUtil::getRunPermission($runId, $this->uid, $processId);
        if (empty($per)) {
            $errMsg = Ibos::lang('Permission denied');
            if (!empty($jump)) {
                $this->error($errMsg, $jump);
            } else {
                exit($errMsg);
            }
        }
    }

// -------------------来自formController结束-----------------------------
// --------------------来自newController -------------------------------

    /**
     * 表单处理提交时对于图片上传控件的特别处理
     * @param array $formData
     */
    protected function handleImgComponent(&$formData)
    {
        // 处理图片上传控件
        foreach ($GLOBALS['_FILES'] as $key => $value) {
            if (strtolower(substr($key, 0, 5)) == "data_") {
                if (isset($_POST["imgid_" . substr($key, 5)])) {
                    $formData["{$key}"] = "";
                    $old = $_POST["imgid_" . substr($key, 5)];
                    if ($value['name'] != "") {
                        if (!empty($old)) {
                            Attach::delAttach($old);
                        }
                        $upload = new CommonAttach($key, 'workflow');
                        $upload->upload();
                        $info = $upload->getUpload()->getAttach();
                        $upload->updateAttach($info['aid'], $this->runid);
                        $formData["{$key}"] = $info['aid'];
                    } else {
                        $formData["{$key}"] = $old;
                    }
                }
            }
        }
    }

    /**
     * 表单处理提交时对于文件上传控件的特别处理
     * @param array $formData
     */
    protected function handleAttachComponent(&$formData)
    {
        // 处理图片上传控件
        foreach ($GLOBALS['_FILES'] as $key => $value) {
            if (strtolower(substr($key, 0, 5)) == "data_") {
                if (isset($_POST["fileid_" . substr($key, 5)])) {
                    $formData["{$key}"] = "";
                    $old = $_POST["fileid_" . substr($key, 5)];
                    if ($value['name'] != "") {
                        if (!empty($old)) {
                            Attach::delAttach($old);
                        }
                        $upload = new CommonAttach($key, 'workflow');
                        $upload->upload();
                        $info = $upload->getUpload()->getAttach();
                        $upload->updateAttach($info['aid'], $this->runid, true);
                        $formData["{$key}"] = $info['aid'];
                    } else {
                        $formData["{$key}"] = $old;
                    }
                }
            }
        }
    }

    /**
     * 检查处理实例是否已删除
     */
    protected function checkRunDel()
    {
        $isDel = FlowRun::model()->countByAttributes(
            array('delflag' => 1, 'runid' => $this->runid)
        );
        if ($isDel) {
            $this->error(Ibos::lang('Form run has been deleted'), $this->createUrl('list/index'));
        }
    }

    /**
     * 检查运行实例是否合法
     */
    protected function checkIllegal()
    {
        $illegal = FlowRunProcess::model()->getIsIllegal(
            $this->runid, $this->processid, $this->flowprocess, $this->uid
        );
        if ($illegal) {
            $this->error(Ibos::lang('Form run has been processed'), $this->createUrl('list/index'));
        }
    }

    /**
     * 设置当前步骤为办理状态
     * @param integer $id
     */
    protected function setSelfToHandle($id)
    {
        FlowRunProcess::model()->modify(
            $id, array('flag' => self::HANDLE, 'processtime' => TIMESTAMP)
        );
    }

// -------------------来自listController-----------------------------

    /**
     * 子流程设置父流程的步骤为办理中
     * @param integer $id
     * @param integer $child
     */
    protected function setParentToHandle($id, $child)
    {
        $criteria = array(
            // 'condition' => array(
            //     array(
            //         'and',
            //         sprintf( 'runid = %d', $id ),
            //         sprintf( 'uid = %d', $this->uid ),
            //         sprintf( 'childrun = %d', $child ),
            //     )
            // )
            'condition' => sprintf("runid = %d AND uid = %d AND childrun = %d", $id, $this->uid, $child)
        );
        FlowRunProcess::model()->updateAll(
            array('flag' => self::HANDLE, 'processtime' => TIMESTAMP), $criteria
        );
    }

    /**
     * 设置流程为已办结
     * @param integer $processID
     */
    protected function setProcessDone($processID)
    {
        $condition = sprintf('processid = %d AND runid = %d', $processID, $this->runid);
        FlowRunProcess::model()->updateAll(array('flag' => self::DONE), $condition);
    }

    /**
     * 是否已有主办人会签
     * @return boolean
     */
    protected function isFeedBackSigned()
    {
        return FlowRunfeedback::model()->getHasSignAccess($this->runid, $this->processid, $this->uid);
    }

    /**
     * 附件是否可用
     * @param ICFlowType $flow
     * @param ICFlowRun $run
     * @param mixed $process
     * @param ICFlowRunProcess $rp
     * @return boolean
     */
    protected function isEnabledAttachment(ICFlowType $flow, ICFlowRun $run, $process, ICFlowRunProcess $rp)
    {
        $enabled = false;
        if ($flow->allowattachment) {
            //$alreadyHaveAttach = $run->attachmentid !== '';
            $enabledInFreeItem = $this->isEnabledInFreeItem($flow, $rp);
            $isHost = $rp->opflag == '1';
            $inProcessItem = $flow->isFixed() && StringUtil::findIn($process->processitem, '[A@]');
            if ($enabledInFreeItem || ($inProcessItem && $isHost)) {
                $enabled = true;
            }
        }
        return $enabled;
    }

    /**
     * 自由流程中的可写字段判断
     * @param ICFlowType $flow
     * @param ICFlowRunProcess $rp
     * @return boolean
     */
    protected function isEnabledInFreeItem(ICFlowType $flow, ICFlowRunProcess $rp)
    {
        return $flow->isFree() && $rp->freeitem == '' || StringUtil::findIn($rp->freeitem, '[A@]');
    }

    /**
     * 获取附件权限
     * @param ICFlowType $flow
     * @param mixed $process
     * @param ICFlowRunProcess $rp
     */
    protected function getAttachPriv(ICFlowType $flow, $process, ICFlowRunProcess $rp)
    {
        $down = $edit = $del = $read = $print = false;
        // 附件权限判定开始 --
        if ($flow->isFree()) {
            // 自由流程不限制
            $down = true;
        } else {
            if (StringUtil::findIn($process->attachpriv, 4)) {
                $down = true;
            }
        }
        if ($flow->isFixed() && empty($process->attachpriv)) {
            $down = $edit = $del = true;
        }
        $isHost = $rp->opflag == '1';
        $inProcessItem = $flow->isFixed() && StringUtil::findIn($process->processitem, '[A@]');
        $enabledInFreeItem = $this->isEnabledInFreeItem($flow, $rp);
        if ($isHost && ($inProcessItem || $enabledInFreeItem)) {
            if (StringUtil::findIn($process->attachpriv, 2)) {
                $edit = true;
            }
            if (StringUtil::findIn($process->attachpriv, 3)) {
                $del = true;
            }
            if ($flow->isFixed()) {
                $edit = $del = true;
            }
        }
        if ($flow->isFixed() && StringUtil::findIn($process->processitem, 5)) {
            $print = true;
        }
        return array(
            'down' => $down,
            'edit' => $edit,
            'del' => $del,
            'read' => $read,
            'print' => $print,
        );
    }

    /**
     * 新建操作
     */
    public function actionAdd()
    {
        $flowId = intval(Env::getRequest('flowid'));
        $flowname = Env::getRequest('name');
        $flow = new ICFlowType($flowId);
        if (!empty($flow->autoname)) {
            $flowname = WfNewUtil::replaceAutoName($flow, $this->uid);
        } else {
            $flowname = sprintf('%s (%s)', $flow->name, date('Y-m-d H:i:s'));
        }
        // if ( Env::submitCheck( 'formhash' ) ) {
        $this->checkFlowAccess($flowId, 1, $this->createUrl('new/add'));
        // $this->beforeAdd( $_POST, $flow );
        // 运行实例记录
        $run = array(
            'name' => $flowname,
            'flowid' => $flowId,
            'beginuser' => $this->uid,
            'begintime' => TIMESTAMP
        );
        $runId = FlowRun::model()->add($run, true);
        // 运行实例步骤记录
        $runProcess = array(
            'runid' => $runId,
            'processid' => 1,
            'uid' => $this->uid,
            'flag' => FlowConst::PRCS_UN_RECEIVE,
            'flowprocess' => 1,
            'createtime' => TIMESTAMP
        );
        FlowRunProcess::model()->add($runProcess);
        // 检查是否有自动编号规则，有的话加1，下次新建时就会递增
        if (strstr($flow->autoname, '{N}')) {
            FlowType::model()->updateCounters(array('autonum' => 1), sprintf('flowid = %d', $flowId));
        }
        // 运行实例表单数据
        $runData = array(
            'runid' => $runId,
            'name' => $flowname,
            'beginuser' => $this->uid,
            'begin' => TIMESTAMP
        );
        $this->handleRunData($flow, $runData);
        $param = array(
            'flowid' => $flowId,
            'runid' => $runId,
            'processid' => 1,
            'flowprocess' => 1,
            'fromnew' => 1
        );
        // if ( Ibos::app()->request->getIsAjaxRequest() ) {
        $this->ajaxReturn(array('isSuccess' => true, 'key' => WfCommonUtil::param($param)), Mobile::dataType());
        // } else {
        //     $url = Ibos::app()->urlManager->createUrl( 'workflow/form/index', array( 'key' => WfCommonUtil::param( $param ) ) );
        //     header( "Location: $url" );
        // }
        // } else {
        // 	$this->checkFlowAccess( $flowId, 1 );
        // 	// 有自动文号表达式的，替换之
        // 	if ( !empty( $flow->autoname ) ) {
        // 		$runName = WfNewUtil::replaceAutoName( $flow, $this->uid );
        // 	} else {
        // 		$runName = sprintf( '%s (%s)', $flow->name, date( 'Y-m-d H:i:s' ) );
        // 	}
        // 	$data = array(
        // 		'flow' => $flow->toArray(),
        // 		'runName' => $runName
        // 		// ,'lang' => Ibos::getLangSources()
        // 	);
        // 	$this->ajaxReturn( $data, Mobile::dataType() );
        // }
    }

    /**
     * 检查流程步骤权限
     * @param integer $flowId 流程类型ID
     * @param integer $processId 步骤ID
     * @param string $jump 出错后跳转的URL
     */
    public function checkFlowAccess($flowId, $processId, $jump = '')
    {
        $per = WfNewUtil::checkProcessPermission($flowId, $processId, $this->uid);
        if (!$per) {
            $errMsg = Ibos::lang('Permission denied');
            if (!empty($jump)) {
                $this->error($errMsg, $jump);
            } else {
                exit($errMsg);
            }
        }
    }

    /**
     * 处理运行实例数据
     * @param ICFlowType $type 工作流类型实例
     * @param array $runData
     */
    protected function handleRunData(ICFlowType $type, &$runData)
    {
        $structure = $type->form->structure;
        foreach ($structure as $k => $v) {
            if ($v['data-type'] == "checkbox" && stristr($v['content'], "checkbox")) {
                if (stristr($v['content'], "checked") || stristr($v['content'], " checked=\"checked\"")) {
                    $itemData = "on";
                } else {
                    $itemData = "";
                }
            } else if (!in_array($v['data-type'], array('select', 'listview'))) {
                if (isset($v['data-value'])) {
                    $itemData = str_replace("\"", "", $v['data-value']);
                    if ($itemData == "{macro}") {
                        $itemData = "";
                    }
                } else {
                    $itemData = '';
                }
            } else {
                $itemData = '';
            }
            $runData[strtolower($k)] = $itemData;
        }
        WfCommonUtil::addRunData($type->getID(), $runData, $structure);
    }

    /**
     * 初始化检索条件[动作，类型，排序三个维度]
     */
    public function init()
    {
        parent::init();
        $op = Env::getRequest('op');
        if (!in_array($op, array('category', 'list'))) {
            $op = 'list';
        }
        $sort = Env::getRequest('sort');
        $sortMap = array(
            'all' => Ibos::lang('All of it'),
            'host' => Ibos::lang('Host'),
            'sign' => Ibos::lang('Sign'),
            'rollback' => Ibos::lang('Rollback'),
        );
        if (!isset($sortMap[$sort])) {
            $sort = 'all';
        }
        $type = Env::getRequest('type');
        if (!isset($this->typeMapping[$type])) {
            $type = 'todo';
        }
        $flowId = Env::getRequest('flowid');
        if ($flowId) {
            $this->flowid = intval($flowId);
        }
        $this->op = $op;
        $this->sort = $sort;
        $this->type = $type;
        $this->sortText = $sortMap[$sort];
    }

    /**
     * 主办页面回退操作
     */
    public function actionFallback()
    {
        $key = Env::getRequest('key');
        $param = WfCommonUtil::param($key, 'DECODE');
        $flowId = $param['flowid'];
        $processId = $param['processid'];
        $flowProcess = $param['flowprocess'];
        $runId = $param['runid'];
        $last = intval(Env::getRequest('id'));
        $msg = StringUtil::filterCleanHtml(Env::getRequest('remind'));
        $per = WfCommonUtil::getRunPermission($runId, $this->uid, $processId);
        if (!StringUtil::findIn($per, 1) && !StringUtil::findIn($per, 2) && !StringUtil::findIn($per, 3)) {
            $this->ajaxReturn(array('isSuccess' => false), Mobile::dataType());
        }

        // 更新会签意见
        // 将回退意见保留至会签意见区
        if (!empty($msg)) {
            $fbData = array(
                'runid' => $runId,
                'processid' => $processId,
                'flowprocess' => $flowProcess,
                'uid' => $this->uid,
                'content' => $msg,
                'attachmentid' => '',
                'edittime' => TIMESTAMP,
            );
            FlowRunfeedback::model()->add($fbData);
        }

        $process = new ICFlowProcess($flowId, $flowProcess);
        $currentStep = Ibos::app()->db->createCommand()
            ->select()
            ->from('{{flow_run_process}}')
            ->where(" processid = '{$processId}' ")
            ->andWhere(" runid = '{$runId}' ")
            ->andWhere(" flowprocess = '{$flowProcess}' ")
            ->queryRow();
        if ($process->allowback > 0 && $processId != 1) {
            $prcsIDNew = $processId + 1;
            //------------直接返回上一步骤-----------------
            if (empty($last)) {
                $temp = Ibos::app()->db->createCommand()
                    ->select('frp.id,frp.flowprocess,frp.uid,fp.name,frp.parent')
                    ->from('{{flow_run}} fr')
                    ->leftJoin('{{flow_process}} fp', 'fr.flowid = fp.flowid')
                    ->leftJoin('{{flow_run_process}} frp', 'fr.runid = frp.runid AND frp.flowprocess = fp.processid')
                    ->where(array(
                        'and',
                        "fr.runid = {$runId}",
                        "frp.flowprocess = '{$currentStep['parent']}' "
                    ))
                    ->order('frp.id DESC')
                    ->limit(1)
                    ->queryRow();
                if ($temp) {
                    $flowProcessNew = $temp['flowprocess'];
                    $lastUID = $temp['uid'];
                    $parent = $temp['parent'];
                }
                $log = Ibos::lang('Return to prev step') . "【{$temp['name']}】";
            } else {
                $flowProcessNew = $last;
                $temp = FlowRunProcess::model()->fetch(array(
                    'select' => 'uid,flowprocess,parent',
                    'condition' => "runid = {$runId} AND flowprocess = '{$last}' AND opflag = 1",
                    'order' => 'processid',
                    'limit' => 1
                ));
                if ($temp) {
                    $lastUID = $temp['uid'];
                    $parent = $temp['parent'];
                }
                $log = Ibos::lang('Return to step', '', array('{step}' => FlowProcess::model()->fetchName($flowId, $flowProcessNew)));
            }

            // todo 如果在回退的路上有步骤正在办理，不允许回退
            $isCanBack = $this->checkBackProcessIsRun($runId, $flowId, $temp);
            if (!$isCanBack) {
                $this->ajaxReturn(
                    array(
                        'isSuccess' => false,
                        'msg' => Ibos::lang("Someone is dealing with the process of not returning the process")
                    ),
                    Mobile::dataType()
                );
            }
            //新建下一步
            $data = array(
                'runid' => $runId,
                'processid' => $prcsIDNew,
                'uid' => $lastUID,
                'flag' => '1',
                'flowprocess' => $flowProcessNew,
                'opflag' => '1',
                'topflag' => '0',
                'parent' => $parent,
                'isfallback' => 1,
                'createtime' => TIMESTAMP
            );
            FlowRunProcess::model()->add($data);
            //更新本步骤状态
            FlowRunProcess::model()->updateAll(array(
                'delivertime' => TIMESTAMP,
                'flag' => FlowConst::PRCS_DONE,
                'isfallback' => '2'
            ), "runid = {$runId} AND processid = {$processId} AND flowprocess = '{$flowProcess}' AND flag IN('1','2')");
            $key = WfCommonUtil::param(array(
                'runid' => $runId,
                'flowid' => $flowId,
                'processid' => $prcsIDNew,
                'flowprocess' => $flowProcessNew
            ));
            $url = Ibos::app()->urlManager->createUrl('workflow/form/index', array('key' => $key));
            $config = array(
                '{url}' => $url,
                '{msg}' => $msg
            );
            Notify::model()->sendNotify($lastUID, 'workflow_goback_notice', $config);
            //通知关注者
            $backname = User::model()->fetchRealnameByUid($this->uid);
            Common::sendNotifyToFocusUser($runId, 'Workflow back to focususer', $backname, $flowProcessNew);
            WfCommonUtil::runlog($runId, $processId, $flowProcess, $this->uid, 1, $log);
            $this->ajaxReturn(array('isSuccess' => true), Mobile::dataType());
        } else {
            $this->ajaxReturn(array('isSuccess' => false), Mobile::dataType());
        }
    }

    /**
     * 固定流程转交下一步提交处理
     */
    public function actionTurnNextPost()
    {
        //-----------参数初始化-----------
        // $runId = filter_input( INPUT_POST, 'runid', FILTER_SANITIZE_NUMBER_INT );
        // $flowId = filter_input( INPUT_POST, 'flowid', FILTER_SANITIZE_NUMBER_INT );
        // $processId = filter_input( INPUT_POST, 'processid', FILTER_SANITIZE_NUMBER_INT );
        // $flowProcess = filter_input( INPUT_POST, 'flowprocess', FILTER_SANITIZE_NUMBER_INT );
        // $topflag = filter_input( INPUT_POST, 'topflag', FILTER_SANITIZE_NUMBER_INT );
        $runId = Env::getRequest('runid');
        $flowId = Env::getRequest('flowid');
        $processId = Env::getRequest('processid');
        $flowProcess = Env::getRequest('flowprocess');
        $topflag = Env::getRequest('topflag');
        $topflag = isset($topflag) ? $topflag : null;
        $this->nextAccessCheck($topflag, $runId, $processId);
        //----------  执行流程插件 ----------------------
        $plugin = FlowProcess::model()->fetchTurnPlugin($flowId, $flowProcess);
        if ($plugin) {
            $pluginFile = './system/modules/workflow/plugins/turn/' . $plugin;
            if (file_exists($pluginFile)) {
                include_once($pluginFile);
            }
        }
        //----------------------------------------------
        //------------------- 开始流程转交或结束的处理 ----------------------
        // $prcsTo = filter_input( INPUT_POST, 'processto', FILTER_SANITIZE_STRING );
        // $prcsChoose = filter_input( INPUT_POST, 'prcs_choose', FILTER_SANITIZE_STRING );
        $prcsTo = Env::getRequest('processto');
        // $prcsChoose = Env::getRequest( 'prcs_choose' );

        $prcsToArr = explode(",", trim($prcsTo, ','));
        // $prcsChooseArr = explode( ",", trim( $prcsChoose, ',' ) );
        $prcsChooseArr = Env::getRequest('prcs_choose');
        if (!isset($prcsChooseArr)) {
            $prcsChooseArr = array();
        }
        $prcsChoose = implode($prcsChooseArr, ",");
        //----------  执行事务提醒 ----------------------
        // $message = filter_input( INPUT_POST, 'message', FILTER_SANITIZE_STRING );
        $message = Env::getRequest('message');
        $toId = $nextId = $beginUserId = $toallId = '';
        //下一步骤主办人
        $remind = Env::getRequest('remind');
        $prcs_user_op = Env::getRequest('prcs_user_op');
        $prcs_user = Env::getRequest('prcs_user');

        if (isset($remind[1])) {
            $nextId = '';
            if (isset($prcs_user_op)) {
                $nextId = intval($prcs_user_op);
            } else {
                foreach ($prcsChooseArr as $k => $v) {
                    $prcs_user_op_k = Env::getRequest('prcs_user_op' . $v);
                    if (isset($prcs_user_op_k)) {
                        // $nextId .= filter_input( INPUT_POST, 'prcs_user_op' . $k, FILTER_SANITIZE_STRING ) . ',';
                        $nextId .= $prcs_user_op_k . ',';
                    }
                }
                $nextId = trim($nextId, ',');
            }
        }
        //流程发起人
        if (isset($remind[2])) {
            $beginuser = FlowRunProcess::model()->fetchAllOPUid($runId, 1, true);
            if ($beginuser) {
                $beginUserId = StringUtil::wrapId($beginuser[0]['uid']);
            }
        }
        //所有经办人
        if (isset($remind['3'])) {
            $toallId = '';
            if (isset($prcs_user)) {
                $toallId = filter_input(INPUT_POST, 'prcs_user', FILTER_SANITIZE_STRING);
            } else {
                foreach ($prcsChooseArr as $k => $v) {
                    $prcs_user_k = Env::getRequest('prcs_user' . $k);
                    if ($prcs_user_k) {
                        $toallId .= filter_input(INPUT_POST, 'prcs_user' . $k, FILTER_SANITIZE_STRING);
                    }
                }
            }
        }
        $idstr = $nextId . ',' . $beginUserId . ',' . $toallId;
        $toId = StringUtil::filterStr($idstr);
        //更新当前步骤为已办结
        FlowRunProcess::model()->updateToOver($runId, $processId, $flowProcess);
        $run = FlowRun::model()->fetchByPk($runId);
        $flowPrcsNext = 0;
        //-----  结束流程 -----
        if ($prcsChoose == "") {
            $prcsUserOp = isset($prcs_user_op) ? intval($prcs_user_op) : ''; //主办人
            $prcsUser = isset($prcs_user) ? $prcs_user : ''; //经办人
            if ($run) {
                $pId = $run['parentrun'];
                $runName = $run["name"];
            }
            //更新当前步骤状态为办结
            FlowRunProcess::model()->updateAll(array('flag' => FlowConst::PRCS_DONE), sprintf("runid = %d AND processid = %d AND flowprocess = %d", $runId, $processId, $flowProcess));
            // 更新所有状态为“转交下一步”的步骤的状态为已办结,用来解决并发流程中，第一个转交并发步骤的运行实例状态总是为转交下一步的情况 @banyan
            FlowRunProcess::model()->updateAll(array('flag' => FlowConst::PRCS_DONE), sprintf("runid = %d AND flag = 3", $runId));
            //更新当前步骤办结时间
            FlowRunProcess::model()->updateAll(array('delivertime' => TIMESTAMP), sprintf("runid = %d AND processid = %d AND flowprocess = %d AND uid = %d", $runId, $processId, $flowProcess, $this->uid));
            //判断是否唯一执行中步骤,如果不是，则强制结束该工作
            $isUnique = FlowRunProcess::model()->getIsUnique($runId);
            if (!$isUnique) {
                FlowRun::model()->modify($runId, array('endtime' => TIMESTAMP));
            }
            // 子流程结束后跳转回父流程处理
            if ($pId != 0) {
                $parentflowId = FlowRun::model()->fetchFlowIdByRunId($pId); //子流程id
                $parentFormId = FlowType::model()->fetchFormIdByFlowId($parentflowId); //子流程表单id
                $parentPrcs = FlowRunProcess::model()->fetchIDByChild($pId, $runId); //子流程步骤id
                if ($parentPrcs) {
                    $parentPrcsId = $parentPrcs['processid'];
                    $parentFlowProcess = $parentPrcs['flowprocess'];
                }
                $parentProcess = FlowProcess::model()->fetchProcess($parentflowId, $parentPrcsId);
                //更新拷贝表单字段到父流程
                if ($parentProcess['relationout'] !== '') {
                    $relationArr = explode(',', trim($parentProcess['relationout'], ','));
                    $src = $des = $set = array();
                    foreach ($relationArr as $field) {
                        $src[] = substr($field, 0, strpos($field, "=>"));
                        $des[] = substr($field, strpos($field, "=>") + strlen("=>"));
                    }
                    $runData = WfHandleUtil::getRunData($runId);
                    $form = new ICFlowForm($parentFormId);
                    $structure = $form->parser->structure;
                    foreach ($structure as $k => $v) {
                        if ($v['data-type'] !== 'label' && in_array($v['data-title'], $des)) {
                            $i = array_search($v['data-title'], $des);
                            $ptitle = $src[$i];
                            $itemData = $runData[$ptitle];
                            if (is_array($itemData) && $v['data-type'] == "listview") {
                                $itemDataStr = "";
                                $newDataStr = "";
                                $j = 1;
                                for (; $j < count($itemData); ++$j) {
                                    foreach ($itemData[$i] as $val) {
                                        $newDataStr .= $val . "`";
                                    }
                                    $itemDataStr .= $newDataStr . "\r\n";
                                    $newDataStr = "";
                                }
                                $itemData = $itemDataStr;
                            }
                            $field = "data_" . $v['itemid'];
                            $set[$field] = $itemData;
                        }
                    }
                    if (!empty($set)) {
                        FlowDataN::model()->update($parentflowId, $pId, $set);
                    }
                }
                //更新父流程节点为办结
                WfHandleUtil::updateParentOver($runId, $pId);
                //返回父流程节点，设置了返回步骤才返回
                $prcsBack = Env::getRequest('prcsback') . '';
                if ($prcsBack != "") {
                    $parentPrcsIdNew = $parentPrcsId + 1;
                    $data = array(
                        'runid' => $pId,
                        'processid' => $parentPrcsIdNew,
                        'uid' => $prcsUserOp,
                        'flag' => '1',
                        'flowprocess' => $prcsBack,
                        'opflag' => 1,
                        'topflag' => 0,
                        'parent' => $parentFlowProcess,
                    );
                    FlowRunProcess::model()->add($data);
                    foreach (explode(",", trim($prcsUser, ',')) as $k => $v) {
                        if ($v != $prcsUserOp && !empty($v)) {
                            $data = array(
                                'runid' => $pId,
                                'processid' => $parentPrcsIdNew,
                                'uid' => $v,
                                'flag' => '1',
                                'flowprocess' => $prcsBack,
                                'opflag' => 0,
                                'topflag' => 0,
                                'parent' => $parentFlowProcess,
                            );
                            FlowRunProcess::model()->add($data);
                        }
                    }
                    //工作流日志 - 返回父流程
                    $parentRunName = FlowRun::model()->fetchNameByRunId($pId);
                    $content = "[{$runName}]" . Ibos::lang('Log return the parent process') . ":[{$parentRunName}]";
                    WfCommonUtil::runlog($runId, $processId, $flowProcess, $this->uid, 1, $content);
                    FlowRun::model()->modify($pId, array('endtime' => null));
                }
            } else {
                //结束办理通知关注者
                $turnUsername = User::model()->fetchRealnameByUid(Ibos::app()->user->uid);
                Common::sendNotifyToFocusUser($runId, 'Workflow end to focususer', $turnUsername);
            }
            //工作流日志
            $content = Ibos::lang('Form endflow');
            WfCommonUtil::runlog($runId, $processId, $flowProcess, $this->uid, 1, $content);
        } else {
            //---------------------是否允许按转交规则转交-----------------------
            $freeother = FlowType::model()->fetchFreeOtherByFlowId($flowId);
            $flowrun = FlowRun::model()->fetchByPk($runId);
            $flowtype = FlowType::model()->fetchTypeByFlowId($runId);
            $turnUsername = User::model()->fetchRealnameByUid(Ibos::app()->user->uid);
            $prcsChooseArrCount = count($prcsChooseArr);
            for ($i = 0; $i < $prcsChooseArrCount; $i++) {
                $flowPrcsNext = $prcsToArr[$prcsChooseArr[$i]]; //下一步骤序号
                $prcsIdNew = $processId + 1; //下一步骤运行编号
                $str = "prcs_user_op" . $prcsChooseArr[$i];
                $prcsUserOp = Env::getRequest($str); //传过来的是直接的用户ID
                //手机端判断转交主办人
                // if ( empty( $prcsUserOp ) ) {
                //     $this->ajaxReturn( array( "isSuccess" => false, "msg" => "必须选择主办人" ), Mobile::dataType() );
                //     exit();
                // }

                if ($freeother == 2) {
                    $prcsUserOp = WfHandleUtil::turnOther($prcsUserOp, $flowId, $runId, $processId, $flowProcess);
                }
                $str = "prcs_user" . $prcsChooseArr[$i];
                $prcsUser = explode(',', Env::getRequest($str));
                array_push($prcsUser, $prcsUserOp); //把主办人添加到经办人中，省去前台判断
                $prcsUser = implode(',', array_unique($prcsUser));
                if ($freeother == 2) {
                    $prcsUser = WfHandleUtil::turnOther($prcsUser, $flowId, $runId, $processId, $flowProcess, $prcsUserOp);
                }

                $str = "topflag" . $prcsChooseArr[$i];
                $topflag = intval(Env::getRequest($str));
                //-- 处理合并规则 --
                //强制合并节点与子流程
                $fp = FlowProcess::model()->fetchProcess($flowId, $flowPrcsNext);
                //非子流程
                if ($fp['childflow'] == 0) {
                    //如果检测到曾经按先到先得转交或者无主办人会签，则此次转交也按先到先得规则
                    $_topflag = FlowRunProcess::model()->fetchTopflag($runId, $prcsIdNew, $flowPrcsNext);
                    if ($_topflag) {
                        $topflag = $_topflag;
                    }
                    //如果检测到有主办人正在办理中，则此次转交设定的主办人作废，主办人依据现存的
                    $isOpHandle = FlowRunProcess::model()->getIsOpOnTurn($runId, $prcsIdNew, $flowPrcsNext);
                    if ($isOpHandle) {
                        $prcsUserOp = "";
                        $t_flag = 1;
                    } else {
                        $t_flag = 0;
                    }
                    foreach (explode(',', trim($prcsUser)) as $k => $v) {
                        if ($v == $prcsUserOp || $topflag == 1) {
                            $opflag = 1;
                        } else {
                            $opflag = 0;
                        }
                        //无主办人会签
                        if ($topflag == 2) {
                            $opflag = 0;
                        }
                        //-- 如果检测到同名用户正在办理，则不再转发给他，但如果曾经办理过，则会再次转发给他 --
                        $workedId = FlowRunProcess::model()->fetchProcessIDOnTurn($runId, $prcsIdNew, $flowPrcsNext, $v, 1);
                        if (!$workedId) {
                            $wrp = FlowRunProcess::model()->fetchRunProcess($runId, $processId, $flowProcess, $this->uid);
                            if ($wrp) {
                                $otherUser = $wrp['otheruser'] != '' ? $wrp['otheruser'] : '';
                            } else {
                                $otherUser = '';
                            }
                            $data = array(
                                'runid' => $runId,
                                'processid' => $prcsIdNew,
                                'uid' => $v,
                                'flag' => 1,
                                'flowprocess' => $flowPrcsNext,
                                'opflag' => $opflag,
                                'topflag' => $topflag,
                                'parent' => $flowProcess,
                                'createtime' => TIMESTAMP,
                                'otheruser' => $otherUser
                            );
                            FlowRunProcess::model()->add($data);
                        } else {
                            if ($prcsIdNew < $workedId) {
                                $prcsIdNew = $workedId;
                            }
                            $lastPrcsId = $workedId;
                            FlowRunProcess::model()->updateTurn($flowProcess, $prcsIdNew, $runId, $lastPrcsId, $flowPrcsNext, $v);
                        }
                    }
                    //主办人依照原来，则不能收回
                    if ($t_flag == 1) {
                        FlowRunProcess::model()->updateToOver($runId, $processId, $flowProcess);
                    } else {
                        FlowRunProcess::model()->updateToTrans($runId, $processId, $flowProcess);
                    }
                    //工作流日志
                    $userNameStr = User::model()->fetchRealnamesByUids($prcsUser);
                    $content = Ibos::lang('To the steps') . $prcsIdNew . "," . Ibos::lang('Transactor') . ":" . $userNameStr;
                    WfCommonUtil::runlog($runId, $processId, $flowProcess, $this->uid, 1, $content);
                } else {
                    //新建子流程
                    $runidNew = WfNewUtil::createNewRun($fp['childflow'], $prcsUserOp, $prcsUser, $runId);
                    $data = array(
                        'runid' => $runId,
                        'processid' => $prcsIdNew,
                        'uid' => $prcsUserOp,
                        'flag' => 1,
                        'flowprocess' => $flowPrcsNext,
                        'opflag' => 1,
                        'topflag' => 0,
                        'parent' => $flowProcess,
                        'childrun' => $runidNew,
                        'createtime' => TIMESTAMP
                    );
                    FlowRunProcess::model()->add($data);
                    //直接更新状态为办结
                    FlowRunProcess::model()->updateToOver($runId, $processId, $flowProcess);
                    //工作流日志
                    $content = Ibos::lang('Log new subflow') . $runidNew;
                    WfCommonUtil::runlog($runId, $processId, $flowProcess, $this->uid, 1, $content);
                }
                //通知关注者
                Common::sendNotifyToFocusUser($runId, 'Turn next to focususer', $turnUsername, $flowPrcsNext);
            }//for
        } // end else
        //------------------- 流程监控的硬性转交，要模拟接收办理过程------手机端不需要 -----------------
        // if ( $op == "manage" ) {
        // 	$parent = Ibos::app()->db->createCommand()
        // 			->select( 'parent' )
        // 			->from( '{{flow_run_process}}' )
        // 			->where( sprintf( "runid = %d AND processid = %d AND flowprocess = %d", $runId, $processId, $flowProcess ) )
        // 			->queryScalar();
        // 	$prcsIdpre = $processId - 1;
        // 	$sql = "UPDATE {{flow_run_process}} SET flag='4' WHERE runid='{$runId}' AND processid='{$prcsIdpre}'";
        // 	if ( $parent && $parent != "0" ) {
        // 		$sql.=" AND flowprocess IN ('$parent')";
        // 	}
        // 	Ibos::app()->db->createCommand()->setText( $sql )->execute();
        // }
        // MainUtil::setCookie( 'flow_turn_flag', 1, 30 );
        // $url = Ibos::app()->urlManager->createUrl( 'workflow/list/index', array( 'op' => 'list', 'type' => 'trans', 'sort' => 'all' ) );
        // $this->redirect( $url );
        //此时检测当前结束的流程是否是最后一个流程，如果是，那么增加一个结束的记录
        $lastRecord = FlowRunProcess::model()->findLastProcessRecord($runId);
        // 最后流程的标识符
        $lastProcessFlag = false;
        if ($lastRecord['flag'] == '4') {
            $lastProcess = FlowProcess::model()->findLastProcess($flowId, $lastRecord['flowprocess']);
            if (!empty($lastProcess)) {
                //如果设计的最后一个步骤和结束的最后一个步骤相等，则表示结束了
                $lastProcessFlag = true;
                Ibos::app()->db->createCommand()
                    ->insert('{{flow_run_process}}', array(
                        'runid' => $runId,
                        'processid' => $processId + 1,
                        'uid' => $this->uid,
                        'processtime' => TIMESTAMP,
                        'delivertime' => TIMESTAMP + 1,
                        'flag' => 4,
                        'flowprocess' => 0, //结束的特殊表示为0
                        'freeitem' => '',
                        'otheruser' => '',
                        'comment' => '',
                        'opflag' => 1,
                        'topflag' => 0,
                        'parent' => $lastRecord['flowprocess'],
                        'createtime' => TIMESTAMP,
                    ));
                //如果已经结束，强制结束所有的步骤
                Ibos::app()->db->createCommand()
                    ->update('{{flow_run_process}}', array('flag' => 4), "runid = '{$runId}' ");
            }
        }
        $toIdArr = explode(',', $toId);
        if (!empty($toIdArr)) {
            $flowType = FlowType::model()->findByPk($flowId);
//            $nextProcess = $processId + 1;
//            $flowRunProcess = Ibos::app()->db->createCommand()
//                ->select('*')
//                ->from('{{flow_run_process}}')
//                ->where('processid = :processid AND runid = :runid', array(':processid' => $nextProcess, ':runid' => $runId))
//                ->queryAll();
            $flowRunProcess = Ibos::app()->db->createCommand()
                ->select('*')
                ->from('{{flow_run_process}}')
                ->where('runid = :runid AND find_in_set(:flowprocess,parent) AND flag != 4', array(':flowprocess' => $flowProcess, ':runid' => $runId))
                ->queryAll();
            $alreadySendUidArr = array();
            for ($i = 0; $i < count($flowRunProcess); $i++) {
                $remindedUid = $flowRunProcess[$i]['uid'];
                if (!in_array($remindedUid, $toIdArr)) {
                    continue;
                }
                $alreadySendUidArr[] = $remindedUid;
                $param = array(
                    'runid' => $runId,
                    'flowid' => $flowId,
                    'processid' => $flowRunProcess[$i]['processid'],
                    'flowprocess' => $flowRunProcess[$i]['flowprocess'],
                    'type' => $flowType->type,
                );
                // 如果是最后一个流程，跳转到预览页面
                $returnUrl = Ibos::app()->urlManager->createUrl('workflow/form/index', array('key' => Common::param($param)));
                if (true === $lastProcessFlag) {
                    $returnUrl = Ibos::app()->urlManager->createUrl('workflow/preview/print', array('key' => Common::param($param)));
                }
                $config = array(
                    '{message}' => html_entity_decode($message),
                    '{url}' => $returnUrl
                );
                // 检查强制合并
                $process = new ICFlowProcess($flowId, $flowPrcsNext);
                if ($process->gathernode == 1) {
                    $isLastProcessTurn = FlowRunProcess::model()->isLastProcessTurn($flowId, $flowPrcsNext, $runId);
                    //下一步骤为强制合并步骤，尚有步骤未转交至此步骤，则不进行提醒
                    if ($isLastProcessTurn === true) {
                        Notify::model()->sendNotify($remindedUid, 'workflow_turn_notice', $config);
                    }
                } else {
                    Notify::model()->sendNotify($remindedUid, 'workflow_turn_notice', $config);
                }
            }
        }

        // 发送提醒消息给发起人
        if (isset($_POST['remind'][2])) {
            $beginUid = $run['beginuser'];
            if (!in_array($beginUid, $alreadySendUidArr) && in_array($beginUserId, $toIdArr)) {
                // 当发起人不在已发送消息的用户列表，并且在需要提醒的用户列表中，才发送提醒消息
                Notify::model()->sendNotify($beginUid, 'workflow_turn_notice', array(
                    '{message}' => $prcsChoose == "" ? html_entity_decode($message) : Ibos::lang('Your work had turn'),
                    '{url}' => Ibos::app()->urlManager->createUrl('workflow/preview/print', array('key' => Common::param(array(
                        'runid' => $runId,
                        'flowid' => $flowId,
                        'processid' => 1,
                        'flowprocess' => 1,
                        'type' => $flowType->type,
                    )))),
                ));
            }
        }
        $this->ajaxReturn(array("isSuccess" => true), Mobile::dataType());
    }

// -------------------来自listController结束-----------------------------
// -------------------来自handleController-----------------------------

    /**
     * 检查下一步的权限
     * @param type $topflag
     * @param type $runID
     * @param type $processID
     */
    protected function nextAccessCheck($topflag, $runId, $processId)
    {
        $per = WfCommonUtil::getRunPermission($runId, $this->uid, $processId);
        if ($topflag != 2) {
            // 如果不是系统管理员，主办人，管理与监控人，退出
            if (!StringUtil::findIn($per, 1) && !StringUtil::findIn($per, 2) && !StringUtil::findIn($per, 3)) {
                Env::iExit('必须是系统管理员，主办人，管理或监控人才能进行操作');
            }
        } else {
            //如果不是经办人
            if (!StringUtil::findIn($per, 4)) {
                Env::iExit('您不是经办人，没有权限进行操作。');
            }
        }
    }

    /**
     * 转交显示下一步
     */
    public function actionShowNext()
    {
        $key = Env::getRequest('key');
        if ($key) {
            $param = WfCommonUtil::param($key, 'DECODE');
            $flowId = $param['flowid'];
            $runId = $param['runid'];
            $processId = $param['processid'];
            $flowProcess = $param['flowprocess'];
            $op = isset($param['op']) ? $param['op'] : '';
            $topflag = Env::getRequest('topflag');
            $lang = Ibos::getLangSources();
            $this->nextAccessCheck($topflag, $runId, $processId);
            $run = new ICFlowRun($runId);
            $process = new ICFlowProcess($flowId, $flowProcess);
            $notAllFinished = array();
            $done = FlowConst::PRCS_DONE;
            $canCombile = true;
            $flag = Ibos::app()->db->createCommand()
                ->select('flag')
                ->from(FlowRunProcess::model()->tableName())
                ->where(" `runid` = '{$runId}' ")
                ->andWhere(" `processid` = '{$processId}' ")
                ->andWhere(" `flowprocess` = '{$flowProcess}' ")
                ->andWhere(" `uid` = '{$this->uid}' ")
                ->queryScalar();
            if (in_array($flag, array(FlowConst::PRCS_DONE, FlowConst::PRCS_TRANS))) {
                // 自己已经转交并且是当前步骤的话不能重复转交
                Env::iExit(Ibos::lang('Already trans'));
            }
            $flowProcessidArray = array($flowProcess);
            $all = $flowProcessidArray;
            $find = $notFind = array();
            while (1) {
                foreach ($flowProcessidArray as $rowid) {
                    $list2 = Ibos::app()->db->createCommand()
                        ->select('uid,flowprocess,flag,opflag,topflag')
                        ->from(FlowRunProcess::model()->tableName())
                        ->where(" `runid` = '{$runId}' ")
                        //->andWhere(" FIND_IN_SET( `flowprocess`, '{$rowid}' ) ")
                        ->queryAll();
                    if (!empty($list2)) {
                        foreach ($list2 as $row2) {
                            $isDone = $row2['flag'] != FlowConst::PRCS_DONE;
                            $isAgentHandleNotComplete = $row2['opflag'] == 0 && $row2['topflag'] != 2;
                            $isSameProcess = $row2['flowprocess'] == $rowid;
                            if ($isDone && $isAgentHandleNotComplete && $isSameProcess) {
                                $notAllFinished[] = $row2['uid'];
                                $find[] = $row2['flowprocess'];
                                $canCombile = false;
                            } else {
                                //都是完成的，则break了
                            }
                        }
                    } else {
                        $notFind[] = $rowid;
                    }
                }
                $diff = array_diff($find, $all);
                if (empty($diff) && empty($notFind)) {
                    break;
                } else {
                    $flowProcessidArray = array_unique(array_merge($diff, $notFind));
                    $all = array_unique(array_merge($all, $find, $notFind));
                    $notFind = array();
                }
            }
            if (!empty($notAllFinished)) {
                $notAllFinished = User::model()->fetchRealnamesbyUids($notAllFinished);
            } else {
                $notAllFinished = '';
            }
            // 检查强制合并
            if ($process->gathernode == 1) {
                $isLastProcessTurn = $this->isLastProcessTurn($flowId, $flowProcess, $runId);
                if ($isLastProcessTurn === false) {
                    //此步骤为强制合并步骤，尚有步骤未转交至此步骤，不能继续转交下一步
                    Env::iExit(Ibos::lang('Gathernode trans error'));
                }
            }
            // $processNext = $processid + 1;
            //未定义下一步骤,自动判断
            if ($process->processto == "") {
                $prcsMax = FlowProcess::model()->fetchMaxProcessIDByFlowID($flowId);
                if ($flowProcess !== $prcsMax) {
                    $process->processto = $flowProcess + 1;
                } else {
                    $process->processto = '0';
                }
            }
            $prcsArr = explode(',', trim($process->processto, ',')); // 有无多个步骤
            $prcsArrCount = count($prcsArr);
            $prcsEnableCount = 0;
            $prcsStop = "S";
            $prcsback = '';
            $prcsEnableFirst = null;
            $list = array();
            // 获取表单值
            $formData = WfHandleUtil::getFormData($flowId, $runId);
            foreach ($prcsArr as $key => $to) {
                $param = array(
                    'checked' => false
                );
                // 结束流程 走你~
                if ($to == '0') {
                    $param['isover'] = true;
                    // 步骤名称显示结束流程还是结束子流程
                    $param['prcsname'] = $run->parentrun !== '0' ? Ibos::lang('End subflow') : Ibos::lang('Form endflow');
                    $prcsStop = $key;
                    $prcsEnableCount++;
                    if ($prcsEnableCount == 1) {
                        $param['checked'] = true;
                        $prcsEnableFirst = $key;
                    }
                    // 如果是子流程，查询回退流程信息
                    if ($run->parentrun !== '0') {
                        $parentFlowId = FlowRun::model()->fetchFlowIdByRunId($run->parentrun);
                        $parentProcess = FlowRunProcess::model()->fetchIDByChild($run->parentrun, $runId);
                        $parentFlowProcess = $parentProcess['flowprocess'];
                        if ($parentFlowId && $parentFlowProcess) {
                            $temp = FlowProcess::model()->fetchProcess($parentFlowId, $parentFlowProcess);
                            if ($temp) {
                                $prcsback = $temp['processto'];
                                $backUserOP = $temp['autouserop'];
                                $param['backuser'] = $temp['autouser'];
                            }
                        }
                        if ($prcsback != '') {
                            $param['prcsEnabledUsers'] = WfHandleUtil::getPrcsUser($flowId, $prcsback);
                            $param['display'] = $prcsEnableFirst !== $prcsStop ? false : true;
                            if (isset($backUserOP)) {
                                $param['prcsopuser'] = $backUserOP;
                            }
                        }
                    }
                } else {
                    $param['isover'] = false;
                    $curProcess = FlowProcess::model()->fetchProcess($flowId, $to);
                    $param['prcsname'] = $curProcess['name'];
                    $processOut = FlowProcessTurn::model()->fetchByUnique($flowId, $flowProcess, $to);
                    if (!$processOut) {
                        $processOut = array('processout' => '', 'conditiondesc' => '');
                    }
                    //检查转入条件
                    $notpass = WfHandleUtil::checkCondition($formData, $processOut['processout'], $processOut['conditiondesc']);
                    if ($curProcess['childflow'] !== '0') {
                        $param['prcsname'] .= "(" . $lang['Subflow'] . ")";
                    }
                    if (substr($notpass, 0, 5) == 'setok') {
                        $notpass = "";
                    }
                    if ($notpass == "") {//符合条件的
                        $prcsEnableCount++;
                        if ($prcsEnableCount == 1 || ($process->syncdeal > 0 && !is_numeric($prcsStop))) {
                            $param['checked'] = true;
                            if ($prcsEnableCount == 1) {
                                $prcsEnableFirst = $key;
                            }
                        }
                        unset($param['notpass']);
                        //获取默认选择人员数组
                        $param['process'] = $curProcess;
                        $userSelect = $this->makeUserSelect($runId, $key, $curProcess, $param['prcsname'], $flowId, $processId);
                        $param = array_merge($param, $userSelect);
                    } else {
                        $param['notpass'] = $notpass;
                    }
                }
                $list[$key] = $param;
            }

            //----------------- 异常处理 ----------------------
            if ($prcsEnableCount == 0) {
                if ($notpass == "") {
                    Env::iExit($lang['Process define error']);
                } else {
                    Env::iExit($notpass);
                }
            }
            $data = array(
                'lang' => $lang,
                'notAllFinished' => $notAllFinished,
                'enableCount' => $prcsEnableCount,
                'prcsto' => $prcsArr,
                'prcsback' => $prcsback,
                'notpass' => isset($notpass) ? $notpass : '',
                'process' => $process->toArray(),
                'run' => $run->toArray(),
                'runid' => $runId,
                'flowid' => $flowId,
                'processid' => $processId,
                'flowprocess' => $flowProcess,
                'count' => $prcsArrCount,
                'prcsStop' => $prcsStop,
                'topflag' => $topflag,
                'list' => $list,
                'op' => $op
            );
            $this->ajaxReturn($data, Mobile::dataType());
        }
    }

    /**
     * 生成转交下一步经办用户选择框组件
     * @param type $runID
     * @param type $index
     * @param string $process
     * @param type $name
     * @param type $flowID
     * @param type $processID
     * @return type
     */
    protected function makeUserSelect($runId, $index, $process, $name, $flowId, $processId)
    {
        $lang = Ibos::getLangSource('workflow.default');
        $nopriv = '';

        /**
         * todo
         * 下一步为合并流程，并且已经有并发流程选择了下一步的主办人和经办人
         * 下一步如果已经被转交过，需要强制只能选择改流程
         * 目前情况为
         * 1 合并流程步骤
         * 2 并发回退步骤
         */
        $isMergerProcess = $this->mergerProcessUserSelect($process, $runId, $processId);

        //子流程
        if ($process['childflow'] != 0) {
            $flow = FlowType::model()->fetchByPk($process['childflow']);
            if ($flow) {
                $type = $flow['type'];
            }
            if ($type == FlowType::FLOW_TYPE_FREE) { //自由流程
                $process['prcs_id_next'] = '';
            }
            //获取子流程第一步的信息
            $subfp = FlowProcess::model()->fetchProcess($process['childflow'], 1);
            if ($subfp) {
                $prcsuser = WfHandleUtil::getPrcsUser($process['childflow'], $processId);
            } else {
                $prcsuser = '';
            }
            // $prcsuser = sprintf( '[%s]', !empty( $prcsuser ) ? StringUtil::iImplode( $prcsuser ) : ''  );
            if (empty($subfp['uid']) && empty($subfp['deptid']) && empty($subfp['positionid'])) {
                $nopriv = $lang['Not set step permissions']; //没有经办权限
            }

            $userSelect = array(
                'prcsEnabledUsers' => $prcsuser,
                'nopriv' => $nopriv
            );
        } else {
            if (empty($process['uid']) && empty($process['deptid']) && empty($process['positionid']) && empty($process['roleid'])) {
                $nopriv = $lang['Not set step permissions']; //没有经办权限
            }
            $prcsOpUser = $prcsUserAuto = '';
            $deptArr = DepartmentUtil::loadDepartment();
            //自动选择流程发起人
            if ($process['autotype'] == 1) {
                //发起人信息
                $uid = FlowRun::model()->fetchBeginUserByRunId($runId);
                $prcsuser = User::model()->fetchByUid($uid);
                //检查该发起人是否有经办权限
                if ($process['deptid'] == "alldept" ||
                    StringUtil::findIn($process['uid'], $prcsuser['uid']) ||
                    StringUtil::findIn($process['deptid'], $prcsuser['alldeptid']) ||
                    StringUtil::findIn($process['positionid'], $prcsuser['allposid']) ||
                    StringUtil::findIn($process['roleid'], $prcsuser['allroleid'])
                ) {
                    $prcsOpUser = $prcsuser['uid'];
                    $prcsUserAuto = $prcsuser['uid'] . ",";
                }
            } //自动选择本部门主管(2) or 上级主管领导(4) or 上级分管领导(6) or 一级部门主管(5)
            elseif (in_array($process['autotype'], array(2, 4, 5, 6))) {
                if ($process['autobaseuser'] != 0) {  //部门针对对象,0为当前步骤
                    // 基准对象
                    $baseUid = FlowRunProcess::model()->fetchBaseUid($runId, $process['autobaseuser']);
                    $baseuser = User::model()->fetchByUid($baseUid);
                    $autodept = $baseuser['deptid'];
                } else {
                    $autodept = Ibos::app()->user->deptid;
                }
                if (intval($autodept) > 0) {
                    if ($process['autotype'] == 2) { //本部门id
                        $tmpdept = $autodept;
                    } elseif ($process['autotype'] == 4 || $process['autotype'] == 6) { //上级部门id
                        $tmpdept = $deptArr[$autodept]['pid'] == 0 ? $autodept : $deptArr[$autodept]['pid'];
                    } elseif ($process['autotype'] == 5) { //一级部门id
                        $deptStr = Department::model()->queryDept($autodept, true);
                        $temp = explode(',', $deptStr);
                        $count = count($temp);
                        $dept = isset($temp[$count - 2]) ? $temp[$count - 2] : $autodept;
                        if ($deptArr[$dept]['pid'] != 0) {
                            $tmpdept = $deptArr[$dept]['deptid'];
                        } else {
                            $tmpdept = $autodept;
                        }
                    }

                    $manager = $deptArr[$tmpdept]['manager']; //部门主管

                    if ($process['autotype'] == 4 || $process['autotype'] == 6) {
                        $leader = $deptArr[$autodept]['leader']; //上级主管领导
                        $subleader = $deptArr[$autodept]['subleader']; //上级分管领导
                        if ($leader != '0' && $process['autotype'] == 4) {
                            $manager = $leader;
                        }
                        if ($subleader != '0' && $process['autotype'] == 6) {
                            $manager = $subleader;
                        }
                    }

                    if (!empty($manager)) {
                        $muser = User::model()->fetchByUid($manager);
                        if (!empty($muser)) {
                            if ($process['deptid'] == "alldept" ||
                                StringUtil::findIn($process['uid'], $muser['uid']) ||
                                StringUtil::findIn($process['deptid'], $muser['alldeptid']) ||
                                StringUtil::findIn($process['positionid'], $muser['allposid']) ||
                                StringUtil::findIn($process['roleid'], $muser['allroleid'])) {
                                $prcsUserAuto = $muser['uid'] . ",";
                            }
                            if ($prcsUserAuto != "") {
                                $prcsOpUser = strtok($prcsUserAuto, ",");
                            }
                        }
                    } else { //如果没设本部门主管
                        $userPerMax = "";
                        foreach (User::model()->fetchAllOtherManager($tmpdept) as $user) {
                            $user = User::model()->fetchByUid($user['uid']);
                            $uid = $user['uid'];
                            if ($process['deptid'] == "alldept" ||
                                StringUtil::findIn($process['uid'], $uid) ||
                                StringUtil::findIn($process['deptid'], $user['alldeptid']) ||
                                StringUtil::findIn($process['positionid'], $user['allposid']) ||
                                StringUtil::findIn($process['roleid'], $user['allroleid'])
                            ) {
                                if ($userPerMax == "") {
                                    $prcsOpUser = $uid;
                                    $prcsUserAuto .= $uid . ",";
                                    $userPerMax = $user['allposid'];
                                } elseif ($user['allposid'] == $userPerMax) {
                                    $prcsUserAuto .= $uid . ",";
                                }
                            }
                        }
                    }
                }
            } elseif ($process['autotype'] == 3) { //指定自动选择默认人员
                //默认主办人
                //默认主办人
                $autouserops = explode(",", $process['autouserop']);
                $lastAutouserop = $autouserops[count($autouserops) - 1];
                $autouserop = User::model()->fetchByUid($lastAutouserop);
                if (!empty($autouserop)) {
                    if ($process['deptid'] == "alldept" ||
                        StringUtil::findIn($process['uid'], $autouserop['uid']) ||
                        StringUtil::findIn($process['deptid'], $autouserop['alldeptid']) ||
                        StringUtil::findIn($process['positionid'], $autouserop['allposid']) ||
                        StringUtil::findIn($process['roleid'], $autouserop['allroleid'])) {
                        $prcsOpUser = $autouserop['uid'];
                    }
                }
                //默认经办人
                if (!empty($process['autouser'])) {
                    foreach (User::model()->fetchAllByUids(explode(',', trim($process['autouser'], ','))) as $user) {
                        if ($process['deptid'] == "alldept" ||
                            StringUtil::findIn($process['uid'], $user['uid']) ||
                            StringUtil::findIn($process['deptid'], $user['alldeptid']) ||
                            StringUtil::findIn($process['positionid'], $user['allposid']) ||
                            StringUtil::findIn($process['roleid'], $user['allroleid'])) {
                            $prcsUserAuto .= $user['uid'] . ',';
                        }
                    }
                }
            } elseif ($process['autotype'] == 7) { //从表单选择
                if (is_numeric($process['autouser'])) {
                    $itemData = FlowDataN::model()->fetchItem($process['autouser'], $process['flowid'], $runId);
                    $tmp = strtok($itemData, ",");
                    $userarr = array();
                    while ($tmp) {
                        $userarr[$tmp] = array();
                        $tmp = strtok(",");
                    }
                    $tempArray = explode(',', trim($itemData, ','));
                    //把uid转为realname，迎合接下来的查询语句。因为输入的可能是输入的姓名
                    foreach ($tempArray as $key => $value) {
                        if (is_numeric($value)) {
                            // 手机端用户选择框直接使用用户ID，和网页端处理U_不同
                            $value = User::model()->fetchRealnameByUid($value, '');
                            $tempArray[$key] = $value;
                        }
                    }
                    $uidArray = User::model()->findUidByRealnameX($tempArray);
                    $temp = array();
                    foreach ($uidArray as $u) {
                        $temp[] = array(
                            'uid' => $u,
                            'alldeptid' => User::model()->findAllDeptidByUid($u),
                            'allposid' => User::model()->findAllPositionidByUid($u),
                            'allroleid' => User::model()->findAllRoleidByUid($u),
                        );
                    }

                    foreach ($temp as $k => $v) {
                        $dept = Department::model()->queryDept($v['alldeptid']);
                        if ($process['deptid'] == "alldept" ||
                            StringUtil::findIn($process['uid'], $v['uid']) ||
                            StringUtil::findIn($process['deptid'], $dept) ||
                            StringUtil::findIn($process['positionid'], $v["allposid"]) ||
                            StringUtil::findIn($process['roleid'], $v['allroleid'])
                        ) {
                            $prcsUserAuto .= $v["uid"] . ",";
                        }
                    }
                    if ($prcsUserAuto != "") {
                        $prcsOpUser = strtok($prcsUserAuto, ",");
                    }
                }
            } elseif ($process['autotype'] == 8 && is_numeric($process['autouser'])) { //自动选择指定步骤主办人
                $uid = FlowRunProcess::model()->fetchBaseUid($runId, $process['autouser']);
                if ($uid) {
                    $temp = array(
                        'uid' => $uid,
                        'alldeptid' => User::model()->findAllDeptidByUid($uid),
                        'allposid' => User::model()->findAllPositionidByUid($uid),
                        'allroleid' => User::model()->findAllRoleidByUid($uid),
                    );
                    if ($temp) {
                        if ($process['deptid'] == 'alldept' ||
                            StringUtil::findIn($process['uid'], $temp['uid']) ||
                            StringUtil::findIn($process['deptid'], $temp["alldeptid"]) ||
                            StringUtil::findIn($process['positionid'], $temp["allposid"]) ||
                            StringUtil::findIn($process['roleid'], $temp['allroleid'])) {
                            $prcsOpUser = $prcsUserAuto = $temp['uid'];
                            $prcsUserAuto .= ",";
                        }
                    }
                }
            } elseif ($process['autotype'] == 9) { //自动选择本部门内符合条件所有人员
                $main = Ibos::app()->user->deptid;
                foreach (User::model()->fetchAllFitDeptUser($main) as $k => $v) {
                    if ($process['deptid'] == 'alldept' ||
                        StringUtil::findIn($process['uid'], $v['uid']) ||
                        StringUtil::findIn($process['deptid'], $v['alldeptid']) ||
                        StringUtil::findIn($process['positionid'], $v['allposid']) ||
                        StringUtil::findIn($process['roleid'], $v['allroleid'])) {
                        $prcsUserAuto .= $v['uid'] . ",";
                    }
                }
                if (!empty($prcsUserAuto)) {
                    $prcsOpUser = strtok($prcsUserAuto, ',');
                }
            } elseif ($process['autotype'] == 10) { //自动选择本一级部门内符合条件所有人员
                $main = Ibos::app()->user->deptid;
                $deptStr = Department::model()->queryDept($main, true);
                $temp = explode(',', $deptStr);
                $count = count($temp);
                $dept = isset($temp[$count - 2]) ? $temp[$count - 2] : $main;
                if ($deptArr[$dept]['pid'] != 0) {
                    $tmpdept = $deptArr[$dept]['deptid'];
                } else {
                    $tmpdept = $main;
                }
                foreach (User::model()->fetchAllFitDeptUser($tmpdept) as $k => $v) {
                    if ($process['deptid'] == "alldept" ||
                        StringUtil::findIn($process['uid'], $v['uid']) ||
                        StringUtil::findIn($process['deptid'], $v["alldeptid"]) ||
                        StringUtil::findIn($process['positionid'], $v["allposid"]) ||
                        StringUtil::findIn($process['roleid'], $v['allroleid'])) {
                        $prcsUserAuto .= $v['uid'] . ",";
                    }
                }
                if (!empty($prcsUserAuto)) {
                    $prcsOpUser = strtok($prcsUserAuto, ',');
                }
            } elseif ($process['autotype'] == 11) {
                $prcsUserAuto = Handle::getPrcsUser($flowId, $process['processid']);
                $prcsUserAuto = StringUtil::getUidAByUDPX($prcsUserAuto);
                $prcsOpUser = current($prcsUserAuto);
                $prcsUserAuto = implode(',', $prcsUserAuto);
            } elseif ($process['uid'] != "" && $process['deptid'] == "" && $process['positionid'] == "") {//非自动选择时，如只有一个经办人
                $prcsUserArr = explode(",", $process['uid']);
                $prcsUserCount = count($prcsUserArr) - 1;
                if ($prcsUserCount == 1) {
                    $prcsUserAuto = $process['uid'];
                    $prcsOpUser = $prcsUserAuto;
                }
            }
            $prcsuser = WfHandleUtil::getPrcsUser($flowId, $process['processid']);
            // $prcsuser = sprintf( '[%s]', !empty( $prcsuser ) ? StringUtil::iImplode( $prcsuser ) : ''  );

            $userSelect = array(
                'topdefault' => $process['topdefault'],
                'topmodify' => $process['userlock'],
                'prcsOpUser' => $prcsOpUser,
                'prcsUser' => $prcsUserAuto,
                'prcsEnabledUsers' => $prcsuser,
                'nopriv' => $nopriv,
                'ismergerprocess' => $isMergerProcess
            );
        }
        return $userSelect;
    }

    /**
     * 自由流程下一步流程或视图
     */
    public function actionFreeNext()
    {
        $key = Env::getRequest('key');
        $op = Env::getRequest('op');
        $topflag = Env::getRequest('topflag');
        $widget = $this->createWidget('application\modules\mobile\utils\FreeNext', array('key' => $key, 'op' => $op, 'topflag' => $topflag));
        if (Ibos::app()->request->getIsPostRequest()) {
            $topflag = Env::getRequest('topflagOld');
        }
        $this->nextAccessCheck($topflag, $widget->getKey('runid'), $widget->getKey('processid'));
        if (Ibos::app()->request->getIsPostRequest()) {
            $widget->nextPost();
        } else {
            $widget->run();
        }
    }

    /**
     * 经办人办理完毕
     */
    public function actionComplete()
    {
        $key = Env::getRequest('key');
        if ($key) {
            $param = Common::param($key, 'DECODE');
            $processId = $param['processid'];
            $flowProcess = $param['flowprocess'];
            $runId = $param['runid'];
            $topflag = Env::getRequest('topflag');
            $opflag = Env::getRequest('opflag');
            $inajax = Env::getRequest('inajax');
            $op = Env::getRequest('op');
            $content = Env::getRequest('content') ? StringUtil::filterCleanHtml(Env::getRequest('content')) : ''; // 会签意见
            $this->complete($runId, $processId, $opflag, $topflag, $inajax, $flowProcess, $op, $content);
        }
    }

// -------------------来自handleController结束-----------------------------

    /**
     * 经办人办理完毕操作
     * @param integer $runId
     * @param integer $processId
     * @param integer $opflag
     * @param integer $topflag
     * @param type $inajax
     * @param integer $flowProcess
     * @param string $op
     */
    protected function complete($runId, $processId, $opflag = 1, $topflag = 0, $inajax = 0, $flowProcess = '', $op = '', $content)
    {
        // 添加会签信息
        if (!empty($content)) {
            $fbData = array(
                'runid' => $runId,
                'processid' => $processId,
                'flowprocess' => $flowProcess,
                'uid' => Ibos::app()->user->uid,
                'content' => $content,
                'attachmentid' => '',
                'edittime' => TIMESTAMP,
            );
            FlowRunfeedback::model()->add($fbData);
        }
        $flowType = FlowRun::model()->fetchFlowTypeByRunId($runId);
        //----------- 自由流程主办人结束流程 或监控人结束流程 -----------
        if ($opflag || $op == 'manage') {
            //--- 检查有无后续预设步骤 ---
            $pidNext = $processId + 1;
            //-- 如果有 --
            // 存在后续预设步骤，不能结束
            if (FlowRunProcess::model()->getHasDefaultStep($runId, $pidNext)) {
                // 如果不是在管理模式进入，提示并退出函数
                if ($op != "manage") {
                    if ($inajax) {
                        $this->ajaxReturn(
                            array(
                                'isSuccess' => false,
                                'msg' => Ibos::lang('Subsequent default steps in the process')
                            )
                        );
                    } else {
                        $this->error(Ibos::lang('Subsequent default steps in the process'), $this->createUrl('list/index'));
                    }
                } else {
                    //删除后续步骤
                    FlowRunProcess::model()->deleteByIDScope($runId, $pidNext);
                }
            }
            //--- 写自己的办理完毕状态 ---
            if ($op != 'manage') {
                FlowRunProcess::model()->updateAll(array('delivertime' => TIMESTAMP), sprintf("runid = %d AND processid = %d AND uid = %d", $runId, $processId, $this->uid));
            } else {
                //-- 监控人结束补全步骤开始和结束时间为当前时间 --
                FlowRunProcess::model()->updateAll(array('delivertime' => TIMESTAMP), sprintf("runid = %d AND delivertime = 0", $runId));
                FlowRunProcess::model()->updateAll(array('processtime' => TIMESTAMP), sprintf("runid = %d AND processtime = 0", $runId));
            }
            //--- 结束本流程 ---
            FlowRunProcess::model()->updateAll(array('flag' => FlowConst::PRCS_DONE), "runid = {$runId}");
            //--- 写入结束时间 ---
            FlowRun::model()->modify($runId, array('endtime' => TIMESTAMP));
            //--- 流程日志 ---
            $content = $op != 'manage' ? Ibos::lang('Form endflow') : Ibos::app()->user->realname . Ibos::lang('Forced end process');
            Common::runlog($runId, $processId, $flowProcess, $this->uid, 1, $content);
            //--- 子流程返回父流程 ---
            $parentRun = FlowRun::model()->fetchParentByRunId($runId);
            if ($parentRun != 0) {
                $parentFlowId = FlowRun::model()->fetchFlowIdByRunId($parentRun);
                $temp = FlowRunProcess::model()->fetchIDByChild($parentRun, $runId);
                if ($temp) {
                    $parentProcessId = $temp['processid'];
                    $parentFlowprocess = $temp['flowprocess'];
                }
                $parentProcess = FlowProcess::model()->fetchProcess($parentFlowId, $parentFlowprocess);
                if ($parentProcess) {
                    $prcsBack = $parentProcess["processto"];
                    $backUserOp = $parentProcess["autouserop"];
                    $backUser = $parentProcess["autouser"];
                }
                //更新父流程节点为办结
                FlowRunProcess::model()->updateToOver($parentRun, $parentProcessId, $parentFlowprocess);
                //--- 创建父流程返回步骤 ---
                if ($prcsBack != "") {
                    $parentProcessIdNew = $parentProcessId + 1;
                    $data = array(
                        'runid' => $parentRun,
                        'processid' => $parentProcessIdNew,
                        'uid' => $backUserOp,
                        'flag' => 1,
                        'flowprocess' => $prcsBack,
                        'opflag' => 1,
                        'topflag' => 0,
                        'parent' => $parentFlowprocess
                    );
                    FlowRunProcess::model()->add($data);
                    $backUserArr = explode(",", $backUser);
                    for ($k = 0; $k < count($backUserArr); $k++) {
                        if ($backUserArr[$k] != '' && $backUserArr[$k] != $backUserOp) {
                            $data = array(
                                'runid' => $parentRun,
                                'processid' => $parentProcessIdNew,
                                'uid' => $backUserArr[$k],
                                'flag' => 1,
                                'flowprocess' => $prcsBack,
                                'opflag' => 0,
                                'topflag' => 0,
                                'parent' => $parentFlowprocess
                            );
                            FlowRunProcess::model()->add($data);
                        }
                    }
                } else {  //父流程若所有节点均已办结 则更新流程结束时间字段
                    if (!FlowRunProcess::model()->getIsNotOver($parentRun)) {
                        FlowRun::model()->modify($parentRun, array('endtime' => TIMESTAMP));
                    }
                }
            }
            $datas = array(
                // 'runid' => $parentRun,
                // 'temp' => $temp,
                // 'processid' => $parentProcessIdNew,
                // 'flowprocess' => $prcsBack,
                // 'topflag' => $topflag,
                // 'opflag' => $opflag,
                // 'inajax' => $inajax,
                // 'op' => $op,
                // 'uid' => $backUserOp
            );
            $flag = Env::getRequest('flag');
            // 结束自由流程通知关注者
            $turnUsername = User::model()->fetchRealnameByUid(Ibos::app()->user->uid);
            Common::sendNotifyToFocusUser($runId, 'Workflow end to focususer', $turnUsername);
            if ($flowType == 2 && $flag != 1) {
                $inajax && $this->ajaxReturn(array('isSuccess' => true, 'data' => $datas));
//                $this->redirect( $this->createUrl( 'list/index' ) );
            }
            $inajax && $this->ajaxReturn(array('isSuccess' => true, 'data' => $datas));
        } else {
            //----------- 从办人点击(办理完毕)按钮 -----------
            $flowId = FlowRun::model()->fetchFlowIDByRunID($runId);
            //无主办人会签，先检查是否最后一个经办人
            if ($topflag == 2) {
                if (!(FlowRunProcess::model()->getHasOtherOPUser($runId, $processId, $flowProcess, $this->uid))) {
                    if (is_null($flowProcess) || $flowProcess == "0") {
                        $turnpage = 'showNextFree';
                    } else {
                        $turnpage = 'showNext';
                    }
                    $param = array(
                        'flowid' => $flowId,
                        'processid' => $processId,
                        'flowprocess' => $flowProcess,
                        'runid' => $runId
                    );
                    $url = $this->createUrl('handle/' . $turnpage, array('key' => Common::param($param), 'topflag' => $topflag));
                    $this->ajaxReturn(array('status' => 2, 'url' => $url));
                }
            }
            //-- 写办理完毕状态 --
            $con = sprintf("runid = %d AND processid = %d AND uid = %d", $runId, $processId, $this->uid);
            if ($flowProcess !== "" && $flowProcess !== "0") {//如果是固定流程
                $con .= " AND flowprocess = " . $flowProcess;
            }
            FlowRunProcess::model()->updateAll(array('flag' => '4', 'delivertime' => TIMESTAMP), $con);
            //-- 经办人办理完毕后，如检查到所有经办人会签全部结束后，短信提醒主办人 --
            if (!FlowRunProcess::model()->getHasOtherAgentNotDone($runId, $processId)) {
                $run = FlowRun::model()->fetchByPk($runId);
                $uid = FlowRunProcess::model()->fetchNotDoneOpuser($runId, $processId);
                if ($uid) {
                    $param = array(
                        'runid' => $runId,
                        'flowid' => $flowId,
                        'processid' => $processId,
                        'flowprocess' => $flowProcess
                    );
                    $key = Common::param($param);
                    $config = array(
                        '{runname}' => $run['name'],
                        '{url}' => Ibos::app()->urlManager->createUrl('workflow/form/index', array('key' => $key)),
                        'id' => $key,
                    );
                    Notify::model()->sendNotify($uid, 'workflow_sign_notice', $config);
                }
            }
            Main::setCookie('flow_complete_flag', 1, 30);
            $data = array(
                // 'run' => $run,
                // 'uid' => $uid,
                // 'runid' => $runId,
                // 'flowid' => $flowId,
                // 'processid' => $processId,
                // 'flowprocess' => $flowProcess,
                // 'topflag' => $topflag,
                // 'opflag' => $opflag,
                // 'inajax' => $inajax,
                // 'op' => $op
            );
//            $url = Ibos::app()->urlManager->createUrl( 'workflow/list/index', array( 'op' => 'list', 'type' => 'trans', 'sort' => 'all' ) );
//            $this->redirect( $url );
            $this->ajaxReturn(array('isSuccess' => true, 'data' => $data), Mobile::dataType());
        }
    }

    /**
     * 结束流程
     */
    public function actionEnd()
    {
        if (Ibos::app()->request->getIsPostRequest()) {
            $id = Env::getRequest('id');
            if (Handle::endRun($id, $this->uid)) {
                $this->ajaxReturn(array('isSuccess' => true));
            } else {
                $this->ajaxReturn(array('isSuccess' => false));
            }
        }
    }

    /**
     * 下一步未接收之前的回收操作
     */
    public function actionTakeBack()
    {
        $key = Env::getRequest('key');
        if ($key) {
            $param = Common::param($key, 'DECODE');
            $status = Handle::takeBack($param['runid'], $param['flowprocess'], $param['processid'], $this->uid);
            if ($status == 0) {
                $this->ajaxReturn(array('isSuccess' => true));
            } else {
                $this->ajaxReturn(array('isSuccess' => false));
            }
        }
    }

    /**
     * 删除工作流
     */
    public function actionDel()
    {
        if (Ibos::app()->request->getIsPostRequest()) {
            $id = Env::getRequest('id');
            $runId = StringUtil::filterStr(StringUtil::filterCleanHtml($id));
            Handle::destroy($runId);
            $this->ajaxReturn(array('isSuccess' => true));
        }
    }

    /**
     * 是否允许回退
     * @param integer $parent
     * @return boolean
     */
    protected function isAllowBack($parent = 0)
    {
        return FlowRunProcess::model()->getIsAllowBack($this->runid, $this->processid, $this->flowprocess, $parent);
    }

    /**
     * 新增工作流运行实例前预处理
     * @param array $data 提交上来的数据
     * @param ICFlowType $type 工作流类型实例
     * @return void
     */
    protected function beforeAdd(&$data, ICFlowType $type)
    {
        $name = $data['name'];
        // 流程运行实例名称等于前缀+工作名称/文号+后缀
        if (isset($data['prefix'])) {
            $name = $data['prefix'] . $name;
        }
        if (isset($data['suffix'])) {
            $name = $name . $data['suffix'];
        }
        $runName = StringUtil::filterCleanHtml($name);
        // 检查流程运行实例名称是否存在
        $runNameExists = FlowRun::model()->checkExistRunName($type->getID(), $runName);
        if ($runNameExists) {
            $this->error(Ibos::lang('Duplicate run name'));
        }
        $data['name'] = $runName;
    }

    /**
     * 获取回退列表
     * @return array
     */
    protected function getBackList($parent)
    {
        $flowProcessIds = $this->getParent($parent);
        $list = array();
        foreach ($flowProcessIds as $flowprocess) {
            // 去掉空值项
            if (!empty($flowprocess)) {
                $list[] = array('id' => $flowprocess, 'name' => FlowProcess::model()->fetchName($this->flowid, $flowprocess));
            }
        }
        return $list;
    }

    /**
     * 获取当前实例步骤的父步骤
     * @staticvar array $ids
     * @param type $parent
     * @return type
     */
    private function getParent($parent, $id = 0)
    {
        static $ids = array();
        $row = Ibos::app()->db->createCommand()
            ->select('id,parent')
            ->from('{{flow_run_process}} frp')
            ->where(array(
                'and',
                "frp.runid = {$this->runid}",
                "frp.isfallback != 1",
                "frp.flowprocess = '{$parent}'",
                !empty($id) ? "frp.id < '{$id}'" : '',
            ))
            ->order('frp.processid DESC')
            ->limit(1)
            ->queryRow();
        if ($row) {
            $tmpParent = $row['parent'];
            $id = $row['id'];
            $ids[] = $tmpParent;
            return $this->getParent($tmpParent, $id);
        } else {
            return array_unique($ids);
        }
    }

    /**
     * 强制合并流程，并且已经有并发流程选择了下一步的主办人的特殊处理 .data('userSelect').setReadOnly()
     * 做到并发流程有第一个选择好了人员，那么不能转交给其他人
     * @param $process   设计流程数组
     * @param $runId     运行id
     * @param $processId 运行步骤id
     */
    private function mergerProcessUserSelect(&$process, $runId, $processId)
    {
        // 判断设计流程是不是合并流程
//        $gatherNodelist = FlowProcess::model()->fetchAllGatherNode($process['flowid'], $process['processid']);
//        if (!empty($gatherNodelist) && count($gatherNodelist) == 1) {
//            return false; // 上一步流程节点只有一个，不为合并流程
//        }
        // 最新操作，这里不一定要合并流程，只要被转交过就需要只能选该人员
        $isTran = FlowRunProcess::model()->getIsUntrans($runId, $process['processid']);
        if (!$isTran) {
            return false; // 未转交
        }
        // 获取运行步骤主办人
        $hostRunProcess = FlowRunProcess::model()->fetchAllRunProcessUserlistByRunIdANDFlowProcessANDOpFlag($runId, $process['processid'], 1);
        // 获取运行步骤经办人
        $agentRunProcess = FlowRunProcess::model()->fetchAllRunProcessUserlistByRunIdANDFlowProcessANDOpFlag($runId, $process['processid'], 0);

        // 没有转交到该合并步骤
        if (empty($hostRunProcess) && empty($agentRunProcess)) {
            return false;
        }

        $process['autotype'] = 3; // 选人为默认选择
        $process['autouserop'] = implode(',', Convert::getSubByKey($hostRunProcess, 'uid')); // 默认选择主办人
        $process['autouser'] = implode(',', Convert::getSubByKey($agentRunProcess, 'uid')); // 默认经办人

        return true;
    }

    /**
     * 检查强制合并流程是否能转交下一步
     * 思路：判断所有未完成步骤，是否能和要检查的强制合并步骤相连成一条线段
     * 如果能相连，标识强制合并步骤有可能被转交，不能转交下一步
     * 如果不能相连，证明与该强制合并步骤无关
     * @param $flowId
     * @param $processId
     */
    private function isLastProcessTurn($flowId, $processId, $runId)
    {
        // 获得所有未完成且需要寻找的步骤
        $needFindNotPrceDoneProcessIds = $this->getNeedFindDoneProcessIds($flowId, $processId, $runId);
        if (empty($needFindNotPrceDoneProcessIds)) {
            return true;
        }
        // 获得设计流程 未完成的步骤到强制合并步骤中是否构成流程线段
        $isProcessLine = $this->isflowProcessLine($flowId, $needFindNotPrceDoneProcessIds, $processId, $runId);
        // 有线段代表强制合并流程未完成
        return !$isProcessLine;
    }

    /**
     * 获得需要判断强制合并的流程步骤
     * @param $flowId
     * @param $processId
     * @param $runId
     * @return array
     */
    private function getNeedFindDoneProcessIds($flowId, $processId, $runId)
    {
        $res = array();
        $notPrceDoneList = FlowRunProcess::model()->fetchAllNotPrceDoneProcess($runId, 'flowprocess,opflag,topflag');
        if (empty($notPrceDoneList)) {
            return array();
        }
        foreach ($notPrceDoneList as $processRun) {
            if ($processRun['flowprocess'] == $processId) {
                continue; // 当前步骤自己没必要寻找
            }
            if ($processRun['opflag'] == '1') {
                // 主办的步骤未办理完成一定要确认是否会走到强制合并步骤
                $res[] = $processRun['flowprocess'];
            } else if ($processRun['opflag'] == '1' && $processRun['topflag'] == '2') {
                // 经办人必须是无主办会签的时候才一定要判断强制合并步骤
                $res[] = $processRun['flowprocess'];
            }
        }
        return array_unique($res);
    }

    /**
     * 是否找到该从开始步骤到目标步骤的流程线路
     * @param string $startProcessId 开始的步骤
     * @param $flowId
     * @param string $processId 结束的步骤
     */
    private function isflowProcessLine($flowId, $startProcessIdA, $processId, $runId)
    {
        $find = $startProcessIdA; // 第一步为起点
        $finish = array();
        $finishIds = array(); // 查找过的id
        while (1) {
            if (empty($find) && empty($finish)) {
                break;
            }
            foreach ($find as $key => $findItem) {
                $line = explode(',', $findItem);
                $findItemEnd = (int)end($line);

                if (max(array_count_values($line)) > 1) {
                    // 一条路径超过两次
                    unset($find[$key]);
                    continue;
                }

                if ($findItemEnd == $processId) {
                    unset($find[$key]);
                    $finish[] = $findItem;
                    continue;
                }

                if ($findItemEnd == 0) {
                    unset($find[$key]); // 彻底结束都没有找到改合并路线
                    continue;
                }

                $nextProcessA = $this->getNextProcess($flowId, $findItemEnd);
                if (empty($nextProcessA)) {
                    // 如果下一步为空，且该步骤id不为0，那么断头路可以不必找
                    unset($find[$key]);
                }

                unset($find[$key]); // 路径将原来那条删除，新增多条路径
                foreach ($nextProcessA as $nextProcess) {
                    $finishIds[] = $nextProcess;
                    $find[] = $findItem . ',' . $nextProcess;
                }
            }

            // 处理找到的线段
            foreach ($finish as $finishKey => $finishItem) {
                $line = explode(',', $finishItem);
                $findItemEnd = (int)end($line);
                if ($findItemEnd == 0) {
                    unset($finish[$finishKey]);
                    continue;
                }
                // 判断这条线该合并流程的上一步是否有结办记录
                // 合并流程上一步
                $lastProcessKey = count($line) - 2;
                // 如果上一步没有结办记录，证明有可能会转交到这一步，强制合并不能转交下一步
                if (!$this->isProcessPrceDone($line[$lastProcessKey], $runId)) {
                    return true;
                } else {
                    unset($finish[$finishKey]);
                    continue;
                }
            }
        }

        return false;
    }

    /**
     * getNextProcess 获得设计流程当前步骤的下一步设计流程id数组
     * @param $flowId
     * @param $nowProcessId
     * @return array
     */
    private function getNextProcess($flowId, $nowProcessId)
    {
        $allflowProcess = $this->getAllFlowProcess($flowId);

        static $nextProcess = array();
        if (empty($nextProcess)) {
            foreach ($allflowProcess as $process) {
                // 结束步骤没必要查找下一步
                if ($process['processid'] === 0) {
                    continue;
                }
                $nextProcess[$process['processid']] = $process['processto'];
            }
        }
        return explode(',', $nextProcess[$nowProcessId]);
    }

    /**
     * 获得一个设计流程的全部步骤，并用静态变量缓存起来
     * @param $flowId
     * @return mixed
     */
    private function getAllFlowProcess($flowId)
    {
        static $allflowProcess = array();
        if (empty($allflowProcess[$flowId])) {
            $allflowProcess[$flowId] = FlowProcess::model()->fetchAllByFlowId($flowId);
        }
        return $allflowProcess[$flowId];
    }

    /**
     * 判断步骤是否有结办记录
     * @param $flowProcess 设计流程id
     * @param $runId
     */
    private function isProcessPrceDone($flowProcess, $runId)
    {
        static $prceDoneList = array();
        if (empty($prceDoneList)) {
            $prceDoneList = FlowRunProcess::model()->fetchAllPrceDoneProcess($runId, "*");
        }
        foreach ($prceDoneList as $processRun) {
            if ($flowProcess == $processRun['flowprocess']) {
                if ($processRun['opflag'] == '1') {
                    // 主办的步骤未办理完成一定要确认是否会走到强制合并步骤
                    return true; // 主办人办理完成该步骤便办理完成
                } else if ($processRun['opflag'] == '0' && $processRun['topflag'] == '2') {
                    // 经办人必须是无主办会签的时候才算办理完成
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 判断并发回退，是否有活动的步骤
     * 如果有活动的步骤，是不能回退的
     * @param $runId 当前流程实例 id
     * @param $flowId 设计流程 id
     * @param $backBrocessTemp 回退步骤模板（准备入库的数据）
     */
    private function checkBackProcessIsRun($runId, $flowId, $backBrocessTemp)
    {
        $flowProcess = $backBrocessTemp['flowprocess'];
        $routeA = $this->getAllRouteByFlowProcess($flowId, $flowProcess);
        $needFlowProcessA = array(); // 所有需要检查的步骤
        foreach ($routeA as $value) {
            $needFlowProcessA = array_merge($needFlowProcessA, $value);
        }
        // 去掉当前步骤
        // 3. 取出所有未结办的流程，并判断是否在回退路径直接有活动的流程步骤
        $allFlowRunProcess = FlowRunProcess::model()->fetchAllNotPrceDoneProcess($runId, 'flowprocess');
        foreach ($allFlowRunProcess as $flowRunProcess) {
            if (in_array($flowRunProcess['flowprocess'], $needFlowProcessA)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 计算到某个步骤的路径
     * @param $flowId
     * @param $nowFlowProcess
     * @return array
     */
    private function getAllRouteByFlowProcess($flowId, $nowFlowProcess)
    {
        // 1. 取出设计流程步骤
        $ret = FlowProcess::model()->fetchAll(
            array(
                'select' => 'processid,name,processto',
                'condition' => "flowid = " . intval($flowId) . ' AND processid NOT IN(-1,0)',
                'order' => 'processid'
            )
        );
        $flowProcessA = array();

        // 2. 计算所有在当前步骤之前的步骤
        foreach ($ret as $value) {
            $flowProcessA[$value['processid']] = array(
                'processid' => $value['processid'],
                'name' => $value['name'],
                'processto' => explode(',', $value['processto'])
            );
        }

        // 流程步骤一定是从 1 开始 并且到0
        $routeA = $findA = array();
        $findA[] = array(1);
        $i = 100;
        while ($i) {
            if (empty($findA)) {
                break;
            }

            $tempA = array_pop($findA); // 出栈
            $next = end($tempA);
            // 如果找到了
            if ($next == $nowFlowProcess) {
                $routeA[] = $tempA;
                continue;
            }
            // 如果找到了0结束， 无效果的路径
            if ($next == 0) {
                continue;
            }
            // 如果下一步已经在路径中，循环的不要
            if (in_array($next, $tempA) && $next != 1) {
                continue;
            }

            // 压入待查找的队伍
            if (empty($flowProcessA[$next])) {
                continue;
            }
            foreach ($flowProcessA[$next]['processto'] as $processto) {
                $cloneTempA = $tempA;
                $cloneTempA[] = $processto;
                array_push($findA, $cloneTempA);
            }
            $i--; // 100 没找到夸张了
        }

        return $routeA;
    }
}
