<?php
/**
 * 试卷-答卷表
 * @author: houyingcai
 * @email:    594609175@qq.com
 * @date :  2017-05-19 17:51:32
 * @version $Id$
 */

namespace Common\Service;

use Common\Common\AttachOperation;
use Common\Common\Cache;
use Common\Common\ExamHelper;
use Common\Common\Integral;
use Common\Common\Lcs;
use Common\Common\User;
use Common\Common\Department;
use Common\Common\TaskCenter;
use Common\Common\Train;

use Common\Model\AnswerDetailExtendModel;
use Common\Model\AnswerDetailModel;
use Common\Model\AnswerModel;
use Common\Model\CountModel;
use Common\Model\LikeModel;
use Common\Model\MedalModel;
use Common\Model\MedalRecordModel;
use Common\Model\MedalRelationModel;
use Common\Model\PaperModel;
use Common\Model\RightModel;
use Common\Model\SnapshotModel;
use Common\Model\StatisticsModel;
use Common\Model\TopicModel;
use Common\Model\AnswerAttachModel;
use Common\Common\StudyMap;

use VcySDK\Member;
use VcySDK\Service;

class AnswerService extends AbstractService
{
    // 考试通过
    const PASS = 1;
    // 考试不通过
    const UNPASS = 0;

    // 不发送消息
    const NOT_NEED_SEND_MSG = 0;
    // 手动阅卷发送阅卷信息
    const SEND_MSG_FOR_MARKING_USER = 1;
    // 手动阅卷系统阅卷成绩出来
    const SEND_MSG_FOR_JOIN_EXAM_USER = 2;

    // 初始批阅状态
    const DEFAULT_MARK_STATUS = 0;

    // 答卷正常状态
    const START_TYPE_INITIAL = 0;
    // 答卷异常状态
    const START_TYPE_END = 1;

    /** @var AnswerAttachModel */
    public $_answer_attach_model;
    /** $var StatisticsModel  */
    public $_statistics_model;
    /** @var AnswerDetailModel */
    protected $_answer_detail_model;
    /** @var TopicModel */
    protected $_topic_model;
    /** @var SnapshotModel */
    protected $_snapshot_model;
    /** @var LikeModel */
    protected $_like_model;
    /** @var MedalModel */
    protected $_medal_model;
    /** @var MedalRelationModel */
    protected $_medal_relation_model;
    /** @var MedalRecordModel */
    protected $_medal_record_model;
    /** @var PaperModel */
    protected $paper_model;
    /** @var RightModel */
    protected $_right_model;
    /** @var AnswerDetailExtendModel */
    protected $_answer_detail_extend_model;

    // 构造方法

    public function __construct()
    {
        $this->_d = new AnswerModel();
        $this->_like_model = new LikeModel();
        $this->_answer_detail_model = new AnswerDetailModel();
        $this->paper_model = new PaperModel();
        $this->_snapshot_model = new SnapshotModel();
        $this->_topic_model = new TopicModel();
        $this->_medal_model = new MedalModel();
        $this->_right_model = new RightModel();
        $this->_medal_relation_model = new MedalRelationModel();
        $this->_medal_record_model = new MedalRecordModel();
        $this->_answer_attach_model = new AnswerAttachModel();
        $this->_statistics_model = new StatisticsModel();
        $this->_answer_detail_extend_model = new AnswerDetailExtendModel();
        parent::__construct();
    }

    /**
     * 查询考试排名数据
     *
     * @param int $ep_id 试卷ID
     * @param string $uid 用户ID
     * @param int $page 页码
     * @param int $limit 每页条数
     *
     * @return array
     */
    public function answer_list_all($ep_id = 0, $uid = '', $page = 1, $limit = 10)
    {

        $cache = &Cache::instance();
        $rank_data = [];

        try {
            // 获取当前试卷缓存信息
            $rank_data = $cache->get('Common.Exam_Rank_List_' . $ep_id);
        } catch (\Exception $e) {

            \Think\Log::record($e);
        }

        // 用户信息
        $users = [];

        // 缓存数据为空
        if (empty($rank_data)) {
            // 查询条件:当前试卷,已批阅状态
            $conds = ['ep_id' => $ep_id, 'answer_status' => self::READ_OVER];

            // 排序:成绩倒序,时间正序
            $order_option = ['my_score' => 'DESC', 'my_time' => 'ASC'];

            // 查询字段
            $fields = 'uid,ea_id,my_score,my_time';

            // 查询考试成绩列表
            $data = $this->_d->list_by_conds($conds, null, $order_option, $fields);

            // 获取用户信息
            $uids = array_filter(array_unique(array_column($data, 'uid')));

            sort($uids);
            $userlist = $this->getUser($uids);

            $k = 1;
            foreach ($data as $v) {
                if (!array_key_exists($v['uid'], $rank_data)) {

                    // 排名
                    $v['ranking'] = $k;

                    $user = $userlist[$v['uid']];

                    $v['memID'] = $user['memUid'] ? $user['memUid'] : '';
                    $v['memUsername'] = $user['memUsername'] ? $user['memUsername'] : '';
                    $v['memFace'] = $user['memFace'] ? $user['memFace'] : '';

                    $rank_data[$v['uid']] = $v;

                    $k++;
                }

                continue;
            }

            unset($data);

            // 将排名列表存入缓存
            $cache->set('Common.Exam_Rank_List_' . $ep_id, $rank_data);
        }

        $start = ($page - 1) * $limit;
        // 取待返数据
        $list = array_slice($rank_data, $start, $limit);

        // 获取我的排名
        $rank_data = array_combine_by_key($rank_data, 'uid');
        $ranking = 0;
        if (isset($rank_data[$uid]['ranking']) && !empty($rank_data[$uid]['ranking'])) {

            $ranking = $rank_data[$uid]['ranking'];
        }

        // 答卷id集合
        $ea_ids = array_column($list, 'ea_id');

        // 获取我的点赞数据
        $my_like_data = $this->_like_model->list_by_conds(['ea_id' => $ea_ids, 'uid' => $uid]);
        // 我的点赞答卷id集合
        $my_like_ea_ids = array_unique(array_column($my_like_data, 'ea_id'));

        // 获取点赞数
        $like_fields = 'ea_id,count(ea_id) as likes';
        $likes_data = $this->_like_model->getLikeCount($ea_ids, $like_fields);
        // ea_id为键,likes为值
        $likes = array_column($likes_data, 'likes', 'ea_id');

        $result = [];
        foreach ($list as $v) {
            // 答卷id
            $v['ea_id'] = intval($v['ea_id']);
            // 点赞数
            $v['likes'] = array_key_exists($v['ea_id'], $likes) ? $likes[$v['ea_id']]['likes'] : 0;
            // 是否点赞
            $v['is_like'] = in_array($v['ea_id'], $my_like_ea_ids) ? self::SUCCES_STATE : self::FALSE_STATE;
            unset($v['uid']);

            $result[] = $v;
        }

        // 缓存不为空,$users为空
        if (empty($users[$uid])) {
            // 获取用户数据
            $users = $this->getUser([$uid]);
        }

        return [
            'ranking' => $ranking,
            'memFace' => $users[$uid]['memFace'],
            'total' => count($rank_data),
            'list' => $result
        ];
    }

    /**
     * 获取所有人的排名信息
     *
     * @param int $ep_id
     *
     * @return array
     */
    public function get_rank_all_list($ep_id = 0)
    {
        // 查询条件:当前试卷,已批阅状态
        $conds = ['ep_id' => $ep_id, 'answer_status' => self::READ_OVER];

        // 排序:成绩倒序,时间正序
        $order_option = ['my_score' => 'DESC', 'my_time' => 'ASC'];

        // 查询字段
        $fields = 'uid,ea_id,my_score,my_time';

        // 查询考试成绩列表
        $data = $this->_d->list_by_conds($conds, null, $order_option, $fields);

        // 获取用户信息
        $uids = array_filter(array_unique(array_column($data, 'uid')));

        sort($uids);
        $userlist = $this->getUser($uids);


        $data_new = [];
        $k = 1;
        foreach ($data as $v) {
            if (!array_key_exists($v['uid'], $data_new)) {

                // 排名
                $v['ranking'] = $k;

                $user = $userlist[$v['uid']];

                $v['memID'] = $user['memUid'] ? $user['memUid'] : '';
                $v['memUsername'] = $user['memUsername'] ? $user['memUsername'] : '';
                $v['memFace'] = $user['memFace'] ? $user['memFace'] : '';

                $data_new[$v['uid']] = $v;

                $k++;
            }

            continue;
        }

        unset($data);

        return $data_new;
    }

    /**
     * 获取答卷详情
     * @author: 蔡建华
     *
     * @param array $params 传入参数
     * @param array $user 当前用户
     * @param array $answer 用户答卷记录
     * @param array $paper 试卷信息
     * @return array|bool
     */
    public function answer_detail_info($params = [], $user = [], $answer = [], $paper = [])
    {

        $obj_id = 0;
        if (isset($params['customtask_id']) && $params['customtask_id']) {
            // 任务id
            $obj_id = intval($params['customtask_id']);
        } elseif (isset($params['plan_id']) && $params['plan_id']) {
            // 培训计划id
            $obj_id = intval($params['plan_id']);
        }

        // 考试答题用时为0:该考试尚未交卷
        if ($answer['my_time'] == 0) {

            E('_ERR_REPEAT_SUBMINT_END');
        }

        // 如果传递的参数为空,则获取答题记录中的任务、培训ID
        if (empty($obj_id)) {

            $obj_id = intval($answer['obj_id']);
        }

        // 判断用户是否有权限访问“任务类”数据
        if (PaperService::TASK_TYPE == $paper['exam_type']) {
            // 任务
            $taskCenter = &TaskCenter::instance();
            $taskCenter->checkCustomtaskRight($obj_id, $answer['ep_id'], $user);
        }

        // 判断用户是否有权限访问“线下培训”数据
        if (PaperService::TRAIN_TYPE == $paper['exam_type']) {
            // 培训计划
            $train = &Train::instance();
            $train->checkCustomTrainRight($obj_id, $answer['ep_id'], $user);
        }

        if (self::EC_CLOSE_STATES == $paper['cate_status']) {
            // 试卷分类被禁用:试卷被禁用
            E('_ERR_DATA_EXAM_DEL');
        }

        // 试卷状态
        $ep_status = $this->paper_status($paper['exam_status'], $paper['begin_time'], $paper['end_time']);

        if (in_array($ep_status, [self::STATUS_END, self::STATUS_STOP])) {
            // 考试已结束或者已终止,则返回的查看权限为考试结束的权限
            $is_see_after = $paper['is_see_after_over'];
        } else {
            // 如果考试未结束,则返回的权限为答卷完成的权限
            $is_see_after = $paper['is_see_after_submit'];
        }

        // 答卷id
        $ea_id = $answer['ea_id'];
        // 答题列表
        $my_answer_detail_list = $this->_answer_detail_model->list_by_conds(['ea_id' => $ea_id]);

        // 答对的试题数据
        $pass_answers = array_filter($my_answer_detail_list, function ($v) {

            return $v['is_pass'] == self::MY_PASS;
        });

        // 做对题数
        $my_success_num = count($pass_answers);
        // 答错的题数(总答题数-作对的题数)
        $my_error_num = count($my_answer_detail_list) - $my_success_num;

        // 我的排名
        $ranking = $this->get_my_rank($answer['ep_id'], $answer['uid'], $obj_id);

        $list = [];
        if ($is_see_after >= 2) {
            // 如果权限为对错可见或者解析可见,则查询答卷情况格式化(全部解析)
            $list = $this->get_answer_detail($my_answer_detail_list, self::ANSWER_TYPE_ALL_ANALYSIS);
        }

        // 剩余补考次数
        $makeup_total = 0;
        // 当前试卷待阅卷状态的记录数
        $marking_count = 0;

        if (self::OPEN_MAKEUP == $paper['is_open_makeup']) {
            // 如果开启了补考,此处需要查询已经补考的次数
            $makeup_num = $this->_d->count_by_conds(['ep_id' => $answer['ep_id'], 'uid' => $answer['uid'], 'is_makeup' => self::IS_MAKEUP, 'my_time>?' => 0]);

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

            if ($makeup_total < 0) {
                $makeup_total = 0;
            }

            // 查询待批阅的答卷记录数
            $marking_count = $this->_d->count_by_conds(['ep_id' => $answer['ep_id'], 'uid' => $answer['uid'], 'answer_status' => self::READ_WAITING]);
        }

        // 查询未交卷的记录数
        $no_submit_count = $this->_d->count_by_conds(['ep_id' => $answer['ep_id'], 'uid' => $answer['uid'], 'my_time' => 0]);

        return [
            'ea_id' => intval($answer['ea_id']),
            'ep_id' => intval($answer['ep_id']),
            'ep_status' => $ep_status,
            'my_score' => $answer['my_score'],
            'my_time' => $answer['my_time'],
            'my_error_num' => $my_error_num,
            'memUid' => $user['memUid'],
            'memFace' => User::instance()->avatar($user['memUid'], $user),
            'ranking' => $ranking,
            'my_is_pass' => intval($answer['my_is_pass']),
            'paper_type' => intval($paper['paper_type']),
            'exam_type' => intval($paper['exam_type']),
            'marking_type' => intval($paper['marking_type']),
            'is_see_after' => intval($is_see_after), // 可见性权限设置
            'is_open_makeup' => intval($paper['is_open_makeup']), // 是否开启补考
            'makeup_num' => $makeup_total, // 剩余补考次数
            'makeup_start_time' => $paper['makeup_start_time'],
            'makeup_end_time' => $paper['makeup_end_time'],
            'marking_count' => intval($marking_count), // 待批阅的答卷记录数
            'no_submit_count' => $no_submit_count,
            'list' => $list
        ];
    }

    /**
     * 获取我的考试排名
     *
     * @param int $ep_id 试卷ID
     * @param string $uid 用户ID
     * @param int $obj_id 任务id或培训安排id
     *
     * @return int|string 我的排名
     */
    public function get_my_rank($ep_id = 0, $uid = '', $obj_id = 0)
    {

        // 我的排名
        $ranking = 0;

        $cache = &Cache::instance();
        // 常规考试,排名从缓存读取
        if (empty($obj_id)) {

            $rank_data = [];
            try {
                // 获取当前试卷缓存信息
                $rank_data = $cache->get('Common.Exam_Rank_List_' . $ep_id);
            } catch (\Exception $e) {

                \Think\Log::record($e);
            }

            // 缓存数据为空
            if (empty($rank_data)) {
                // 查询考试成绩列表
                $conditions = [
                    'ep_id' => $ep_id,
                    'answer_status' => self::READ_OVER // 已批阅
                ];
                $page_option = null;

                // 排序方式:成绩倒序,用时正序
                $order_option = ['my_score' => 'DESC', 'my_time' => 'ASC'];

                // 查询字段
                $fields = 'uid,ea_id,my_score,my_time';

                // 按照成绩倒序查询已批阅的回答记录列表
                $data = $this->_d->list_by_conds($conditions, $page_option, $order_option, $fields);

                // 获取用户信息
                $uids = array_filter(array_unique(array_column($data, 'uid')));

                sort($uids);
                $users = $this->getUser($uids);

                // 排名列表
                $data_new = [];
                $k = 1;
                foreach ($data as $v) {
                    // 用户不在新数组中,加入新数组,
                    if (!array_key_exists($v['uid'], $data_new)) {
                        // 因为成绩是按照倒序排列的,此处只取每个用户的第一条记录,即为用户的最高分
                        $v['ranking'] = $k; // 排名

                        $user_info = $users[$v['uid']];

                        $v['memUsername'] = $user_info['memUsername'] ? $user_info['memUsername'] : '';
                        $v['memFace'] = $user_info['memFace'] ? $user_info['memFace'] : '';

                        $data_new[$v['uid']] = $v;

                        $k++;
                    }

                    continue;
                }

                // 将排名列表存入缓存
                $cache->set('Common.Exam_Rank_List_' . $ep_id, $rank_data);
            }

            $rank_data = array_combine_by_key($rank_data, 'uid');
            if (isset($rank_data[$uid]['ranking']) && !empty($rank_data[$uid]['ranking'])) {
                // 我的排名
                $ranking = $rank_data[$uid]['ranking'];
            }

            return $ranking;
        }

        // 查询条件
        $conditions = [
            'ep_id' => $ep_id,
            'obj_id' => $obj_id,
            'answer_status' => self::READ_OVER, // 已批阅
        ];
        // 查询字段
        $fields = 'uid,ea_id,my_score,my_time';

        // 排序方式:成绩倒序,用时正序
        $order_option = ['my_score' => 'DESC', 'my_time' => 'ASC'];

        // 如果是线下培训或者任务类,查询当前试卷的考试记录列表
        $data = $this->_d->list_by_conds($conditions, null, $order_option, $fields);

        // 排名数据列表
        $data_new = [];

        $k = 1;
        $ranking = 0;
        foreach ($data as $v) {
            // 用户不在新数组中,加入新数组
            if (!array_key_exists($v['uid'], $data_new)) {

                $v['ranking'] = $k;
                $data_new[$v['uid']] = $v;

                if ($uid == $v['uid']) {
                    // 如果找到当前用户的排名
                    $ranking = $k;
                    break;
                }

                $k++;
            }

            continue;
        }

        unset($data);

        return $ranking;
    }

    /**
     * 开始考试
     *
     * @author: 蔡建华
     * @param array $data 试卷信息数据
     * @param string $uid 用户ID
     * @param int $data_type 考试类型(1;常规,2:任务,3:培训)
     * @param int $obj_id 培训计划或者任务ID
     * @param int $is_makeup 是否是补考(0:否,1:是)
     *
     * @return array|bool
     */
    public function paper_start_exam($data = [], $uid = '', $data_type = 1, $obj_id = 0, $is_makeup = 0)
    {
        $ep_id = $data['ep_id'];
        $is_old = $data['is_old'];
        $ep_type = $data['ep_type'];
        // 组装考试开始基本数据
        $base = [
            'ep_id' => $ep_id,
            'uid' => $uid,
            'my_begin_time' => MILLI_TIME,
            'paper_info' => serialize($data),
            'my_time' => 0,
            'my_error_num' => 0,
            'my_is_pass' => 0,
            'is_makeup' => $is_makeup,
            'data_type' => $data_type,
            'obj_id' => $obj_id
        ];

        // 随机抽取
        if (self::TOPIC_RANDOM == $ep_type) {

            $answer_temp_serv = new AnswerTempService();
            // 根据试卷ID从临时答卷表获取答卷
            $snapshot = $answer_temp_serv->get_answer_temp($ep_id);

            if (empty($snapshot)) {
                // 实例化试卷Service
                $paper = new PaperService();

                // 兼容老数据
                if ($is_old == self::OLD_DATA_STATE) {

                    $snapshot = $paper->get_temp_list_by_epid($ep_id, 1); // 0:后台抽题,1:前台抽题
                } else {
                    // 题库信息
                    $bank_topic_data = unserialize($data['bank_topic_data']);
                    //题库ID集合
                    $bank_ids = array_column($bank_topic_data, 'eb_id');
                    // 选题规则
                    $rule_data = [];
                    if (!empty($data['rule'])) {
                        $rule_data = unserialize($data['rule']);
                    }
                    // 按照题库题目类型抽题
                    $snapshot = $paper->random_rule_topic($bank_ids, $data, $bank_topic_data, $rule_data);

                }
            }

            // 按照单选、多选、判断、问答、语音重新组合数组
            $singles = $multiples = $judgments = $questions = $voices = [];
            foreach ($snapshot as $key => $val) {
                // 单选
                if (self::TOPIC_TYPE_SINGLE == $val['et_type']) {

                    $singles[] = $val;
                }

                // 多选
                if (self::TOPIC_TYPE_MULTIPLE == $val['et_type']) {

                    $multiples[] = $val;
                }

                // 判断
                if (self::TOPIC_TYPE_JUDGMENT == $val['et_type']) {

                    $judgments[] = $val;
                }

                // 问答
                if (self::TOPIC_TYPE_QUESTION == $val['et_type']) {

                    $questions[] = $val;
                }

                // 语音
                if (self::TOPIC_TYPE_VOICE == $val['et_type']) {

                    $voices[] = $val;
                }
            }

            $snapshot = array_merge($singles, $multiples, $judgments, $questions, $voices);
        } else {
            // 获取从快照组获取题
            $snapshot = $this->_snapshot_model->list_by_conds(['ep_id' => $ep_id]);

            if (self::UPSET_TOPIC_YES == $data['is_upset_topic']) {
                // 如果开启试题打乱
                shuffle($snapshot);
            }
        }

        // 获取试题失败
        if (empty($snapshot)) {

            E('_ERR_BANK_TOPIC_FAILED');
        }

        $topic = [];

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

            // 插入考试信息
            $ea_id = $this->_d->insert($base);

            foreach ($snapshot as $key => $value) {
                if (array_key_exists('er_id', $value)) {
                    // 随机快照表
                    $esr_id = $value['er_id'];
                } elseif (array_key_exists('es_id', $value)) {
                    // 试卷照表
                    $esr_id = $value['es_id'];
                } else {
                    // 题库
                    $esr_id = 0;
                }

                // 考试试题拼接数据
                if ($ep_type == self::TOPIC_RANDOM) {

                    $order_num = $key + 1;
                } else {

                    $order_num = $value['order_num'];
                }

                $option_index = '';
                if (self::UPSET_OPTION_YES == $data['is_upset_option'] && in_array($value['et_type'], [self::TOPIC_TYPE_SINGLE, self::TOPIC_TYPE_MULTIPLE])) {
                    // 如果开启了选项打乱且题目是单选或者多选,则此处需要打乱题目选项
                    $exam_help = new ExamHelper();

                    // 打乱选项,返回值为打乱的选项(如:A,C,B,D)
                    $option_index = $exam_help->options_shuffle($value['options']);
                }

                $val = [
                    'et_type' => $value['et_type'],
                    'ea_id' => $ea_id,
                    'order_num' => intval($order_num),
                    'is_pass' => 0,
                    'score' => $value['score'],
                    'my_score' => 0,
                    'paper_type' => $data['paper_type'], //试卷使用类型(0:测评试卷,1:模拟试卷)
                    'ep_id' => $data['ep_id'],
                    'esr_id' => $esr_id,
                    'et_id' => $value['et_id'],
                    'option_index' => $option_index
                ];
                $topic[] = $val;
            }

            if (self::UPSET_TOPIC_YES == $data['is_upset_topic']) {
                // 如果开启了试题打乱

                // 打乱试题数组
                shuffle($topic);

                $topic_index = 1;
                foreach ($topic as &$topinfo) {
                    // 重组题目顺序
                    $topinfo['order_num'] = $topic_index;

                    $topic_index++;
                }

                unset($topinfo);
            }

            // 插入试题
            $this->_answer_detail_model->insert_all($topic);

            // 开始创建答卷试题
            $this->commit();
        } catch (\Exception $e) {

            $this->rollback();
            E('_ERR_ADD_EXAM_FAILED');
            return false;
        }

        // 返回答卷ID
        return ['ea_id' => $ea_id];
    }

    /**
     * 获取考试参与人员列表
     *
     * @author: 侯英才
     * @param array $conds 查询条件参数列表
     * @param array $page_option 分页参数
     * @param array $order_option 排序参数
     * @param string $fields 返回字段
     *
     * @return array|bool
     */
    public function get_mock_join_list($conds, $page_option = null, $order_option = [], $fields = '*')
    {
        try {
            return $this->_d->get_mock_answer_list($conds, $page_option, $order_option, $fields);
        } catch (\Exception $e) {
            E($e->getCode() . ':' . $e->getMessage());

            return false;
        }
    }

    /**
     * 统计考试参与人员总数
     * @author: 侯英才
     * @param array $conds 查询条件参数列表
     * @return array|bool
     */
    public function count_mock_answer($conds)
    {
        try {
            return $this->_d->count_mock_answer($conds);
        } catch (\Exception $e) {
            E($e->getCode() . ':' . $e->getMessage());

            return false;
        }
    }

    /**
     * 获取试卷记录
     *
     * @author: 蔡建华
     * @param array $params 请求参数
     * @param string $uid 用户id
     * @param int $type 0:不分页;1:分页
     *
     * @return array|bool
     */
    public function test_record_list($params = [], $uid = '', $type = 0)
    {

        // 试卷id
        $ep_id = intval($params['ep_id']);
        // 试卷id为空
        if (!$ep_id) {

            E('_EMPTY_EP_ID');
        }

        $obj_id = 0;
        // 任务id
        if (isset($params['customtask_id']) && $params['customtask_id']) {

            $obj_id = intval($params['customtask_id']);
        } elseif (isset($params['plan_id']) && $params['plan_id']) {
            // 培训计划id
            $obj_id = intval($params['plan_id']);
        }

        // 获取我的答卷数据
        $info = $this->get_answer_info($ep_id, $uid, $obj_id);

        // 获取用户详情(为了兼容后台管理Apicp/Answer/JoinRecord接口,用户名、头像字段改为username、avatar)
        $user_info = $this->getUser([$uid]);
        $info['username'] = $user_info[$uid]['memUsername'];
        $info['avatar'] = $user_info[$uid]['memFace'];

        // 数据分页
        if ($type) {
            // 判断分页参数是否为空,为空赋默认值
            $page = !empty($params['page']) ? intval($params['page']) : self::DEFAULT_PAGE;
            $limit = !empty($params['limit']) ? intval($params['limit']) : self::DEFAULT_LIMIT;
            // 分页
            list($start, $limit) = page_limit($page, $limit);

            // 分页参数
            $page_option = [$start, $limit];
            // 排序:发布时间倒序
            $order_option['created'] = 'DESC';

            // 答题记录查询条件(模拟考试有初始化=0和已批阅=3两种状态,只查已批阅状态,)
            $condition = [
                'ep_id' => $ep_id,
                'uid' => $uid,
                'answer_status' => self::READ_OVER,
            ];
            // 任务或培训ID存在
            if ($obj_id) {

                $condition['obj_id'] = $obj_id;
            }
            $total = $this->count_by_conds($condition);

            // get_answer_info里面如果答卷数据为空,会抛错"尚未参加考试",所以此处列表必有数据
            $list = $this->list_by_conds($condition, $page_option, $order_option);
        } else { // 数据不分页
            // 不分页,直接取我的答卷数据返回的列表
            $list = $info['my_answer_list_all'];

            // 总数
            $total = $limit = count($list);
            // 页码
            $page = 1;
        }

        unset($info['my_answer_list_all']);

        $result = [
            'page' => intval($page),
            'limit' => intval($limit),
            'total' => intval($total),
            'list' => $this->format_testRecord($list) // 格式化模拟记录
        ];

        return array_merge($info, $result);
    }

    /**
     * 模拟试卷详情列表
     *
     * @author: 蔡建华
     * @param string $ep_id 试卷ID
     * @param string $uid 用户ID
     * @param int $obj_id 任务、培训ID
     *
     * @return array|bool
     */
    public function get_answer_info($ep_id = '', $uid = '', $obj_id = 0)
    {

        // 答题记录查询条件(模拟考试有初始化=0和已批阅=3两种状态,只查已批阅状态,)
        $condition = [
            'ep_id' => $ep_id,
            'uid' => $uid,
            'answer_status' => self::READ_OVER,
        ];
        // 任务或培训ID存在
        if ($obj_id) {

            $condition['obj_id'] = $obj_id;
        }
        // 分页
        $page_option = null;
        // 排序:发布时间倒序
        $order_option['created'] = 'DESC';
        // 查询字段
        $fields = 'ea_id,uid,ep_id,my_score,my_time,my_is_pass,paper_info,answer_status,my_begin_time';
        // 查询当前用户试卷的答题记录
        $my_answer_list_all = $this->_d->list_by_conds($condition, $page_option, $order_option, $fields);

        // 答题记录为空:尚未参加考试
        if (empty($my_answer_list_all)) {

            E('_ERR_MY_VISIT_NO');
        }

        // 试卷信息
        $info = unserialize($my_answer_list_all[0]['paper_info']);
        // 不是模拟试卷
        if ($info['paper_type'] != self::SIMULATION_PAPER_TYPE) {

            E('_ERR_SIMULATION_PAPER_TYPE');
        }

        // 试卷状态
        $ep_status = $this->paper_status($info['exam_status'], $info['begin_time'], $info['end_time']);

        // 历史最高成绩
        $high_score = max(array_column($my_answer_list_all, 'my_score'));
        // 累计模拟考试次数
        $exam_times = count($my_answer_list_all);

        // 通过考试的答卷数据
        $pass_answers = array_filter($my_answer_list_all, function ($v) {

            return $v['my_is_pass'] == self::PASS;
        });
        // 模拟考试通过次数(及格次数)
        $pass_times = count($pass_answers);

        // 我的排名
        $ranking = $this->get_my_rank($ep_id, $uid);

        return [
            'high_score' => $high_score,
            'pass_times' => intval($pass_times),
            'ep_status' => $ep_status,
            'exam_times' => intval($exam_times),
            'exam_type' => intval($info['exam_type']),
            'ranking' => intval($ranking),
            'my_answer_list_all' => $my_answer_list_all
        ];
    }

    /**
     * @author: 蔡建华
     *
     * 格式化模拟详情页面
     *
     * @param array $data
     *
     * @return array
     */
    public function format_testRecord($data = [])
    {

        $result = [];
        foreach ($data as $key => $val) {

            $result[$key] = [
                'ea_id' => intval($val['ea_id']),
                'my_score' => $val['my_score'],
                'my_time' => $val['my_time'],
                'my_begin_time' => $val['my_begin_time'],
                'status' => $val['answer_status']
            ];
        }

        return $result;
    }

    /**
     * 判断试卷状态
     *
     * @author: 蔡建华
     * @param $ea_id int 答卷ID
     * @param $uid string 用户ID
     *
     * @return array|bool
     */
    public function paper_answer_status($ea_id = 0, $uid = '')
    {
        //获取答卷信息
        $ea_data = $this->get_by_conds(['ea_id' => $ea_id, 'uid' => $uid]);
        // 答卷信息不存在
        if (empty($ea_data)) {

            E('_ERR_MY_VISIT_NO');
        }

        // 查询试卷信息
        $ep_id = $ea_data['ep_id'];
        $paper = $this->paper_model->get($ep_id);
        // 判断试卷状态,其中包括分类状态和试卷状态
        if (self::EC_CLOSE_STATES == $paper['cate_status'] || self::PAPER_STOP == $paper['exam_status']) {

            E('_ERR_EC_CLOSE_STATES');
        }
        // 你已经交卷无法答题
        if ($ea_data['answer_status'] >= self::READ_WAITING) {

            E('_ERR_SUBMIT_ANSWER');
        }

        // 我的考试开始时间
        $my_begin_time = $ea_data['my_begin_time'];
        // 分钟
        $paper_time = to_milli_time($paper['paper_time'] * 60);
        $time = MILLI_TIME;
        // 时间到了交卷
        $end_time = $my_begin_time + $paper_time;
        if ($time > $paper['end_time'] || $time > $end_time) {

            $this->submit_papers($ea_id, $uid);
            E('_ERR_EC_EXIT_FINISH');
        }

        // 考试结束时间大于时间结束时间
        if ($end_time > $paper['end_time']) {

            $left_time = $paper['end_time'] - $time;
            // 考试结束时间小与时间结束时间
        } else {

            $left_time = $end_time - $time;
        }

        return [
            'ep_name' => $paper['ep_name'],
            'marking_type' => $paper['marking_type'],
            'exam_type' => $paper['exam_type'],
            'paper_type' => $paper['paper_type'],
            'left_time' => $left_time, // 返回倒计时
            'my_begin_time' => $my_begin_time,
            'paper_time' => $paper['paper_time'],
            'end_time' => $paper['end_time']
        ];
    }

    /**
     * 根据条件读取数据
     *
     * @author: 侯英才
     * @param array $conds 查询条件数组
     * @param array $order_option 排序数组
     * @param String $fields 查询字段
     *
     * @return array|bool
     */
    public function get_by_conds($conds, $order_option = [], $fields = '*')
    {

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

            return false;
        }
    }

    /**
     * 自动提交试卷
     *
     * @author: 蔡建华
     * @param int $ea_id 答卷ID
     * @param int $uid 用户ID
     * @param array $award
     * @param int $type 交卷类型 0,自动交卷 1手动交卷
     * @param array $score_list 积分策略返回值
     *
     * @return bool
     */
    public function submit_papers($ea_id = 0, $uid = 0, &$award = [], $type = 0, &$score_list = [])
    {
        // 判断答卷ID是否为空
        if (!$ea_id) {

            E('_EMPTY_EA_ID');
        }
        // 判断用户ID不能为空
        if (!$uid) {

            E('_EMPTY_UID');
        }

        //获取答卷信息
        $ea_data = $this->get_by_conds(['ea_id' => $ea_id, 'uid' => $uid]);
        // 答卷信息不存在
        if (empty($ea_data)) {

            E('_ERR_MY_VISIT_NO');
        }

        $ep_id = $ea_data['ep_id'];
        $paper = $this->paper_model->get_by_conds(['ep_id' => $ep_id]);
        $time = MILLI_TIME;
        if (self::EC_CLOSE_STATES == $paper['cate_status'] || self::PAPER_STOP == $paper['exam_status']) {

            E('_ERR_EC_CLOSE_STATES');
        }
        // 你已经交卷
        if ($ea_data['answer_status'] > 0) {

            E('_ERR_REPEAT_SUBMIT_EXAM');
        }
        // 分钟
        $paper_time = to_milli_time($paper['paper_time'] * 60);
        // 我的考试开始时间
        $my_begin_time = $ea_data['my_begin_time'];
        // 时间到了交卷
        $end_time = $my_begin_time + $paper_time;
        if ($time <= $paper['end_time'] && $time <= $end_time) {

            $submit = 1;
            //提前交卷
            $my_time = ($time - $my_begin_time);
        } else {

            $submit = 2;
            //时间到自动交卷
            if ($end_time > $paper['end_time']) {

                $my_time = ($paper['end_time'] - $my_begin_time);
            } else {

                $my_time = ($end_time - $my_begin_time);
            }
        }

        $marking_type = $paper['marking_type'];
        $count = 0;

        // 交卷处理
        try {
            // 开始事务
            $this->start_trans();
            // 未作答的处理成已批阅 0分
            $this->_answer_detail_model->update_by_conds([
                'marking_status' => self::NO_DO_MK_STATE,
                'is_status' => self::NO_DO_MK_STATE,
                'ea_id' => $ea_id
            ], [
                'marking_status' => self::DO_MK_STATE,
                'my_score' => 0,
                'is_pass' => self::NO_MY_PASS
            ]);

            $this->update_paper_join_count($paper, $marking_type, $ep_id, $uid);

            // 测评试卷手动阅卷
            if (self::MANUAL_MARKING_TYPE == $marking_type && self::EVALUATION_PAPER_TYPE == $paper['paper_type']) {

                // 查询已经作答且未批阅
                $count = $this->_answer_detail_model->count_by_conds([
                    'marking_status' => self::NO_DO_MK_STATE,
                    'is_status' => self::DO_STATE,
                    'ea_id' => $ea_id,
                    'et_type' => [self::TOPIC_TYPE_QUESTION, self::TOPIC_TYPE_VOICE]
                ]);
            }

            // 手动阅卷有主观题
            if ($count > 0) {
                // 手动阅卷
                $this->update(
                    ['ea_id' => $ea_id],
                    ['my_time' => $my_time, 'my_end_time' => $my_begin_time + $my_time, 'answer_status' => self::READ_WAITING]
                );
                // 发送消息给阅卷人
                $result_status = self::SEND_MSG_FOR_MARKING_USER;
            } else {
                // 算分函数
                $my_score = $this->_answer_detail_model->get_score(['ea_id' => $ea_id, 'marking_status' => 1]);

                // 判断是否通过
                if ($paper['pass_score'] > $my_score) {

                    $my_is_pass = 0;
                } else {

                    $my_is_pass = 1;
                }

                $this->update(
                    ['ea_id' => $ea_id],
                    [
                        'my_is_pass' => $my_is_pass, // 是否通过
                        'my_time' => $my_time, // 用时
                        'my_end_time' => $my_begin_time + $my_time, //交卷时间
                        'my_score' => $my_score, // 分数
                        'answer_status' => self::READ_OVER, // 已阅卷
                        'marking_time' => MILLI_TIME // 阅卷时间
                    ]
                );

                // 积分策略业务ID=应用目录_数据ID
                $businessid = sprintf('%s_%s', APP_DIR, $ep_id);

                // 调用积分策略
                $score_list = $this->get_score_list($uid, $paper, $my_score, $my_is_pass, $businessid);

                if (self::MANUAL_MARKING_TYPE == $marking_type && self::EVALUATION_PAPER_TYPE == $paper['paper_type']) {

                    // 考试成绩已出,发送消息
                    $result_status = self::SEND_MSG_FOR_JOIN_EXAM_USER;
                } else {
                    // 不发送消息
                    $result_status = self::NOT_NEED_SEND_MSG;
                }
            }

            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();
            E('_ERR_SUBMIT_FAIL');
            return false;
        }

        //添加积分勋章接口
        $this->paper_statistics($paper, $uid);

        // 添加积分勋章接口
        $this->medal($ep_id, $uid, $award);
        // 手动阅卷发送阅卷信息
        if (self::SEND_MSG_FOR_MARKING_USER == $result_status) {

            $this->send_cms_answer($ea_id, AnswerService::MARKING_USER_MSG);

        } elseif (self::SEND_MSG_FOR_JOIN_EXAM_USER == $result_status) {
            // 手动阅卷系统阅卷成绩出来
            $this->send_cms_answer($ea_id, AnswerService::MARKING_RESULT_MSG);
        }

        // 时间到自动交卷
        if (!$type && self::MANUAL_MARKING_TYPE == $submit) {

            E('_ERR_AUTO_SUBMIT_TIME');
        }

        return true;
    }

    /**
     * 统计参加考试与未参加考试的人
     *
     * @param array $paper 试卷信息数组
     * @param int $marking_type 试卷类型
     * @param int $ep_id 试卷ID
     *
     * @param string $uid 考生uid
     */
    public function update_paper_join_count($paper = [], $marking_type = 0, $ep_id = 0, $uid = '')
    {
        // 未参与人员
        $unjoin_count = $paper['unjoin_count'] - 1;
        if ($unjoin_count < 0) {

            $unjoin_count = 0;
        }
        // 已参与人员
        $join_count = $paper['join_count'] + 1;
        if ($marking_type == self::MANUAL_MARKING_TYPE) {

            // 如果是测评试卷,参与人加1
            $this->paper_model->update_by_conds(['ep_id' => $ep_id],
                ['join_count' => $join_count, 'unjoin_count' => $unjoin_count]);

        } else {
            // 判断模拟试卷是否已有交卷记录
            $submit_count = $this->get_by_conds(['ep_id' => $ep_id, 'uid' => $uid, 'answer_status>?' => self::READ_WAITING]);
            if (!$submit_count) {
                $this->paper_model->update_by_conds(['ep_id' => $ep_id],
                    ['join_count' => $join_count, 'unjoin_count' => $unjoin_count]);
            }
        }
    }

    /**
     * 根据积分策略获取积分
     *
     * @param string $uid 答卷人ID
     * @param array $paper 试卷信息
     * @param int $my_score 我的得分
     * @param int $my_is_pass 是否通过考试(0=未通过,1=通过)
     * @param string $businessid 积分策略业务ID
     *
     * @return array|mixed
     */
    public function get_score_list($uid = '', $paper = [], $my_score = 0, $my_is_pass = 0, $businessid = '')
    {
        $score_list = [];

        if (self::INTEGRAL_ACTION_TYPE_NO == $paper['integral_action_type'] && self::CREDIT_ACTION_TYPE_FALSE == $paper['credit_action_type']) {
            // 如果不启用策略
            return $score_list;
        }

        // 初始化积分模型
        $integral = new Integral();

        $count = new CountModel();

        // 此处查询通过次数
        $num_data = $count->get_by_conds(['uid' => $uid, 'paper_type' => $paper['paper_type']]);

        // 组织策略规则
        $triggerTypes = [
            [
                'triggerKey' => 'exam_score', // 考试分数
                'value' => intval($my_score),
                'remark' => '考试分数'
            ],
            [
                'triggerKey' => 'exam_score_range', //考试分数区间
                'value' => intval($my_score),
                'remark' => '考试分数区间'
            ]
        ];


        if (!empty($num_data) && !empty($my_is_pass)) {

            // 如果是模拟试卷,先查一下是否有已通过的记录
            if (self::SIMULATION_PAPER_TYPE == $paper['paper_type']) {

                $answer_serv = new AnswerModel();

                $pass_total = $answer_serv->count_by_conds([
                    'uid' => $uid,
                    'ep_id' => $paper['ep_id'],
                    'my_is_pass' => 1
                ]);

                // 如果不存在答题通过记录,则加次数
                if ($pass_total == 0) {

                    $u_conds = [
                        'uid' => $uid,
                        'paper_type' => $paper['paper_type']
                    ];

                    $pass_num = $num_data['num'] + 1;
                    $count->update_by_conds($u_conds, ['num' => $pass_num]);

                    $triggerTypes[] = [
                        'triggerKey' => 'pass_num', // 通过试卷数
                        'value' => intval($pass_num),
                        'remark' => '通过试卷数'
                    ];
                }
            } else {
                // 测评试卷
                // 如果记录存在并且通过考试,则按条件进行数据更新
                $u_conds = [
                    'uid' => $uid,
                    'paper_type' => $paper['paper_type']
                ];

                $pass_num = $num_data['num'] + 1;
                $count->update_by_conds($u_conds, ['num' => $pass_num]);

                $triggerTypes[] = [
                    'triggerKey' => 'pass_num', // 通过试卷数
                    'value' => intval($pass_num),
                    'remark' => '通过试卷数'
                ];
            }
        } elseif (empty($num_data) && !empty($my_is_pass)) {
            // 如果记录不存在并且通过考试,则插入记录
            $pass_num = 1;
            $i_data = [
                'uid' => $uid,
                'paper_type' => $paper['paper_type'],
                'num' => $pass_num
            ];
            $count->insert($i_data);

            $triggerTypes[] = [
                'triggerKey' => 'pass_num', // 通过试卷数
                'value' => intval($pass_num),
                'remark' => '通过试卷数'
            ];
        }

        // 如果开始通过
        if ($my_is_pass) {
            $triggerTypes[] = [
                'triggerKey' => 'pass', // 通过
                'value' => 1,
                'remark' => '通过考试'
            ];
        }

        // 是否触发策略(默认不触发)
        $is_score_flag = false;

        // 初始化触发策略参数
        $triggers = [];

        // 开启积分策略
        if (in_array($paper['integral_action_type'], [self::INTEGRAL_ACTION_TYPE_DEFAULT, self::INTEGRAL_ACTION_TYPE_MY])) {

            $is_score_flag = true;

            $triggers[] = [
                'miType' => 'mi_type0',
                'businessStrategyId' => $paper['integral_strategyid'] ? $paper['integral_strategyid'] : '',
            ];
        }

        // 开启学分策略
        if (in_array($paper['credit_action_type'], [self::CREDIT_ACTION_TYPE_DEFAULT, self::CREDIT_ACTION_TYPE_CUSTOMIZE])) {

            $is_score_flag = true;

            $triggers[] = [
                'miType' => 'mi_type1',
                'businessStrategyId' => $paper['credit_strategyid'] ? $paper['credit_strategyid'] : '',
            ];
        }

        // 触发策略
        if ($is_score_flag) {
            // 默认为测评试卷
            $businessAct = 'official_exam';

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

                $businessAct = 'mock_exam';
            }

            foreach ($triggers as $k => $trigger) {
                $triggers[$k]['triggerTypes'] = $triggerTypes;
            }

            // 组装积分策略请求参数
            $integral_param = [
                'memUid' => $uid,
                'businessId' => $businessid,
                'businessKey' => 'exam_center',
                'businessAct' => $businessAct,
                'triggers' => $triggers,
            ];

            $score_list = $integral->changeScore($integral_param);
        }

        return $score_list;
    }

    /**
     * 更新参与未参与人数
     *
     * @author: 蔡建华
     * @param $data array 试题
     *
     * @param $uid string 用户ID
     */
    public function paper_statistics($data = [], $uid = '')
    {
        $count = $this->_d->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0]);
        if (!$count) {
            if (!$data['unjoin_count']) {

                $data['unjoin_count'] = 1;
            }
            $this->paper_model->update_by_paper(
                ['ep_id' => $data['ep_id']],
                ['join_count' => $data['join_count'] + 1, 'unjoin_count' => $data['unjoin_count'] - 1]
            );
        }
    }

    /**
     * 考试激励处理
     *
     * @author: 蔡建华
     * @param int $ep_id 试卷ID
     * @param string $uid 用户ID
     * @param array &$award 返回数据
     *
     * @return bool
     */
    public function medal($ep_id, $uid = '', &$award = [])
    {
        // 获取用户信息
        $userServ = &User::instance();
        $user = $userServ->getByUid($uid);

        // 获取用户权限
        $rightServ = new RightService();
        $right = $rightServ->get_by_right($user);

        $data['right'] = $right;
        $data['er_type'] = self::RIGHT_MEDAL;
        $medal = $this->_medal_model->fetch_all_medal($data);
        // 查询出相关激励
        if (empty($medal)) {

            return true;
        }

        // 获取符合条件的激励IDS
        $em_ids = array_column($medal, 'em_id');
        // 查询激励关系列表
        $list = $this->_medal_relation_model->list_by_conds(['em_id' => $em_ids]);

        // 如果存在符合条件激励
        if (empty($list)) {

            return true;
        }

        // 获取激励列表IDS
        $em_id_arr = array_column($list, 'em_id');
        // 如果查询出激励IDS
        if (empty($em_id_arr)) {

            return true;
        }

        // 初始化勋章
        $integral = new Integral();
        // 获取勋章列表
        $integral_list = $integral->listMedal();
        // 获取激励列表
        $integral_key_list = array_combine_by_key($integral_list, 'im_id');
        // 获取激励列表
        $result = $this->_medal_model->list_by_conds(['em_id' => $em_id_arr]);
        // 实例化积分勋章
        $integralUtil = &Integral::instance();
        // 遍历激励列表
        foreach ($result as $val) {

            // 获取当前用户是否已经领取过当期激励
            $count = $this->_medal_record_model->count_by_conds(['uid' => $uid, 'em_id' => $val['em_id']]);

            // 如果已领取积分或者勋章
            if ($count) {

                continue;
            }

            // 查询勋章对应关系
            $relate_total = $this->_medal_relation_model->count_by_conds(['ep_id' => $ep_id, 'em_id' => $val['em_id']]);
            // 如果没有对应关系
            if (!$relate_total) {

                continue;
            }

            // 查出对应ep_list
            $em_list = $this->_medal_relation_model->list_by_conds(['em_id' => $val['em_id']]);
            $ep_ids = array_column($em_list, 'ep_id');

            // 答题列表
            $score_list = $this->_d->list_by_conds([
                'ep_id' => $ep_ids,
                'uid' => $uid,
                'my_score >= ?' => $val['em_score']
            ]);

            // 获取分数的epids
            $score_ep_ids = array_unique(array_column($score_list, 'ep_id'));
            // 判断满足条件的次数
            if (count($score_ep_ids) >= $val['em_number']) {

                // 获取勋章
                if (self::EC_MEDAL_TYPE_MEDAL == $val['em_type']) {

                    $integralUtil->endowMedal($val['im_id'], $uid, $user['memUsername']);
                    $params = [
                        'name' => $integral_key_list[$val['im_id']]['name'],
                        'url' => '',
                        'uids' => [$uid],
                        'power_type' => self::ENDOW_END,
                        'title' => $val['title']
                    ];
                    $this->send_msg($params, self::ENDOW_END);

                    // 激励信息
                    $award[] = [
                        'award_id' => intval($val['em_id']), // 激励ID
                        'award_action' => strval($val['title']), // 激励行为
                        'description' => strval($val['em_desc']), // 描述
                        'award_type' => intval($val['em_type']), // 激励类型(1=勋章;2=积分)
                        'medals' => [
                            'im_id' => intval($val['im_id']), // 勋章ID
                            'icon' => strval($integral_key_list[$val['im_id']]['icon']), // 勋章图标URL或者前端路径
                            'icon_type' => intval($integral_key_list[$val['im_id']]['icon_type']), // 勋章图标来源(1=用户上传;2=系统预设)
                            'name' => strval($integral_key_list[$val['im_id']]['name']), // 勋章名称
                            'desc' => strval($integral_key_list[$val['im_id']]['desc']), // 勋章描述
                        ],
                        'integral' => intval($val['em_integral']), // 积分
                    ];

                    // 满足次数加入记录
                    $this->_medal_record_model->insert([
                        'uid' => $uid,
                        'em_id' => $val['em_id']
                    ]);
                }
            }
        }

        return true;
    }

    /**
     * 发送考试消息
     *
     * @param int $ea_id
     * @param int $type
     */
    public function send_cms_answer($ea_id = 0, $type = 0)
    {
        $ea_data = $this->get_by_conds(['ea_id' => $ea_id]);

        $ep_id = $ea_data['ep_id'];
        $paper = $this->paper_model->get_by_conds(['ep_id' => $ep_id]);
        switch ($type) {
            case self::MARKING_RESULT_MSG:
                // 获取阅卷人信息
                $user = User::instance()->getByUid($ea_data['marking_uid']);
                $params = [
                    'name' => $paper['ep_name'],
                    'is_cover_open' => $paper['is_cover_open'],
                    'img_id' => $paper['cover_id'],
                    'my_end_time' => $ea_data['marking_time'],
                    'user_name' => $ea_data['marking_uid'] ? $user['memUsername'] : '系统阅卷',
                    'uids' => ['memID' => $ea_data['uid']],
                    'ea_id' => $ea_id,
                    'id' => $ep_id,
                    'obj_id' => $ea_data['obj_id']
                ];

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

                break;
            case self::MARKING_USER_MSG:
                if (self::EVALUATION_PAPER_TYPE == $paper['paper_type'] && self::MANUAL_MARKING_TYPE == $paper['marking_type']) {

                    // 获取权限
                    $right = $this->_d_right->list_by_conds([
                        'epc_id' => $ep_id,
                        'er_type' => self::RIGHT_MARKING
                    ]);

                    $uids = array_column($right, 'uid');

                    // 获取用户信息
                    $user = User::instance()->getByUid($ea_data['uid']);

                    $params = [
                        'name' => $paper['ep_name'],
                        'is_cover_open' => $paper['is_cover_open'],
                        'img_id' => $paper['cover_id'],
                        'user_name' => $paper['is_open_anonymous_marking'] == self::OPEN_ANONYMOUS_MARKING ? '匿名' : $user['memUsername'],
                        'user_time' => $ea_data['my_end_time'],
                        'exam_type' => $paper['exam_type'],
                        'uids' => $uids,
                        'ea_id' => $ea_id,
                        'id' => $ep_id
                    ];

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

    /**
     * 判断试卷状态
     *
     * @author: 蔡建华
     * @param $ea_id int 答卷ID
     * @param $uid string 用户ID
     *
     * @return array|bool
     */
    public function paper_answer_status_new($ea_id = 0, $uid = '')
    {
        //获取答卷信息
        $ea_data = $this->get_by_conds(['ea_id' => $ea_id, 'uid' => $uid]);
        // 答卷信息不存在
        if (empty($ea_data)) {

            E('_ERR_MY_VISIT_NO');
        }

        // 查询试卷信息
        $ep_id = $ea_data['ep_id'];
        $paper = $this->paper_model->get($ep_id);
        // 判断试卷状态,其中包括分类状态和试卷状态
        if (self::EC_CLOSE_STATES == $paper['cate_status'] || self::PAPER_STOP == $paper['exam_status']) {

            E('_ERR_EC_CLOSE_STATES');
        }
        // 你已经交卷无法答题
        if ($ea_data['answer_status'] >= self::READ_WAITING) {

            E('_ERR_SUBMIT_ANSWER');
        }

        // 我的考试开始时间
        $my_begin_time = $ea_data['my_begin_time'];
        // 分钟
        $paper_time = to_milli_time($paper['paper_time'] * 60);
        $time = MILLI_TIME;
        // 时间到了交卷
        $end_time = $my_begin_time + $paper_time;
        if ($time > $paper['end_time'] || $time > $end_time) {

            E('_ERR_EC_EXIT_FINISH');
        }

        // 考试结束时间大于时间结束时间
        if ($end_time > $paper['end_time']) {

            $left_time = $paper['end_time'] - $time;
            // 考试结束时间小与时间结束时间
        } else {

            $left_time = $end_time - $time;
        }

        return [
            'ep_name' => $paper['ep_name'],
            'exam_type' => $paper['exam_type'],
            'marking_type' => $paper['marking_type'],
            'paper_type' => $paper['paper_type'],
            // 返回倒计时
            'left_time' => $left_time,
            'my_begin_time' => $my_begin_time,
            'paper_time' => $paper['paper_time'],
            'end_time' => $paper['end_time']
        ];
    }

    /**
     * 自动提交试卷
     *
     * @author: 何岳龙
     * @param int $ea_id 答卷ID
     * @param int $uid 用户ID
     * @param array $paper 试卷详情
     *
     * @return bool
     */
    public function stop_submit_papers($ea_id = 0, $uid = 0, $paper = [])
    {
        // 判断答卷ID是否为空
        if (!$ea_id) {

            return false;
        }
        // 判断用户ID不能为空
        if (!$uid) {

            return false;
        }

        //获取答卷信息
        $ea_data = $this->get_by_conds(['ea_id' => $ea_id, 'uid' => $uid]);
        // 答卷信息不存在
        if (empty($ea_data)) {

            return false;
        }
        $ep_id = $ea_data['ep_id'];

        $time = MILLI_TIME;

        // 你已经交卷
        if (PaperService::READ_OVER == $ea_data['answer_status']) {

            return false;
        }

        // 分钟
        $paper_time = to_milli_time($paper['paper_time'] * 60);
        // 我的考试开始时间
        $my_begin_time = $ea_data['my_begin_time'];
        // 时间到了交卷
        $end_time = $my_begin_time + $paper_time;
        if ($time <= $paper['end_time'] && $time <= $end_time) {

            // 提前交卷
            $my_time = ($time - $my_begin_time);
        } else {

            // 时间到自动交卷
            if ($end_time > $paper['end_time']) {

                $my_time = ($paper['end_time'] - $my_begin_time);
            } else {

                $my_time = ($end_time - $my_begin_time);
            }
        }
        $marking_type = $paper['marking_type'];

        // 交卷处理
        try {
            // 开始事务
            $this->start_trans();
            // 未作答的处理成已批阅 0分
            $this->_answer_detail_model->update_by_conds([
                'marking_status' => self::NO_DO_MK_STATE,
                'is_status' => self::DO_PASS_STATE,
                'ea_id' => $ea_id
            ], [
                'marking_status' => self::DO_MK_STATE,
                'my_score' => 0
            ]);

            // 更新未读人员数已读人员数
            $this->update_paper_join_count($paper, $marking_type, $ep_id, $uid);

            $my_is_pass = 0; // 未通过
            $my_score = 0; // 我的得分
            $this->update(
                ['ea_id' => $ea_id],
                [
                    'my_is_pass' => $my_is_pass, // 是否通过
                    'my_time' => $my_time, // 用时
                    'my_end_time' => $my_begin_time + $my_time, //交卷时间
                    'my_score' => $my_score, // 分数
                    'answer_status' => self::READ_OVER, // 已阅卷
                    'marking_time' => MILLI_TIME, // 阅卷时间
                ]
            );

            // 积分策略业务ID=应用目录_数据ID
            $businessid = sprintf('%s_%s', APP_DIR, $paper['ep_id']);

            // 调用积分策略
            $this->get_score_list($uid, $paper, $my_score, $my_is_pass, $businessid);


            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();

            return false;
        }

        // 更新参与未参与人数
        $this->paper_statistics($paper, $uid);
        // 添加积分勋章接口(此处因为都是0分,所以不会触发发勋章)
        // $this->medal($ep_id, $uid, $award);

        return true;
    }

    /**
     * 【微信端】 提交题目试卷验证
     *
     * @author: 何岳龙
     * @param array $params
     * @param string $uid
     *
     * @return bool
     */
    public function end_time_validation($params = [], $uid = '')
    {

        // 获取答卷详情
        $answer_info = $this->_answer_detail_model->get($params['ead_id']);

        // 获取得分详情
        $info = $this->_d->get($answer_info['ea_id']);

        // 获取试卷详情
        $paper_info = $this->paper_model->get($info['ep_id']);

        // 如果考试考生考试开始时间+考试时长大于结束时间
        if (($info['my_begin_time'] + (to_milli_time($paper_info['paper_time'] * 60))) > $paper_info['end_time']) {
            // 如果当前时间大于或者等于结束时间
            if ($paper_info['end_time'] <= MILLI_TIME) {
                // 交卷
                $this->submit_papers($answer_info['ea_id'], $uid);

                return false;
            }
        } else {
            // 如果当前时间大于开始的结束时间
            if (MILLI_TIME >= ($info['my_begin_time'] + (to_milli_time($paper_info['paper_time'] * 60)))) {
                // 交卷
                $this->submit_papers($answer_info['ea_id'], $uid);

                return false;
            }
        }
        return true;
    }

    /**
     * 批阅详情函数
     *
     * 蔡建华
     * @param int $ep_id 试卷ID
     * @param string $uid 阅卷人ID
     *
     * @return bool|array
     */
    public function marking_detail($ep_id, $uid = '')
    {
        $paper = $this->paper_model->get($ep_id);
        if (empty($paper)) {

            E('_EMPTY_EXAM_DELETED');
        }
        // 判断试卷类型
        if ($paper['paper_type'] != self::EVALUATION_PAPER_TYPE) {

            E('_ERR_EVALUATION_PAPER_TYPE');
        }
        // 判断用户是否有权进行操作
        $count = $this->_right_model->count_by_conds([
            'epc_id' => $ep_id,
            'er_type' => self::RIGHT_MARKING,
            'uid' => $uid
        ]);
        if (!$count) {

            E('_ERR_MARKING_NO_QUIT');
        }

        // 当前试卷的批阅题数
        if (self::TOPIC_CUSTOMER == $paper['ep_type']) {
            // 自主选题,批阅题数从快照表中获取
            $marking_topics = $this->_snapshot_model->count_by_conds([
                'ep_id' => $ep_id,
                'et_type' => [self::TOPIC_TYPE_QUESTION, self::TOPIC_TYPE_VOICE],
            ]);
        } else {
            // 非自主选题,批阅题数从出题规则中获取
            $paper_rule = unserialize($paper['rule']);
            $marking_topics = $paper_rule['question_count'] + $paper_rule['voice_count'];
        }

        // 待批阅人数
        $marking_waiting_users = $this->_d->count_by_conds([
            'ep_id' => $ep_id,
            'answer_status' => self::READ_WAITING,
        ]);

        // 是否是首次批阅
        $is_first = $this->_d->count_by_conds([
            'ep_id' => $ep_id,
            'marking_uid' => $uid,
        ]);

        // 组装格式化数据
        $arr = [
            'ep_id' => $paper['ep_id'],
            'ep_name' => $paper['ep_name'], // 试卷名称
            'total_score' => $paper['total_score'], // 考试总分
            'pass_score' => $paper['pass_score'], // 及格分数
            'topic_count' => intval($paper['topic_count']), // 总题数
            'paper_time' => $paper['paper_time'], // 考试时长
            'begin_time' => $paper['begin_time'], // 开始时间
            'end_time' => $paper['end_time'], // 结束时间
            'intro' => $paper['intro'], // 考试说明
            'marking_topics' => intval($marking_topics), // 批阅题数
            'marking_waiting_users' => intval($marking_waiting_users), // 待批阅人数
            'check_status' => !$is_first ? self::FIRST_MARKING_PAPER : self::SECOND_MARKING_PAPER, // 批阅状态(1:首页批阅,2:再次批阅)
            'exam_type' => intval($paper['exam_type']),
        ];

        // 封面图片
        if (self::IS_COVER_PIC_OPEN == $paper['is_cover_open']) {

            $arr['cover_url'] = $this->format_cover($paper['cover_id']);
        } else {

            $arr['cover_url'] = '';
        }

        return $arr;
    }

    /**
     * 阅卷交卷接口
     *
     * 蔡建华
     * @param array $params 参数
     * @param string $uid 用户ID
     *
     * @return bool
     */
    public function marking_submit($params = [], $uid = '')
    {
        $ea_id = $params['ea_id'];
        // 答卷ID不能为空
        if (!$ea_id) {

            E('_EMPTY_EA_ID');
        }

        // 查询答卷记录
        $data = $this->_d->get($ea_id);
        // 考试记录是否存在
        if (empty($data)) {

            E('_EMPTY_MARKING_INFO');
        }

        $ep_id = $data['ep_id'];
        $paper = $this->paper_model->get($ep_id);
        if (empty($paper)) {

            E('_EMPTY_EXAM_DELETED');
        }

        // 判断试卷类型
        if ($paper['paper_type'] != self::EVALUATION_PAPER_TYPE) {

            E('_ERR_EVALUATION_PAPER_TYPE');
        }

        // 不是手动阅卷
        if (self::AUTO_MARKING_TYPE == $paper['marking_type']) {

            E('_ERR_MARKING_PAPER_TYPE');
        }

        // 阅卷是否在阅卷
        if (self::READ_OVER == $data['answer_status']) {

            E('_ERR_MARKING_FINISH');
        }

        //  判断阅卷人不相同
        if ($data['marking_uid'] != $uid) {

            E('_ERR_MARKING_NOT_SOME');
        }

        // 未作答的处理成已批阅 0分
        $this->_answer_detail_model->update_by_conds([
            'marking_status' => self::NO_DO_MK_STATE,
            'is_status' => self::NO_DO_MK_STATE,
            "ea_id" => $ea_id
        ], [
            'marking_status' => self::DO_MK_STATE,
            'my_score' => 0
        ]);

        // 查询没有批阅的状态
        $count = $this->_answer_detail_model->count_by_conds([
            'marking_status' => self::NO_DO_MK_STATE,
            'ea_id' => $ea_id
        ]);
        // 是否有尚未批阅的题
        if ($count) {

            E('_ERR_CAN_NOT_FAILED');
        }

        // 计算总分数
        $my_score = $this->_answer_detail_model->get_score([
            'ea_id' => $ea_id,
            'marking_status' => self::DO_MK_STATE
        ]);

        try {
            // 开始事务
            $this->start_trans();
            // 判断是否通过
            if ($paper['pass_score'] > $my_score) {
                // 未通过
                $my_is_pass = 0;
            } else {
                // 通过
                $my_is_pass = 1;
            }

            // 积分策略业务ID=应用目录_数据ID
            $businessid = sprintf('%s_%s', APP_DIR, $ep_id);

            // 调用积分策略( 此处uid必须传答卷人ID)
            $this->get_score_list($data['uid'], $paper, $my_score, $my_is_pass, $businessid);

            // 阅卷交卷算分
            $this->_d->update_by_conds(['ea_id' => $ea_id],
                [
                    'my_score' => $my_score,
                    'my_is_pass' => $my_is_pass,
                    'answer_status' => self::READ_OVER,
                    'marking_time' => MILLI_TIME
                ]);

            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();
            return false;
        }
        // 考试激励规则调用
        $this->paper_statistics($paper, $data['uid']);

        $award = [];
        // 添加积分勋章接口
        $this->medal($paper['ep_id'], $data['uid'], $award);

        return true;
    }

    /**
     * 通过试卷IDS查询对应试卷已参与人员统计列表
     *
     * @param array $ep_ids 试卷IDS
     *
     * @return array
     */
    public function list_by_total($ep_ids = [])
    {
        // 如果试卷IDS为空
        if (empty($ep_ids)) {

            return [];
        }

        // 获取列表
        $list = $this->_d->list_by_total($ep_ids);

        return $list;
    }

    /**
     * 执行自定义查询SQL语句(安装应用回调时用)
     * @author 侯英才
     * @param string $sql 执行的SQL语句
     * @return mixed
     */
    public function query($sql)
    {
        return $this->_d->query($sql);
    }

    /**
     * 执行自定义非查询SQL语句(安装应用回调时用)
     *
     * @author 侯英才
     * @param string $sql 执行的SQL语句
     *
     * @return mixed
     */
    public function execute($sql)
    {
        return $this->_d->execute($sql);
    }

    /**
     * 组装查询条件
     *
     * @param $paper array 试卷信息
     * @param $params array 传入的参数
     *
     * @return array 返回组装完成的条件数组
     */
    public function get_conditions($paper, $params)
    {
        // 查询条件初始化
        $conditions = [
            'ep_id' => $paper['ep_id'],
            'my_time > ?' => 0
        ];

        // 部门、岗位、角色的用户id交集
        $djr_uids = $this->get_dp_job_role_uids($params, $paper['ep_id']);

        if (!empty($djr_uids)) {

            $conditions['uid'] = $djr_uids;
        }

        return $conditions;
    }

    /**
     * 根据部门、岗位、角色、员工名称获取用户的id集合
     *
     * @param array $params
     *          + dpIds       -部门ID数组
     *          + memJob      -岗位ID
     *          + memRole     -角色ID
     *          + memUsername -员工名称
     * @param int $ep_id 试卷ID
     *
     * @return array
     */
    public function get_dp_job_role_uids($params, $ep_id)
    {
        // 部门、岗位、角色的用户id交集
        $djr_uids = [];
        $user = User::instance();

        // 【部门】
        if (isset($params['dpIds']) && !empty($params['dpIds']) && is_array($params['dpIds'])) {

            $params_dpids = $params['dpIds'];
            $dpServ = &Department::instance();
            // 取子级部门ID
            $child_ids = $dpServ->list_childrens_by_cdid($params_dpids);
            // 合并部门ID
            $dp_ids = array_merge($params_dpids, array_values($child_ids));

            $dp_conds = ['dpIdList' => $dp_ids];
            $dp_users = $user->listAll($dp_conds);

            if (!empty($dp_users)) {
                // 选择部门下的员工id集合
                $d_uids = array_column($dp_users, 'memUid');
            } else {

                $d_uids = [0];
            }

            // 组装查询条件
            $dp_uids = $this->get_uids($ep_id, $d_uids);
            if (!empty($djr_uids)) {

                $djr_uids = array_intersect($djr_uids, $dp_uids);
                if (empty($djr_uids)) {
                    // 空查询所有 为了避免查询所有传入0
                    $djr_uids = [0];
                }
            } else {

                $djr_uids = $dp_uids;
            }
        }

        // 【岗位】
        if (isset($params['memJob']) && $params['memJob']) {

            $job_ids = [$params['memJob']];
            $job_conds = [
                'jobIdList' => $job_ids
            ];
            // 获取岗位的员工列表
            $job_users = $user->listAll($job_conds);

            if (!empty($job_users)) {
                // 选择岗位下的员工id集合
                $j_uids = array_column($job_users, 'memUid');
            } else {

                $j_uids = [0];
            }

            // 组装查询条件
            $job_uids = $this->get_uids($ep_id, $j_uids);
            if (!empty($djr_uids)) {

                $djr_uids = array_intersect($djr_uids, $job_uids);
                if (empty($djr_uids)) {
                    // 空查询所有 为了避免查询所有传入0
                    $djr_uids = [0];
                }
            } else {

                $djr_uids = $job_uids;
            }
        }

        // 【角色】
        if (isset($params['memRole']) && $params['memRole']) {

            $role_ids = [$params['memRole']];
            // 查询条件:角色id数组
            $role_conds = ['roleIdList' => $role_ids];
            // 获取角色的员工列表
            $role_users = $user->listAll($role_conds);

            if (!empty($role_users)) {
                // 角色的员工id集合
                $r_uids = array_column($role_users, 'memUid');
            } else {

                $r_uids = [0];
            }

            // 组装查询条件
            $role_uids = $this->get_uids($ep_id, $r_uids);
            if (!empty($djr_uids)) {

                $djr_uids = array_intersect($djr_uids, $role_uids);
                if (empty($djr_uids)) {
                    // 空查询所有 为了避免查询所有传入0
                    $djr_uids = [0];
                }
            } else {

                $djr_uids = $role_uids;
            }
        }

        // 【标签】
        if (isset($params['memTag']) && $params['memTag']) {

            $tag_ids = [$params['memTag']];
            // 查询条件:标签id数组
            $tag_conds = ['tagIdList' => $tag_ids];
            // 获取标签的员工列表
            $tag_users = $user->listAll($tag_conds);

            if (!empty($tag_users)) {
                // 标签的员工id集合
                $r_uids = array_column($tag_users, 'memUid');
            } else {

                $r_uids = [0];
            }

            // 组装查询条件
            $tag_uids = $this->get_uids($ep_id, $r_uids);
            if (!empty($djr_uids)) {

                $djr_uids = array_intersect($djr_uids, $tag_uids);
                if (empty($djr_uids)) {
                    // 空查询所有 为了避免查询所有传入0
                    $djr_uids = [0];
                }
            } else {

                $djr_uids = $tag_uids;
            }
        }

        // 【员工名称】
        if (isset($params['memUsername']) && $params['memUsername']) {

            $user_conds = ['memUsername' => $params['memUsername']];
            // 查询
            $sel_users = $user->listAll($user_conds);
            // 检索员工存在
            if (!empty($sel_users)) {
                // 员工id
                $memUserids = array_column($sel_users, 'memUid');
            } else {
                // 空查询所有 为了避免查询所有传入0
                $memUserids = [0];
            }

            if (!empty($djr_uids)) {

                $djr_uids = array_intersect($djr_uids, $memUserids);
                if (empty($djr_uids)) {
                    // 空查询所有 为了避免查询所有传入0
                    $djr_uids = [0];
                }
            } else {

                $djr_uids = $memUserids;
            }
        }

        return $djr_uids;
    }


    /**
     * 组装查询条件-部门、岗位或角色下答卷的员工id集合
     *
     * @param $ep_id string 试卷id
     * @param $jr_uids array 岗位或角色下的员工id
     *
     * @return mixed 员工id
     */
    public function get_uids($ep_id, $jr_uids)
    {
        // 返回结果初始化
        $res = [];
        // 答卷的员工id集合
        $an_conds = [
            'ep_id' => $ep_id,
            'my_time > ?' => 0
        ];
        $joins = $this->list_by_conds($an_conds);
        if (!empty($joins)) {

            // 答卷的员工id集合
            $join_uids = array_unique(array_column($joins, 'uid'));
            // 选择岗位/角色下的答卷的员工id(取交集)
            $res_uids = array_intersect($jr_uids, $join_uids);
            if (!empty($res_uids)) {

                $res = $res_uids;
            } else {

                $res = [0];
            }
        }

        return $res;
    }

    /**
     * 根据键值查找数组
     *
     * @param array $arr 待查找数组
     * @param string $key 键
     * @param string $val 值
     *
     * @return array|mixed
     */
    public function seekarr($arr = [], $key, $val)
    {
        // 初始化返回数组
        $data = [];
        foreach ($arr as $v) {
            $str = json_encode($v);
            preg_match_all("/\{[^\{]*\"" . $key . "\"\:\"" . $val . "\"[^\}]*\}/", $str, $m);
            // 键值匹配
            if ($m && $m[0]) {

                foreach ($m[0] as $va) {
                    $res = json_decode($va, true);
                    array_push($data, $res);
                }
            }
        }

        return $data;
    }

    /**
     *
     *
     * @param $ep_id int 试卷ID
     * @param $paper array 试卷信息
     * @param $params array 参数
     *
     * @return int
     */
    public function get_un_total($ep_id, $paper, $params)
    {

        $conds = [
            'epc_id' => $ep_id,
            'er_type' => AnswerService::RIGHT_PAPER
        ];
        // 获取未参与考试人员列表
        $unjoin_data = $this->get_unjoin_data($conds, $ep_id, $paper['is_all']);
        // 未参加人的列表
        $unjoin_list = $unjoin_data['unjoin_list'];

        // 【部门】
        if (isset($params['dpIds']) && !empty($params['dpIds']) && is_array($params['dpIds'])) {

            $params_dpids = $params['dpIds'];
            $dpServ = &Department::instance();
            // 取子级部门ID
            $dp_ids = $dpServ->list_childrens_by_cdid($params_dpids, true);
            sort($dp_ids);

            $conditions['dpIdList'] = $dp_ids;
        }

        // 【岗位】
        if (isset($params['memJob']) && $params['memJob']) {

            $conditions['jobIdList'] = [$params['memJob']];
        }

        // 【角色】
        if (isset($params['memRole']) && $params['memRole']) {

            $conditions['roleIdList'] = [$params['memRole']];
        }

        // 【标签】
        if (isset($params['memTag']) && $params['memTag']) {

            $conditions['tagIdList'] = [$params['memTag']];
        }

        // 【员工名称】
        if (isset($params['memUsername']) && $params['memUsername']) {

            $conditions['memUsername'] = $params['memUsername'];

        }

        // 如果存在查询条件
        if (!empty($conditions)) {
            $user_select_list = User::instance()->listAllBasic($conditions);

            if (!empty($user_select_list)) {
                $unjoin_list = array_intersect($unjoin_list, array_column($user_select_list, 'memUid'));
            } else {
                $unjoin_list = [];
            }
        }

        $total = 0;
        if (!empty($unjoin_list)) {

            sort($unjoin_list);
            // 实例化
            $member = new Member(Service::instance());
            $member_conds = [
                'memUids' => $unjoin_list,
            ];
            // 获取未读人员列表
            $list = $member->listAll($member_conds);

            // 总人数
            $total = $list['total'];
        }

        return $total;
    }

    /**
     * 获取考试未参与考试人员列表及人数
     *
     * @author: 侯英才
     * @param array $conds 查询权限条件
     * @param int $ep_id 试卷ID
     * @param int $is_all 是否是全公司
     *
     * @return array -返回参数
     *           + unjoin_list    -未参与人列表
     *           + join_count     -已参与人总数
     */
    public function get_unjoin_data($conds, $ep_id, $is_all = 0)
    {
        $right_serv = new RightService();

        // 参与考试的权限范围
        if ($is_all != self::AUTH_ALL) {

            $rights = $right_serv->list_by_conds($conds);
            // 格式化权限范围
            $right_view = $right_serv->format_db_data($rights);
        }

        $right_view['is_all'] = $is_all;

        // 获取要参加考试的全部人员
        $all_join = $right_serv->list_uids_by_right($right_view);

        // 已经参加了考试的人员
        $join = $this->_d->list_by_conds(['ep_id' => $ep_id, 'my_time > ?' => 0]);
        $join = array_column($join, 'uid');
        $join = array_unique($join);

        // 初始未参加考试人员
        $unjoin = [];

        // 遍历全部邀请人员
        foreach ($all_join as $key => $uid) {
            // 如果不在已参加的人员中
            if (!in_array($uid, $join) && !empty($uid)) {

                $unjoin[] = $uid;
            }
        }

        // 已参与总人数
        $join_count = count($join);
        return [
            'unjoin_list' => $unjoin, // 未参与列表
            'join_list' => $join,
            'join_count' => (int)$join_count, // 已参与人数
        ];
    }

    /**
     * 组装查询条件
     *
     * @param $paper array 试卷信息
     * @param $params array 传入的参数
     *
     * @return array 返回组装完成的条件数组
     */
    public function get_unjoin_conditions($paper, $params)
    {
        // 查询条件初始化
        $conditions['ep_id'] = $paper['ep_id'];
        // 部门、岗位、角色的用户id交集
        $djr_uids = $this->get_dp_job_role_uids($params, $paper['ep_id']);

        if (!empty($djr_uids)) {

            $conditions['uid'] = $djr_uids;
        }

        return $conditions;
    }

    /**
     * 交卷-全部试题
     * @param array $params 参数
     *  + int $ea_id 答卷id
     *  + int $uid 用户id
     *  + array $qa_list 答题列表
     *  + Int $data_type 考试类型(1:常规试卷,2:任务类,3:线下培训)
     *  + Int $obj_id 任务、培训ID
     * @param array $award 激励返回
     * @param array $answer_info 答卷数据(answer表详情)
     *
     * @return mixed
     */
    public function submit_all($params = [], &$award = [], $answer_info = [])
    {
        // 答卷信息
        $ea_id = $params['ea_id'];

        // 试卷id
        $ep_id = $answer_info['ep_id'];
        // 试卷信息
        $paper = $this->paper_model->get($ep_id);
        // 答卷信息不存在
        if (empty($paper)) {

            E('_EMPTY_PAPER_DATA');
        }

        // 试卷为禁止状态
        if (self::EC_CLOSE_STATES == $paper['cate_status']) {

            E('_ERR_EC_CLOSE_STATES');
        }

        // 已经交卷
        if ($answer_info['answer_status'] > 0) {

            E('_ERR_REPEAT_SUBMIT_EXAM');
        }

        // 当前毫秒时间
        $now_time = MILLI_TIME;
        // 考试时长
        $paper_time = to_milli_time($paper['paper_time'] * 60);

        // 判断用户是否有权限访问“学习地图类”数据
        if (PaperService::OTHER_TYPE == $paper['exam_type']) {
            $map_id = $params['map_id'];
            $path_id = $params['path_id'];

            if (!empty($map_id) && !empty($path_id)) {
                $mapServ = new StudyMap($map_id);
                $mapServ->checkRight($path_id, $ep_id, $params['user_info'], $app = '');
            }
        }

        if (self::IS_MAKEUP == $answer_info['is_makeup']) {
            // 如果是补考,考试结束时间为补考结束时间
            $paper_end_time = $paper['makeup_end_time'];

        } else {

            // 如果不是补考
            if (self::NOMAL_TYPE == $paper['exam_type']) {
                // 常规试卷
                $paper_end_time = $paper['end_time'];
            } else {
                // 任务类,线下培训类
                $paper_end_time = $answer_info['my_begin_time'] + $paper_time;
            }

        }

        // 考生考试开始时间
        $my_begin_time = $answer_info['my_begin_time'];
        // 考生交卷截止时间
        $answer_end_time = $my_begin_time + $paper_time;

        // 提前交卷
        if ($now_time <= $answer_end_time && $now_time <= $paper_end_time) {
            // 考生用时
            $my_time = $now_time - $my_begin_time;
        } else { // 到截止时间交卷
            // 考生在考试结束时间之后交卷
            if ($answer_end_time > $paper_end_time) {
                // 考生用时:考试结束时间-考生考试开始时间
                $my_time = $paper_end_time - $my_begin_time;
            } else { // 考生在考试结束时间之前交卷
                // 考试用时:考试交卷截止时间-考生考试开始时间
                $my_time = $answer_end_time - $my_begin_time;
            }
        }

        // 交卷所需数据
        $data = [
            'uid' => $answer_info['uid'],
            'ea_id' => $ea_id,
            'paper' => $paper,
            'my_time' => $my_time,
            'my_begin_time' => $my_begin_time,
            'data_type' => $params['data_type'], // 数据类型
            'obj_id' => $params['obj_id']        // 任务、培训ID
        ];
        // 处理交卷  $score_list为积分策略返回值
        $this->do_submit($data, $params['qa_list'], $result_status, $score_list, $answer_info['is_makeup']);

        // 如果不是补考
        if (self::IS_MAKEUP != $answer_info['is_makeup']) {
            // 更新考试已参加与未参加人数
            $this->paper_statistics($paper, $answer_info['uid']);
            // 添加积分勋章接口,返回激励、积分策略信息$award
            $this->medal($ep_id, $answer_info['uid'], $award);
        }

        // 手动阅卷发送阅卷信息
        if (self::SEND_MSG_FOR_MARKING_USER == $result_status) {

            $this->send_cms_answer($ea_id, AnswerService::MARKING_USER_MSG);
        } elseif (self::SEND_MSG_FOR_JOIN_EXAM_USER == $result_status) {
            // 手动阅卷系统阅卷成绩出来(过滤其他类型)
            if (PaperService::OTHER_TYPE != $paper['exam_type']) {
                $this->send_cms_answer($ea_id, AnswerService::MARKING_RESULT_MSG);
            }
        }

        return [
            'res' => true,
            'score_list' => $score_list
        ];
    }

    /**
     * 交卷
     * @param array $data 函数所需数据
     * @param array $qa_list 答题列表
     * @param int $result_status 手动阅卷状态
     * @param array $score_list 积分策略返回数据
     * @param int $is_makeup 是否是补考(0:否,1:是)
     *
     * @return bool
     */
    public function do_submit($data = [], $qa_list = [], &$result_status = 0, &$score_list = [], $is_makeup = 0)
    {
        // 答卷id
        $ea_id = $data['ea_id'];
        // 答卷详情列表
        $detail_conds['ea_id'] = $ea_id;
        $answer_detail_list = $this->_answer_detail_model->list_by_conds($detail_conds);
        $answer_detail_list = array_combine_by_key($answer_detail_list, 'ead_id');

        // 阅卷类型(1:自动阅卷 2:手动阅卷)
        $marking_type = $data['paper']['marking_type'];

        // 试卷id
        $ep_id = $data['paper']['ep_id'];
        // 根据试卷id获取题目扩展信息列表
        $answer_detail_extend_list = $this->_answer_detail_extend_model->list_by_conds(['ep_id' => $ep_id]);

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

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

            // 题目类型:问答题、语音题
            $et_types = [
                self::TOPIC_TYPE_QUESTION,
                self::TOPIC_TYPE_VOICE
            ];

            // 手动阅卷主观题数量
            $count = 0;

            // 回答中的图片附件
            $at_ids = [];

            // 循环处理答题列表
            foreach ($qa_list as $key => $val) {
                // 题目扩展信息
                $extend_info = $answer_detail_extend_list[$answer_detail_list[$val['ead_id']]['et_id']];
                // 题目详情
                $et_detail = unserialize($extend_info['et_detail']);

                // 获取答案字符串
                $answer = $this->get_answer($val, $et_detail);

                // 空答案,更改状态为不通过、未作答、已批阅,得分为0
                if (!$answer) {

                    // 组装更新数据
                    $un_data = [
                        'my_answer' => '',
                        'my_score' => 0,
                        'is_pass' => self::NO_MY_PASS, // 答题状态 0未作答(没有卵用,代码中未使用此判断作答状态,而是使用的is_status) 1已通过 [2未通过]
                        'is_status' => self::DO_PASS_STATE, // 作答状态 [0未作答] 1已作答
                        'marking_status' => self::DO_MK_STATE // 批阅状态 0待批阅 [1已批阅]
                    ];

                    $this->_answer_detail_model->update($val['ead_id'], $un_data);

                } else {

                    $option_index = $answer_detail_list[$val['ead_id']]['option_index'];
                    // 如果该题目启用了选项打乱
                    if (!empty($option_index)) {
                        $exam_help = new ExamHelper();
                        // 装换用户提交的答案为标准题目选项对应的答案
                        $answer = $exam_help->change_option_save($answer, $option_index);
                    }

                    // 提交的答案不为空,保存答案
                    // ============= 事物 写入不能再方法内 =====================
                    // 更新答案、答题状态
                    $de_data = [
                        'my_answer' => $answer,
                        'is_status' => self::MY_PASS // 答题状态:已作答
                    ];

                    $res = $this->_answer_detail_model->update($val['ead_id'], $de_data);


                    if ($et_detail['et_type'] == self::TOPIC_TYPE_VOICE) {
                        // 如果是语音题,并且答案是不为空的,取出其中的附件ID
                        $my_answer = unserialize($answer);

                        foreach ($my_answer as $at_info) {
                            // 如果回答中含有图片
                            if ($at_info['type'] == self::ANSWER_TYPE_IMG) {
                                // 取出图片ID
                                $at_ids[] = $at_info['opt'];
                            }
                        }
                    }

                    // 更新成功
                    if ($res) {
                        // 计算试题分数
                        $ead_id = $val['ead_id'];
                        $et_type = $et_detail['et_type'];
                        $detail_data = $this->_answer_detail_model->get($ead_id);

                        // 手动阅卷 && 题目类型是问答或语音
                        if (self::MANUAL_MARKING_TYPE == $marking_type &&
                            in_array($et_type, $et_types)
                        ) {
                            // do nothing
                        } else {
                            // 自动阅卷
                            $arr = $this->get_topic_score($detail_data);
                            $arr['marking_status'] = self::DO_MK_STATE; // 批阅状态 0待批阅 [1已批阅],此处默认已批阅
                            $this->_answer_detail_model->update($ead_id, $arr);
                        }
                    }
                    // ======================================================
                }

                // 开启手动阅卷 && 测评试卷 && 答案不为空 && 问答题或语音题
                if (self::MANUAL_MARKING_TYPE == $marking_type
                    && self::EVALUATION_PAPER_TYPE == $data['paper']['paper_type']
                    && $answer
                    && in_array($et_detail['et_type'], $et_types)
                ) {

                    $count++;
                }
            }

            // 答卷表待更新数据
            $read_data = [
                'my_time' => $data['my_time'],
                'my_end_time' => $data['my_begin_time'] + $data['my_time']
            ];

            // 通知uc附件使用情况
            $attrch_serv = new AttachOperation();
            $attrch_serv->insert_attach(
                APP_DIR,
                'paper',
                $ep_id,
                ['attach_ids' => $at_ids]
            );

            // 手动阅卷主观题存在(循环中,只有【开启手动阅卷】的【测评试卷】才会加数量,所以这里直接用这个数量判断就可以)
            if ($count) {
                // 追加答卷表待更新数据
                $read_data['answer_status'] = self::READ_WAITING; // 批阅状态:待批阅

                $result_status = self::SEND_MSG_FOR_MARKING_USER; // 手动阅卷发送阅卷信息
            } else {
                // 自动阅卷,直接出分,并更新状态是否已通过考试

                // 计算已批阅的总分
                $score_conds = [
                    'ea_id' => $ea_id,
                    'marking_status' => self::DO_MK_STATE // 批阅状态 0待批阅 [1已批阅]
                ];
                // 考试得分
                $my_score = $this->_answer_detail_model->get_score($score_conds);

                // 及格分大于得分
                if ($data['paper']['pass_score'] > $my_score) {
                    // 不通过
                    $my_is_pass = self::UNPASS;
                } else {
                    // 通过
                    $my_is_pass = self::PASS;
                }

                // 追加答卷表待更新数据
                $read_data['my_score'] = $my_score;
                $read_data['my_is_pass'] = $my_is_pass;
                $read_data['answer_status'] = self::READ_OVER; // 批阅状态 0初始化 1待批阅 2批阅中 [3已批阅]
                $read_data['marking_time'] = MILLI_TIME;

                // 开启手动阅卷 && 测评试卷
                if (self::MANUAL_MARKING_TYPE == $marking_type
                    && self::EVALUATION_PAPER_TYPE == $data['paper']['paper_type']
                ) {
                    // 手动阅卷系统阅卷成绩出来
                    $result_status = self::SEND_MSG_FOR_JOIN_EXAM_USER;
                } else {
                    // 不发送消息
                    $result_status = self::NOT_NEED_SEND_MSG;
                }


                if ($data['paper']['exam_type'] == self::NOMAL_TYPE) {

                    // 积分策略业务ID=应用目录_数据ID
                    $businessid = sprintf('%s_%s', APP_DIR, $data['paper']['ep_id']);

                    $read_data['businessid'] = $businessid;

                    if (self::IS_MAKEUP != $is_makeup) {
                        // 不是补考,调用积分策略
                        $score_list = $this->get_score_list($data['uid'], $data['paper'], $my_score, $my_is_pass, $businessid);
                    }

                } else {

                    $read_data['businessid'] = '';
                }

            }

            // 更新答卷表数据
            $read_cond['ea_id'] = $ea_id;
            $read_data['data_type'] = $data['data_type']; // 数据类型(1:常规试卷,2:任务类,3:线下培训)
            $read_data['obj_id'] = $data['obj_id']; // 任务、培训ID

            // 此处查询是否存在最高分
            $top_score = $this->get_by_conds(['ep_id' => $ep_id, 'is_score_top' => self::IS_SCORE_TOP_TRUE, 'uid' => $data['uid'], 'answer_status' => self::READ_OVER]);

            // 默认不是最高分
            $is_score_top = self::IS_SCORE_TOP_FALSE;
            if (empty($top_score)) {
                // 如果之前的最高分记录为空,设置为最高分
                $is_score_top = self::IS_SCORE_TOP_TRUE;
            } elseif ($top_score['my_score'] < $read_data['my_score']) {
                // 如果之前的最高分小于当前得分,设置为最高分
                $is_score_top = self::IS_SCORE_TOP_TRUE;
                // 将之前的最高分记录置为非最高分
                $this->update($top_score['ea_id'], ['is_score_top' => self::IS_SCORE_TOP_FALSE]);
            }

            $read_data['is_score_top'] = $is_score_top; // 是否最高分
            $this->update($read_cond, $read_data);

            $this->commit();
        } catch (\Exception $e) {

            $this->rollback();
            E('_ERR_SUBMIT_FAIL');
            return false;
        }

        return true;
    }

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

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

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

                $answer = array_filter(array_column($val['my_answer'], 'opt'));
            }

            if (!empty($answer)) {
                foreach ($answer as $key => &$value) {
                    $value = strtoupper(trim($value));
                }

                sort($answer);
                $str_answer = implode(',', $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 = '';
            if (isset($val['my_answer']) && !empty($val['my_answer'])) {

                $answer = array_filter(array_column($val['my_answer'], 'opt'));
                $str_answer = implode(',', $answer);
                // 单选题
                if (self::TOPIC_TYPE_SINGLE == $et_detail['et_type'] && $answer[0]) {
                    // 答案转大写
                    $str_answer = strtoupper(trim($answer[0]));
                }
            }

        } else { // 语音题
            // 语音答案
            $attach_cond['ead_id'] = $val['ead_id'];
            $answerattr = $this->_answer_attach_model->list_by_conds($attach_cond);
            // 媒体文件顺序编号
            $order_ids = array_column($answerattr, 'order_id');

            if ($val['my_answer']) {

                $answer_arr = [];
                foreach ($val['my_answer'] as $k => $v) {
                    // 语音答题
                    if (self::ANSWER_TYPE_VOICE == $v['type']) {
                        // 媒体文件存在
                        if ($v['opt'] && in_array($v['opt'], $order_ids)) {

                            $answer_arr[] = $v;
                        }
                    } else { // 其他

                        $answer_arr[] = $v;
                    }
                }

                // 答案不为空
                if (!empty($answer_arr)) {
                    // 答案序列化
                    $str_answer = serialize($answer_arr);
                }
            }
        }

        return $str_answer;
    }

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

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

            // 题目扩展信息查询条件
            $conditions_extend = [
                'et_id' => intval($data['et_id']), // 题目id
                'ep_id' => intval($data['ep_id']) // 试卷id
            ];

            // 获取题目扩展信息
            $answer_detail_extend = $this->_answer_detail_extend_model->get_by_conds($conditions_extend);

            $et_detail = unserialize($answer_detail_extend['et_detail']);
            $et_type = $et_detail['et_type'];


            // 如果是选择题或者多选题
            if (self::TOPIC_TYPE_SINGLE == $et_type || self::TOPIC_TYPE_MULTIPLE == $et_type) {

                $answer = explode(',', $et_detail['answer']);
                // 循环我的回答,判断我的回答里有没有不在答案里的
                $my_answer_arr = explode(',', $data['my_answer']);
                $my_answer_arr = array_filter($my_answer_arr);
                $my_score = $data['score']; // 得分,默认是有分数的
                $is_pass = self::MY_PASS; // 已作答
                foreach ($my_answer_arr as $v) {
                    // 如果我的回答不在答案里,就是错的
                    if (!in_array($v, $answer)) {

                        $my_score = 0;
                        $is_pass = self::NO_MY_PASS;
                        break;
                    }
                }

                /*
                 * 如果我的回答里没有不在答案里的,再判断下我的回答数量和答案数量是否一致
                 * 数量一致就是正确的,否则就是少选了
                 */
                if ($my_score > 0 && count($my_answer_arr) != count($answer)) {
                    $my_score = 0;
                    $is_pass = self::NO_MY_PASS;
                }

            } elseif (self::TOPIC_TYPE_JUDGMENT == $et_type) {

                // 如果是判断题
                $my_answer = trim($data['my_answer']);
                if ($et_detail['answer'] == $my_answer) {

                    $my_score = $data['score'];
                    $is_pass = self::MY_PASS;
                } else {

                    $my_score = 0;
                    if (empty($my_answer)) {
                        // 未作答,但是注意!!!这里的is_pass没有被用来判断是否作答,而是使用is_status判断的,所以这里其实是无意义的
                        $is_pass = self::DO_PASS_STATE;
                    } else {

                        $is_pass = self::NO_MY_PASS;
                    }
                }
            } elseif (self::TOPIC_TYPE_VOICE == $et_type) {
                // 如果是语音题
                // 存在答案就认为得满分,不存在则为0
                $my_score = $data['score'];
                $is_pass = self::MY_PASS;
            } else {

                $my_answer = trim($data['my_answer']);
                // 如果是问答题
                if (self::KEYWORD_OPEN == $et_detail['match_type']) {

                    // 匹配关键字方式
                    $my_score = $this->_key_question_score($et_detail['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 = $this->_coverage_question_score($et_detail['answer'], $et_detail['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
        ];
    }

    /**
     * @author: 蔡建华
     *
     * 计算问答题关键字匹配方式的分数
     * @param array $keywords 关键字数组
     * @param string $my_answer 我的回答
     * @param int $score 总分数
     *
     * @return int 得分
     */
    public function _key_question_score($keywords, $my_answer, $score)
    {
        $my_score = 0; // 最后得分
        $match_all = true; // 是否全部匹配

        // 循环关键字
        foreach ($keywords as $v) {
            $keyWord = trim($v['keyword']);
            // 正则匹配关键字
            if (preg_match("/{$keyWord}/", $my_answer)) {

                $percent = (float)((int)$v['percent'] / 100.00);
                $my_score += $score * $percent;
            } else {
                // 有一个不匹配就记录全部匹配为false
                $match_all = false;
            }
        }
        // 如果全部匹配,就返回满分,否则返回对应的分数
        return $match_all === true ? $score : $my_score;
    }

    /**
     * @author: 蔡建华
     *
     * 计算问答题覆盖率方式的分数
     * @param string $sys_answer 答案
     * @param string $answer_coverage 合格覆盖率
     * @param string $my_answer 我的回答
     *
     * @return boolean 我的回答的覆盖率满足合格覆盖率返回true,否则返回false
     */
    public function _coverage_question_score($sys_answer, $answer_coverage, $my_answer)
    {
        $answer_coverage = (float)(intval($answer_coverage) / 100.0);
        // 当类型为问答题,进行文本比对
        $user_answer = str_replace(["\r\n", "\r", "\n"], " ", $my_answer);
        $user_answer = preg_replace("/\s(?=\s)/", "\\1", $user_answer);

        $sys_answer = str_replace(["\r\n", "\r", "\n"], " ", $sys_answer);
        $sys_answer = preg_replace("/\s(?=\s)/", "\\1", $sys_answer);

        // 实例化对比类
        $lcs = new Lcs();
        // 我的答案的覆盖率
        $coverage = $lcs->getSimilar($user_answer, $sys_answer);
        return $coverage >= $answer_coverage;
    }

    /**
     * 保存答卷
     * @param array $val 答案
     * @param array $et_detail 题目详情
     * @param int $marking_type 试卷类型(1:自动阅卷 2:手动阅卷)
     *
     * @return bool
     */
    public function save_answer($val = [], $et_detail = [], $marking_type = self::AUTO_MARKING_TYPE)
    {

        // 获取答案字符串
        $str_answer = $this->get_answer($val, $et_detail);
        // 答案不为空,更新答卷信息
        if ($str_answer) {
            // 更新答案、答题状态
            $de_cond['ead_id'] = $val['ead_id'];
            $de_data = [
                'my_answer' => $str_answer,
                'is_status' => self::MY_PASS // 答题通过
            ];
            $res = $this->_answer_detail_model->update_by_conds($de_cond, $de_data);

            // 更新成功
            if ($res) {
                // 计算试题分数
                $this->get_answer_score($val['ead_id'], $marking_type, $et_detail['et_type']);
            }

            return $res;
        } else {

            return false;
        }
    }

    /**
     * 试题分数计算
     *
     * @author: 蔡建华
     * @param $ead_id int 答卷ID
     * @param int $marking_type 1自动阅卷 2手动阅卷
     * @param int $et_type 题型
     *
     * @return int 返回分数
     */
    protected function get_answer_score($ead_id = 0, $marking_type = 0, $et_type = 0)
    {
        $data = $this->_answer_detail_model->get($ead_id);
        if (self::MANUAL_MARKING_TYPE == $marking_type && in_array($et_type,
                [self::TOPIC_TYPE_VOICE, self::TOPIC_TYPE_QUESTION])
        ) {
            return false;
        }

        // 自动阅卷
        $arr = $this->get_topic_score($data);
        $arr['marking_status'] = self::DO_MK_STATE;
        $this->_answer_detail_model->update_by_conds(['ead_id' => $ead_id], $arr);

        return true;
    }

    /**
     * 判断试卷状态
     *
     * @author: 蔡建华
     * @param $ea_id int 答卷ID
     * @param $uid string 用户ID
     * @param $data_type int 数据类型(1:常规考试,2:任务类,3:线下培训)
     * @param $obj_id int 任务、培训ID
     *
     * @return array|bool
     */
    public function answer_status($ea_id = 0, $uid = '', $data_type = 1, $obj_id = 0)
    {
        // 初始化答卷状态
        $start_type = self::START_TYPE_INITIAL;

        // 用户答卷查询
        $condition = ['ea_id' => $ea_id, 'uid' => $uid, 'obj_id' => $obj_id];

        // 获取答卷信息
        $ea_data = $this->get_by_conds($condition);
        // 答卷信息不存在
        if (empty($ea_data)) {

            E('_ERR_MY_VISIT_NO');
        }

        // 查询试卷信息
        $ep_id = $ea_data['ep_id'];
        $paper = $this->paper_model->get($ep_id);

        // 你已经交卷无法答题
        if ($ea_data['answer_status'] >= self::READ_WAITING) {
            // 已经交卷
            $start_type = self::START_TYPE_END;
        }

        // 我的考试开始时间
        $my_begin_time = $ea_data['my_begin_time'];
        // 试卷时长
        $paper_time = $paper['paper_time'] * 60 * 1000;
        $time = MILLI_TIME;
        // 结束时间
        $end_time = $my_begin_time + $paper_time;

        // 判断试卷状态,其中包括分类状态和试卷状态 (这里的试卷状态 0初始化,1草稿,2已发布,3终止)
        if (self::EC_CLOSE_STATES == $paper['cate_status'] || self::PAPER_STOP == $paper['exam_status']) {

            $start_type = self::START_TYPE_END;
        }

        // 如果考试时间到
        if (($paper['exam_type'] == self::NOMAL_TYPE && $time > $paper['end_time']
                || $time > $end_time) || ($paper['exam_type'] != self::NOMAL_TYPE && $time > $end_time)) {

            $start_type = self::START_TYPE_END;

            // 答卷信息
            $answer_info = $this->get($ea_id);
            // 阅卷状态
            if ($answer_info['answer_status'] == 0) {
                // 查询条件:答卷id
                $conds = ['ea_id' => $ea_id];
                // 全部题目数量
                $my_error_num = $this->_answer_detail_model->count_by_conds($conds);

                // 此处查询是否存在最高分
                $top_score = $this->get_by_conds(['ep_id' => $answer_info['ep_id'], 'is_score_top' => self::IS_SCORE_TOP_TRUE, 'uid' => $answer_info['uid'], 'answer_status' => self::READ_OVER]);

                // 默认不是最高分
                $is_score_top = self::IS_SCORE_TOP_FALSE;
                if (empty($top_score)) {
                    // 设置为最高分
                    $is_score_top = self::IS_SCORE_TOP_TRUE;
                }

                // 交卷分数
                $data = [
                    'answer_status' => self::READ_OVER,
                    'my_is_pass' => self::UNPASS,
                    'my_score' => 0,
                    'is_score_top' => $is_score_top,
                    'my_error_num' => $my_error_num,
                    'my_time' => $end_time - $my_begin_time,
                    'my_end_time' => $end_time,
                    'data_type' => $data_type, // 数据类型
                    'obj_id' => $obj_id        // 任务、培训ID
                ];

                // 交卷
                $num = $this->update_by_conds($condition, $data);

                // 交卷成功
                if ($num) {
                    // 试卷使用类型(0:测评试卷,1:模拟试卷)
                    $paper_type = $paper['paper_type'];
                    // 是否更新参与人数(0=不更新,1=更新)
                    $is_update_join = 0;

                    // 测评试卷
                    if (self::EVALUATION_PAPER_TYPE == $paper_type) {
                        // 更新
                        $is_update_join = 1;

                        // 常规任务埋点:考试完成(测评试卷只要参加考试并交卷就算完成)
                        if (PaperService::TASK_TYPE == $paper['exam_type'] && $obj_id) {

                            $params = [
                                'uid' => $uid,
                                'customtask_id' => $obj_id,
                                'app_data_id' => $ep_id,
                                'action_key' => 'exam_pass',
                                'description' => '考试完成',
                            ];
                            $taskCenter = &TaskCenter::instance();
                            $taskCenter->triggerCustomtask($params);
                        }

                    } elseif (self::SIMULATION_PAPER_TYPE == $paper_type) { // 模拟试卷
                        // 交卷次数
                        $condition = [
                            'ep_id' => $ep_id,
                            'uid' => $uid,
                            'answer_status' => self::READ_OVER, // 已批阅
                            'data_type' => self::NOMAL_TYPE, // 常规考试
                        ];
                        $answer_count = $this->_d->count_by_conds($condition);

                        // 模拟试卷第一次交卷
                        if (1 == $answer_count) {
                            // 更新
                            $is_update_join = 1;
                        }
                    }

                    // 更新参与人数
                    if ($is_update_join) {
                        // 已参与人数加1,未参与人数减1
                        $update_data = [
                            'join_count' => $paper['join_count'] + 1,
                            'unjoin_count' => $paper['unjoin_count'] - 1
                        ];
                        $this->paper_model->update($ep_id, $update_data);
                    }

                    // 培训id不为空, 类型为线下培训
                    if ($obj_id && PaperService::TRAIN_TYPE == $data_type) {

                        $Train = &Train::instance();
                        $Train->syncTrainStatus($obj_id, $ep_id);
                    }
                }
            }
        }

        // 考试结束时间大于时间结束时间
        if ($end_time > $paper['end_time']) {

            $left_time = $paper['end_time'] - $time;
            // 考试结束时间小与时间结束时间
        } else {

            $left_time = $end_time - $time;
        }

        return [
            'ep_name' => $paper['ep_name'],
            'marking_type' => $paper['marking_type'],
            'paper_type' => $paper['paper_type'],
            'left_time' => $left_time, // 返回倒计时
            'my_begin_time' => $my_begin_time,
            'paper_time' => $paper['paper_time'],
            'end_time' => $paper['end_time'],
            'start_type' => intval($start_type)
        ];
    }


    /**
     * 判断补考试卷状态
     *
     * @author: 蔡建华
     * @param $ea_id int 答卷ID
     * @param $uid string 用户ID
     * @param $paper array 试卷数据
     *
     * @return array|bool
     */
    public function answer_status_makeup($ea_id = 0, $uid = '', $paper = [])
    {
        // 初始化答卷状态
        $start_type = self::START_TYPE_INITIAL;

        // 用户答卷查询
        $condition = ['ea_id' => $ea_id, 'uid' => $uid];

        // 获取答卷信息
        $ea_data = $this->get_by_conds($condition);
        // 答卷信息不存在
        if (empty($ea_data)) {

            E('_ERR_MY_VISIT_NO');
        }

        // 你已经交卷无法答题
        if ($ea_data['answer_status'] >= self::READ_WAITING) {
            // 已经交卷
            E('_ERR_SUBMIT_ANSWER');
        }

        // 我的考试开始时间
        $my_begin_time = $ea_data['my_begin_time'];
        // 试卷时长
        $paper_time = $paper['paper_time'] * 60 * 1000;
        $time = MILLI_TIME;
        // 结束时间
        $end_time = $my_begin_time + $paper_time;


        if (self::EC_CLOSE_STATES == $paper['cate_status']) {
            // 判断试卷状态,其中包括分类状态禁用
            E('_ERR_DATA_EXAM_DEL');
        }

        // 如果考试时间到
        if ($time > $paper['makeup_end_time'] || $time > $end_time) {

            // 答卷信息
            $answer_info = $this->get($ea_id);
            // 阅卷状态
            if ($answer_info['answer_status'] == 0) {
                // 查询条件:答卷id
                $conds = ['ea_id' => $ea_id];
                // 全部题目数量
                $my_error_num = $this->_answer_detail_model->count_by_conds($conds);

                // 此处查询是否存在最高分
                $top_score = $this->get_by_conds(['ep_id' => $answer_info['ep_id'], 'is_score_top' => self::IS_SCORE_TOP_TRUE, 'uid' => $answer_info['uid'], 'answer_status' => self::READ_OVER]);

                // 默认不是最高分
                $is_score_top = self::IS_SCORE_TOP_FALSE;
                if (empty($top_score)) {
                    // 设置为最高分
                    $is_score_top = self::IS_SCORE_TOP_TRUE;
                }

                // 交卷分数
                $data = [
                    'answer_status' => self::READ_OVER,
                    'my_is_pass' => self::UNPASS,
                    'my_score' => 0,
                    'is_score_top' => $is_score_top,
                    'my_error_num' => $my_error_num,
                    'my_time' => $end_time - $my_begin_time,
                    'my_end_time' => $end_time,
                    'data_type' => self::NOMAL_TYPE, // 数据类型
                    'obj_id' => 0        // 任务、培训ID
                ];

                // 交卷
                $this->update($ea_id, $data);
            }

            E('_ERR_AUTO_SUBMIT_TIME');
        }

        // 考试结束时间大于时间结束时间
        if ($end_time > $paper['makeup_end_time']) {

            $left_time = $paper['makeup_end_time'] - $time;
            // 考试结束时间小与时间结束时间
        } else {

            $left_time = $end_time - $time;
        }

        return [
            'ep_name' => $paper['ep_name'],
            'marking_type' => $paper['marking_type'],
            'paper_type' => $paper['paper_type'],
            'left_time' => $left_time, // 返回倒计时
            'my_begin_time' => $my_begin_time,
            'paper_time' => $paper['paper_time'],
            'end_time' => $paper['makeup_end_time'],
            'start_type' => intval($start_type)
        ];
    }


    /**
     * 删除答卷记录重新考试
     *
     * @param $ep_id int 试卷ID
     * @param $conds array 条件
     *
     * @return bool
     */
    public function delete_answer($ep_id, $conds)
    {
        $list = $this->_d->list_by_conds($conds);

        if (empty($list)) {

            E('_EMPTY_ANSWER_PAPER');
        }

        $user_uids = array_unique(array_column($list, 'uid'));
        $paper = $this->paper_model->get($ep_id);

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

        if (self::STATUS_ING != $ep_status) {

            E('_ERR_RESET_PAPER');
        }
        // 已参加人数减去要删除的
        $join_count = $paper['join_count'] - count($user_uids);
        // 未参加人数减去要删除的
        $unjoin_count = $paper['unjoin_count'] + count($user_uids);
        // 要删除的答卷
        $ea_ids = array_column($list, 'ea_id');
        // 查询要删除的试题

        $detail_list = $this->_answer_detail_model->list_by_conds(['ea_id' => $ea_ids]);
        if (empty($ea_ids)) {
            E('_EMPTY_EA_ID');
        }
        $et_ids = array_column($detail_list, 'et_id');
        try {
            // 开始事务
            $this->start_trans();
            // 删除答卷信息
            $rel = $this->_d->delete_by_conds(['ea_id' => $ea_ids]);
            if (!$rel) {
                $this->rollback();
                E('_ERR_DELETE_ANSWER_FAILE');
            }

            // 更新试卷的参加人数
            $rel = $this->paper_model->update_by_conds(
                ['ep_id' => $ep_id],
                ['join_count' => $join_count, 'unjoin_count' => $unjoin_count]
            );

            if (!$rel) {

                $this->rollback();
                E('_ERR_UPDATE_JOIN_PEOPLE');
            }
            // 数据减去使用次数
            if ($et_ids) {
                $rel = $this->_topic_model->MinusIncNum(['et_id' => $et_ids]);
                if (!$rel) {

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

            // 删除答卷详情
            $rel = $this->_answer_detail_model->delete_by_conds(['ea_id' => $ea_ids]);
            if (!$rel) {
                $this->rollback();
                E('_ERR_UPDATE_ANSWER_DETAIL');
            }
            $this->commit();
        } catch (\Think\Exception $e) {
            $this->rollback();
            E('_ERR_DELETE_FAILE');
        } catch (\Exception $e) {
            $this->rollback();
            E('_ERR_DELETE_FAILE');
        }
        // 发送消息
        $params = [
            'name' => $paper['ep_name'],
            'begin_time' => $paper['begin_time'],
            'end_time' => $paper['end_time'],
            'uids' => $user_uids,
            'id' => $paper['ep_id']
        ];

        // 封面图片
        if (self::IS_COVER_PIC_OPEN == $paper['is_cover_open']) {
            $params['img_id'] = $paper['cover_id'];
        } else {
            $params['img_id'] = '';
        }

        $this->send_msg($params, self::RESET_USER_MSG);
        return true;
    }

    /**
     * 获取批阅列表
     *
     * @param array $conds 查询条件
     * @param array $page_option 分页
     * @param array $order_option 排序
     *
     * @return array|bool
     */
    public function list_marking_paper($conds, $page_option = [], $order_option = [])
    {
        try {
            return $this->_d->list_marking_paper($conds, $page_option, $order_option);
        } catch (\Exception $e) {
            E($e->getCode() . ':' . $e->getMessage());

            return false;
        }
    }

    /**
     * 统计批阅列表总数
     *
     * @param array $conds 查询条件
     *
     * @return array|bool
     */
    public function count_marking_paper($conds)
    {
        try {
            return $this->_d->count_marking_paper($conds);
        } catch (\Exception $e) {
            E($e->getCode() . ':' . $e->getMessage());

            return false;
        }
    }


    /**
     * 根据条件查询测评考试的参与人总数
     * @param array $conds
     * @return mixed
     */
    public function count_by_where($conds = [])
    {

        return $this->_d->count_mock_answer($conds);
    }


    /**
     * 根据条件分页查询测评考试的参与记录
     * @param array $conds
     * @param null $page_option
     * @param array $order_by
     * @param string $fields
     * @return array|bool
     */
    public function list_by_where($conds = [], $page_option = null, $order_by = [], $fields = '*')
    {

        return $this->_d->get_mock_answer_list($conds, $page_option, $order_by, $fields);

    }

}