<?php
/**
 * 试卷表
 * @author: houyingcai
 * @email:  594609175@qq.com
 * @date :  2017-05-19 17:43:16
 */

namespace Common\Service;

use Common\Common\AttachOperation;
use Common\Common\Cache;
use Common\Common\Msg;
use Common\Common\TaskCenter;
use Common\Common\User;
use Common\Model\AnswerDetailExtendModel;
use Common\Model\AnswerModel;
use Common\Model\AttrModel;
use Common\Model\BankModel;
use Common\Model\CategoryModel;
use Common\Model\PaperModel;
use Common\Model\PaperTempModel;
use Common\Model\RandomSnapshotModel;
use Common\Model\RightModel;
use Common\Model\SnapshotModel;
use Common\Model\TagModel;
use Common\Model\TopicAttrModel;
use Common\Model\TopicModel;
use VcySDK\Cron;
use VcySDK\Logger;
use VcySDK\Service;
use Common\Common\StudyMap;

class PaperService extends AbstractService
{
    // 闯关通过
    const  MY_BREAK_PASS = 1;
    // 闯关不通过
    const  MY_BREAK_NO_PASS = 2;

    // 闯关未交卷状态
    const  NOT_ASSIGNMENT = 0;
    // 闯关已交卷状态
    const  ASSIGNMENT = 1;

    /** @var RandomSnapshotModel */
    protected $_d_random;
    /** @var CategoryModel */
    protected $_d_cate;
    /** @var AnswerModel */
    protected $_d_answer;
    /** @var RightModel */
    protected $_d_right;
    /** @var BankModel */
    protected $_d_bank;
    /** @var TagModel */
    protected $_d_tag;
    /** @var AttrModel */
    protected $_d_attr;
    /** @var SnapshotModel */
    protected $_d_snapshot;
    /** @var PaperTempModel */
    protected $_d_temp;
    /** @var TopicModel */
    protected $_d_topic;
    /** @var TopicAttrModel */
    protected $_d_topic_attr;

    protected $_d_answer_detail_extend;

    // 构造方法

    public function __construct()
    {
        $this->_d = new PaperModel();
        $this->_d_cate = new CategoryModel();
        $this->_d_answer = new AnswerModel();
        $this->_d_right = new RightModel();
        $this->_d_bank = new BankModel();
        $this->_d_tag = new TagModel();
        $this->_d_attr = new AttrModel();
        $this->_d_snapshot = new SnapshotModel();
        $this->_d_temp = new PaperTempModel();
        $this->_d_topic = new TopicModel();
        $this->_d_topic_attr = new TopicAttrModel();
        $this->_d_random = new RandomSnapshotModel();
        $this->_d_answer_detail_extend = new AnswerDetailExtendModel();
        parent::__construct();
    }

    /**
     * 获取试卷基本信息(详情用)
     *
     * @author:daijun
     *
     * @param int $ep_id 试卷ID
     *
     * @return array|bool
     */
    public function get_paper_detail_admin($ep_id = 0)
    {
        // 获取试卷信息(此处需加上不是初始化状态的数据)
        $data = $this->_d->get_by_conds(['ep_id' => $ep_id, 'exam_status>?' => self::PAPER_INIT]);

        // 判断试卷是否存在
        if (empty($data)) {

            E('_EMPTY_PAPER_DATA');
        }

        // 默认关闭积分策略
        $is_open_integral = self::CLOSE_INTEGRAL;

        // 如果启用的自定义或者默认积分策略
        if ($data['integral_action_type'] != self::INTEGRAL_ACTION_TYPE_NO) {

            // 开启策略
            $is_open_integral = self::OPEN_INTEGRAL;
        }

        // 默认关闭学分策略
        $is_open_credit = self::CLOSE_INTEGRAL;

        // 如果启用的自定义或者默认学分策略
        if ($data['credit_action_type'] != self::CREDIT_ACTION_TYPE_FALSE) {

            // 开启策略
            $is_open_credit = self::OPEN_INTEGRAL;
        }

        // 获取试卷分类名称
        $cate_data = $this->_d_cate->get($data['ec_id']);


        $result = [
            'ep_id' => intval($ep_id),
            'ep_name' => $data['ep_name'],
            'exam_type' => intval($data['exam_type']),
            'paper_type' => intval($data['paper_type']),
            'ep_type' => intval($data['ep_type']),
            'is_all' => intval($data['is_all']),
            'begin_time' => $data['begin_time'],
            'end_time' => $data['end_time'],
            'paper_time' => intval($data['paper_time']),
            'is_notify' => intval($data['is_notify']),
            'notify_begin' => intval($data['notify_begin']),
            'notify_end' => intval($data['notify_end']),
            'is_recommend' => intval($data['is_recommend']),
            'answer_resolve' => intval($data['answer_resolve']),
            'total_score' => intval($data['total_score']),
            'pass_score' => intval($data['pass_score']),
            'intro' => $data['intro'],
            'reason' => $data['reason'], // 终止原因
            'reason_time' => $data['reason_time'], // 终止时间
            'marking_type' => intval($data['marking_type']),// 阅卷类型
            'is_open_integral' => $is_open_integral, // 是否开启积分策略
            'integral_action_type' => intval($data['integral_action_type']), // 积分策略
            'integral_strategyid' => strval($data['integral_strategyid']), // 积分策略ID
            'is_open_credit' => $is_open_credit, // 是否开启积分策略
            'credit_action_type' => intval($data['credit_action_type']), // 积分策略
            'credit_strategyid' => strval($data['credit_strategyid']), // 积分策略ID
            'is_pushmsg' => intval($data['is_pushmsg']), // 是否发送及时消息
            'ep_status' => $this->paper_status($data['exam_status'], $data['begin_time'], $data['end_time']), // 获取试卷当前状态
            'ec_id' => intval($data['ec_id']),
            'ec_name' => $cate_data['ec_name'],
            'import_num' => 0, // 导入人员数量
            'is_upset_topic' => intval($data['is_upset_topic']), // 是否打乱题目 (1:是 2: 否)
            'is_upset_option' => intval($data['is_upset_option']), // 是否打乱选项 (1:是 2: 否)
            'is_see_after_submit' => intval($data['is_see_after_submit']), // 交卷后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见)
            'is_see_after_over' => intval($data['is_see_after_over']), // 考试结束后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见)
            'is_open_anonymous_marking' => intval($data['is_open_anonymous_marking']), // 是否开启匿名阅卷:(1:开启 2:不开启)
            'is_open_makeup' => intval($data['is_open_makeup']), // 是否开启补考功能:(1:开启 2:不开启)
            'makeup_num' => intval($data['makeup_num']), // 补考次数限制
            'makeup_start_time' => intval($data['makeup_start_time']), // 补考开始时间
            'makeup_end_time' => intval($data['makeup_end_time']), // 补考结束时间
        ];


        // 获取封面图片
        $result['is_cover_open'] = intval($data['is_cover_open']);
        if (self::IS_COVER_PIC_OPEN == $data['is_cover_open']) {
            // 如果开启,则获取路径
            $result['cover_url'] = $this->format_cover($data['cover_id']);
        } else {
            $result['cover_url'] = '';
        }

        // 获取阅卷信息
        list($show_marking, $marking_list) = $this->get_marking($ep_id);

        $result['show_marking'] = $show_marking;// 是否显示阅卷
        $result['marking_list'] = $marking_list; // 阅卷人员列表

        return $result;
    }

    /**
     * 【后台】根据试卷ID获取试卷信息
     *
     * @author 何岳龙
     *
     * @param int $ep_id 试卷ID
     *
     * @return array
     */
    private function get_marking($ep_id = 0)
    {

        // 初始化是否显示阅卷
        $show_marking = self::NOT_SHOW_MARKING;

        // 初始化阅卷人员列表
        $marking_list = [];

        // 获取试卷信息(此处需加上不是初始化状态的数据)
        $data = $this->_d->get_by_conds(['ep_id' => $ep_id]);

        // 如果是模拟试卷
        if ($data['paper_type'] == self::SIMULATION_PAPER_TYPE) {

            return [$show_marking, $marking_list];
        }

        // 自主抽题或者固定抽题
        if (self::TOPIC_CUSTOMER == $data['ep_type'] || self::TOPIC_RULE == $data['ep_type']) {
            // 语音问答题总数
            $voice_total = $this->_d_snapshot->count_by_conds(['ep_id' => $ep_id, 'et_type' => self::TOPIC_TYPE_VOICE]);
            // 问答题总数
            $question_total = $this->_d_snapshot->count_by_conds([
                'ep_id' => $ep_id,
                'et_type' => self::TOPIC_TYPE_QUESTION
            ]);
            // 获取题目总数
            $total = intval($voice_total) + intval($question_total);
        }

        // 随机抽题
        if (self::TOPIC_RANDOM == $data['ep_type']) {
            // 解析rule权限
            $rule = unserialize($data['rule']);
            // 计算题目总数
            $total = intval($rule['question_count']) + intval($rule['voice_count']);
        }

        // 如果存在问答题以及语音问答题
        if (!empty($total)) {

            $show_marking = self::SHOW_MARKING;
            // 获取阅读人员权限
            $right_uids = $this->_d_right->list_by_conds(['er_type' => self::RIGHT_MARKING, 'epc_id' => $ep_id],
                null, [], 'uid');

            // 如果有阅读权限
            if ($right_uids) {
                // 获取有权限的阅读人UID
                $uids = array_unique(array_column($right_uids, 'uid'));

                // 初始化用户类
                $userServ = &User::instance();
                // 排序
                sort($uids);
                // 获取用户类型表
                $users = $userServ->listAll(['memUids' => $uids]);

                // 遍历用户
                foreach ($users as $user) {
                    // 组装数据
                    $marking_list[] = [
                        'memID' => $user['memUid'],
                        'memUsername' => $user['memUsername'],
                        'memFace' => $user['memFace'],
                    ];
                }
            }
        }

        return [$show_marking, $marking_list];
    }

    /**
     * 获取试卷基本信息(编辑用)
     *
     * @author:daijun
     *
     * @param int $ep_id 试卷ID
     *
     * @return array|bool
     */
    public function get_paper_base_detail($ep_id = 0)
    {
        // 获取试卷信息
        $data = $this->_d->get($ep_id);

        // 判断试卷是否存在
        if (empty($data)) {

            E('_EMPTY_PAPER_DATA');
        }

        // 默认关闭积分策略
        $is_open_integral = self::CLOSE_INTEGRAL;

        // 如果启用的自定义或者默认积分策略
        if ($data['integral_action_type'] != self::INTEGRAL_ACTION_TYPE_NO) {

            // 开启策略
            $is_open_integral = self::OPEN_INTEGRAL;
        }

        // 默认关闭学分策略
        $is_open_credit = self::CLOSE_INTEGRAL;

        // 如果启用的自定义或者默认学分策略
        if ($data['credit_action_type'] != self::CREDIT_ACTION_TYPE_FALSE) {

            // 开启策略
            $is_open_credit = self::OPEN_INTEGRAL;
        }


        // 获取阅卷信息
        list($show_marking, $marking_list) = $this->get_marking($ep_id);
        // 格式化返回数据
        $result = [
            'ep_id' => intval($data['ep_id']),
            'exam_type' => intval($data['exam_type']),
            'ep_type' => intval($data['ep_type']),
            'ep_name' => $data['ep_name'],
            'is_all' => intval($data['is_all']),
            'begin_time' => $data['begin_time'],
            'end_time' => $data['end_time'],
            'paper_time' => intval($data['paper_time']),
            'is_notify' => intval($data['is_notify']),
            'notify_begin' => intval($data['notify_begin']),
            'notify_end' => intval($data['notify_end']),
            'is_recommend' => intval($data['is_recommend']),
            'is_pushmsg' => intval($data['is_pushmsg']),
            'answer_resolve' => intval($data['answer_resolve']),
            'total_score' => intval($data['total_score']),
            'pass_score' => intval($data['pass_score']),
            'exam_status' => intval($data['exam_status']),
            'intro' => $data['intro'],
            'marking_type' => intval($data['marking_type']), // 阅卷类型
            'show_marking' => $show_marking, // 是否显示阅卷
            'marking_list' => $marking_list, // 阅卷人员列表
            'paper_type' => intval($data['paper_type']), // 试卷类型
            'is_open_integral' => $is_open_integral, // 是否启用积分策略
            'integral_action_type' => intval($data['integral_action_type']), // 积分策略类型
            'integral_strategyid' => $data['integral_strategyid'], // 积分策略ID
            'is_open_credit' => $is_open_credit, // 是否启用学分策略
            'credit_action_type' => intval($data['credit_action_type']), // 学分策略类型
            'credit_strategyid' => $data['credit_strategyid'], // 学分策略ID
            'is_cover_open' => intval($data['is_cover_open']), // 是否开启封面图片上传 (1:开启2:关闭)
            'import_num' => 0, // 导入人员数量
            'is_upset_topic' => intval($data['is_upset_topic']), // 是否打乱题目 (1:是 2: 否)
            'is_upset_option' => intval($data['is_upset_option']), // 是否打乱选项 (1:是 2: 否)
            'is_see_after_submit' => intval($data['is_see_after_submit']), // 交卷后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见)
            'is_see_after_over' => intval($data['is_see_after_over']), // 考试结束后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见)
            'is_open_anonymous_marking' => intval($data['is_open_anonymous_marking']), // 是否开启匿名阅卷:(1:开启 2:不开启)
            'is_open_makeup' => intval($data['is_open_makeup']), // 是否开启补考功能:(1:开启 2:不开启)
            'makeup_num' => intval($data['makeup_num']), // 补考次数限制
            'makeup_start_time' => intval($data['makeup_start_time']), // 补考开始时间
            'makeup_end_time' => intval($data['makeup_end_time']), // 补考结束时间

        ];

        if (self::IS_COVER_PIC_OPEN == $data['is_cover_open']) {
            // 如果开启,则格式化图片路径
            $result['cover_id'] = $data['cover_id'];
            $result['cover_url'] = $this->format_cover($data['cover_id']);
        } else {
            $result['cover_id'] = '';
            $result['cover_url'] = '';
        }

        return $result;
    }

    /**
     * 查询总数
     *
     * @param $data array 查询条件
     *
     * @return int|mixed
     */
    public function count_by_paper($data)
    {
        return $this->_d->count_by_paper($data);
    }

    /**
     * 查询列表
     *
     * @author: 蔡建华
     *
     * @param array $data 查询条件
     * @param null $page_option 分页参数
     * @param array $order_option 排序参数
     * @param string $fields 查询的字段
     *
     * @return array|bool
     */
    public function list_by_paper($data, $page_option = null, $order_option = [], $fields = '*')
    {
        return $this->_d->list_by_paper($data, $page_option, $order_option, $fields);
    }

    /**
     * 获取手机端试卷列表
     *
     * @param array $conds 查询条件
     * @param null $page_option 分页
     * @param array $order_option 排序
     * @param string $fields 字段
     *
     * @return array|bool 试卷列表
     */
    public function paper_api_list($conds = [], $page_option = null, $order_option = [], $fields = '*')
    {

        return $this->_d->paper_api_list($conds, $page_option, $order_option, $fields);
    }

    /**
     * 格式化试题列表返回数据
     * @author: 蔡建华
     *
     * @param array $list 待格式化数据
     * @param int $join_type 参与类型
     * @param string $uid 用户ID
     *
     * @return array 格式化后数据
     */
    public function paper_format($list = [], $join_type = 0, $uid = '')
    {

        // 待格式化数据不为空
        if ($list) {

            $ep_ids = array_column($list, 'ep_id');

            // 查询补考次数
            $answer_list = $this->_d_answer->list_by_conds(['ep_id' => $ep_ids, 'uid' => $uid, 'is_makeup' => self::IS_MAKEUP], null, ['ep_id' => 'DESC'], 'ea_id,ep_id');

            // 转换成以ep_id为主键的二维数组
            $new_data = array_count_values(array_column($answer_list, 'ep_id'));

            foreach ($list as $key => $val) {

                // 默认不显示补考标签
                $makeup_tag = 0;

                $ep_status = $this->paper_status($val['exam_status'], $val['begin_time'], $val['end_time']);

                $now_time = MILLI_TIME;
                // 如果是常规考试 && 是测评试卷 && 开启补考  && 当前时间大于补考开始时间 &&  当前时间小于补考结束时间
                if (self::NOMAL_TYPE == $val['exam_type'] && self::EVALUATION_PAPER_TYPE == $val['paper_type'] && self::OPEN_MAKEUP == $val['is_open_makeup'] && $now_time < $val['makeup_end_time'] && $now_time > $val['makeup_start_time']) {

                    if ($val['my_is_pass'] != self::MY_PASS && intval($new_data[$val['ep_id']]) == 0 && in_array($ep_status, [self::STATUS_STOP, self::STATUS_END])) {

                        // 考试已结束或者已终止 && 未参加考试
                        $makeup_tag = 1; // 显示补考标签

                    } elseif ($val['my_is_pass'] != self::MY_PASS && $val['ea_id'] > 0 && intval($new_data[$val['ep_id']]) < $val['makeup_num']) {

                        // 尚有补考次数
                        $makeup_tag = 1; // 显示补考标签
                    }

                    // 查询待批阅的答卷记录 和未交卷的 答卷记录
                    $answer_list_marking = $this->_d_answer->list_by_conds(['ep_id' => $val['ep_id'], 'uid' => $uid, 'answer_status<?' => self::READ_OVER], [0, 1], ['ea_id' => 'ASC']);

                    if (!empty($answer_list_marking)) {
                        // 存在待批阅或者待交卷的答卷信息
                        $val['answer_status'] = $answer_list_marking[0]['answer_status'];
                        $val['ea_id'] = $answer_list_marking[0]['ea_id'];

                        $makeup_tag = 0;
                    }
                }

                // 重新赋值
                $list[$key] = [
                    'ep_id' => intval($val['ep_id']),
                    'join_type' => $join_type,
                    'paper_type' => intval($val['paper_type']),
                    'ep_name' => $val['ep_name'],
                    'total_score' => $val['total_score'],
                    'ep_status' => $ep_status,
                    'my_score' => $join_type ? $val['my_score'] : '0',
                    'my_is_pass' => $join_type ? intval($val['my_is_pass']) : 0,
                    'answer_status' => $join_type ? intval($val['answer_status']) : 0,
                    'end_time' => $val['end_time'],
                    'join_count' => intval($val['join_count']),
                    'makeup_tag' => $makeup_tag,
                    'ea_id' => intval($val['ea_id']),
                ];
            }
        }

        return $list;
    }


    /**
     * 根据权限查询
     * @param array $conds
     *        +++ right array 权限数据
     *        +++  ec_id int 试卷分类
     * @param string $fields 查询参数
     *
     * @return array
     */
    public function list_by_right($conds = [], $fields = '*')
    {

        $right_ep_list = $this->_d->get_ep_ids_by_right($conds['right']);

        // 根据权限查询出来的试卷ID集合
        $ep_ids = array_column($right_ep_list, 'epc_id');

        $paper_list = [];
        if ($ep_ids) {

            $conds_new = [
                'ep_id' => $ep_ids,
                'exam_type' => self::NOMAL_TYPE, // 试卷类型:常规试卷
                'exam_status' => [self::PAPER_PUBLISH, self::PAPER_STOP], // 状态为已发布或者已终止
            ];

            if ($conds['ec_id']) {
                // 如果试卷分类不为空
                $conds_new['ec_id'] = $conds['ec_id'];
            }

            $paper_list = $this->_d->list_by_conds($conds_new, null, [], $fields);
        }

        return $paper_list;
    }

    /**
     * 新增试卷规则
     *
     * @author:daijun
     *
     * @param array $param 传入参数
     *
     * @return mixed
     */
    public function rule_add($param = [])
    {
        // 验证数据
        $data = $this->check_rule_add($param);

        // 属性序列化(序列化)
        $tag_data = !empty($param['tag_data']) ? serialize($param['tag_data']) : '';

        $data['tag_data'] = $tag_data;
        // 发布人ID
        $data['admin_id'] = '';
        // 发布人
        $data['launch_man'] = '';
        // 选中题目列表(序列化)
        $data['check_topic_data'] = '';
        // 考试说明
        $data['intro'] = '';
        // 终止理由
        $data['reason'] = '';
        // 终止人员ID
        $data['reason_user_id'] = '';
        // 终止人员名称
        $data['reason_user'] = '';
        // 试卷状态
        $data['exam_status'] = self::PAPER_INIT;
        // 新数据
        $data['is_old'] = self::NEW_DATA_STATE;

        if (!empty($param['rule'])) {
            // 随机选题 此处需要计算总分
            $rule = $param['rule'];

            // 将题目数为0的题目分数置为0
            $rule = $this->set_rule($rule);

            // 计算总分
            $data['total_score'] = intval($rule['single_count']) * intval($rule['single_score'])
                + intval($rule['multiple_count']) * intval($rule['multiple_score'])
                + intval($rule['judgment_count']) * intval($rule['judgment_score'])
                + intval($rule['question_count']) * intval($rule['question_score'])
                + intval($rule['voice_count']) * intval($rule['voice_score']);
        }

        try {
            // 开始事务
            $this->start_trans();

            // 保存基本信息
            $ep_id = $this->_d->insert($data);

            $temp_list = [];
            // 按照抽取规则和抽取条件进行抽取符合条件的题目
            $topic_list = $this->get_temp_list_by_epid($ep_id);

            if (!$topic_list || !is_array($topic_list) || empty($topic_list)) {
                // 若没有抽到题目回滚
                $this->rollback();
                E('_ERR_TOPIC_NOT_GET_LIST_DATA');
            }
            // 非随机抽取
            if ($data['ep_type'] != self::TOPIC_RANDOM) {

                // 初始化编号
                $i = self::ORDER_NUM;

                foreach ($topic_list as $v) {

                    $topic_data['ep_id'] = $ep_id;
                    $topic_data['et_id'] = $v['et_id'];
                    $topic_data['score'] = $v['score'];
                    $topic_data['order_num'] = $i;
                    $temp_list[] = $topic_data;
                    $i++;
                }
                if (!empty($temp_list)) {
                    // 存入备选题目列表
                    $this->_d_temp->insert_all($temp_list);
                }
            } elseif (self::TOPIC_RANDOM == $data['ep_type']) {

                // 题库题目数验证
                $this->check_topic_data($topic_list, $data);

                // / 初始化编号
                $k = self::ORDER_NUM;

                foreach ($topic_list as $v) {
                    // 根据规则设置分数
                    $score_rule = unserialize($data['rule']);
                    $score = $this->get_score_by_type($v['et_type'], $score_rule);
                    $topic_data['ep_id'] = $ep_id;
                    $topic_data['et_id'] = $v['et_id'];
                    $topic_data['eb_id'] = $v['eb_id'];
                    $topic_data['et_type'] = $v['et_type'];
                    $topic_data['score'] = $score;
                    $topic_data['title'] = $v['title'];
                    $topic_data['title_pic'] = $v['title_pic'];
                    $topic_data['options'] = $v['options'];
                    $topic_data['answer'] = $v['answer'];
                    $topic_data['answer_resolve'] = $v['answer_resolve'];
                    $topic_data['answer_coverage'] = $v['answer_coverage'];
                    $topic_data['match_type'] = $v['match_type'];
                    $topic_data['answer_keyword'] = $v['answer_keyword'];
                    $topic_data['order_num'] = $k;
                    $temp_list[] = $topic_data;
                    $k++;
                }

                if (!empty($temp_list)) {
                    // 存入随机题库列表
                    $this->_d_random->insert_all($temp_list);
                }
            }

            // 如果没有抽到对应的题
            if (empty($temp_list)) {

                $this->rollback();
                E('_ERR_TOPIC_NOT_DATA');
            }

            // 提交事务
            $this->commit();

        } catch (\Think\Exception $e) {
            \Think\Log::record($e);
            // 事务回滚
            $this->_set_error($e->getMessage(), $e->getCode());
            $this->rollback();

            return false;
        } catch (\Exception $e) {

            \Think\Log::record($e);
            $this->_set_error($e->getMessage(), $e->getCode());
            // 事务回滚
            $this->rollback();

            return false;
        }

        return $ep_id;
    }

    /**
     * 添加试题规则参数验证
     *
     * @author:daijun
     *
     * @param array $param
     *
     * @return array|bool
     */
    public function check_rule_add($param = [])
    {

        $data = [];
        // 验证试卷名称不能为空
        if (empty($param['ep_name'])) {

            E('_EMPTY_PAPER_NAME');
        }

        // 验证试卷名称长度
        if (get_str_len($param['ep_name']) > 64) {

            E('_ERR_PAPER_NAME_LENGTH');
        }
        $data['ep_name'] = $param['ep_name'];

        // 验证试卷分类
        if (empty($param['ec_id'])) {

            E('_EMPTY_CATE_ID');
        }
        $data['ec_id'] = $param['ec_id'];

        // 验证考试类型
        if (empty($param['exam_type'])) {

            E('_EMPTY_EXAM_TYPE');
        }
        $data['exam_type'] = $param['exam_type'];

        // 验证试卷类型
        if (!isset($param['paper_type'])) {

            E('_EMPTY_PAPER_TYPE');
        }
        $data['paper_type'] = $param['paper_type'];

        // 验证出题类型
        if (empty($param['ep_type'])) {

            E('_EMPTY_CHOICE_TYPE');
        }
        $data['ep_type'] = $param['ep_type'];

        // 验证所选题库列表
        if (empty($param['bank_list']) || !is_array($param['bank_list'])) {

            E('_EMPTY_BANK_LIST');
        }

        // 如果选择的题库多于50,则抛错
        if (count($param['bank_list']) > 50) {

            E('_ERR_BANK_LIST_MAX');
        }

        // 获取题库列表
        $eb_ids = array_column($param['bank_list'], 'eb_id');
        if (empty($eb_ids)) {

            E('_EMPTY_BANK_LIST');
        }
        sort($eb_ids);
        $data['bank_data'] = implode(',', $eb_ids);

        // 验证筛选类型
        if (!empty($param['search_type'])) {

            $data['search_type'] = $param['search_type'];
        }

        // 如果是规则抽题 和 随机选题
        if (self::TOPIC_RULE == $param['ep_type'] || self::TOPIC_RANDOM == $param['ep_type']) {

            // 固定规则抽题和随机生成试卷规则调整,验证是否开启高级抽取规则
            if (!in_array($param['advanced_choose'], [self::ADVANCED_CHOOSE_OPEN, self::ADVANCED_CHOOSE_CLOSE])) {
                // 高级抽取规则类型不正确
                E('_ERR_ADVANCED_CHOOSE');
            }

            $data['advanced_choose'] = $param['advanced_choose'];

            // 验证出题规则是否设置
            if (empty($param['rule'])) {

                E('_EMPTY_BANK_RULE');
            }

            $rule = $param['rule'];

            // 根据规则前端传的计算总题数
            $total_topic = intval($rule['single_count']) + intval($rule['multiple_count']) + intval($rule['judgment_count']) + intval($rule['question_count']) + intval($rule['voice_count']);

            if ($total_topic == 0) {
                // 题目总数为0,则抛错
                E('_ERR_TOTAL_TOPIC');
            }
            // 判断如果题目数不为空,则对应分数也不能为空,否则抛错
            if (is_int($rule['single_count']) && !$rule['single_count']) {
                // 单选题
                E('_ERR_SINGLE_TOPIC_SCORE');

            }
            if (is_int($rule['multiple_count']) && !$rule['multiple_count']) {
                // 多选题
                E('_ERR_MULTIPLE_TOPIC_SCORE');

            }
            if (is_int($rule['judgment_count']) && !$rule['judgment_count']) {
                // 判断题
                E('_ERR_JUDGMENT_TOPIC_SCORE');

            }
            if (is_int($rule['question_count']) && !$rule['question_count']) {
                // 问答题
                E('_ERR_QUESTION_TOPIC_SCORE');
            }
            if (is_int($rule['voice_count']) && !$rule['voice_count']) {
                // 语音题
                E('_ERR_VOICE_TOPIC_SCORE');
            }

            // 序列化抽题规则
            $data['rule'] = serialize($rule);
            // 开启高级抽取规则的,允许设置题库规则
            if (self::ADVANCED_CHOOSE_OPEN == $param['advanced_choose']) {
                // 验证所选题库设置的题目数
                if (empty($param['bank_topic_data'])) {
                    // 试卷所选题库题目设置不能为空
                    E('_EMPTY_BANK_TOPIC_DATA');
                }

                // 循环去除多余的字段
                foreach ($param['bank_topic_data'] as &$v) {
                    unset($v['$$hashKey']);
                }
                // 序列化题库题目设置
                $data['bank_topic_data'] = serialize($param['bank_topic_data']);
            } else {
                if (!empty($param['bank_topic_data'])) {
                    // 没有开启高级抽取规则时 设置题库抽取规则为空
                    E("_EMPTY_BANK_TOPIC_DATABASE");
                }
                $data['bank_topic_data'] = '';
            }

        } else {
            if ($data['rule'] && $data['bank_topic_data']) {

                E("_EMPTY_BANK_CHOOLSE_TOPIC_DATA");
            }

            // 自主抽题
            $data['rule'] = '';
            $data['bank_topic_data'] = '';
            $data['advanced_choose'] = self::ADVANCED_CHOOSE_CLOSE;
        }

        return $data;
    }

    /**
     * 将选择题目数为0的题目分数置0
     *
     * @param array $rule 题目规则
     *
     * @return array
     */
    public function set_rule($rule = [])
    {
        if ($rule['single_count'] == 0) {
            $rule['single_score'] = 0;
        }

        if ($rule['multiple_count'] == 0) {
            $rule['multiple_score'] = 0;
        }

        if ($rule['judgment_count'] == 0) {
            $rule['judgment_score'] = 0;
        }

        if ($rule['question_count'] == 0) {
            $rule['question_score'] = 0;
        }

        if ($rule['voice_count'] == 0) {
            $rule['voice_score'] = 0;
        }

        return $rule;
    }

    /**
     * 按照试卷设置的规则抽题
     *
     * @author:daijun
     *
     * @param int $ep_id 试卷ID
     * @param int $type (0:后台抽题,1:前台抽题)
     *
     * @return array
     */
    public function get_temp_list_by_epid($ep_id = 0, $type = 0)
    {
        $paper = $this->_d->get($ep_id);
        if (empty($paper)) {

            E('_EMPTY_PAPER_DATA');
        }

        // 标签属性数据
        $attr_ids = [];
        if (!empty($paper['tag_data'])) {
            // 反序标签属性
            $tag_data = unserialize($paper['tag_data']);
            foreach ($tag_data as $v) {
                $attr_ids_arr = [];
                // 判断是不是数组
                if (is_array($v['attr_data'])) {

                    $attr_ids_arr = array_column($v['attr_data'], 'attr_id');
                }

                if (!empty($attr_ids_arr) && is_array($attr_ids_arr)) {

                    $attr_ids = array_merge($attr_ids, $attr_ids_arr);
                }
            }
        }

        // 固定抽取和随机抽取有抽取规则(其中包括每种类型题目数量和分数)
        $rule_data = [];
        if (!empty($paper['rule'])) {

            $rule_data = unserialize($paper['rule']);
        }

        // 开启高级抽取是有每个题库每个类型的题目数和分数
        $bank_topic_data = [];
        if (!empty($paper['bank_topic_data'])) {

            $bank_topic_data = unserialize($paper['bank_topic_data']);
        }

        // 获取题库的ID集合
        $bank_ids = explode(',', $paper['bank_data']);

        $temp_list = [];
        // 自主选题 || 随机抽题
        if ((self::TOPIC_CUSTOMER == $paper['ep_type'] || self::TOPIC_RANDOM == $paper['ep_type']) && $type == 0) {
            // 后台手动自主抽取和随机抽取时调用,查询出所有符合条件的题目
            $temp_list = $this->choice_topic($bank_ids, $attr_ids, $paper);
        } elseif ((self::TOPIC_RULE == $paper['ep_type'] && $type == 0) || (self::TOPIC_RANDOM == $paper['ep_type'] && $type == 1)) {
            //  固定抽取和前端随机抽取时调用
            $temp_list = $this->rule_topic($bank_ids, $attr_ids, $paper, $bank_topic_data, $rule_data);

        }

        return $temp_list;
    }

    /**
     * 通过标签属性和题库进行自主自主选题
     *
     * @author daijun
     *
     * @param array $bank_ids 题库ID集合
     * @param array $attr_ids 属性ID集合
     * @param array $paper 试卷信息
     *
     * @return array|bool
     */
    public function choice_topic($bank_ids = [], $attr_ids = [], $paper = [])
    {
        // 属性ID去除重复
        $attr_ids = array_unique($attr_ids);

        $topic_list = [];
        if (empty($attr_ids)) {
            // 没有选择属性,则查询所选题库的所有试题
            $topic_list = $this->_d_topic->list_by_conds(['eb_id' => $bank_ids]);
        } else {
            // 获取满足其中之一条件的所有数据标签属性关系列表数据
            $topic_attr_all_list = $this->_d_topic_attr->list_by_conds(['eb_id' => $bank_ids, 'attr_id' => $attr_ids],
                null, ['eb_id' => 'ASC']);

            // 如果设置了属性:满足任意一个
            if (self::SEARCH_ATTR_TYPE_NOT_ALL == $paper['search_type']) {
                // 获取题目ID,并去除重复
                $topic_ids = array_unique(array_column($topic_attr_all_list, 'et_id'));
            } else {
                // 满足所有属性的抽题
                // 初始化题库下的题目IDS数组
                $eb_list = [];

                // 根据题目ID重新组合属性,以题目ID为键值,合并标签属性ID
                foreach ($topic_attr_all_list as $item) {
                    $eb_list[$item['et_id']][] = $item['attr_id'];
                }

                // 组装符合条件的题目id集合
                $topic_ids = [];
                foreach ($eb_list as $key => $v) {
                    // 属性ID去除重复
                    $v = array_unique($v);

                    if (array_intersect($attr_ids, $v) == $attr_ids) {
                        $topic_ids[] = $key;
                    }
                }
            }

            // 没有获取任何符合条件的题目
            if (empty($topic_ids)) {

                E('_ERR_TOPIC_NOT_IDS_DATA');
            }

            // 判断题目总数
            $topic_total = count($topic_ids);
            // 设置题目每次抽取50条件记录
            $count = self::TOPIC_MAX_COUNT;
            // 题目ID集合分组
            $topic_ids_arr = [];

            if ($topic_total > $count) {

                // 如果题目总数超过50,则需要进行分页查询,否则用in查询会死掉
                $countpage = ceil($topic_total / $count); // 计算总页面数
                // 对题目ID集合进行分页组装
                for ($i = 0; $i < $countpage; $i++) {
                    $topic_ids_arr[] = array_slice($topic_ids, $i * $count, $count);
                }
            } else {
                sort($topic_ids);
                $topic_ids_arr[] = $topic_ids;
            }

            // 循环查询题目列表
            foreach ($topic_ids_arr as $v) {
                // 查询题目列表
                $topic_arr = $this->_d_topic->list_by_conds(['et_id' => $v]);
                // 合并结果数组
                $topic_list = array_merge($topic_arr, $topic_list);
            }
        }

        if (empty($topic_list)) {

            E('_ERR_TOPIC_NOT_GET_LIST_DATA');
        }

        return $topic_list;

    }

    /**
     * 按规则抽题
     *
     * @author daijun
     *
     * @param array $bank_ids 题库ID集合
     * @param array $attr_ids 属性ID集合
     * @param array $paper 试卷信息
     * @param array $bank_topic_data 题库题目数量设置
     * @param array $rule_data 题目得分规则设置
     *
     * @return array
     */
    public function rule_topic($bank_ids = [], $attr_ids = [], $paper = [], $bank_topic_data = [], $rule_data = [])
    {

        $topic_all_list = [];
        if (empty($attr_ids)) {
            // 没有选择标签,则查询所选题库的所有试题
            $topic_list = $this->_d_topic->list_by_conds(['eb_id' => $bank_ids]);
            // 根据题目ID去除重复数据
            $topic_list_ids = array_combine_by_key($topic_list, 'et_id');
        } else {

            $attr_ids = array_unique($attr_ids);

            // 满足任意一个标签的抽题
            $topic_attr_all_list = $this->_d_topic_attr->list_by_conds(['attr_id' => $attr_ids, 'eb_id' => $bank_ids],
                null, ['eb_id' => 'ASC']);

            if (self::SEARCH_ATTR_TYPE_NOT_ALL == $paper['search_type']) {

                // 获取对应所有题目IDS
                $et_ids = array_column($topic_attr_all_list, 'et_id');

                // 查询题目详情
                $topic_et_id_list = $this->_d_topic->list_by_conds(['et_id' => $et_ids]);

                $topic_et_id_list = array_combine_by_key($topic_et_id_list, 'et_id');

                // 新增题目类型
                foreach ($topic_attr_all_list as $key => &$topic_v) {

                    $topic_v['et_type'] = $topic_et_id_list[$topic_v['et_id']]['et_type'];
                }

                // 满足任意一个标签的抽题
                $topic_list_ids = $topic_attr_all_list;
            } else {
                // 满足所有标签的抽题

                // 初始化题库下的题目IDS数组
                $eb_list = [];

                // 根据题目ID重新组合属性,以题目ID为键值,合并标签属性ID
                foreach ($topic_attr_all_list as $item) {
                    $eb_list[$item['et_id']][] = $item['attr_id'];
                }

                $topic_ids = [];
                // 组装符合条件的数据id集合
                foreach ($eb_list as $key => $v) {
                    $v = array_unique($v);
                    if (array_intersect($attr_ids, $v) == $attr_ids) {
                        $topic_ids[] = $key;
                    }
                }

                if (!empty($topic_ids)) {
                    // 查询符合条件的试题列表
                    $topic_list_ids = $this->_d_topic->list_by_conds(['et_id' => $topic_ids]);
                } else {
                    $topic_list_ids = [];
                }
            }
        }

        if (self::ADVANCED_CHOOSE_OPEN == $paper['advanced_choose']) {
            // 开启高级抽题规则
            $bank_topic_attr = [];

            foreach ($topic_list_ids as $item_v) {
                $bank_topic_attr[$item_v['eb_id']][] = $item_v['et_id'];
            }

            // 遍历题库ID
            foreach ($bank_ids as $k => $v) {
                // 检查是否需要在该题库抽题
                $is_bank_topic = true; // 默认需要从该题库需要抽题
                // 此处查询该题库是否设置了题目
                foreach ($bank_topic_data as $val) {

                    if ($val['eb_id'] == $v) {
                        // 计算当前题库设置测抽题数量
                        $t_count = $val['single_count'] + $val['multiple_count'] + $val['judgment_count'] + $val['question_count'] + $val['voice_count'];
                        // 不需要抽取题目的题库
                        if (!$t_count) {
                            $is_bank_topic = false;
                        }
                        break;
                    }
                }

                // 如果规则设置不从该题库抽题,则continue
                if (!$is_bank_topic) {
                    continue;
                }

                // 对该题库下的题目ID集合去重
                $et_ids = array_unique($bank_topic_attr[$v]);

                if (empty($et_ids)) {

                    E('_ERR_DELETE_RULE_TOPIC');
                }

                // 查询该题库下的符合条件的题目列表
                $topic_list = $this->_d_topic->list_by_conds(['et_id' => $et_ids]);

                // 将题目列表按照题目类型分组返回
                $topic_data = $this->format_by_et_type($topic_list, $bank_topic_data, $rule_data, $v);

                if (!empty($topic_data) && is_array($topic_data)) {
                    // 合并组装数组
                    $topic_all_list = array_merge($topic_all_list, $topic_data);
                }

            }
        } else {

            // 没有开高级抽题
            $topic_all_list = $this->format_simple_by_et_type($topic_list_ids, $rule_data);
        }

        if (empty($topic_all_list)) {

            E('_ERR_TOPIC_NOT_GET_LIST_DATA');
        }
        // 对抽取的题目进行打乱
        $topics = array_combine_by_key($topic_all_list, 'et_id');
        $tp_ids = array_column($topic_all_list, 'et_id');
        // 打乱ID
        shuffle($tp_ids);

        // 循环组装试题信息
        $result_list = [];
        foreach ($tp_ids as $k => $v) {

            $data = $topics[$v];
            $data['order_num'] = $k + 1;
            $result_list[] = $data;
        }

        return $result_list;
    }

    /**
     * 将题库题目列表按照题目类型分组返回
     *
     * @author daijun
     *
     * @param array $topic_list 题目列表
     * @param array $bank_topic_data 题库题目数量设置
     * @param array $rule_data 抽题规则
     * @param Int $eb_id 题库ID
     * @param bool $is_type_data true 默认 false 不处理分数
     *
     * @return array
     */
    public function format_by_et_type(
        $topic_list = [],
        $bank_topic_data = [],
        $rule_data = [],
        $eb_id = 0,
        $is_type_data = true
    )
    {
        $single_attr = [];   //单选题
        $multiple_attr = []; //多选题
        $judgment_attr = []; //判断题
        $question_attr = []; //问答题
        $voice_attr = [];    //语音题

        // 所属题库ID
        $bank_id = $eb_id;

        // 循环题目,按照类型组成新数组
        foreach ($topic_list as $_v) {

            // 试题类型:单选题
            if (self::TOPIC_TYPE_SINGLE == $_v['et_type']) {
                $single_attr[] = $_v;
                continue;
            }

            // 试题类型:判断题
            if (self::TOPIC_TYPE_JUDGMENT == $_v['et_type']) {
                $judgment_attr[] = $_v;
                continue;
            }

            // 试题类型:问答题
            if (self::TOPIC_TYPE_QUESTION == $_v['et_type']) {
                $question_attr[] = $_v;
                continue;
            }

            // 试题类型:多选题
            if (self::TOPIC_TYPE_MULTIPLE == $_v['et_type']) {
                $multiple_attr[] = $_v;
                continue;
            }

            // 试题类型:语音题
            if (self::TOPIC_TYPE_VOICE == $_v['et_type']) {
                $voice_attr[] = $_v;
                continue;
            }
        }

        // 取出该题库的出题规则
        $bank_topic = [];
        foreach ($bank_topic_data as $v) {
            if ($v['eb_id'] == $bank_id) {
                $bank_topic = $v;
            }
        }

        // *************************抽题开始*************************
        $single_list = []; //单选题
        $multiple_list = []; //多选题
        $judgment_list = []; //判断题
        $question_list = []; //问答题
        $voice_list = []; //语音题


        if (intval($bank_topic['single_count']) > 0) {

            // 存在单选题
            if (intval($bank_topic['single_count']) > count($single_attr)) {
                // 错误日志
                Logger::write('============单选题抽题失败日志记录begin===============');
                Logger::write('题库ID:' . $eb_id);
                Logger::write('抽题规则设置数量:');
                Logger::write(var_export($bank_topic['single_count'], true));
                Logger::write('实际抽出数量:');
                Logger::write(var_export(count($single_attr), true));
                Logger::write('============单选题抽题失败日志记录end===============');
                E('_ERR_SINGLE_CHOICE_PAPER');

            }

            $single_list = $this->get_rand_arr($single_attr, intval($bank_topic['single_count']));
            // 重新算分
            if ($is_type_data == true) {
                foreach ($single_list as &$v) {
                    $v['score'] = $rule_data['single_score'];
                }
            }
        }

        if (intval($bank_topic['multiple_count']) > 0) {

            // 存在多选题题
            if (intval($bank_topic['multiple_count']) > count($multiple_attr)) {

                // 错误日志
                Logger::write('============多选题抽题失败日志记录begin===============');
                Logger::write('题库ID:' . $eb_id);
                Logger::write('抽题规则设置数量:');
                Logger::write(var_export($bank_topic['multiple_count'], true));
                Logger::write('实际抽出数量:');
                Logger::write(var_export(count($multiple_attr), true));
                Logger::write('============多选题抽题失败日志记录end===============');

                E('_ERR_MULTIPLE_CHOICE_PAPER');
            }

            $multiple_list = $this->get_rand_arr($multiple_attr, intval($bank_topic['multiple_count']));
            if ($is_type_data == true) {
                foreach ($multiple_list as &$v) {
                    $v['score'] = $rule_data['multiple_score'];
                }
            }
        }

        if (intval($bank_topic['judgment_count']) > 0) {

            // 存在判断题
            if (intval($bank_topic['judgment_count']) > count($judgment_attr)) {
                // 错误日志
                Logger::write('============判断题抽题失败日志记录begin===============');
                Logger::write('题库ID:' . $eb_id);
                Logger::write('抽题规则设置数量:');
                Logger::write(var_export($bank_topic['judgment_count'], true));
                Logger::write('实际抽出数量:');
                Logger::write(var_export(count($judgment_attr), true));
                Logger::write('============判断题抽题失败日志记录end===============');

                E('_ERR_JUDGMENT_CHOICE_PAPER');
            }

            $judgment_list = $this->get_rand_arr($judgment_attr, intval($bank_topic['judgment_count']));
            if ($is_type_data == true) {
                foreach ($judgment_list as &$v) {
                    $v['score'] = $rule_data['judgment_score'];
                }
            }
        }

        if (intval($bank_topic['question_count']) > 0) {

            // 存在问答题
            if (intval($bank_topic['question_count']) > count($question_attr)) {

                // 错误日志
                Logger::write('============问答题抽题失败日志记录begin===============');
                Logger::write('题库ID:' . $eb_id);
                Logger::write('抽题规则设置数量:');
                Logger::write(var_export($bank_topic['question_count'], true));
                Logger::write('实际抽出数量:');
                Logger::write(var_export(count($question_attr), true));
                Logger::write('============问答题抽题失败日志记录end===============');

                E('_ERR_QUESTION_CHOICE_PAPER');
            }

            $question_list = $this->get_rand_arr($question_attr, intval($bank_topic['question_count']));
            if ($is_type_data == true) {
                foreach ($question_list as &$v) {
                    $v['score'] = $rule_data['question_score'];
                }
            }
        }

        if (intval($bank_topic['voice_count']) > 0) {

            // 存在语音题
            if (intval($bank_topic['voice_count']) > count($voice_attr)) {

                // 错误日志
                Logger::write('============语音题抽题失败日志记录begin===============');
                Logger::write('题库ID:' . $eb_id);
                Logger::write('抽题规则设置数量:');
                Logger::write(var_export($bank_topic['voice_count'], true));
                Logger::write('实际抽出数量:');
                Logger::write(var_export(count($voice_attr), true));
                Logger::write('============语音题抽题失败日志记录end===============');

                E('_ERR_VOICE_CHOICE_PAPER');
            }

            $voice_list = $this->get_rand_arr($voice_attr, intval($bank_topic['voice_count']));
            if ($is_type_data == true) {
                foreach ($voice_list as &$v) {
                    $v['score'] = $rule_data['voice_score'];
                }
            }
        }

        return array_merge($single_list, $judgment_list, $multiple_list, $question_list, $voice_list);
    }

    /**
     * 数组随机返回列表
     *
     * @author daijun
     *
     * @param array $topic 原始列表
     * @param int $num 随机数量
     *
     * @return array
     */
    public function get_rand_arr($topic = [], $num = 0)
    {
        if (count($topic) == $num) {
            return $topic;
        } else {
            $topic_list = [];
            if ($num == 1) {
                $index = rand(0, count($topic) - 1);
                $topic_list[] = $topic[$index];
            } else {
                $index_num = array_rand($topic, $num);
                foreach ($index_num as $v) {
                    $topic_list[] = $topic[$v];
                }
            }
        }

        return $topic_list;
    }

    /**
     * 将未开启高级抽取题目列表按照题目类型分组返回
     *
     * @param array $topic_list 题目列表
     * @param array $rule_data 抽题规则
     * @param bool $is_type_data true 默认 false 不处理分数
     *
     * @return array
     */

    public function format_simple_by_et_type($topic_list = [], $rule_data = [], $is_type_data = true)
    {

        $single_attr = []; // 单选题
        $multiple_attr = []; // 多选题
        $judgment_attr = []; // 判断题
        $question_attr = []; // 问答题
        $voice_attr = []; // 语音题
        // 循环题目,按照类型组成新数组
        foreach ($topic_list as $_v) {

            // 试题类型:单选题
            if (self::TOPIC_TYPE_SINGLE == $_v['et_type']) {
                $single_attr[] = $_v;
                continue;
            }

            // 试题类型:判断题
            if (self::TOPIC_TYPE_JUDGMENT == $_v['et_type']) {
                $judgment_attr[] = $_v;
                continue;
            }

            // 试题类型:问答题
            if (self::TOPIC_TYPE_QUESTION == $_v['et_type']) {
                $question_attr[] = $_v;
                continue;
            }

            // 试题类型:多选题
            if (self::TOPIC_TYPE_MULTIPLE == $_v['et_type']) {
                $multiple_attr[] = $_v;
                continue;
            }

            // 试题类型:语音题
            if (self::TOPIC_TYPE_VOICE == $_v['et_type']) {
                $voice_attr[] = $_v;
                continue;
            }
        }


        // *************************抽题开始*************************
        $single_list = []; //单选题
        $multiple_list = []; //多选题
        $judgment_list = []; //判断题
        $question_list = []; //问答题
        $voice_list = []; //语音题

        if (intval($rule_data['single_count']) > 0) {

            // 存在单选题
            if (intval($rule_data['single_count']) > count($single_attr)) {

                // 错误日志
                Logger::write('============没有题库单选题抽题失败日志记录begin===============');
                Logger::write('没有题库抽题规则设置数量:');
                Logger::write(var_export($rule_data['single_count'], true));
                Logger::write('没有题库实际抽出数量:');
                Logger::write(var_export(count($single_attr), true));
                Logger::write('============没有题库单选题抽题失败日志记录end===============');

                E('_ERR_SINGLE_CHOICE_PAPER');
            }

            $single_list = $this->get_rand_arr($single_attr, intval($rule_data['single_count']));
            if ($is_type_data == true) {
                foreach ($single_list as &$v) {
                    $v['score'] = $rule_data['single_score'];
                }
            }
        }

        if (intval($rule_data['multiple_count']) > 0) {

            // 存在多选题题
            if (intval($rule_data['multiple_count']) > count($multiple_attr)) {

                // 错误日志
                Logger::write('============没有题库多选题抽题失败日志记录begin===============');
                Logger::write('没有题库抽题规则设置数量:');
                Logger::write(var_export($rule_data['multiple_count'], true));
                Logger::write('没有题库实际抽出数量:');
                Logger::write(var_export(count($multiple_attr), true));
                Logger::write('============没有题库多选题抽题失败日志记录end===============');

                E('_ERR_MULTIPLE_CHOICE_PAPER');
            }

            $multiple_list = $this->get_rand_arr($multiple_attr, intval($rule_data['multiple_count']));
            if ($is_type_data == true) {
                foreach ($multiple_list as &$v) {
                    $v['score'] = $rule_data['multiple_score'];
                }
            }
        }

        if (intval($rule_data['judgment_count']) > 0) {

            // 存在判断题
            if (intval($rule_data['judgment_count']) > count($judgment_attr)) {
                // 错误日志
                Logger::write('============没有题库判断题抽题失败日志记录begin===============');
                Logger::write('没有题库抽题规则设置数量:');
                Logger::write(var_export($rule_data['judgment_count'], true));
                Logger::write('没有题库实际抽出数量:');
                Logger::write(var_export(count($judgment_attr), true));
                Logger::write('============没有题库判断题抽题失败日志记录end===============');

                E('_ERR_JUDGMENT_CHOICE_PAPER');
            }

            $judgment_list = $this->get_rand_arr($judgment_attr, intval($rule_data['judgment_count']));
            if ($is_type_data == true) {
                foreach ($judgment_list as &$v) {
                    $v['score'] = $rule_data['judgment_score'];
                }
            }
        }

        if (intval($rule_data['question_count']) > 0) {

            // 存在问答题
            if (intval($rule_data['question_count']) > count($question_attr)) {

                // 错误日志
                Logger::write('============没有题库问答题抽题失败日志记录begin===============');
                Logger::write('没有题库抽题规则设置数量:');
                Logger::write(var_export($rule_data['question_count'], true));
                Logger::write('没有题库实际抽出数量:');
                Logger::write(var_export(count($question_attr), true));
                Logger::write('============没有题库问答题抽题失败日志记录end===============');

                E('_ERR_QUESTION_CHOICE_PAPER');
            }

            $question_list = $this->get_rand_arr($question_attr, intval($rule_data['question_count']));
            if ($is_type_data == true) {
                foreach ($question_list as &$v) {
                    $v['score'] = $rule_data['question_score'];
                }
            }

        }

        if (intval($rule_data['voice_count']) > 0) {

            // 存在语音题
            if (intval($rule_data['voice_count']) > count($voice_attr)) {

                // 错误日志
                Logger::write('============没有题库语音题抽题失败日志记录begin===============');
                Logger::write('没有题库抽题规则设置数量:');
                Logger::write(var_export($rule_data['voice_count'], true));
                Logger::write('没有题库实际抽出数量:');
                Logger::write(var_export(count($voice_attr), true));
                Logger::write('============没有题库语音题抽题失败日志记录end===============');

                E('_ERR_VOICE_CHOICE_PAPER');
            }

            $voice_list = $this->get_rand_arr($voice_attr, intval($rule_data['voice_count']));
            if ($is_type_data == true) {
                foreach ($voice_list as &$v) {
                    $v['score'] = $rule_data['voice_score'];
                }
            }

        }

        return array_merge($single_list, $judgment_list, $multiple_list, $question_list, $voice_list);

    }

    /**
     * 根据U规则验证题目数量
     *
     * @author:daijun
     *
     * @param $topic_list array 题目列表
     * @param $params array 试卷表单参数
     */
    protected function check_topic_data($topic_list = [], $params = [])
    {
        if ($params['rule']) {
            $score_rule = unserialize($params['rule']);
        }

        if ($params['bank_topic_data']) {

            $bank_topic_data = unserialize($params['bank_topic_data']);
        }

        $single_attr = []; //单选题
        $multiple_attr = []; //多选题
        $judgment_attr = []; //判断题
        $question_attr = []; //问答题
        $voice_attr = []; //语音题

        // 循环题目,按照类型组成新数组
        foreach ($topic_list as $_v) {
            // 试题类型:单选题
            if (self::TOPIC_TYPE_SINGLE == $_v['et_type']) {

                if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) {
                    // 开启高级抽题规则 按照题库组合
                    $single_attr[$_v['eb_id']][] = $_v;
                } else {

                    $single_attr[] = $_v;
                }

                continue;
            }

            // 试题类型:判断题
            if (self::TOPIC_TYPE_JUDGMENT == $_v['et_type']) {

                if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) {
                    // 开启高级抽题规则 按照题库组合
                    $judgment_attr[$_v['eb_id']][] = $_v;
                } else {
                    $judgment_attr[] = $_v;
                }
                continue;
            }

            // 试题类型:问答题
            if (self::TOPIC_TYPE_QUESTION == $_v['et_type']) {

                if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) {
                    // 开启高级抽题规则 按照题库组合
                    $question_attr[$_v['eb_id']][] = $_v;
                } else {
                    $question_attr[] = $_v;
                }

                continue;
            }

            // 试题类型:多选题
            if (self::TOPIC_TYPE_MULTIPLE == $_v['et_type']) {

                if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) {
                    // 开启高级抽题规则 按照题库组合
                    $multiple_attr[$_v['eb_id']][] = $_v;
                } else {
                    $multiple_attr[] = $_v;
                }

                continue;
            }

            // 试题类型:语音题
            if (self::TOPIC_TYPE_VOICE == $_v['et_type']) {

                if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) {
                    // 开启高级抽题规则 按照题库组合
                    $voice_attr[$_v['eb_id']][] = $_v;
                } else {
                    $voice_attr[] = $_v;
                }

                continue;
            }
        }

        if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) {
            foreach ($bank_topic_data as $val) {
                // 单选
                if (intval($val['single_count']) > count($single_attr[$val['eb_id']])) {

                    E('_ERR_SINGLE_CHOICE_PAPER');
                }

                // 多选
                if (intval($val['multiple_count']) > count($multiple_attr[$val['eb_id']])) {

                    E('_ERR_MULTIPLE_CHOICE_PAPER');
                }

                // 判断
                if (intval($val['judgment_count']) > count($judgment_attr[$val['eb_id']])) {

                    E('_ERR_JUDGMENT_CHOICE_PAPER');
                }

                // 问题
                if (intval($val['question_count']) > count($question_attr[$val['eb_id']])) {

                    E('_ERR_QUESTION_CHOICE_PAPER');
                }

                // 语音
                if (intval($val['voice_count']) > count($voice_attr[$val['eb_id']])) {

                    E('_ERR_VOICE_CHOICE_PAPER');
                }
            }
        } else {
            // 单选
            if (intval($score_rule['single_count']) > count($single_attr)) {
                E('_ERR_SINGLE_CHOICE_PAPER');
            }
            // 多选
            if (intval($score_rule['multiple_count']) > count($multiple_attr)) {
                E('_ERR_MULTIPLE_CHOICE_PAPER');
            }
            // 判断
            if (intval($score_rule['judgment_count']) > count($judgment_attr)) {
                E('_ERR_JUDGMENT_CHOICE_PAPER');
            }
            // 问题
            if (intval($score_rule['question_count']) > count($question_attr)) {
                E('_ERR_QUESTION_CHOICE_PAPER');
            }
            // 语音
            if (intval($score_rule['voice_count']) > count($voice_attr)) {
                E('_ERR_VOICE_CHOICE_PAPER');
            }
        }
    }

    /**
     * 编辑试卷规则
     *
     * @author:daijun
     *
     * @param array $param 传入参数
     *
     * @return bool
     */
    public function rule_save($param = [])
    {
        // 验证ID
        if (empty($param['ep_id'])) {

            E('_EMPTY_EP_ID');
        }

        $ep_id = $param['ep_id'];

        // 验证其他数据
        if (!$data = $this->check_rule_add($param)) {

            E('_EMPTY_SAVE_DATA');
        }

        // 此处组装其他必填的字段信息
        $data['tag_data'] = !empty($param['tag_data']) ? serialize($param['tag_data']) : '';

        // 此处查询试卷原始数据
        $paper = $this->_d->get($ep_id);

        // 如果试卷规则和题库抽题规则无变化,则无需重新抽题
        if ($paper['rule'] == $data['rule']
            && $paper['bank_topic_data'] == $data['bank_topic_data']
            && $paper['search_type'] == $data['search_type']
            && $data['tag_data'] == $paper['tag_data']
            && $data['advanced_choose'] == $paper['advanced_choose']
            && $data['ep_type'] == $paper['ep_type'] && $data['bank_data'] == $paper['bank_data']
        ) {
            // 不重新抽题
            $is_choice = self::NOT_CHOICE;
        } else {
            // 重新抽题
            $is_choice = self::AGAIN_CHOICE;
        }

        if (!empty($param['rule'])) {
            // 随机选题 此处需要计算总分
            $rule = $param['rule'];

            // 将题目数为0的题目分数置为0
            $rule = $this->set_rule($rule);

            $data['total_score'] = intval($rule['single_count']) * intval($rule['single_score'])
                + intval($rule['multiple_count']) * intval($rule['multiple_score'])
                + intval($rule['judgment_count']) * intval($rule['judgment_score'])
                + intval($rule['question_count']) * intval($rule['question_score'])
                + intval($rule['voice_count']) * intval($rule['voice_score']);
        }

        $data['is_old'] = self::NEW_DATA_STATE; // 新数据

        try {
            // 开始事务
            $this->start_trans();
            // 更新试卷表
            $this->_d->update($ep_id, $data);
            // 如果重新抽题
            if (self::AGAIN_CHOICE == $is_choice) {
                // 抽题存入
                $temp_list = [];
                // 如果试卷出题规则不是随机,则按配置抽题
                $topic_list = $this->get_temp_list_by_epid($ep_id);
                if (!$topic_list || !is_array($topic_list) || empty($topic_list)) {
                    // 若没有抽到题目回滚
                    $this->rollback();
                    E('_ERR_TOPIC_NOT_GET_LIST_DATA');
                }

                if ($data['ep_type'] != self::TOPIC_RANDOM) {

                    // 删除备选题目列表
                    $this->_d_temp->delete_by_conds(['ep_id' => $ep_id]);
                    // 删除已选题目列表
                    $this->_d_snapshot->delete_by_conds(['ep_id' => $ep_id]);
                    // 非随机抽取
                    // 初始化编号
                    $i = self::ORDER_NUM;

                    // 循环组装入库数据
                    foreach ($topic_list as $v) {

                        $temp_list[] = [
                            'ep_id' => $ep_id,
                            'et_id' => $v['et_id'],
                            'score' => $v['score'],
                            'order_num' => $i
                        ];

                        $i++;
                    }

                    if (!empty($temp_list)) {
                        // 存入备选题目列表
                        $this->_d_temp->insert_all($temp_list);
                    }
                } elseif (self::TOPIC_RANDOM == $data['ep_type']) {
                    //按照题库设置进行匹配
                    $this->check_topic_data($topic_list, $data);

                    // 删除已选题目列表
                    $this->_d_random->delete_by_conds(['ep_id' => $ep_id]);
                    // 取出符合规则的所有试题
                    $rule_data = unserialize($data['rule']);
                    // 初始化编号
                    $k = self::ORDER_NUM;

                    foreach ($topic_list as $v) {
                        // 根据规则设置分数
                        $score = $this->get_score_by_type($v['et_type'], $rule_data);
                        $topic_data = [];
                        $topic_data['ep_id'] = $ep_id;
                        $topic_data['eb_id'] = $v['eb_id'];
                        $topic_data['et_type'] = $v['et_type'];
                        $topic_data['et_id'] = $v['et_id'];
                        $topic_data['title'] = $v['title'];
                        $topic_data['title_pic'] = $v['title_pic'];
                        $topic_data['options'] = $v['options'];
                        $topic_data['score'] = $score;
                        $topic_data['answer'] = $v['answer'];
                        $topic_data['answer_resolve'] = $v['answer_resolve'];
                        $topic_data['answer_coverage'] = $v['answer_coverage'];
                        $topic_data['match_type'] = $v['match_type'];
                        $topic_data['answer_keyword'] = $v['answer_keyword'];
                        $topic_data['order_num'] = $k;
                        $temp_list[] = $topic_data;
                        $k++;
                    }

                    if (!empty($temp_list)) {
                        $this->_d_random->insert_all($temp_list);
                    }
                }
            }

            // 如果没有抽到对应的题
            if (empty($temp_list) && self::AGAIN_CHOICE == $is_choice) {

                $this->rollback();
                E('_ERR_TOPIC_NOT_DATA');
            }

            // 提交事务
            $this->commit();

        } catch (\Think\Exception $e) {
            \Think\Log::record($e);
            // 事务回滚
            $this->_set_error($e->getMessage(), $e->getCode());
            $this->rollback();

            return false;
        } catch
        (\Exception $e) {

            \Think\Log::record($e);
            $this->_set_error($e->getMessage(), $e->getCode());
            // 事务回滚
            $this->rollback();

            return false;
        }

        return $ep_id;
    }

    /**
     * 编辑试卷基本信息(基本试卷)
     *
     * @author:daijun
     *
     * @param array $param 传入参数
     *
     * @return bool
     */
    public function base_save($param = [])
    {

        $ep_id = $param['ep_id'];
        // 获取试卷信息
        $paper = $this->_d->get($ep_id);

        if ($paper['exam_type'] > 1) {
            // 如果是任务,线下培训,则走新的验证和更新
            $this->base_save_new($param, $paper);

            return true;
        }

        // 被学习路径关联的课程不可删除
        $mapUsedIds = StudyMap::checkAppDataUsed((array)$ep_id);
        if (!empty($mapUsedIds)) {
            E('_ERR_MAP_USED_CANNOT_DELETE');
        }

        // 验证属性
        if (!$data = $this->check_base_save($param)) {

            E('_EMPTY_SAVE_DATA');
        }

        $right = $data['right'];
        unset($data['right']);

        $uids_old = [];

        // 试卷题目列表初始化
        $topic_list = [];

        // 此处获取所有应参与人员的ID集合
        $right_serv = new RightService();
        $right_res = $right_serv->format_db_data($right);

        // 获取数据更新后的权限用户列表
        $right_res['is_all'] = $data['is_all'];
        $uids_now = $right_serv->list_uids_by_right($right_res);

        // 参与人数
        $data['unjoin_count'] = count($uids_now);

        // 如果是发布动作
        if ($data['exam_status'] == self::PAPER_PUBLISH) {

            // 试卷题目查询条件
            $conditions_topic['ep_id'] = $ep_id;

            // 查询组装总题目数量
            if ($paper['ep_type'] != self::TOPIC_RANDOM) {
                // 如果不是随机出题,此处查询该试卷的试题总数
                $data['topic_count'] = $this->_d_snapshot->count_by_conds($conditions_topic);

                // 获取试卷题目列表
                $topic_list = $this->_d_snapshot->list_by_conds($conditions_topic);

            } else {
                // 如果是随机出题
                $rule = unserialize($paper['rule']);
                $data['topic_count'] = intval($rule['single_count'])
                    + intval($rule['multiple_count'])
                    + intval($rule['judgment_count'])
                    + intval($rule['question_count'])
                    + intval($rule['voice_count']);

                // 获取试卷题目列表
                $topic_list = $this->_d_random->list_by_conds($conditions_topic);
            }

            // 如果是已发布的试卷进行编辑再次发布,此处需查询之前的权限数据,推送消息会用到
            if ($paper['exam_status'] == self::PAPER_PUBLISH) {

                // 权限查询条件
                $conds = [
                    'epc_id' => $ep_id,
                    'er_type' => AnswerService::RIGHT_PAPER
                ];

                // 获取未参与考试人员列表及人数
                $answer_serv = new AnswerService();
                $unjoin_data = $answer_serv->get_unjoin_data($conds, $ep_id, $paper['is_all']);

                // 获取数据更新前的权限用户列表
                $uids_old = $unjoin_data['unjoin_list'];
            }

            // 发布时间
            $data['publish_time'] = MILLI_TIME;
        }

        // 初始化阅卷人权限
        $right_marking = [];

        if (isset($param['marking_list']) && !empty($param['marking_list'])) {

            // 重组阅卷人列表
            foreach ($param['marking_list'] as $v) {

                // 如果阅卷人存在
                if ($v['memID']) {

                    $right_marking[] = [
                        'epc_id' => $ep_id,
                        'er_type' => self::RIGHT_MARKING,
                        'uid' => $v['memID'],
                        'cd_id' => '',
                        'tag_id' => '',
                        'job_id' => '',
                        'role_id' => ''
                    ];
                }
            }
        }

        // 查询分类详情
        $cate_data = $this->_d_cate->get($paper['ec_id']);

        // 将分类的状态赋值给试卷状态
        $data['cate_status'] = $cate_data['ec_status'];

        // 阅卷类型
        $data['marking_type'] = $param['marking_type'];

        // 获取最后更新时间
        $data['last_time'] = MILLI_TIME;

        // 如果阅卷人不为空 并且是发布状态
        if (!empty($right_marking) && self::PAPER_PUBLISH == $data['exam_status']) {
            // 阅卷人发送消息
            $this->marking_send($param);
        }

        // 获取答卷扩展信息
        $extend_list = $this->_d_answer_detail_extend->list_by_conds(['ep_id' => $ep_id]);
        $extend_list = array_combine_by_key($extend_list, 'et_id');

        try {
            // 开始事务
            $this->start_trans();

            // 更新试卷表
            $this->_d->update($ep_id, $data);

            // 删除之前的权限
            $this->_d_right->delete_by_conds(['epc_id' => $ep_id, 'er_type' => self::RIGHT_PAPER]);
            // 删除之前的阅卷人权限
            $this->_d_right->delete_by_conds(['epc_id' => $ep_id, 'er_type' => self::RIGHT_MARKING]);

            // 如果有权限数据
            if (!empty($right)) {
                // 新增权限数据
                $this->_d_right->insert_all($right);
            }

            // 如果有阅卷人
            if (!empty($right_marking)) {
                // 新增阅卷
                $this->_d_right->insert_all($right_marking);
            }

            // 分表oa_exam_answer_detail_extend待增数据初始化
            $detail_extend_data = [];
            if (!empty($topic_list)) {

                foreach ($topic_list as $topic) {

                    $extend_info = $extend_list[$topic['et_id']];
                    if (empty($extend_info)) {
                        // 组装题目详情数据
                        $et_detail = [
                            'et_type' => $topic['et_type'], // 题目类型(1:单选题 2:判断题 3:问答题 4:多选题,5:语音题)
                            'title' => $topic['title'], // 题目名称
                            'title_pic' => $topic['title_pic'], // 题目图片(逗号分割
                            'answer' => $topic['answer'], // 正确答案(多选用逗号分隔)
                            'answer_resolve' => $topic['answer_resolve'], // 答案解析
                            'answer_coverage' => $topic['answer_coverage'], // 答案覆盖率(问答题)
                            'match_type' => $topic['match_type'], // 是否匹配关键字(0:否 1:是)
                            'answer_keyword' => unserialize($topic['answer_keyword']) // 答案关键字(序列化:关键字,百分比)
                        ];

                        // 组装分表数据
                        $detail_extend_data[] = [
                            'ep_id' => $ep_id, // 试卷id
                            'et_id' => $topic['et_id'], // 题目id
                            'et_option' => $topic['options'], // 题目选项序列化
                            'et_detail' => serialize($et_detail) // 题目详情序列化
                        ];
                    }
                }
            }

            if (!empty($detail_extend_data)) {
                // 待插入数组去重
                $detail_extend_data = $this->remove_duplicate($detail_extend_data);
                // 同步往分表新增数据
                $this->_d_answer_detail_extend->insert_all($detail_extend_data);
            }

            // 提交事务
            $this->commit();
        } catch (\Think\Exception $e) {
            \Think\Log::record($e);
            // 事务回滚
            $this->_set_error($e->getMessage(), $e->getCode());
            $this->rollback();

            E('_ERR_DATA_SAVE_FAIL');

        } catch (\Exception $e) {

            \Think\Log::record($e);
            $this->_set_error($e->getMessage(), $e->getCode());
            // 事务回滚
            $this->rollback();

            E('_ERR_DATA_SAVE_FAIL');
        }

        // 附件处理开始
        $attach_serv = new AttachOperation();
        if (!empty($data['cover_id'])) {
            // 将图片设置为已使用
            $attach_serv->insert_attach(
                APP_DIR,
                'paper',
                $ep_id,
                ['attach_ids' => [$data['cover_id']]]
            );
        }

        // 1.推送消息 2.设置定时任务
        if (self::PAPER_PUBLISH == $data['exam_status']) {

            $ep_id = $param['ep_id'];
            // 是否推荐到首页feed流
            if ($data['is_recommend']) {
                // 判断feed流权限发送
                if (self::AUTH_ALL == $data['is_all']) {

                    $feed_right = [];
                } else {

                    $feed_right = $this->format_feed_data($right);
                }

                $url = rpcUrl('/Public/Rpc/Recommender/ArticleUpdate');
                $data_send = [
                    'app' => APP_DIR,
                    'dataCategoryId' => '',
                    'dataId' => $ep_id,
                    'title' => $paper['ep_name'],
                    'summary' => $data['intro'],
                    'dataType' => $paper['paper_type'] == self::SIMULATION_PAPER_TYPE ? '模拟' : '',
                    'endTime' => $param['end_time'],
                    'attachId' => $data['cover_id'],
                    'pic' => $this->format_cover($data['cover_id'], false),
                    'url' => 'Exam/Frontend/Index/Msg?ep_id=' . $ep_id,
                    'right' => $feed_right
                ];

                \Com\Rpc::phprpc($url)->invoke('Index', $data_send);
            } else {
                // 删除首页feed流
                $url = rpcUrl('/Public/Rpc/Recommender/ArticleDelete');
                $data_send = [
                    'app' => APP_DIR,
                    'dataCategoryId' => '',
                    'dataId' => $ep_id
                ];
                \Com\Rpc::phprpc($url)->invoke('Index', $data_send);
            }

            // 如果发送消息通知
            if ($data['is_pushmsg']) {

                $params['name'] = $paper['ep_name'];
                $params['description'] = $data['intro'];
                $params['img_id'] = $data['cover_id'];
                $params['is_cover_open'] = $data['is_cover_open'];
                $params['id'] = $ep_id;
                $params['begin_time'] = $data['begin_time'];
                $params['end_time'] = $data['end_time'];

                if ($paper['exam_status'] == self::PAPER_PUBLISH) {
                    // 如果之前就是已发布的状态,此处取差集
                    $uids = array_diff($uids_now, $uids_old);
                } else {
                    // 从草稿发布
                    $uids = $uids_now;
                }

                // 如果存在需要发送消息的人员
                if (!empty($uids)) {

                    sort($uids);

                    // 用户UIDS集合赋值
                    $params['uids'] = $uids;

                    // 发送及时消息
                    $this->new_send_msg($params);
                }

            }

            // 定时任务
            $this->cron_add($ep_id);
        }

        return true;
    }

    /**
     * 编辑试卷基本信息(任务类,线下培训)
     *
     * @author:daijun
     *
     * @param array $param 传入参数
     * @param array $paper 试卷信息
     *
     * @return bool
     */
    public function base_save_new($param = [], $paper = [])
    {

        $used_ids = [];
        if (!empty($used_ids)) {

            E('_ERR_EXAM_USED_EDIT');
        }

        // 验证属性
        if (!$data = $this->check_base_save_new($param)) {

            E('_EMPTY_SAVE_DATA');
        }

        // 如果是发布动作
        if ($data['exam_status'] == self::PAPER_PUBLISH) {

            // 试卷题目查询条件
            $conditions_topic['ep_id'] = $paper['ep_id'];

            // 查询组装总题目数量
            if ($paper['ep_type'] != self::TOPIC_RANDOM) {
                // 如果不是随机出题,此处查询该试卷的试题总数
                $data['topic_count'] = $this->_d_snapshot->count_by_conds($conditions_topic);

                // 获取试卷题目列表
                $topic_list = $this->_d_snapshot->list_by_conds($conditions_topic);

            } else {
                // 如果是随机出题
                $rule = unserialize($paper['rule']);
                $data['topic_count'] = intval($rule['single_count'])
                    + intval($rule['multiple_count'])
                    + intval($rule['judgment_count'])
                    + intval($rule['question_count'])
                    + intval($rule['voice_count']);

                // 获取试卷题目列表
                $topic_list = $this->_d_random->list_by_conds($conditions_topic);
            }

            // 发布时间
            $data['publish_time'] = MILLI_TIME;
        }

        // 初始化阅卷人权限
        $right_marking = [];

        // 重组阅卷人列表
        foreach ($param['marking_list'] as $v) {

            // 如果阅卷人存在
            if ($v['memID']) {

                $right_marking[] = [
                    'epc_id' => $param['ep_id'],
                    'er_type' => self::RIGHT_MARKING,
                    'uid' => $v['memID'],
                    'cd_id' => '',
                    'tag_id' => '',
                    'job_id' => '',
                    'role_id' => ''
                ];
            }
        }

        // 查询分类详情
        $cate_data = $this->_d_cate->get($paper['ec_id']);

        // 将分类的状态赋值给试卷状态
        $data['cate_status'] = $cate_data['ec_status'];

        // 阅卷类型
        $data['marking_type'] = $param['marking_type'];

        // 获取最后更新时间
        $data['last_time'] = MILLI_TIME;
        $data['begin_time'] = 0;
        $data['end_time'] = 0;

        $data['integral_action_type'] = 2; // 不启用积分策略

        // 获取答卷扩展信息
        $extend_list = $this->_d_answer_detail_extend->list_by_conds(['ep_id' => $paper['ep_id']]);
        $extend_list = array_combine_by_key($extend_list, 'et_id');

        // 是否启用封面图片
        $data['is_cover_open'] = $param['is_cover_open'];
        // 如果上传图片
        if (!empty($param['cover_id'])) {
            $data['cover_id'] = $param['cover_id'];
        }

        // 阅卷人发送消息
        // $this->marking_send($param);

        try {
            // 开始事务
            $this->start_trans();

            // 更新试卷表
            $this->_d->update($param['ep_id'], $data);

            // 删除之前的阅卷人权限
            $this->_d_right->delete_by_conds(['epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_MARKING]);

            // 如果有阅卷人
            if (!empty($right_marking)) {
                // 新增阅卷
                $this->_d_right->insert_all($right_marking);
            }

            // 分表oa_exam_answer_detail_extend待增数据初始化
            $detail_extend_data = [];
            if (!empty($topic_list)) {

                foreach ($topic_list as $topic) {

                    $extend_info = $extend_list[$topic['et_id']];
                    if (empty($extend_info)) {
                        // 组装题目详情数据
                        $et_detail = [
                            'et_type' => $topic['et_type'], // 题目类型(1:单选题 2:判断题 3:问答题 4:多选题,5:语音题)
                            'title' => $topic['title'], // 题目名称
                            'title_pic' => $topic['title_pic'], // 题目图片(逗号分割
                            'answer' => $topic['answer'], // 正确答案(多选用逗号分隔)
                            'answer_resolve' => $topic['answer_resolve'], // 答案解析
                            'answer_coverage' => $topic['answer_coverage'], // 答案覆盖率(问答题)
                            'match_type' => $topic['match_type'], // 是否匹配关键字(0:否 1:是)
                            'answer_keyword' => unserialize($topic['answer_keyword']) // 答案关键字(序列化:关键字,百分比)
                        ];

                        // 组装分表数据
                        $detail_extend_data[] = [
                            'ep_id' => $paper['ep_id'], // 试卷id
                            'et_id' => $topic['et_id'], // 题目id
                            'et_option' => $topic['options'], // 题目选项序列化
                            'et_detail' => serialize($et_detail) // 题目详情序列化
                        ];
                    }
                }
            }

            if (!empty($detail_extend_data)) {
                // 待插入数组去重
                $detail_extend_data = $this->remove_duplicate($detail_extend_data);
                // 同步往分表新增数据
                $this->_d_answer_detail_extend->insert_all($detail_extend_data);
            }

            // 提交事务
            $this->commit();
        } catch (\Think\Exception $e) {
            \Think\Log::record($e);
            // 事务回滚
            $this->_set_error($e->getMessage(), $e->getCode());
            $this->rollback();

            E('_ERR_DATA_SAVE_FAIL');

        } catch (\Exception $e) {

            \Think\Log::record($e);
            $this->_set_error($e->getMessage(), $e->getCode());
            // 事务回滚
            $this->rollback();

            E('_ERR_DATA_SAVE_FAIL');
        }

        // 1.定时任务
        if (self::PAPER_PUBLISH == $data['exam_status']) {
            $ep_id = $param['ep_id'];
            // 定时任务
            $this->cron_add_new($ep_id);
        }

        // 附件处理开始
        $attach_aerv = new AttachOperation();
        if (!empty($data['cover_id'])) {
            // 将图片设置为已使用
            $attach_aerv->make_active([$data['cover_id']]);
        }

        return true;
    }

    /**
     * 添加试卷基本信息参数验证
     *
     * @author:daijun
     *
     * @param array $param
     *
     * @return array|bool
     */
    public function check_base_save_new($param = [])
    {
        $data = [];

        // 验证试卷ID
        if (empty($param['ep_id'])) {

            E('_EMPTY_EP_ID');
        }

        // 验证考试时长
        if (empty($param['paper_time'])) {

            E('_EMPTY_PAPER_TIME');
        }

        // 验证及格分数
        if (empty($param['pass_score'])) {

            E('_EMPTY_PASS_SCORE');
        }

        // 验证考试说明
        if (empty($param['intro'])) {

            E('_EMPTY_INTRO');
        }

        // 验证试卷状态
        if (empty($param['exam_status'])) {

            E('_EMPTY_EXAM_STATUS');
        }

        // 如果不在指定类型中
        if (!in_array($param['marking_type'], [self::MANUAL_MARKING_TYPE, self::AUTO_MARKING_TYPE])) {

            E('_ERR_MARKING_TYPE');
        }


        // 赋值
        if (!empty($param['cover_id'])) {

            $data['cover_id'] = $param['cover_id'];
        }

        $data['is_all'] = self::AUTH_ALL;
        $data['paper_time'] = $param['paper_time'];
        // 显示答案
        $data['answer_resolve'] = 1;
        $data['pass_score'] = intval($param['pass_score']);
        $data['intro'] = $param['intro'];

        // 获取是否有权限选择阅卷类型
        list($show_marking, $marking_list) = $this->get_marking($param['ep_id']);

        // 如果没有权限选择阅卷类型
        if (!$show_marking) {
            // 如果选择手动阅卷
            if (self::MANUAL_MARKING_TYPE == $param['marking_type']) {

                E('_ERR_NOT_MARKING_TYPE');
            }
        }

        // 如果是手动阅卷
        if (self::MANUAL_MARKING_TYPE == $param['marking_type']) {

            // 如果阅读人员列表不是数组
            if (!is_array($param['marking_list'])) {

                E('_ERR_MARKING_LIST');
            }

            // 阅卷人IDS
            $mem_uids = array_unique(array_column($param['marking_list'], 'memID'));

            // 如果阅卷人不存在
            if (empty($mem_uids)) {

                E('_ERR_MARKING_UID');
            }
        }

        $data['exam_status'] = intval($param['exam_status']);

        return $data;
    }

    /**
     * 发布试卷定时任务添加(添加任务,线下培训)
     *
     * @param array
     *          ep_id => 试卷id
     *
     * @return bool
     */
    public function cron_add_new($ep_id)
    {

        // 实例化定时任务类
        $cron_serv = new Cron(Service::instance());

        // 获取试卷基本信息
        $data = $this->_d->get($ep_id);

        // 需要定时通知的试卷id
        $res_params = ['ep_id' => $ep_id];
        // json参数
        $json_params = json_encode($res_params);

        // 初始化定时任务入库id
        $corn_create_exam = $data['corn_create_exam'] ? $data['corn_create_exam'] : '';

        // 如果抽题我随机抽题
        if ($data['ep_type'] == self::TOPIC_RANDOM && empty($data['corn_create_exam'])) {
            // 生成考卷定时任务
            $create_exam = [
                'crRemark' => 'corn_create_exam',
                'crType' => 2,
                'crParams' => $json_params,
                'crMethod' => 'POST',
                'crReqUrl' => oaUrl('Frontend/Callback/CreateExam'), // 回调地址
                'crTimes' => 0,
                'crCron' => '0 0/5 * * * ?',
                'crMonitorExecution' => 0,
                'crDescription' => '自动创建考卷'
            ];
            $res_create_exam = $cron_serv->add($create_exam); // crId
            $corn_create_exam = $res_create_exam['crId'];
        }


        $this->_d->update($ep_id, ['corn_create_exam' => $corn_create_exam]);

        return true;
    }

    /**
     * 添加试卷基本信息参数验证
     *
     * @author:daijun
     *
     * @param array $param
     *
     * @return array|bool
     */
    public function check_base_save($param = [])
    {
        $data = [];

        // 验证试卷ID
        if (empty($param['ep_id'])) {

            E('_EMPTY_EP_ID');
        }

        // 验证权限设置
        if (!in_array($param['is_all'], [self::AUTH_ALL, self::NO_AUTH_ALL])) {
            // 权限类型必须为0和1
            E('_ERR_RIGHT_TYPE');
        }

        // 验证开始时间不能为空
        if (empty($param['begin_time'])) {

            E('_EMPTY_BEGIN_TIME');
        }

        // 验证开始时间不能早于当前时间
        if ($param['begin_time'] <= MILLI_TIME) {

            E('_ERR_BEGIN_TIME');
        }

        // 验证结束时间不能为空
        if (empty($param['end_time'])) {

            E('_EMPTY_END_TIME');
        }

        // 验证结束时间不能早于开始时间
        if ($param['end_time'] <= $param['begin_time']) {

            E('_ERR_END_TIME');
        }

        // 验证考试时长
        if (($param['begin_time'] + to_milli_time($param['paper_time'] * 60)) > $param['end_time']) {

            E('_ERR_PAPER_TIME');
        }

        // 验证考试时长
        if (empty($param['paper_time'])) {

            E('_EMPTY_PAPER_TIME');
        }

        // 验证发送提醒
        if (!isset($param['is_pushmsg'])) {

            E('_EMPTY_PUSH_MSG');
        }

        // 验证设置考试通知
        if (!isset($param['is_notify'])) {

            E('_EMPTY_IS_NOTIFY');
        }

        // 是否开启封面图片上传验证
        if (!in_array($param['is_cover_open'], [self::IS_COVER_PIC_OPEN, self::IS_COVER_PIC_CLOSE])) {

            E('_ERR_PIC_TYPE');
        }

        // 如果开启了考试通知,则时间设置不能为空
        if ($param['is_notify']) {
            if (empty($param['notify_begin'])) {

                E('_ERR_BEGIN_NOTIFY_TIME');
            }

            if (empty($param['notify_end'])) {

                E('_ERR_END_NOTIFY_TIME');
            }
        }

        // 开始前通知时间
        if (!empty($param['notify_begin'])) {
            // 考试提醒时间验证
            if (($param['begin_time'] - intval(to_milli_time($param['notify_begin']) * 60)) <= MILLI_TIME) {

                E('_ERR_BEGIN_NOTIFY_TIME');
            }
        }

        // 结束前通知时间
        if (!empty($param['notify_end'])) {
            // 考试提醒时间验证
            if (($param['end_time'] - intval(to_milli_time($param['notify_end']) * 60)) <= MILLI_TIME) {

                E('_ERR_END_NOTIFY_TIME');
            }
        }

        // 验证开启推荐
        if (!isset($param['is_recommend'])) {

            E('_EMPTY_IS_RECOMMEND');
        }

        // 验证及格分数
        if (empty($param['pass_score'])) {

            E('_EMPTY_PASS_SCORE');
        }

        // 验证考试说明
        if (empty($param['intro'])) {

            E('_EMPTY_INTRO');
        }


        // 验证试卷状态
        if (empty($param['exam_status'])) {

            E('_EMPTY_EXAM_STATUS');
        }

        // 如果不在指定类型中
        if (!in_array($param['marking_type'], [self::MANUAL_MARKING_TYPE, self::AUTO_MARKING_TYPE])) {

            E('_ERR_MARKING_TYPE');
        }

        // 验证可见性设置(交卷后)
        $data['is_see_after_submit'] = intval($param['is_see_after_submit']);

        // 验证可见性设置(结束后)
        $data['is_see_after_over'] = intval($param['is_see_after_over']);


        if (self::OPEN_MAKEUP == $param['is_open_makeup']) {
            // 如果开启了补考,则补考次数和补考时间不能为空

            if (empty($param['makeup_num']) || empty($param['makeup_start_time']) || empty($param['makeup_end_time'])) {

                E('_EMPTY_MAKEUP_DATA');
            }

            if ($param['makeup_start_time'] <= $param['begin_time']) {
                // 如果补考开始时间大于等于考试开始时间,则抛错
                E('_ERR_MAKEUP_START_TIME');
            }

            if ($param['makeup_start_time'] >= $param['makeup_end_time']) {
                // 如果补考开始时间大于等于补考结束时间,则抛错
                E('_ERR_MAKEUP_END_TIME');
            }

            if ($param['makeup_start_time'] + to_milli_time($param['paper_time'] * 60) > $param['makeup_end_time']) {
                // 如果开始补考时间加上考试时长大于补考结束时间,则抛错
                E('_ERR_MAKEUP_END_TIME_FOR_PAPER_TIME');
            }
        }

        // 是否开启补考
        $data['is_open_makeup'] = empty($param['is_open_makeup']) ? self::CLOSE_MAKEUP : $param['is_open_makeup'];
        // 补考开始时间
        $data['makeup_start_time'] = empty($param['makeup_start_time']) ? 0 : $param['makeup_start_time'];
        // 补考结束时间
        $data['makeup_end_time'] = empty($param['makeup_end_time']) ? 0 : $param['makeup_end_time'];
        // 补考次数
        $data['makeup_num'] = intval($param['makeup_num']);

        // 初始化封面ID
        $data['cover_id'] = '';

        // 如果显示封面
        if (self::IS_COVER_PIC_OPEN == $param['is_cover_open']) {

            $data['cover_id'] = $param['cover_id'];
        }

        $data['is_cover_open'] = $param['is_cover_open'];
        $data['is_all'] = $param['is_all'];
        $data['begin_time'] = $param['begin_time'];
        $data['end_time'] = $param['end_time'];
        $data['paper_time'] = $param['paper_time'];
        $data['is_pushmsg'] = intval($param['is_pushmsg']);
        $data['is_notify'] = intval($param['is_notify']);
        $data['notify_begin'] = intval($param['notify_begin']);
        $data['notify_end'] = intval($param['notify_end']);
        $data['is_recommend'] = intval($param['is_recommend']);
        // 显示答案
        $data['answer_resolve'] = 1;
        $data['pass_score'] = intval($param['pass_score']);
        $data['intro'] = $param['intro'];

        $data['is_upset_topic'] = empty($param['is_upset_topic']) ? self::UPSET_TOPIC_NO : $param['is_upset_topic']; // 是否打乱题目
        $data['is_upset_option'] = empty($param['is_upset_option']) ? self::UPSET_OPTION_NO : $param['is_upset_option'];// 是否打乱选项


        // 获取是否有权限选择阅卷类型
        list($show_marking, $marking_list) = $this->get_marking($param['ep_id']);

        // 如果没有权限选择阅卷类型 && 选择手动阅卷
        if (!$show_marking && self::MANUAL_MARKING_TYPE == $param['marking_type']) {

            E('_ERR_NOT_MARKING_TYPE');
        }

        // 如果是手动阅卷
        if (self::MANUAL_MARKING_TYPE == $param['marking_type']) {

            // 如果阅读人员列表不是数组
            if (!is_array($param['marking_list'])) {

                E('_ERR_MARKING_LIST');
            }

            // 阅卷人IDS
            $mem_uids = array_unique(array_column($param['marking_list'], 'memID'));

            // 如果阅卷人不存在
            if (empty($mem_uids)) {

                E('_ERR_MARKING_UID');
            }
        }

        // 是否开启匿名阅卷
        $data['is_open_anonymous_marking'] = empty($param['is_open_anonymous_marking']) ? self::CLOSE_ANONYMOUS_MARKING : $param['is_open_anonymous_marking'];

        $data['exam_status'] = intval($param['exam_status']);

        // 如果不是全公司,组装right数据
        $right = [];
        if ($data['is_all'] == self::AUTH_NOT_ALL) {

            // 用户集合
            if (!empty($param['right']['user_list'])) {
                $users = array_column($param['right']['user_list'], 'memID');

                $users_list = array_combine_by_key($param['right']['user_list'], 'memID');
                foreach ($users as $v) {
                    $right[] = [
                        'epc_id' => $param['ep_id'],
                        'er_type' => self::RIGHT_PAPER,
                        'source_type' => empty($users_list[$v]['source_type']) ? 1 : $users_list[$v]['source_type'], // 权限来源
                        'uid' => $v,
                        'cd_id' => '',
                        'tag_id' => '',
                        'job_id' => '',
                        'role_id' => ''
                    ];
                }
            }

            // 部门集合
            if (!empty($param['right']['dp_list'])) {
                $dps = array_column($param['right']['dp_list'], 'dpID');
                foreach ($dps as $v) {
                    $right[] = [
                        'epc_id' => $param['ep_id'],
                        'er_type' => self::RIGHT_PAPER,
                        'source_type' => 1, // 权限来源为选择
                        'uid' => '',
                        'cd_id' => $v,
                        'tag_id' => '',
                        'job_id' => '',
                        'role_id' => ''
                    ];
                }
            }

            // 岗位集合
            if (!empty($param['right']['job_list'])) {
                $jobs = array_column($param['right']['job_list'], 'jobID');
                foreach ($jobs as $v) {
                    $right[] = [
                        'epc_id' => $param['ep_id'],
                        'er_type' => self::RIGHT_PAPER,
                        'source_type' => 1, // 权限来源为选择
                        'uid' => '',
                        'cd_id' => '',
                        'tag_id' => '',
                        'job_id' => $v,
                        'role_id' => ''
                    ];
                }
            }

            // 角色集合
            if (!empty($param['right']['role_list'])) {
                $roles = array_column($param['right']['role_list'], 'roleID');
                foreach ($roles as $v) {

                    $right[] = [
                        'epc_id' => $param['ep_id'],
                        'er_type' => self::RIGHT_PAPER,
                        'source_type' => 1, // 权限来源为选择
                        'uid' => '',
                        'cd_id' => '',
                        'tag_id' => '',
                        'job_id' => '',
                        'role_id' => $v
                    ];
                }
            }

            // 标签集合
            if (!empty($param['right']['tag_list'])) {
                $tags = array_column($param['right']['tag_list'], 'tagID');
                foreach ($tags as $v) {
                    $right[] = [
                        'epc_id' => $param['ep_id'],
                        'er_type' => self::RIGHT_PAPER,
                        'source_type' => 1, // 权限来源为选择
                        'uid' => '',
                        'cd_id' => '',
                        'tag_id' => $v,
                        'job_id' => '',
                        'role_id' => ''
                    ];
                }
            }

            // 权限不能都为空
            if (empty($right)) {

                E('_EMPTY_RIGHT');
            }
        }

        $data['right'] = $right;

        // 如果是否开启策略不符合规范
        if (!in_array($param['is_open_integral'], [self::CLOSE_INTEGRAL, self::OPEN_INTEGRAL])) {

            E('ERR_IS_OPEN_INTEGRAL');
        }

        // 默认积分策略ID为空
        $data['integral_strategyid'] = '';
        // 默认学分策略ID为空
        $data['credit_strategyid'] = '';

        // 初始化积分策略类型为不使用策略
        $data['integral_action_type'] = self::INTEGRAL_ACTION_TYPE_NO;
        // 初始化学分策略类型为不使用策略
        $data['credit_action_type'] = self::CREDIT_ACTION_TYPE_FALSE;

        // 如果开启积分策略
        if (self::OPEN_INTEGRAL == $param['is_open_integral']) {
            // 如果积分策略为空
            if (empty($param['integral_action_type'])) {

                E('_EMPTY_INTEGRAL_ACTION');
            }

            // 如果策略类型不是自定义或者默认
            if (!in_array($param['integral_action_type'],
                [self::INTEGRAL_ACTION_TYPE_DEFAULT, self::INTEGRAL_ACTION_TYPE_MY])) {

                E('ERR_OPEN_INTEGRAL_ACTION_TYPE');
            }

            $data['integral_action_type'] = $param['integral_action_type'];

            // 如果开启的是自定义的策略,则策略ID不能为空
            if (self::INTEGRAL_ACTION_TYPE_MY == $param['integral_action_type'] && empty($param['integral_strategyid'])) {

                E('_EMPTY_INTEGRAL_ACTIONID');
            }

            $data['integral_strategyid'] = strval($param['integral_strategyid']);
        }

        // 如果开启学分策略
        if (self::OPEN_INTEGRAL == $param['is_open_credit']) {
            // 如果学分策略为空
            if (empty($param['credit_action_type'])) {

                E('_EMPTY_CREDIT_ACTION');
            }

            // 如果策略类型不是默认或者自定义
            if (!in_array($param['credit_action_type'],
                [self::CREDIT_ACTION_TYPE_DEFAULT, self::CREDIT_ACTION_TYPE_CUSTOMIZE])) {

                E('ERR_OPEN_CREDIT_ACTION_TYPE');
            }

            $data['credit_action_type'] = $param['credit_action_type'];

            // 如果开启的是自定义的策略,则策略ID不能为空
            if (self::CREDIT_ACTION_TYPE_CUSTOMIZE == $param['credit_action_type'] && empty($param['credit_strategyid'])) {

                E('_EMPTY_CREDIT_ACTIONID');
            }

            $data['credit_strategyid'] = strval($param['credit_strategyid']);
        }

        return $data;
    }

    /**
     *【后台】给阅卷人发送消息
     *
     * @author 何岳龙
     *
     * @param array $param POST 数组
     *
     * @return bool
     */
    public function marking_send($param = [])
    {
        // 初始化原始
        $old_marking_user = [];

        // 如果是手动阅卷
        if (self::MANUAL_MARKING_TYPE == $param['marking_type']) {

            // 获取最新阅读人UIDS集合
            $new_marking_user = array_unique(array_filter(array_column($param['marking_list'], 'memID')));
            // 获取原始阅卷人权限
            $right_list = $this->_d_right->list_by_conds([
                'epc_id' => $param['ep_id'],
                'er_type' => self::RIGHT_MARKING
            ]);

            // 如果有原始数据
            if (!empty($right_list)) {
                // 获取原始阅卷人用户UIDS集合
                $old_marking_user = array_unique(array_filter(array_column($right_list, 'uid')));
            }

            // 取查集获取发消息用户
            $msg_uids = array_diff($new_marking_user, $old_marking_user);
            // 获取试卷信息
            $paper = $this->_d->get($param['ep_id']);

            // 如果有待发消息用户
            if (!empty($msg_uids)) {

                $msg = [
                    'uids' => $msg_uids,
                    'name' => $paper['ep_name'],
                    'img_id' => $param['cover_id'],
                    'is_cover_open' => $param['is_cover_open'],
                    'begin_time' => $param['begin_time'],
                    'end_time' => $param['end_time'],
                    'id' => $param['ep_id']
                ];

                // 给阅卷人发送消息
                $this->send_msg($msg, self::MARKING_MSG);
            }
        }

        return true;
    }

    /**
     * 格式化feed流权限数据
     *
     * @author 蔡建华
     *
     * @param  array $right 权限数组
     *
     * @return array
     */
    protected function format_feed_data($right)
    {
        $feed_right = [];
        // 格式化权限字段
        if (!empty($right)) {

            $users = array_filter(array_column($right, 'uid'));
            $departments = array_filter(array_column($right, 'cd_id'));
            $jobs = array_filter(array_column($right, 'job_id'));
            $roles = array_filter(array_column($right, 'role_id'));
            $tags = array_filter(array_column($right, 'tag_id'));

            sort($users);
            sort($departments);
            sort($jobs);
            sort($roles);
            sort($tags);

            $feed_right['users'] = !empty($users) ? $users : '';
            $feed_right['departments'] = !empty($departments) ? $departments : '';
            $feed_right['jobs'] = !empty($jobs) ? $jobs : '';
            $feed_right['roles'] = !empty($roles) ? $roles : '';
            $feed_right['tags'] = !empty($tags) ? $tags : '';
        }

        return $feed_right;
    }

    /**
     * 考试通知发送
     *
     * @param array $params POST 参数
     *
     * @return bool
     */
    public function new_send_msg($params = [])
    {
        // 分割成多个元素分组
        $uid_groups = array_chunk($params['uids'], Msg::USER_MAX_COUNT);

        // 分批发送
        foreach ($uid_groups as $uid_group) {

            $params['uids'] = $uid_group;

            $this->send_msg($params, self::ANSWER_COMING_MSG);
        }

        return true;
    }

    /**
     * 发布试卷定时任务添加
     *
     * @param array
     *          ep_id => 试卷id
     *
     * @return bool
     */
    public function cron_add($ep_id)
    {

        // 实例化定时任务类
        $cron_serv = new Cron(Service::instance());

        // 获取试卷基本信息
        $data = $this->_d->get($ep_id);

        // 存在定时发布
        if (!empty($data['begin_corn'])) {
            $cron_serv->delete($data['begin_corn']);
        }
        // 存在定时提醒
        if (!empty($data['end_cron'])) {
            $cron_serv->delete($data['end_cron']);
        }

        // 需要定时通知的试卷id
        $res_params = ['ep_id' => $ep_id];
        // json参数
        $json_params = json_encode($res_params);

        // 初始化定时任务入库id
        $begin_corn = '';
        $end_cron = '';
        $corn_create_exam = $data['corn_create_exam'] ? $data['corn_create_exam'] : '';
        $cron_statistics = $data['cron_statistics'] ? $data['cron_statistics'] : '';
        $cron_rank_id = $data['cron_rank_id'] ? $data['cron_rank_id'] : '';

        // 如果开始前提醒时间不为空
        if (!empty($data['notify_begin'])) {

            // 获取截止提醒时间
            $remind_time = $data['begin_time'] - to_milli_time($data['notify_begin'] * 60);
            // 考试开始前提醒
            $conds_remind = [
                'crRemark' => 'exam_notify_begin',
                'crType' => 2,
                'crParams' => $json_params,
                'crMethod' => 'POST',
                'crReqUrl' => oaUrl('Frontend/Callback/RemindBegin'), // 回调地址
                'crTimes' => 1,
                'crCron' => rgmdate((String)$remind_time, 's i G j n ? Y'),
                'crMonitorExecution' => 0,
                'crDescription' => '考试开始提醒'
            ];

            // 创建定时任务
            $res_remind = $cron_serv->add($conds_remind); // crId
            $begin_corn = $res_remind['crId'];
        }

        // 如果结束前提醒时间不为空
        if (!empty($data['notify_end'])) {

            // 获取截止提醒时间
            $remind_time = $data['end_time'] - to_milli_time($data['notify_end'] * 60);
            //考试结束前提醒
            $conds_remind = [
                'crRemark' => 'exam_notify_end',
                'crType' => 2,
                'crParams' => $json_params,
                'crMethod' => 'POST',
                'crReqUrl' => oaUrl('Frontend/Callback/RemindEnd'), // 回调地址
                'crTimes' => 1,
                'crCron' => rgmdate((String)$remind_time, 's i G j n ? Y'),
                'crMonitorExecution' => 0,
                'crDescription' => '考试结束提醒'
            ];

            // 创建定时任务
            $res_remind = $cron_serv->add($conds_remind); // crId
            $end_cron = $res_remind['crId'];
        }


        // 如果抽题我随机抽题
        if (self::TOPIC_RANDOM == $data['ep_type'] && empty($data['corn_create_exam'])) {
            // 生成考卷定时任务
            $create_exam = [
                'crRemark' => 'corn_create_exam',
                'crType' => 2,
                'crParams' => $json_params,
                'crMethod' => 'POST',
                'crReqUrl' => oaUrl('Frontend/Callback/CreateExam'), // 回调地址
                'crTimes' => 0,
                'crCron' => '0 0/5 * * * ?',
                'crMonitorExecution' => 0,
                'crDescription' => '自动创建考卷'
            ];
            $res_create_exam = $cron_serv->add($create_exam); // crId
            $corn_create_exam = $res_create_exam['crId'];
        }

        $this->_d->update($ep_id,
            [
                'begin_corn' => $begin_corn,
                'end_cron' => $end_cron,
                'corn_create_exam' => $corn_create_exam,
            ]);

        return true;
    }

    /**
     * 格式化获取试卷规则数据
     *
     * @author daijun
     *
     * @param array $data 试卷信息
     * @param array $bank_topic_total 各题库含有的各题型的总数
     * @param array $topic_count_list 普通抽题数列表
     *
     * @return array
     */
    public function format_rule_data($data = [], $bank_topic_total = [], $topic_count_list = [])
    {
        $result = [
            'ep_name' => $data['ep_name'],
            'ec_id' => intval($data['ec_id']),
            'exam_type' => intval($data['exam_type']),
            'paper_type' => intval($data['paper_type']),
            'ep_type' => intval($data['ep_type']),
            'search_type' => intval($data['search_type']),
            'advanced_choose' => intval($data['advanced_choose'])
        ];

        // 获取题库ID集合(一维数组)
        $eb_ids = explode(',', $data['bank_data']);
        // 查询题库列表
        $bank_data = $this->_d_bank->list_by_conds(['eb_id' => $eb_ids], null, [],
            'eb_id,eb_name,single_count,multiple_count,judgment_count,question_count,voice_count');

        // 格式化组装数据
        $bank_list = [];
        foreach ($bank_data as $v) {

            $bank_list[] = [
                'eb_id' => intval($v['eb_id']),
                'eb_name' => $v['eb_name'],
                'single_count' => intval($v['single_count']),
                'multiple_count' => intval($v['multiple_count']),
                'judgment_count' => intval($v['judgment_count']),
                'question_count' => intval($v['question_count']),
                'voice_count' => intval($v['voice_count'])
            ];
        }

        $result['topic_count_list'] = $topic_count_list;
        $result['bank_list'] = $bank_list;

        $bank_topic = array_combine_by_key($bank_topic_total, 'eb_id');
        // 格式化题库出题数量数据

        if (!empty($data['bank_topic_data'])) {
            $bank_topic_data = unserialize($data['bank_topic_data']);

            $b_topic_data = [];

            foreach ($bank_topic_data as $k => $v) {

                $b_topic_data[] = [
                    'eb_id' => intval($v['eb_id']),
                    'eb_name' => $v['eb_name'],
                    'single_count' => intval($v['single_count']),
                    'multiple_count' => intval($v['multiple_count']),
                    'judgment_count' => intval($v['judgment_count']),
                    'question_count' => intval($v['question_count']),
                    'voice_count' => intval($v['voice_count']),
                    'single_count_max' => $bank_topic[$v['eb_id']]['single_count'],
                    'multiple_count_max' => $bank_topic[$v['eb_id']]['multiple_count'],
                    'judgment_count_max' => $bank_topic[$v['eb_id']]['judgment_count'],
                    'question_count_max' => $bank_topic[$v['eb_id']]['question_count'],
                    'voice_count_max' => $bank_topic[$v['eb_id']]['voice_count']
                ];
            }

            $result['bank_topic_data'] = $b_topic_data;
        } else {

            $result['bank_topic_data'] = [];
        }

        // 格式化出题规则数据
        if (!empty($data['rule'])) {

            $rule = unserialize($data['rule']);
            foreach ($rule as $k => $v) {

                $rule[$k] = intval($v);
            }

            $result['rule'] = $rule;
        } else {

            $result['rule'] = [];
        }

        // 格式化标签数据
        if (!empty($data['tag_data'])) {
            $tag_data = unserialize($data['tag_data']);
            // 标签ID集合
            $tag_ids = array_column($tag_data, 'etag_id');
            // 查询标签列表
            $tag_list = $this->_d_tag->list_by_conds(['etag_id' => $tag_ids]);
            $tag_list = array_combine_by_key($tag_list, 'etag_id');

            // 属性ID集合
            $attr_ids = [];
            foreach ($tag_data as $v) {

                $attr_ids_arr = [];
                if (is_array($v['attr_data'])) {

                    $attr_ids_arr = array_column($v['attr_data'], 'attr_id');
                }

                if (!empty($attr_ids_arr) && is_array($attr_ids_arr)) {

                    $attr_ids = array_merge($attr_ids, $attr_ids_arr);
                }
            }

            // 查询属性列表
            $attr_list = $this->_d_attr->list_by_conds(['attr_id' => $attr_ids]);
            $attr_list = array_combine_by_key($attr_list, 'attr_id');

            // 循环格式化组装标签属性
            $tag_format_data = [];
            foreach ($tag_data as $k => $v) {

                $tag_format_data[$k]['etag_id'] = intval($v['etag_id']);
                $tag_format_data[$k]['etag_name'] = $tag_list[$v['etag_id']]['tag_name'];
                // 循环格式化属性
                $attr = [];
                foreach ($v['attr_data'] as $_k => $_v) {

                    $attr[$_k]['attr_id'] = intval($_v['attr_id']);
                    $attr[$_k]['attr_name'] = $attr_list[$_v['attr_id']]['attr_name'];
                }

                $tag_format_data[$k]['attr_data'] = $attr;
            }

            $result['tag_data'] = $tag_format_data;

        } else {

            $result['tag_data'] = [];
        }

        return $result;
    }

    /**
     * 格式化试卷信息
     *
     * @author: 蔡建华
     *
     * @param array $data 试卷信息
     * @param string $uid 用户uid
     *
     * @return array
     */
    public function format_paper_detail($data = [], $uid = '')
    {

        // 组装格式化数据
        $detail = [
            'paper_type' => intval($data['paper_type']),
            'exam_type' => intval($data['exam_type']),
            'ep_name' => $data['ep_name'],
            'total_score' => $data['total_score'],
            'pass_score' => $data['pass_score'],
            'topic_count' => intval($data['topic_count']),
            'paper_time' => $data['paper_time'],
            'begin_time' => $data['begin_time'],
            'end_time' => $data['end_time'],
            'join_count' => intval($data['join_count']),
            'intro' => $data['intro'],
            'marking_type' => intval($data['marking_type'])
        ];

        if (self::IS_COVER_PIC_OPEN == $data['is_cover_open']) {
            // 开启封面图片
            $detail['cover_url'] = $this->format_cover($data['cover_id']);
        } else {
            $detail['cover_url'] = '';
        }


        // 默认未参加
        $detail['is_join_test'] = 0;

        $makeup_tag = 0; // 补考状态(0:不可补考,1:可补考)
        // 常规考试
        if (self::NOMAL_TYPE == $data['exam_type']) {

            $detail['ep_status'] = $this->paper_status($data['exam_status'], $data['begin_time'], $data['end_time']);

            $now_time = MILLI_TIME;

            if (self::EVALUATION_PAPER_TYPE == $data['paper_type'] && self::OPEN_MAKEUP == $data['is_open_makeup'] && in_array($detail['ep_status'], [self::STATUS_END, self::STATUS_STOP, self::STATUS_ING]) && $now_time < $data['makeup_end_time'] && $now_time > $data['makeup_start_time']) {
                // 如果是测评试卷 && 开启补考  && 是进行中,已结束或者已终止的 && 在补考时间段内

                // 查询该用户参加补考的次数
                $num = $this->_d_answer->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0, 'is_makeup' => self::IS_MAKEUP]);

                if ($data['makeup_num'] > $num && in_array($detail['ep_status'], [self::STATUS_END, self::STATUS_STOP])) {
                    // 如果补考次数小于设定的次数,则可补考
                    $makeup_tag = 1;
                }
            }

            // 查询该用户正常参与的次数
            $num = $this->_d_answer->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0, 'is_makeup' => self::IS_MAKEUP_FALSE]);

            if ($num == 0 && $detail['ep_status'] == self::STATUS_ING) {
                // 如果是进行中,并且没有交卷记录,则不可补考
                $makeup_tag = 0;
            }

            if ($num) {
                // 已经参加过测评
                $detail['is_join_test'] = 1;
            }

            // 查询该用户补考参与的次数
            $num = $this->_d_answer->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0, 'is_makeup' => self::IS_MAKEUP]);

            // 计算剩余补考次数
            $makeup_num = $data['makeup_num'] - $num;

            if ($makeup_num < 0) {

                $makeup_num = 0;
            }

        } else { // 任务类或线下培训:进行中

            $detail['ep_status'] = self::STATUS_ING;
        }

        $detail['makeup_tag'] = $makeup_tag;

        $detail['is_open_makeup'] = $data['is_open_makeup']; // 是否开启补考功能:(1:开启 2:不开启)
        $detail['makeup_num'] = $makeup_num; // 剩余补考次数
        $detail['makeup_start_time'] = $data['makeup_start_time']; // 补考开始时间
        $detail['makeup_end_time'] = $data['makeup_end_time']; // 补考结束时间

        return $detail;
    }

    /**
     * 重新抽题
     *
     * @author:daijun
     *
     * @param int $ep_id
     *
     * @return bool
     */
    public function extract_topic($ep_id = 0)
    {
        // 如果试卷出题规则不是随机,则按配置抽题
        if (!$topic_list = $this->get_temp_list_by_epid($ep_id)) {
            E('_ERR_CHOICE_PAPER');
        }

        try {
            // 开始事务
            $this->start_trans();

            // 删除快照表(即已选择的试题)
            $this->_d_snapshot->delete_by_conds(['ep_id' => $ep_id]);

            // 删除试卷备选题目信息
            $this->_d_temp->delete_by_conds(['ep_id' => $ep_id]);

            $temp_list = [];
            foreach ($topic_list as $k => $v) {

                $temp_list[] = [
                    'ep_id' => $ep_id,
                    'et_id' => $v['et_id'],
                    'score' => $v['score'],
                    'order_num' => $k,
                ];
            }

            if (!empty($temp_list)) {
                // 存入备选题目列表
                $this->_d_temp->insert_all($temp_list);
            }

            // 提交事务
            $this->commit();
        } catch (\Think\Exception $e) {
            \Think\Log::record($e);
            // 事务回滚
            $this->_set_error($e->getMessage(), $e->getCode());
            $this->rollback();

            return false;
        } catch (\Exception $e) {

            \Think\Log::record($e);
            $this->_set_error($e->getMessage(), $e->getCode());
            // 事务回滚
            $this->rollback();

            return false;
        }

        return true;
    }

    /**
     * 查询考试统计列表
     *
     * @author: 英才
     *
     * @param array $params 查询条件数组
     * @param null $page_option 分页
     * @param array $order_option 排序数组
     * @param String $fields 查询字段
     *
     * @return array|bool
     */
    public function list_search_where($params, $page_option = null, $order_option = [], $fields = '*')
    {

        try {
            return $this->_d->list_search_where($params, $page_option, $order_option, $fields);
        } catch (\Exception $e) {
            E($e->getCode() . ':' . $e->getMessage());

            return false;
        }
    }

    /**
     * 统计考试统计列表总数
     *
     * @author: 英才
     *
     * @param array $params 查询条件数组
     *
     * @return array|bool
     */
    public function count_search_where($params)
    {
        try {
            return $this->_d->count_search_where($params);
        } catch (\Exception $e) {
            E($e->getCode() . ":" . $e->getMessage());

            return false;
        }
    }

    /**
     * 推送停止考试消息与交卷
     *
     * @author daijun
     *
     * @param array $data 试卷信息
     */
    public function stop_paper($data = [])
    {
        $right_serv = new RightService();
        // 去权限表查询权限信息
        $right = $this->_d_right->list_by_conds(['er_type' => 0, 'epc_id' => $data['ep_id']]);

        // 格式化权限信息
        $right_res = $right_serv->format_db_data($right);

        // 获取数据更新后的权限用户列表
        $right_res['is_all'] = $data['is_all'];

        $params['name'] = $data['ep_name'];
        $params['description'] = $data['intro'];
        $params['img_id'] = $data['cover_id'];
        $params['is_cover_open'] = $data['is_cover_open'];
        $params['msg'] = $data['reason'];
        $params['id'] = $data['ep_id'];
        $params['uids'] = $right_serv->list_uids_by_right($right_res);

        // 如果填写了发送的文字则推送考试停止消息
        if (!empty($data['reason'])) {
            $right_serv->send_msg($params, self::ANSWER_AHEAD_END);
        }

        // 临时避免定时任务查询或删除失败抛出错误
        try {
            // 删除定时任务
            $this->cron_delete($data);
        } catch (\Think\Exception $e) {
            \Think\Log::record($e);
        }

    }

    /**
     *  删除试卷定时任务
     *
     * @param $data array 试卷信息
     *
     * @return bool
     */
    public function cron_delete($data = [])
    {

        // 实例化定时任务类
        $cron_serv = new Cron(Service::instance());

        // 临时避免定时任务查询或删除失败抛出错误
        try {
            // 存在定时发布
            if (!empty($data['begin_corn'])) {

                $cron_serv->delete($data['begin_corn']);
            }

            // 存在定时提醒
            if (!empty($data['end_cron'])) {

                $cron_serv->delete($data['end_cron']);
            }

            // 存在试卷定时任务
            if (!empty($data['corn_exam'])) {

                $cron_serv->delete($data['corn_exam']);
            }

            // 存在自动创建考卷的cornid
            if (!empty($data['corn_create_exam'])) {

                $cron_serv->delete($data['corn_create_exam']);
            }

            // 存在考试统计cornid
            if (!empty($data['cron_statistics'])) {

                $cron_serv->delete($data['cron_statistics']);
            }

            // 存在考试分批推送消息cornid
            if (!empty($data['cron_send_msg'])) {

                $cron_serv->delete($data['cron_send_msg']);
            }

            // 存在考试排名列表接口定时更新cornid
            if (!empty($data['cron_rank_id'])) {

                $cron_serv->delete($data['cron_rank_id']);
            }
        } catch (\Think\Exception $e) {
            \Think\Log::record($e);

            return true;
        }

        return true;
    }

    /**
     * 根据条件更新试卷且不修改最后更新时间
     *
     * @author 鲜彤
     *
     * @param $conds array 条件
     * @param $data array 数据
     *
     * @return mixed
     */
    public function update_by_paper($conds = [], $data = [])
    {

        return $this->_d->update_by_paper($conds, $data);
    }

    /**
     * 随机抽取按规则抽题
     *
     * @author caijainhua
     *
     * @param array $bank_ids 题库ID集合
     * @param array $paper 试卷ID
     * @param array $bank_topic_data 题库题目数量设置
     * @param array $rule_data 题目得分规则设置
     *
     * @return array
     */
    public function random_rule_topic($bank_ids = [], $paper = [], $bank_topic_data = [], $rule_data = [])
    {

        $ep_id = $paper['ep_id'];
        $topic_all_list = [];
        $topic_list_ids = $this->_d_random->list_by_conds(['ep_id' => $ep_id]);
        // 开启高级抽取规则值
        if (self::ADVANCED_CHOOSE_OPEN == $paper['advanced_choose']) {
            // 题库题目属性数组
            $bank_topic_attr = [];
            foreach ($topic_list_ids as $v) {

                $bank_topic_attr[$v['eb_id']][] = $v;
            }

            // 循环题库
            foreach ($bank_ids as $k => $v) {

                // 检查是否需要在该题库抽题
                $is_bank_topic = 1; // 默认需要从该题库需要抽题
                // 此处查询该题库是否设置了题目
                foreach ($bank_topic_data as $val) {

                    if ($val['eb_id'] == $v) {
                        // 计算当前题库设置测抽题数量
                        $t_count = $val['single_count'] + $val['multiple_count'] + $val['judgment_count'] + $val['question_count'] + $val['voice_count'];
                        if (0 == $t_count) {
                            $is_bank_topic = 0;
                        }
                        break;
                    }
                }

                // 如果规则设置不从该题库抽题,则continue
                if (!$is_bank_topic) {
                    continue;
                }

                // 查询该题库下的符合条件的题目列表
                $topic_list = $bank_topic_attr[$v];
                if (empty($topic_list)) {

                    E('_ERR_DELETE_RULE_TOPIC');
                }

                // 将题目列表按照题目类型分组返回
                if (!$topic_data = $this->format_by_et_type($topic_list, $bank_topic_data, $rule_data, $v, false)) {

                    return false;
                }

                if (!empty($topic_data) && is_array($topic_data)) {
                    // 合并组装数组
                    $topic_all_list = array_merge($topic_all_list, $topic_data);
                }
            }
            if (empty($topic_all_list)) {

                E('_ERR_CHOICE_PAPER');
            }
        } else {
            // 没有开启高级随机抽题,按照规则总数抽题
            $topic_all_list = $this->format_simple_by_et_type(
                $topic_list_ids,
                $rule_data,
                false
            );
        }
        // 对抽取的题目进行打乱
        $topics = array_combine_by_key($topic_all_list, 'et_id');
        $tp_ids = array_column($topic_all_list, 'et_id');
        // 打乱ID
        shuffle($tp_ids);

        // 循环组装试题信息
        $result_list = [];
        foreach ($tp_ids as $k => $v) {

            $data = $topics[$v];
            $data['order_num'] = $k + 1;
            $result_list[] = $data;
        }

        return $result_list;
    }

    /**
     *【后台】将试卷信息写入缓存中
     * @author 何岳龙
     *
     * @param array $param 试卷详情信息
     * @param int $exam_status 试卷发布状态
     *
     * @return bool
     */
    public function exam_set_cache($param = [], $exam_status = 0)
    {

        // 组装前台跳转数据
        $cache_data = [
            'ep_id' => $param['ep_id'],
            'exam_status' => $exam_status,
            'begin_time' => $param['begin_time'],
            'end_time' => $param['end_time'],
            'uids' => $param['uids'],  // 应推送新消息人员ID
            'cron_id' => $param['cron_id'],   // 应推送新消息任务ID
            'join_uids' => $param['join_uids'], // 有权参与人员ID集合
        ];

        // 写入缓存
        $cache = &Cache::instance();
        $cache->set('Common.Exam_' . $param['ep_id'], $cache_data, 7200);

        return true;
    }

    /**
     * 闯关交卷
     *
     * @param array $params 闯关参数
     * @param int $uid 用户ID
     *
     * @return mixed
     */
    public function submit_break_papers($params = [], $uid = 0)
    {
        // 判断课程ID
        if (!intval($params['article_id'])) {

            E('_EMPTY_BREAK_CID');
        }
        // 判断用户UID
        if (!$uid) {

            E('_EMPTY_UID');
        }
        // 闯关答题列表
        $qa_list = $params['qa_list'];
        if (empty($qa_list)) {

            E('_EMPTY_ANSWER_LIST');
        }

        $result_list = [];
        // 从题库获取闯关试题列表
        $et_ids = array_column($qa_list, 'et_id');
        $topic_list = $this->_d_topic->list_by_pks($et_ids);


        $error_total = 0;
        $right_total = 0;
        // 闯关实例列表为空时,默认为闯关通过(为了兼容闯关过程中,管理员在后台题库中把闯关题目给删除掉)
        if (empty($topic_list)) {

            $error_total = 0;
            $right_total = count($et_ids);
        } else {
            // 格式化成以题目ID为键的二维数组
            $topic_list = array_combine_by_key($topic_list, 'et_id');

            // 循环处理答题列表
            foreach ($qa_list as $key => $val) {

                $topic_detail = $topic_list[$val['et_id']];
                // 获取答案字符串
                $answer = $this->get_break_answer($val, $topic_detail);

                $answer_detail = $topic_detail;
                $answer_detail['my_answer'] = $answer;
                // 判断是否正确
                $score_res = $this->get_topic_score($answer_detail);

                if (self::MY_PASS == $score_res['is_pass']) {
                    // 答对题目数统计
                    $right_total++;
                } else {
                    // 答错题目数统计
                    $error_total++;
                }

                $result_list[] = [
                    'et_index' => $key,
                    'et_id' => $val['et_id'],
                    'is_pass' => $score_res['is_pass']
                ];
            }
        }

        $is_pass = $error_total > 0 ? self::MY_BREAK_NO_PASS : self::MY_BREAK_PASS;

        unset($params['qa_list']);
        $data_params = @urldecode($params['params']);

        // 闯关参数拼接
        $data = [];
        if ($data_params) {

            parse_str($data_params, $data);
        }

        // prc参数拼装
        $rpc_params = [
            'uid' => $uid,
            'article_id' => $params['article_id'],
            'is_pass' => $error_total > 0 ? 1 : 2, // 测评是否通过(1=未通过;2=已通过)
            'no_total' => intval($error_total),
            'yes_total' => intval($right_total),
            'article_chapter_id' => intval($params['article_chapter_id']),
            'source_id' => intval($params['source_id']),
            'customtask_id' => intval($params['customtask_id']),
            'plan_id' => intval($params['plan_id']),
            'ed_id' => intval($params['ed_id']),
            'map_id' => (int)$params['map_id'],
            'path_id' => (int)$params['path_id'],
        ];

        $rpc_params = array_merge($rpc_params, $data);
        $rpc_url = rpcUrl('/Course/Rpc/Exam/Update');
        $rpc_care = \Com\Rpc::phprpc($rpc_url)->invoke('Index', $rpc_params);

        if (!empty($rpc_care)) {

            foreach ($rpc_care as $key => $value) {
                $rpc_care[$key]['content'] = '积分';
            }
        }

        // 闯关通过后,调用常规任务埋点
        if ($is_pass == self::MY_BREAK_PASS) {

            $taskCenterServ = &TaskCenter::instance();
            $taskCenterServ->triggerCustomtask([
                'uid' => $uid,
                'customtask_id' => $params['customtask_id'],
                'app_data_id' => $params['article_id'],
                'action_key' => 'course_study',
                'description' => '完成课程',
                'app' => 'course',
            ]);
        }

        return [
            'is_pass' => $is_pass,
            'care' => $rpc_care,
            'answer_list' => $result_list
        ];

    }


    /**
     * 获取答案字符串
     *
     * @param array $val 答案数据
     * @param array $et_detail 题目详情
     *
     * @return array|string 答案字符串
     */
    public function get_break_answer($val = [], $et_detail = [])
    {

        $str_answer = '';
        // 多选题
        if (self::TOPIC_TYPE_MULTIPLE == $et_detail['et_type']) {

            if (isset($val['my_answer']) && !empty($val['my_answer'])) {

                // 分割答案选项
                $my_answer = explode(',', $val['my_answer']);
                // 答案选项重新排序
                sort($my_answer);
                // 答案选项拼接为字符串
                $str_answer = implode(',', $my_answer);
            }

        } elseif (self::TOPIC_TYPE_QUESTION == $et_detail['et_type']
            || self::TOPIC_TYPE_SINGLE == $et_detail['et_type']
            || self::TOPIC_TYPE_JUDGMENT == $et_detail['et_type']
        ) { // 问答、单选、判断题

            $str_answer = strval($val['my_answer']);
        }

        return $str_answer;
    }


    /**
     * 题目分数正确
     *
     * @author: 蔡建华
     *
     * @param array $data 题目信息
     *
     * @return array 返回 分数和答案是否正确
     */
    protected function get_topic_score($data = [])
    {
        $my_answer = trim($data['my_answer']);
        if (empty($my_answer)) {

            $my_score = 0;
            $is_pass = self::NO_MY_PASS;
        } else {
            $et_type = $data['et_type'];
            if (self::TOPIC_TYPE_SINGLE == $et_type || self::TOPIC_TYPE_MULTIPLE == $et_type || self::TOPIC_TYPE_JUDGMENT == $et_type) {
                // 如果是单选或者多选题 或者判断题

                if ($my_answer == $data['answer']) {
                    $my_score = $data['score'];
                    $is_pass = self::MY_PASS;
                } else {
                    $my_score = $data['score'];
                    $is_pass = self::NO_MY_PASS;
                }

            } else {

                $answer_serv = new AnswerService();
                // 如果是问答题
                if (self::KEYWORD_OPEN == $data['match_type']) {

                    $answer_keyword = unserialize($data['answer_keyword']);
                    // 匹配关键字方式
                    $my_score = $answer_serv->_key_question_score($answer_keyword, $my_answer,
                        $data['score']);

                    // 得满分算回答正确,否则为回答错误
                    if ($my_score == $data['score']) {

                        $is_pass = self::MY_PASS;
                    } else {

                        $is_pass = self::NO_MY_PASS;
                    }
                } else {

                    // 覆盖率方式
                    $is_pass = $answer_serv->_coverage_question_score($data['answer'],
                        $data['answer_coverage'],
                        $my_answer);
                    $my_score = $is_pass === true ? $data['score'] : 0;

                    // 得满分算回答正确,否则为回答错误
                    if ($my_score == $data['score']) {

                        $is_pass = self::MY_PASS;
                    } else {

                        $is_pass = self::NO_MY_PASS;
                    }
                }
            }
        }

        return [
            'is_pass' => $is_pass,
            'my_score' => $my_score
        ];
    }

}