<?php /** * 试卷表 * @author: houyingcai * @email: 594609175@qq.com * @date : 2017-05-19 17:43:16 */ namespace Common\Service; use Common\Common\AttachOperation; use Common\Common\Cache; use Common\Common\Msg; use Common\Common\TaskCenter; use Common\Common\User; use Common\Model\AnswerDetailExtendModel; use Common\Model\AnswerModel; use Common\Model\AttrModel; use Common\Model\BankModel; use Common\Model\CategoryModel; use Common\Model\PaperModel; use Common\Model\PaperTempModel; use Common\Model\RandomSnapshotModel; use Common\Model\RightModel; use Common\Model\SnapshotModel; use Common\Model\TagModel; use Common\Model\TopicAttrModel; use Common\Model\TopicModel; use VcySDK\Cron; use VcySDK\Logger; use VcySDK\Service; use Common\Common\StudyMap; class PaperService extends AbstractService { // 闯关通过 const MY_BREAK_PASS = 1; // 闯关不通过 const MY_BREAK_NO_PASS = 2; // 闯关未交卷状态 const NOT_ASSIGNMENT = 0; // 闯关已交卷状态 const ASSIGNMENT = 1; /** @var RandomSnapshotModel */ protected $_d_random; /** @var CategoryModel */ protected $_d_cate; /** @var AnswerModel */ protected $_d_answer; /** @var RightModel */ protected $_d_right; /** @var BankModel */ protected $_d_bank; /** @var TagModel */ protected $_d_tag; /** @var AttrModel */ protected $_d_attr; /** @var SnapshotModel */ protected $_d_snapshot; /** @var PaperTempModel */ protected $_d_temp; /** @var TopicModel */ protected $_d_topic; /** @var TopicAttrModel */ protected $_d_topic_attr; protected $_d_answer_detail_extend; // 构造方法 public function __construct() { $this->_d = new PaperModel(); $this->_d_cate = new CategoryModel(); $this->_d_answer = new AnswerModel(); $this->_d_right = new RightModel(); $this->_d_bank = new BankModel(); $this->_d_tag = new TagModel(); $this->_d_attr = new AttrModel(); $this->_d_snapshot = new SnapshotModel(); $this->_d_temp = new PaperTempModel(); $this->_d_topic = new TopicModel(); $this->_d_topic_attr = new TopicAttrModel(); $this->_d_random = new RandomSnapshotModel(); $this->_d_answer_detail_extend = new AnswerDetailExtendModel(); parent::__construct(); } /** * 获取试卷基本信息(详情用) * * @author:daijun * * @param int $ep_id 试卷ID * * @return array|bool */ public function get_paper_detail_admin($ep_id = 0) { // 获取试卷信息(此处需加上不是初始化状态的数据) $data = $this->_d->get_by_conds(['ep_id' => $ep_id, 'exam_status>?' => self::PAPER_INIT]); // 判断试卷是否存在 if (empty($data)) { E('_EMPTY_PAPER_DATA'); } // 默认关闭积分策略 $is_open_integral = self::CLOSE_INTEGRAL; // 如果启用的自定义或者默认积分策略 if ($data['integral_action_type'] != self::INTEGRAL_ACTION_TYPE_NO) { // 开启策略 $is_open_integral = self::OPEN_INTEGRAL; } // 默认关闭学分策略 $is_open_credit = self::CLOSE_INTEGRAL; // 如果启用的自定义或者默认学分策略 if ($data['credit_action_type'] != self::CREDIT_ACTION_TYPE_FALSE) { // 开启策略 $is_open_credit = self::OPEN_INTEGRAL; } // 获取试卷分类名称 $cate_data = $this->_d_cate->get($data['ec_id']); $result = [ 'ep_id' => intval($ep_id), 'ep_name' => $data['ep_name'], 'exam_type' => intval($data['exam_type']), 'paper_type' => intval($data['paper_type']), 'ep_type' => intval($data['ep_type']), 'is_all' => intval($data['is_all']), 'begin_time' => $data['begin_time'], 'end_time' => $data['end_time'], 'paper_time' => intval($data['paper_time']), 'is_notify' => intval($data['is_notify']), 'notify_begin' => intval($data['notify_begin']), 'notify_end' => intval($data['notify_end']), 'is_recommend' => intval($data['is_recommend']), 'answer_resolve' => intval($data['answer_resolve']), 'total_score' => intval($data['total_score']), 'pass_score' => intval($data['pass_score']), 'intro' => $data['intro'], 'reason' => $data['reason'], // 终止原因 'reason_time' => $data['reason_time'], // 终止时间 'marking_type' => intval($data['marking_type']),// 阅卷类型 'is_open_integral' => $is_open_integral, // 是否开启积分策略 'integral_action_type' => intval($data['integral_action_type']), // 积分策略 'integral_strategyid' => strval($data['integral_strategyid']), // 积分策略ID 'is_open_credit' => $is_open_credit, // 是否开启积分策略 'credit_action_type' => intval($data['credit_action_type']), // 积分策略 'credit_strategyid' => strval($data['credit_strategyid']), // 积分策略ID 'is_pushmsg' => intval($data['is_pushmsg']), // 是否发送及时消息 'ep_status' => $this->paper_status($data['exam_status'], $data['begin_time'], $data['end_time']), // 获取试卷当前状态 'ec_id' => intval($data['ec_id']), 'ec_name' => $cate_data['ec_name'], 'import_num' => 0, // 导入人员数量 'is_upset_topic' => intval($data['is_upset_topic']), // 是否打乱题目 (1:是 2: 否) 'is_upset_option' => intval($data['is_upset_option']), // 是否打乱选项 (1:是 2: 否) 'is_see_after_submit' => intval($data['is_see_after_submit']), // 交卷后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见) 'is_see_after_over' => intval($data['is_see_after_over']), // 考试结束后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见) 'is_open_anonymous_marking' => intval($data['is_open_anonymous_marking']), // 是否开启匿名阅卷:(1:开启 2:不开启) 'is_open_makeup' => intval($data['is_open_makeup']), // 是否开启补考功能:(1:开启 2:不开启) 'makeup_num' => intval($data['makeup_num']), // 补考次数限制 'makeup_start_time' => intval($data['makeup_start_time']), // 补考开始时间 'makeup_end_time' => intval($data['makeup_end_time']), // 补考结束时间 ]; // 获取封面图片 $result['is_cover_open'] = intval($data['is_cover_open']); if (self::IS_COVER_PIC_OPEN == $data['is_cover_open']) { // 如果开启,则获取路径 $result['cover_url'] = $this->format_cover($data['cover_id']); } else { $result['cover_url'] = ''; } // 获取阅卷信息 list($show_marking, $marking_list) = $this->get_marking($ep_id); $result['show_marking'] = $show_marking;// 是否显示阅卷 $result['marking_list'] = $marking_list; // 阅卷人员列表 return $result; } /** * 【后台】根据试卷ID获取试卷信息 * * @author 何岳龙 * * @param int $ep_id 试卷ID * * @return array */ private function get_marking($ep_id = 0) { // 初始化是否显示阅卷 $show_marking = self::NOT_SHOW_MARKING; // 初始化阅卷人员列表 $marking_list = []; // 获取试卷信息(此处需加上不是初始化状态的数据) $data = $this->_d->get_by_conds(['ep_id' => $ep_id]); // 如果是模拟试卷 if ($data['paper_type'] == self::SIMULATION_PAPER_TYPE) { return [$show_marking, $marking_list]; } // 自主抽题或者固定抽题 if (self::TOPIC_CUSTOMER == $data['ep_type'] || self::TOPIC_RULE == $data['ep_type']) { // 语音问答题总数 $voice_total = $this->_d_snapshot->count_by_conds(['ep_id' => $ep_id, 'et_type' => self::TOPIC_TYPE_VOICE]); // 问答题总数 $question_total = $this->_d_snapshot->count_by_conds([ 'ep_id' => $ep_id, 'et_type' => self::TOPIC_TYPE_QUESTION ]); // 获取题目总数 $total = intval($voice_total) + intval($question_total); } // 随机抽题 if (self::TOPIC_RANDOM == $data['ep_type']) { // 解析rule权限 $rule = unserialize($data['rule']); // 计算题目总数 $total = intval($rule['question_count']) + intval($rule['voice_count']); } // 如果存在问答题以及语音问答题 if (!empty($total)) { $show_marking = self::SHOW_MARKING; // 获取阅读人员权限 $right_uids = $this->_d_right->list_by_conds(['er_type' => self::RIGHT_MARKING, 'epc_id' => $ep_id], null, [], 'uid'); // 如果有阅读权限 if ($right_uids) { // 获取有权限的阅读人UID $uids = array_unique(array_column($right_uids, 'uid')); // 初始化用户类 $userServ = &User::instance(); // 排序 sort($uids); // 获取用户类型表 $users = $userServ->listAll(['memUids' => $uids]); // 遍历用户 foreach ($users as $user) { // 组装数据 $marking_list[] = [ 'memID' => $user['memUid'], 'memUsername' => $user['memUsername'], 'memFace' => $user['memFace'], ]; } } } return [$show_marking, $marking_list]; } /** * 获取试卷基本信息(编辑用) * * @author:daijun * * @param int $ep_id 试卷ID * * @return array|bool */ public function get_paper_base_detail($ep_id = 0) { // 获取试卷信息 $data = $this->_d->get($ep_id); // 判断试卷是否存在 if (empty($data)) { E('_EMPTY_PAPER_DATA'); } // 默认关闭积分策略 $is_open_integral = self::CLOSE_INTEGRAL; // 如果启用的自定义或者默认积分策略 if ($data['integral_action_type'] != self::INTEGRAL_ACTION_TYPE_NO) { // 开启策略 $is_open_integral = self::OPEN_INTEGRAL; } // 默认关闭学分策略 $is_open_credit = self::CLOSE_INTEGRAL; // 如果启用的自定义或者默认学分策略 if ($data['credit_action_type'] != self::CREDIT_ACTION_TYPE_FALSE) { // 开启策略 $is_open_credit = self::OPEN_INTEGRAL; } // 获取阅卷信息 list($show_marking, $marking_list) = $this->get_marking($ep_id); // 格式化返回数据 $result = [ 'ep_id' => intval($data['ep_id']), 'exam_type' => intval($data['exam_type']), 'ep_type' => intval($data['ep_type']), 'ep_name' => $data['ep_name'], 'is_all' => intval($data['is_all']), 'begin_time' => $data['begin_time'], 'end_time' => $data['end_time'], 'paper_time' => intval($data['paper_time']), 'is_notify' => intval($data['is_notify']), 'notify_begin' => intval($data['notify_begin']), 'notify_end' => intval($data['notify_end']), 'is_recommend' => intval($data['is_recommend']), 'is_pushmsg' => intval($data['is_pushmsg']), 'answer_resolve' => intval($data['answer_resolve']), 'total_score' => intval($data['total_score']), 'pass_score' => intval($data['pass_score']), 'exam_status' => intval($data['exam_status']), 'intro' => $data['intro'], 'marking_type' => intval($data['marking_type']), // 阅卷类型 'show_marking' => $show_marking, // 是否显示阅卷 'marking_list' => $marking_list, // 阅卷人员列表 'paper_type' => intval($data['paper_type']), // 试卷类型 'is_open_integral' => $is_open_integral, // 是否启用积分策略 'integral_action_type' => intval($data['integral_action_type']), // 积分策略类型 'integral_strategyid' => $data['integral_strategyid'], // 积分策略ID 'is_open_credit' => $is_open_credit, // 是否启用学分策略 'credit_action_type' => intval($data['credit_action_type']), // 学分策略类型 'credit_strategyid' => $data['credit_strategyid'], // 学分策略ID 'is_cover_open' => intval($data['is_cover_open']), // 是否开启封面图片上传 (1:开启2:关闭) 'import_num' => 0, // 导入人员数量 'is_upset_topic' => intval($data['is_upset_topic']), // 是否打乱题目 (1:是 2: 否) 'is_upset_option' => intval($data['is_upset_option']), // 是否打乱选项 (1:是 2: 否) 'is_see_after_submit' => intval($data['is_see_after_submit']), // 交卷后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见) 'is_see_after_over' => intval($data['is_see_after_over']), // 考试结束后可见性设置:(0:全部不可见 1:得分可见,2:对错可见,3:解析可见) 'is_open_anonymous_marking' => intval($data['is_open_anonymous_marking']), // 是否开启匿名阅卷:(1:开启 2:不开启) 'is_open_makeup' => intval($data['is_open_makeup']), // 是否开启补考功能:(1:开启 2:不开启) 'makeup_num' => intval($data['makeup_num']), // 补考次数限制 'makeup_start_time' => intval($data['makeup_start_time']), // 补考开始时间 'makeup_end_time' => intval($data['makeup_end_time']), // 补考结束时间 ]; if (self::IS_COVER_PIC_OPEN == $data['is_cover_open']) { // 如果开启,则格式化图片路径 $result['cover_id'] = $data['cover_id']; $result['cover_url'] = $this->format_cover($data['cover_id']); } else { $result['cover_id'] = ''; $result['cover_url'] = ''; } return $result; } /** * 查询总数 * * @param $data array 查询条件 * * @return int|mixed */ public function count_by_paper($data) { return $this->_d->count_by_paper($data); } /** * 查询列表 * * @author: 蔡建华 * * @param array $data 查询条件 * @param null $page_option 分页参数 * @param array $order_option 排序参数 * @param string $fields 查询的字段 * * @return array|bool */ public function list_by_paper($data, $page_option = null, $order_option = [], $fields = '*') { return $this->_d->list_by_paper($data, $page_option, $order_option, $fields); } /** * 获取手机端试卷列表 * * @param array $conds 查询条件 * @param null $page_option 分页 * @param array $order_option 排序 * @param string $fields 字段 * * @return array|bool 试卷列表 */ public function paper_api_list($conds = [], $page_option = null, $order_option = [], $fields = '*') { return $this->_d->paper_api_list($conds, $page_option, $order_option, $fields); } /** * 格式化试题列表返回数据 * @author: 蔡建华 * * @param array $list 待格式化数据 * @param int $join_type 参与类型 * @param string $uid 用户ID * * @return array 格式化后数据 */ public function paper_format($list = [], $join_type = 0, $uid = '') { // 待格式化数据不为空 if ($list) { $ep_ids = array_column($list, 'ep_id'); // 查询补考次数 $answer_list = $this->_d_answer->list_by_conds(['ep_id' => $ep_ids, 'uid' => $uid, 'is_makeup' => self::IS_MAKEUP], null, ['ep_id' => 'DESC'], 'ea_id,ep_id'); // 转换成以ep_id为主键的二维数组 $new_data = array_count_values(array_column($answer_list, 'ep_id')); foreach ($list as $key => $val) { // 默认不显示补考标签 $makeup_tag = 0; $ep_status = $this->paper_status($val['exam_status'], $val['begin_time'], $val['end_time']); $now_time = MILLI_TIME; // 如果是常规考试 && 是测评试卷 && 开启补考 && 当前时间大于补考开始时间 && 当前时间小于补考结束时间 if (self::NOMAL_TYPE == $val['exam_type'] && self::EVALUATION_PAPER_TYPE == $val['paper_type'] && self::OPEN_MAKEUP == $val['is_open_makeup'] && $now_time < $val['makeup_end_time'] && $now_time > $val['makeup_start_time']) { if ($val['my_is_pass'] != self::MY_PASS && intval($new_data[$val['ep_id']]) == 0 && in_array($ep_status, [self::STATUS_STOP, self::STATUS_END])) { // 考试已结束或者已终止 && 未参加考试 $makeup_tag = 1; // 显示补考标签 } elseif ($val['my_is_pass'] != self::MY_PASS && $val['ea_id'] > 0 && intval($new_data[$val['ep_id']]) < $val['makeup_num']) { // 尚有补考次数 $makeup_tag = 1; // 显示补考标签 } // 查询待批阅的答卷记录 和未交卷的 答卷记录 $answer_list_marking = $this->_d_answer->list_by_conds(['ep_id' => $val['ep_id'], 'uid' => $uid, 'answer_status<?' => self::READ_OVER], [0, 1], ['ea_id' => 'ASC']); if (!empty($answer_list_marking)) { // 存在待批阅或者待交卷的答卷信息 $val['answer_status'] = $answer_list_marking[0]['answer_status']; $val['ea_id'] = $answer_list_marking[0]['ea_id']; $makeup_tag = 0; } } // 重新赋值 $list[$key] = [ 'ep_id' => intval($val['ep_id']), 'join_type' => $join_type, 'paper_type' => intval($val['paper_type']), 'ep_name' => $val['ep_name'], 'total_score' => $val['total_score'], 'ep_status' => $ep_status, 'my_score' => $join_type ? $val['my_score'] : '0', 'my_is_pass' => $join_type ? intval($val['my_is_pass']) : 0, 'answer_status' => $join_type ? intval($val['answer_status']) : 0, 'end_time' => $val['end_time'], 'join_count' => intval($val['join_count']), 'makeup_tag' => $makeup_tag, 'ea_id' => intval($val['ea_id']), ]; } } return $list; } /** * 根据权限查询 * @param array $conds * +++ right array 权限数据 * +++ ec_id int 试卷分类 * @param string $fields 查询参数 * * @return array */ public function list_by_right($conds = [], $fields = '*') { $right_ep_list = $this->_d->get_ep_ids_by_right($conds['right']); // 根据权限查询出来的试卷ID集合 $ep_ids = array_column($right_ep_list, 'epc_id'); $paper_list = []; if ($ep_ids) { $conds_new = [ 'ep_id' => $ep_ids, 'exam_type' => self::NOMAL_TYPE, // 试卷类型:常规试卷 'exam_status' => [self::PAPER_PUBLISH, self::PAPER_STOP], // 状态为已发布或者已终止 ]; if ($conds['ec_id']) { // 如果试卷分类不为空 $conds_new['ec_id'] = $conds['ec_id']; } $paper_list = $this->_d->list_by_conds($conds_new, null, [], $fields); } return $paper_list; } /** * 新增试卷规则 * * @author:daijun * * @param array $param 传入参数 * * @return mixed */ public function rule_add($param = []) { // 验证数据 $data = $this->check_rule_add($param); // 属性序列化(序列化) $tag_data = !empty($param['tag_data']) ? serialize($param['tag_data']) : ''; $data['tag_data'] = $tag_data; // 发布人ID $data['admin_id'] = ''; // 发布人 $data['launch_man'] = ''; // 选中题目列表(序列化) $data['check_topic_data'] = ''; // 考试说明 $data['intro'] = ''; // 终止理由 $data['reason'] = ''; // 终止人员ID $data['reason_user_id'] = ''; // 终止人员名称 $data['reason_user'] = ''; // 试卷状态 $data['exam_status'] = self::PAPER_INIT; // 新数据 $data['is_old'] = self::NEW_DATA_STATE; if (!empty($param['rule'])) { // 随机选题 此处需要计算总分 $rule = $param['rule']; // 将题目数为0的题目分数置为0 $rule = $this->set_rule($rule); // 计算总分 $data['total_score'] = intval($rule['single_count']) * intval($rule['single_score']) + intval($rule['multiple_count']) * intval($rule['multiple_score']) + intval($rule['judgment_count']) * intval($rule['judgment_score']) + intval($rule['question_count']) * intval($rule['question_score']) + intval($rule['voice_count']) * intval($rule['voice_score']); } try { // 开始事务 $this->start_trans(); // 保存基本信息 $ep_id = $this->_d->insert($data); $temp_list = []; // 按照抽取规则和抽取条件进行抽取符合条件的题目 $topic_list = $this->get_temp_list_by_epid($ep_id); if (!$topic_list || !is_array($topic_list) || empty($topic_list)) { // 若没有抽到题目回滚 $this->rollback(); E('_ERR_TOPIC_NOT_GET_LIST_DATA'); } // 非随机抽取 if ($data['ep_type'] != self::TOPIC_RANDOM) { // 初始化编号 $i = self::ORDER_NUM; foreach ($topic_list as $v) { $topic_data['ep_id'] = $ep_id; $topic_data['et_id'] = $v['et_id']; $topic_data['score'] = $v['score']; $topic_data['order_num'] = $i; $temp_list[] = $topic_data; $i++; } if (!empty($temp_list)) { // 存入备选题目列表 $this->_d_temp->insert_all($temp_list); } } elseif (self::TOPIC_RANDOM == $data['ep_type']) { // 题库题目数验证 $this->check_topic_data($topic_list, $data); // / 初始化编号 $k = self::ORDER_NUM; foreach ($topic_list as $v) { // 根据规则设置分数 $score_rule = unserialize($data['rule']); $score = $this->get_score_by_type($v['et_type'], $score_rule); $topic_data['ep_id'] = $ep_id; $topic_data['et_id'] = $v['et_id']; $topic_data['eb_id'] = $v['eb_id']; $topic_data['et_type'] = $v['et_type']; $topic_data['score'] = $score; $topic_data['title'] = $v['title']; $topic_data['title_pic'] = $v['title_pic']; $topic_data['options'] = $v['options']; $topic_data['answer'] = $v['answer']; $topic_data['answer_resolve'] = $v['answer_resolve']; $topic_data['answer_coverage'] = $v['answer_coverage']; $topic_data['match_type'] = $v['match_type']; $topic_data['answer_keyword'] = $v['answer_keyword']; $topic_data['order_num'] = $k; $temp_list[] = $topic_data; $k++; } if (!empty($temp_list)) { // 存入随机题库列表 $this->_d_random->insert_all($temp_list); } } // 如果没有抽到对应的题 if (empty($temp_list)) { $this->rollback(); E('_ERR_TOPIC_NOT_DATA'); } // 提交事务 $this->commit(); } catch (\Think\Exception $e) { \Think\Log::record($e); // 事务回滚 $this->_set_error($e->getMessage(), $e->getCode()); $this->rollback(); return false; } catch (\Exception $e) { \Think\Log::record($e); $this->_set_error($e->getMessage(), $e->getCode()); // 事务回滚 $this->rollback(); return false; } return $ep_id; } /** * 添加试题规则参数验证 * * @author:daijun * * @param array $param * * @return array|bool */ public function check_rule_add($param = []) { $data = []; // 验证试卷名称不能为空 if (empty($param['ep_name'])) { E('_EMPTY_PAPER_NAME'); } // 验证试卷名称长度 if (get_str_len($param['ep_name']) > 64) { E('_ERR_PAPER_NAME_LENGTH'); } $data['ep_name'] = $param['ep_name']; // 验证试卷分类 if (empty($param['ec_id'])) { E('_EMPTY_CATE_ID'); } $data['ec_id'] = $param['ec_id']; // 验证考试类型 if (empty($param['exam_type'])) { E('_EMPTY_EXAM_TYPE'); } $data['exam_type'] = $param['exam_type']; // 验证试卷类型 if (!isset($param['paper_type'])) { E('_EMPTY_PAPER_TYPE'); } $data['paper_type'] = $param['paper_type']; // 验证出题类型 if (empty($param['ep_type'])) { E('_EMPTY_CHOICE_TYPE'); } $data['ep_type'] = $param['ep_type']; // 验证所选题库列表 if (empty($param['bank_list']) || !is_array($param['bank_list'])) { E('_EMPTY_BANK_LIST'); } // 如果选择的题库多于50,则抛错 if (count($param['bank_list']) > 50) { E('_ERR_BANK_LIST_MAX'); } // 获取题库列表 $eb_ids = array_column($param['bank_list'], 'eb_id'); if (empty($eb_ids)) { E('_EMPTY_BANK_LIST'); } sort($eb_ids); $data['bank_data'] = implode(',', $eb_ids); // 验证筛选类型 if (!empty($param['search_type'])) { $data['search_type'] = $param['search_type']; } // 如果是规则抽题 和 随机选题 if (self::TOPIC_RULE == $param['ep_type'] || self::TOPIC_RANDOM == $param['ep_type']) { // 固定规则抽题和随机生成试卷规则调整,验证是否开启高级抽取规则 if (!in_array($param['advanced_choose'], [self::ADVANCED_CHOOSE_OPEN, self::ADVANCED_CHOOSE_CLOSE])) { // 高级抽取规则类型不正确 E('_ERR_ADVANCED_CHOOSE'); } $data['advanced_choose'] = $param['advanced_choose']; // 验证出题规则是否设置 if (empty($param['rule'])) { E('_EMPTY_BANK_RULE'); } $rule = $param['rule']; // 根据规则前端传的计算总题数 $total_topic = intval($rule['single_count']) + intval($rule['multiple_count']) + intval($rule['judgment_count']) + intval($rule['question_count']) + intval($rule['voice_count']); if ($total_topic == 0) { // 题目总数为0,则抛错 E('_ERR_TOTAL_TOPIC'); } // 判断如果题目数不为空,则对应分数也不能为空,否则抛错 if (is_int($rule['single_count']) && !$rule['single_count']) { // 单选题 E('_ERR_SINGLE_TOPIC_SCORE'); } if (is_int($rule['multiple_count']) && !$rule['multiple_count']) { // 多选题 E('_ERR_MULTIPLE_TOPIC_SCORE'); } if (is_int($rule['judgment_count']) && !$rule['judgment_count']) { // 判断题 E('_ERR_JUDGMENT_TOPIC_SCORE'); } if (is_int($rule['question_count']) && !$rule['question_count']) { // 问答题 E('_ERR_QUESTION_TOPIC_SCORE'); } if (is_int($rule['voice_count']) && !$rule['voice_count']) { // 语音题 E('_ERR_VOICE_TOPIC_SCORE'); } // 序列化抽题规则 $data['rule'] = serialize($rule); // 开启高级抽取规则的,允许设置题库规则 if (self::ADVANCED_CHOOSE_OPEN == $param['advanced_choose']) { // 验证所选题库设置的题目数 if (empty($param['bank_topic_data'])) { // 试卷所选题库题目设置不能为空 E('_EMPTY_BANK_TOPIC_DATA'); } // 循环去除多余的字段 foreach ($param['bank_topic_data'] as &$v) { unset($v['$$hashKey']); } // 序列化题库题目设置 $data['bank_topic_data'] = serialize($param['bank_topic_data']); } else { if (!empty($param['bank_topic_data'])) { // 没有开启高级抽取规则时 设置题库抽取规则为空 E("_EMPTY_BANK_TOPIC_DATABASE"); } $data['bank_topic_data'] = ''; } } else { if ($data['rule'] && $data['bank_topic_data']) { E("_EMPTY_BANK_CHOOLSE_TOPIC_DATA"); } // 自主抽题 $data['rule'] = ''; $data['bank_topic_data'] = ''; $data['advanced_choose'] = self::ADVANCED_CHOOSE_CLOSE; } return $data; } /** * 将选择题目数为0的题目分数置0 * * @param array $rule 题目规则 * * @return array */ public function set_rule($rule = []) { if ($rule['single_count'] == 0) { $rule['single_score'] = 0; } if ($rule['multiple_count'] == 0) { $rule['multiple_score'] = 0; } if ($rule['judgment_count'] == 0) { $rule['judgment_score'] = 0; } if ($rule['question_count'] == 0) { $rule['question_score'] = 0; } if ($rule['voice_count'] == 0) { $rule['voice_score'] = 0; } return $rule; } /** * 按照试卷设置的规则抽题 * * @author:daijun * * @param int $ep_id 试卷ID * @param int $type (0:后台抽题,1:前台抽题) * * @return array */ public function get_temp_list_by_epid($ep_id = 0, $type = 0) { $paper = $this->_d->get($ep_id); if (empty($paper)) { E('_EMPTY_PAPER_DATA'); } // 标签属性数据 $attr_ids = []; if (!empty($paper['tag_data'])) { // 反序标签属性 $tag_data = unserialize($paper['tag_data']); foreach ($tag_data as $v) { $attr_ids_arr = []; // 判断是不是数组 if (is_array($v['attr_data'])) { $attr_ids_arr = array_column($v['attr_data'], 'attr_id'); } if (!empty($attr_ids_arr) && is_array($attr_ids_arr)) { $attr_ids = array_merge($attr_ids, $attr_ids_arr); } } } // 固定抽取和随机抽取有抽取规则(其中包括每种类型题目数量和分数) $rule_data = []; if (!empty($paper['rule'])) { $rule_data = unserialize($paper['rule']); } // 开启高级抽取是有每个题库每个类型的题目数和分数 $bank_topic_data = []; if (!empty($paper['bank_topic_data'])) { $bank_topic_data = unserialize($paper['bank_topic_data']); } // 获取题库的ID集合 $bank_ids = explode(',', $paper['bank_data']); $temp_list = []; // 自主选题 || 随机抽题 if ((self::TOPIC_CUSTOMER == $paper['ep_type'] || self::TOPIC_RANDOM == $paper['ep_type']) && $type == 0) { // 后台手动自主抽取和随机抽取时调用,查询出所有符合条件的题目 $temp_list = $this->choice_topic($bank_ids, $attr_ids, $paper); } elseif ((self::TOPIC_RULE == $paper['ep_type'] && $type == 0) || (self::TOPIC_RANDOM == $paper['ep_type'] && $type == 1)) { // 固定抽取和前端随机抽取时调用 $temp_list = $this->rule_topic($bank_ids, $attr_ids, $paper, $bank_topic_data, $rule_data); } return $temp_list; } /** * 通过标签属性和题库进行自主自主选题 * * @author daijun * * @param array $bank_ids 题库ID集合 * @param array $attr_ids 属性ID集合 * @param array $paper 试卷信息 * * @return array|bool */ public function choice_topic($bank_ids = [], $attr_ids = [], $paper = []) { // 属性ID去除重复 $attr_ids = array_unique($attr_ids); $topic_list = []; if (empty($attr_ids)) { // 没有选择属性,则查询所选题库的所有试题 $topic_list = $this->_d_topic->list_by_conds(['eb_id' => $bank_ids]); } else { // 获取满足其中之一条件的所有数据标签属性关系列表数据 $topic_attr_all_list = $this->_d_topic_attr->list_by_conds(['eb_id' => $bank_ids, 'attr_id' => $attr_ids], null, ['eb_id' => 'ASC']); // 如果设置了属性:满足任意一个 if (self::SEARCH_ATTR_TYPE_NOT_ALL == $paper['search_type']) { // 获取题目ID,并去除重复 $topic_ids = array_unique(array_column($topic_attr_all_list, 'et_id')); } else { // 满足所有属性的抽题 // 初始化题库下的题目IDS数组 $eb_list = []; // 根据题目ID重新组合属性,以题目ID为键值,合并标签属性ID foreach ($topic_attr_all_list as $item) { $eb_list[$item['et_id']][] = $item['attr_id']; } // 组装符合条件的题目id集合 $topic_ids = []; foreach ($eb_list as $key => $v) { // 属性ID去除重复 $v = array_unique($v); if (array_intersect($attr_ids, $v) == $attr_ids) { $topic_ids[] = $key; } } } // 没有获取任何符合条件的题目 if (empty($topic_ids)) { E('_ERR_TOPIC_NOT_IDS_DATA'); } // 判断题目总数 $topic_total = count($topic_ids); // 设置题目每次抽取50条件记录 $count = self::TOPIC_MAX_COUNT; // 题目ID集合分组 $topic_ids_arr = []; if ($topic_total > $count) { // 如果题目总数超过50,则需要进行分页查询,否则用in查询会死掉 $countpage = ceil($topic_total / $count); // 计算总页面数 // 对题目ID集合进行分页组装 for ($i = 0; $i < $countpage; $i++) { $topic_ids_arr[] = array_slice($topic_ids, $i * $count, $count); } } else { sort($topic_ids); $topic_ids_arr[] = $topic_ids; } // 循环查询题目列表 foreach ($topic_ids_arr as $v) { // 查询题目列表 $topic_arr = $this->_d_topic->list_by_conds(['et_id' => $v]); // 合并结果数组 $topic_list = array_merge($topic_arr, $topic_list); } } if (empty($topic_list)) { E('_ERR_TOPIC_NOT_GET_LIST_DATA'); } return $topic_list; } /** * 按规则抽题 * * @author daijun * * @param array $bank_ids 题库ID集合 * @param array $attr_ids 属性ID集合 * @param array $paper 试卷信息 * @param array $bank_topic_data 题库题目数量设置 * @param array $rule_data 题目得分规则设置 * * @return array */ public function rule_topic($bank_ids = [], $attr_ids = [], $paper = [], $bank_topic_data = [], $rule_data = []) { $topic_all_list = []; if (empty($attr_ids)) { // 没有选择标签,则查询所选题库的所有试题 $topic_list = $this->_d_topic->list_by_conds(['eb_id' => $bank_ids]); // 根据题目ID去除重复数据 $topic_list_ids = array_combine_by_key($topic_list, 'et_id'); } else { $attr_ids = array_unique($attr_ids); // 满足任意一个标签的抽题 $topic_attr_all_list = $this->_d_topic_attr->list_by_conds(['attr_id' => $attr_ids, 'eb_id' => $bank_ids], null, ['eb_id' => 'ASC']); if (self::SEARCH_ATTR_TYPE_NOT_ALL == $paper['search_type']) { // 获取对应所有题目IDS $et_ids = array_column($topic_attr_all_list, 'et_id'); // 查询题目详情 $topic_et_id_list = $this->_d_topic->list_by_conds(['et_id' => $et_ids]); $topic_et_id_list = array_combine_by_key($topic_et_id_list, 'et_id'); // 新增题目类型 foreach ($topic_attr_all_list as $key => &$topic_v) { $topic_v['et_type'] = $topic_et_id_list[$topic_v['et_id']]['et_type']; } // 满足任意一个标签的抽题 $topic_list_ids = $topic_attr_all_list; } else { // 满足所有标签的抽题 // 初始化题库下的题目IDS数组 $eb_list = []; // 根据题目ID重新组合属性,以题目ID为键值,合并标签属性ID foreach ($topic_attr_all_list as $item) { $eb_list[$item['et_id']][] = $item['attr_id']; } $topic_ids = []; // 组装符合条件的数据id集合 foreach ($eb_list as $key => $v) { $v = array_unique($v); if (array_intersect($attr_ids, $v) == $attr_ids) { $topic_ids[] = $key; } } if (!empty($topic_ids)) { // 查询符合条件的试题列表 $topic_list_ids = $this->_d_topic->list_by_conds(['et_id' => $topic_ids]); } else { $topic_list_ids = []; } } } if (self::ADVANCED_CHOOSE_OPEN == $paper['advanced_choose']) { // 开启高级抽题规则 $bank_topic_attr = []; foreach ($topic_list_ids as $item_v) { $bank_topic_attr[$item_v['eb_id']][] = $item_v['et_id']; } // 遍历题库ID foreach ($bank_ids as $k => $v) { // 检查是否需要在该题库抽题 $is_bank_topic = true; // 默认需要从该题库需要抽题 // 此处查询该题库是否设置了题目 foreach ($bank_topic_data as $val) { if ($val['eb_id'] == $v) { // 计算当前题库设置测抽题数量 $t_count = $val['single_count'] + $val['multiple_count'] + $val['judgment_count'] + $val['question_count'] + $val['voice_count']; // 不需要抽取题目的题库 if (!$t_count) { $is_bank_topic = false; } break; } } // 如果规则设置不从该题库抽题,则continue if (!$is_bank_topic) { continue; } // 对该题库下的题目ID集合去重 $et_ids = array_unique($bank_topic_attr[$v]); if (empty($et_ids)) { E('_ERR_DELETE_RULE_TOPIC'); } // 查询该题库下的符合条件的题目列表 $topic_list = $this->_d_topic->list_by_conds(['et_id' => $et_ids]); // 将题目列表按照题目类型分组返回 $topic_data = $this->format_by_et_type($topic_list, $bank_topic_data, $rule_data, $v); if (!empty($topic_data) && is_array($topic_data)) { // 合并组装数组 $topic_all_list = array_merge($topic_all_list, $topic_data); } } } else { // 没有开高级抽题 $topic_all_list = $this->format_simple_by_et_type($topic_list_ids, $rule_data); } if (empty($topic_all_list)) { E('_ERR_TOPIC_NOT_GET_LIST_DATA'); } // 对抽取的题目进行打乱 $topics = array_combine_by_key($topic_all_list, 'et_id'); $tp_ids = array_column($topic_all_list, 'et_id'); // 打乱ID shuffle($tp_ids); // 循环组装试题信息 $result_list = []; foreach ($tp_ids as $k => $v) { $data = $topics[$v]; $data['order_num'] = $k + 1; $result_list[] = $data; } return $result_list; } /** * 将题库题目列表按照题目类型分组返回 * * @author daijun * * @param array $topic_list 题目列表 * @param array $bank_topic_data 题库题目数量设置 * @param array $rule_data 抽题规则 * @param Int $eb_id 题库ID * @param bool $is_type_data true 默认 false 不处理分数 * * @return array */ public function format_by_et_type( $topic_list = [], $bank_topic_data = [], $rule_data = [], $eb_id = 0, $is_type_data = true ) { $single_attr = []; //单选题 $multiple_attr = []; //多选题 $judgment_attr = []; //判断题 $question_attr = []; //问答题 $voice_attr = []; //语音题 // 所属题库ID $bank_id = $eb_id; // 循环题目,按照类型组成新数组 foreach ($topic_list as $_v) { // 试题类型:单选题 if (self::TOPIC_TYPE_SINGLE == $_v['et_type']) { $single_attr[] = $_v; continue; } // 试题类型:判断题 if (self::TOPIC_TYPE_JUDGMENT == $_v['et_type']) { $judgment_attr[] = $_v; continue; } // 试题类型:问答题 if (self::TOPIC_TYPE_QUESTION == $_v['et_type']) { $question_attr[] = $_v; continue; } // 试题类型:多选题 if (self::TOPIC_TYPE_MULTIPLE == $_v['et_type']) { $multiple_attr[] = $_v; continue; } // 试题类型:语音题 if (self::TOPIC_TYPE_VOICE == $_v['et_type']) { $voice_attr[] = $_v; continue; } } // 取出该题库的出题规则 $bank_topic = []; foreach ($bank_topic_data as $v) { if ($v['eb_id'] == $bank_id) { $bank_topic = $v; } } // *************************抽题开始************************* $single_list = []; //单选题 $multiple_list = []; //多选题 $judgment_list = []; //判断题 $question_list = []; //问答题 $voice_list = []; //语音题 if (intval($bank_topic['single_count']) > 0) { // 存在单选题 if (intval($bank_topic['single_count']) > count($single_attr)) { // 错误日志 Logger::write('============单选题抽题失败日志记录begin==============='); Logger::write('题库ID:' . $eb_id); Logger::write('抽题规则设置数量:'); Logger::write(var_export($bank_topic['single_count'], true)); Logger::write('实际抽出数量:'); Logger::write(var_export(count($single_attr), true)); Logger::write('============单选题抽题失败日志记录end==============='); E('_ERR_SINGLE_CHOICE_PAPER'); } $single_list = $this->get_rand_arr($single_attr, intval($bank_topic['single_count'])); // 重新算分 if ($is_type_data == true) { foreach ($single_list as &$v) { $v['score'] = $rule_data['single_score']; } } } if (intval($bank_topic['multiple_count']) > 0) { // 存在多选题题 if (intval($bank_topic['multiple_count']) > count($multiple_attr)) { // 错误日志 Logger::write('============多选题抽题失败日志记录begin==============='); Logger::write('题库ID:' . $eb_id); Logger::write('抽题规则设置数量:'); Logger::write(var_export($bank_topic['multiple_count'], true)); Logger::write('实际抽出数量:'); Logger::write(var_export(count($multiple_attr), true)); Logger::write('============多选题抽题失败日志记录end==============='); E('_ERR_MULTIPLE_CHOICE_PAPER'); } $multiple_list = $this->get_rand_arr($multiple_attr, intval($bank_topic['multiple_count'])); if ($is_type_data == true) { foreach ($multiple_list as &$v) { $v['score'] = $rule_data['multiple_score']; } } } if (intval($bank_topic['judgment_count']) > 0) { // 存在判断题 if (intval($bank_topic['judgment_count']) > count($judgment_attr)) { // 错误日志 Logger::write('============判断题抽题失败日志记录begin==============='); Logger::write('题库ID:' . $eb_id); Logger::write('抽题规则设置数量:'); Logger::write(var_export($bank_topic['judgment_count'], true)); Logger::write('实际抽出数量:'); Logger::write(var_export(count($judgment_attr), true)); Logger::write('============判断题抽题失败日志记录end==============='); E('_ERR_JUDGMENT_CHOICE_PAPER'); } $judgment_list = $this->get_rand_arr($judgment_attr, intval($bank_topic['judgment_count'])); if ($is_type_data == true) { foreach ($judgment_list as &$v) { $v['score'] = $rule_data['judgment_score']; } } } if (intval($bank_topic['question_count']) > 0) { // 存在问答题 if (intval($bank_topic['question_count']) > count($question_attr)) { // 错误日志 Logger::write('============问答题抽题失败日志记录begin==============='); Logger::write('题库ID:' . $eb_id); Logger::write('抽题规则设置数量:'); Logger::write(var_export($bank_topic['question_count'], true)); Logger::write('实际抽出数量:'); Logger::write(var_export(count($question_attr), true)); Logger::write('============问答题抽题失败日志记录end==============='); E('_ERR_QUESTION_CHOICE_PAPER'); } $question_list = $this->get_rand_arr($question_attr, intval($bank_topic['question_count'])); if ($is_type_data == true) { foreach ($question_list as &$v) { $v['score'] = $rule_data['question_score']; } } } if (intval($bank_topic['voice_count']) > 0) { // 存在语音题 if (intval($bank_topic['voice_count']) > count($voice_attr)) { // 错误日志 Logger::write('============语音题抽题失败日志记录begin==============='); Logger::write('题库ID:' . $eb_id); Logger::write('抽题规则设置数量:'); Logger::write(var_export($bank_topic['voice_count'], true)); Logger::write('实际抽出数量:'); Logger::write(var_export(count($voice_attr), true)); Logger::write('============语音题抽题失败日志记录end==============='); E('_ERR_VOICE_CHOICE_PAPER'); } $voice_list = $this->get_rand_arr($voice_attr, intval($bank_topic['voice_count'])); if ($is_type_data == true) { foreach ($voice_list as &$v) { $v['score'] = $rule_data['voice_score']; } } } return array_merge($single_list, $judgment_list, $multiple_list, $question_list, $voice_list); } /** * 数组随机返回列表 * * @author daijun * * @param array $topic 原始列表 * @param int $num 随机数量 * * @return array */ public function get_rand_arr($topic = [], $num = 0) { if (count($topic) == $num) { return $topic; } else { $topic_list = []; if ($num == 1) { $index = rand(0, count($topic) - 1); $topic_list[] = $topic[$index]; } else { $index_num = array_rand($topic, $num); foreach ($index_num as $v) { $topic_list[] = $topic[$v]; } } } return $topic_list; } /** * 将未开启高级抽取题目列表按照题目类型分组返回 * * @param array $topic_list 题目列表 * @param array $rule_data 抽题规则 * @param bool $is_type_data true 默认 false 不处理分数 * * @return array */ public function format_simple_by_et_type($topic_list = [], $rule_data = [], $is_type_data = true) { $single_attr = []; // 单选题 $multiple_attr = []; // 多选题 $judgment_attr = []; // 判断题 $question_attr = []; // 问答题 $voice_attr = []; // 语音题 // 循环题目,按照类型组成新数组 foreach ($topic_list as $_v) { // 试题类型:单选题 if (self::TOPIC_TYPE_SINGLE == $_v['et_type']) { $single_attr[] = $_v; continue; } // 试题类型:判断题 if (self::TOPIC_TYPE_JUDGMENT == $_v['et_type']) { $judgment_attr[] = $_v; continue; } // 试题类型:问答题 if (self::TOPIC_TYPE_QUESTION == $_v['et_type']) { $question_attr[] = $_v; continue; } // 试题类型:多选题 if (self::TOPIC_TYPE_MULTIPLE == $_v['et_type']) { $multiple_attr[] = $_v; continue; } // 试题类型:语音题 if (self::TOPIC_TYPE_VOICE == $_v['et_type']) { $voice_attr[] = $_v; continue; } } // *************************抽题开始************************* $single_list = []; //单选题 $multiple_list = []; //多选题 $judgment_list = []; //判断题 $question_list = []; //问答题 $voice_list = []; //语音题 if (intval($rule_data['single_count']) > 0) { // 存在单选题 if (intval($rule_data['single_count']) > count($single_attr)) { // 错误日志 Logger::write('============没有题库单选题抽题失败日志记录begin==============='); Logger::write('没有题库抽题规则设置数量:'); Logger::write(var_export($rule_data['single_count'], true)); Logger::write('没有题库实际抽出数量:'); Logger::write(var_export(count($single_attr), true)); Logger::write('============没有题库单选题抽题失败日志记录end==============='); E('_ERR_SINGLE_CHOICE_PAPER'); } $single_list = $this->get_rand_arr($single_attr, intval($rule_data['single_count'])); if ($is_type_data == true) { foreach ($single_list as &$v) { $v['score'] = $rule_data['single_score']; } } } if (intval($rule_data['multiple_count']) > 0) { // 存在多选题题 if (intval($rule_data['multiple_count']) > count($multiple_attr)) { // 错误日志 Logger::write('============没有题库多选题抽题失败日志记录begin==============='); Logger::write('没有题库抽题规则设置数量:'); Logger::write(var_export($rule_data['multiple_count'], true)); Logger::write('没有题库实际抽出数量:'); Logger::write(var_export(count($multiple_attr), true)); Logger::write('============没有题库多选题抽题失败日志记录end==============='); E('_ERR_MULTIPLE_CHOICE_PAPER'); } $multiple_list = $this->get_rand_arr($multiple_attr, intval($rule_data['multiple_count'])); if ($is_type_data == true) { foreach ($multiple_list as &$v) { $v['score'] = $rule_data['multiple_score']; } } } if (intval($rule_data['judgment_count']) > 0) { // 存在判断题 if (intval($rule_data['judgment_count']) > count($judgment_attr)) { // 错误日志 Logger::write('============没有题库判断题抽题失败日志记录begin==============='); Logger::write('没有题库抽题规则设置数量:'); Logger::write(var_export($rule_data['judgment_count'], true)); Logger::write('没有题库实际抽出数量:'); Logger::write(var_export(count($judgment_attr), true)); Logger::write('============没有题库判断题抽题失败日志记录end==============='); E('_ERR_JUDGMENT_CHOICE_PAPER'); } $judgment_list = $this->get_rand_arr($judgment_attr, intval($rule_data['judgment_count'])); if ($is_type_data == true) { foreach ($judgment_list as &$v) { $v['score'] = $rule_data['judgment_score']; } } } if (intval($rule_data['question_count']) > 0) { // 存在问答题 if (intval($rule_data['question_count']) > count($question_attr)) { // 错误日志 Logger::write('============没有题库问答题抽题失败日志记录begin==============='); Logger::write('没有题库抽题规则设置数量:'); Logger::write(var_export($rule_data['question_count'], true)); Logger::write('没有题库实际抽出数量:'); Logger::write(var_export(count($question_attr), true)); Logger::write('============没有题库问答题抽题失败日志记录end==============='); E('_ERR_QUESTION_CHOICE_PAPER'); } $question_list = $this->get_rand_arr($question_attr, intval($rule_data['question_count'])); if ($is_type_data == true) { foreach ($question_list as &$v) { $v['score'] = $rule_data['question_score']; } } } if (intval($rule_data['voice_count']) > 0) { // 存在语音题 if (intval($rule_data['voice_count']) > count($voice_attr)) { // 错误日志 Logger::write('============没有题库语音题抽题失败日志记录begin==============='); Logger::write('没有题库抽题规则设置数量:'); Logger::write(var_export($rule_data['voice_count'], true)); Logger::write('没有题库实际抽出数量:'); Logger::write(var_export(count($voice_attr), true)); Logger::write('============没有题库语音题抽题失败日志记录end==============='); E('_ERR_VOICE_CHOICE_PAPER'); } $voice_list = $this->get_rand_arr($voice_attr, intval($rule_data['voice_count'])); if ($is_type_data == true) { foreach ($voice_list as &$v) { $v['score'] = $rule_data['voice_score']; } } } return array_merge($single_list, $judgment_list, $multiple_list, $question_list, $voice_list); } /** * 根据U规则验证题目数量 * * @author:daijun * * @param $topic_list array 题目列表 * @param $params array 试卷表单参数 */ protected function check_topic_data($topic_list = [], $params = []) { if ($params['rule']) { $score_rule = unserialize($params['rule']); } if ($params['bank_topic_data']) { $bank_topic_data = unserialize($params['bank_topic_data']); } $single_attr = []; //单选题 $multiple_attr = []; //多选题 $judgment_attr = []; //判断题 $question_attr = []; //问答题 $voice_attr = []; //语音题 // 循环题目,按照类型组成新数组 foreach ($topic_list as $_v) { // 试题类型:单选题 if (self::TOPIC_TYPE_SINGLE == $_v['et_type']) { if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) { // 开启高级抽题规则 按照题库组合 $single_attr[$_v['eb_id']][] = $_v; } else { $single_attr[] = $_v; } continue; } // 试题类型:判断题 if (self::TOPIC_TYPE_JUDGMENT == $_v['et_type']) { if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) { // 开启高级抽题规则 按照题库组合 $judgment_attr[$_v['eb_id']][] = $_v; } else { $judgment_attr[] = $_v; } continue; } // 试题类型:问答题 if (self::TOPIC_TYPE_QUESTION == $_v['et_type']) { if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) { // 开启高级抽题规则 按照题库组合 $question_attr[$_v['eb_id']][] = $_v; } else { $question_attr[] = $_v; } continue; } // 试题类型:多选题 if (self::TOPIC_TYPE_MULTIPLE == $_v['et_type']) { if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) { // 开启高级抽题规则 按照题库组合 $multiple_attr[$_v['eb_id']][] = $_v; } else { $multiple_attr[] = $_v; } continue; } // 试题类型:语音题 if (self::TOPIC_TYPE_VOICE == $_v['et_type']) { if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) { // 开启高级抽题规则 按照题库组合 $voice_attr[$_v['eb_id']][] = $_v; } else { $voice_attr[] = $_v; } continue; } } if (self::ADVANCED_CHOOSE_OPEN == $params['advanced_choose']) { foreach ($bank_topic_data as $val) { // 单选 if (intval($val['single_count']) > count($single_attr[$val['eb_id']])) { E('_ERR_SINGLE_CHOICE_PAPER'); } // 多选 if (intval($val['multiple_count']) > count($multiple_attr[$val['eb_id']])) { E('_ERR_MULTIPLE_CHOICE_PAPER'); } // 判断 if (intval($val['judgment_count']) > count($judgment_attr[$val['eb_id']])) { E('_ERR_JUDGMENT_CHOICE_PAPER'); } // 问题 if (intval($val['question_count']) > count($question_attr[$val['eb_id']])) { E('_ERR_QUESTION_CHOICE_PAPER'); } // 语音 if (intval($val['voice_count']) > count($voice_attr[$val['eb_id']])) { E('_ERR_VOICE_CHOICE_PAPER'); } } } else { // 单选 if (intval($score_rule['single_count']) > count($single_attr)) { E('_ERR_SINGLE_CHOICE_PAPER'); } // 多选 if (intval($score_rule['multiple_count']) > count($multiple_attr)) { E('_ERR_MULTIPLE_CHOICE_PAPER'); } // 判断 if (intval($score_rule['judgment_count']) > count($judgment_attr)) { E('_ERR_JUDGMENT_CHOICE_PAPER'); } // 问题 if (intval($score_rule['question_count']) > count($question_attr)) { E('_ERR_QUESTION_CHOICE_PAPER'); } // 语音 if (intval($score_rule['voice_count']) > count($voice_attr)) { E('_ERR_VOICE_CHOICE_PAPER'); } } } /** * 编辑试卷规则 * * @author:daijun * * @param array $param 传入参数 * * @return bool */ public function rule_save($param = []) { // 验证ID if (empty($param['ep_id'])) { E('_EMPTY_EP_ID'); } $ep_id = $param['ep_id']; // 验证其他数据 if (!$data = $this->check_rule_add($param)) { E('_EMPTY_SAVE_DATA'); } // 此处组装其他必填的字段信息 $data['tag_data'] = !empty($param['tag_data']) ? serialize($param['tag_data']) : ''; // 此处查询试卷原始数据 $paper = $this->_d->get($ep_id); // 如果试卷规则和题库抽题规则无变化,则无需重新抽题 if ($paper['rule'] == $data['rule'] && $paper['bank_topic_data'] == $data['bank_topic_data'] && $paper['search_type'] == $data['search_type'] && $data['tag_data'] == $paper['tag_data'] && $data['advanced_choose'] == $paper['advanced_choose'] && $data['ep_type'] == $paper['ep_type'] && $data['bank_data'] == $paper['bank_data'] ) { // 不重新抽题 $is_choice = self::NOT_CHOICE; } else { // 重新抽题 $is_choice = self::AGAIN_CHOICE; } if (!empty($param['rule'])) { // 随机选题 此处需要计算总分 $rule = $param['rule']; // 将题目数为0的题目分数置为0 $rule = $this->set_rule($rule); $data['total_score'] = intval($rule['single_count']) * intval($rule['single_score']) + intval($rule['multiple_count']) * intval($rule['multiple_score']) + intval($rule['judgment_count']) * intval($rule['judgment_score']) + intval($rule['question_count']) * intval($rule['question_score']) + intval($rule['voice_count']) * intval($rule['voice_score']); } $data['is_old'] = self::NEW_DATA_STATE; // 新数据 try { // 开始事务 $this->start_trans(); // 更新试卷表 $this->_d->update($ep_id, $data); // 如果重新抽题 if (self::AGAIN_CHOICE == $is_choice) { // 抽题存入 $temp_list = []; // 如果试卷出题规则不是随机,则按配置抽题 $topic_list = $this->get_temp_list_by_epid($ep_id); if (!$topic_list || !is_array($topic_list) || empty($topic_list)) { // 若没有抽到题目回滚 $this->rollback(); E('_ERR_TOPIC_NOT_GET_LIST_DATA'); } if ($data['ep_type'] != self::TOPIC_RANDOM) { // 删除备选题目列表 $this->_d_temp->delete_by_conds(['ep_id' => $ep_id]); // 删除已选题目列表 $this->_d_snapshot->delete_by_conds(['ep_id' => $ep_id]); // 非随机抽取 // 初始化编号 $i = self::ORDER_NUM; // 循环组装入库数据 foreach ($topic_list as $v) { $temp_list[] = [ 'ep_id' => $ep_id, 'et_id' => $v['et_id'], 'score' => $v['score'], 'order_num' => $i ]; $i++; } if (!empty($temp_list)) { // 存入备选题目列表 $this->_d_temp->insert_all($temp_list); } } elseif (self::TOPIC_RANDOM == $data['ep_type']) { //按照题库设置进行匹配 $this->check_topic_data($topic_list, $data); // 删除已选题目列表 $this->_d_random->delete_by_conds(['ep_id' => $ep_id]); // 取出符合规则的所有试题 $rule_data = unserialize($data['rule']); // 初始化编号 $k = self::ORDER_NUM; foreach ($topic_list as $v) { // 根据规则设置分数 $score = $this->get_score_by_type($v['et_type'], $rule_data); $topic_data = []; $topic_data['ep_id'] = $ep_id; $topic_data['eb_id'] = $v['eb_id']; $topic_data['et_type'] = $v['et_type']; $topic_data['et_id'] = $v['et_id']; $topic_data['title'] = $v['title']; $topic_data['title_pic'] = $v['title_pic']; $topic_data['options'] = $v['options']; $topic_data['score'] = $score; $topic_data['answer'] = $v['answer']; $topic_data['answer_resolve'] = $v['answer_resolve']; $topic_data['answer_coverage'] = $v['answer_coverage']; $topic_data['match_type'] = $v['match_type']; $topic_data['answer_keyword'] = $v['answer_keyword']; $topic_data['order_num'] = $k; $temp_list[] = $topic_data; $k++; } if (!empty($temp_list)) { $this->_d_random->insert_all($temp_list); } } } // 如果没有抽到对应的题 if (empty($temp_list) && self::AGAIN_CHOICE == $is_choice) { $this->rollback(); E('_ERR_TOPIC_NOT_DATA'); } // 提交事务 $this->commit(); } catch (\Think\Exception $e) { \Think\Log::record($e); // 事务回滚 $this->_set_error($e->getMessage(), $e->getCode()); $this->rollback(); return false; } catch (\Exception $e) { \Think\Log::record($e); $this->_set_error($e->getMessage(), $e->getCode()); // 事务回滚 $this->rollback(); return false; } return $ep_id; } /** * 编辑试卷基本信息(基本试卷) * * @author:daijun * * @param array $param 传入参数 * * @return bool */ public function base_save($param = []) { $ep_id = $param['ep_id']; // 获取试卷信息 $paper = $this->_d->get($ep_id); if ($paper['exam_type'] > 1) { // 如果是任务,线下培训,则走新的验证和更新 $this->base_save_new($param, $paper); return true; } // 被学习路径关联的课程不可删除 $mapUsedIds = StudyMap::checkAppDataUsed((array)$ep_id); if (!empty($mapUsedIds)) { E('_ERR_MAP_USED_CANNOT_DELETE'); } // 验证属性 if (!$data = $this->check_base_save($param)) { E('_EMPTY_SAVE_DATA'); } $right = $data['right']; unset($data['right']); $uids_old = []; // 试卷题目列表初始化 $topic_list = []; // 此处获取所有应参与人员的ID集合 $right_serv = new RightService(); $right_res = $right_serv->format_db_data($right); // 获取数据更新后的权限用户列表 $right_res['is_all'] = $data['is_all']; $uids_now = $right_serv->list_uids_by_right($right_res); // 参与人数 $data['unjoin_count'] = count($uids_now); // 如果是发布动作 if ($data['exam_status'] == self::PAPER_PUBLISH) { // 试卷题目查询条件 $conditions_topic['ep_id'] = $ep_id; // 查询组装总题目数量 if ($paper['ep_type'] != self::TOPIC_RANDOM) { // 如果不是随机出题,此处查询该试卷的试题总数 $data['topic_count'] = $this->_d_snapshot->count_by_conds($conditions_topic); // 获取试卷题目列表 $topic_list = $this->_d_snapshot->list_by_conds($conditions_topic); } else { // 如果是随机出题 $rule = unserialize($paper['rule']); $data['topic_count'] = intval($rule['single_count']) + intval($rule['multiple_count']) + intval($rule['judgment_count']) + intval($rule['question_count']) + intval($rule['voice_count']); // 获取试卷题目列表 $topic_list = $this->_d_random->list_by_conds($conditions_topic); } // 如果是已发布的试卷进行编辑再次发布,此处需查询之前的权限数据,推送消息会用到 if ($paper['exam_status'] == self::PAPER_PUBLISH) { // 权限查询条件 $conds = [ 'epc_id' => $ep_id, 'er_type' => AnswerService::RIGHT_PAPER ]; // 获取未参与考试人员列表及人数 $answer_serv = new AnswerService(); $unjoin_data = $answer_serv->get_unjoin_data($conds, $ep_id, $paper['is_all']); // 获取数据更新前的权限用户列表 $uids_old = $unjoin_data['unjoin_list']; } // 发布时间 $data['publish_time'] = MILLI_TIME; } // 初始化阅卷人权限 $right_marking = []; if (isset($param['marking_list']) && !empty($param['marking_list'])) { // 重组阅卷人列表 foreach ($param['marking_list'] as $v) { // 如果阅卷人存在 if ($v['memID']) { $right_marking[] = [ 'epc_id' => $ep_id, 'er_type' => self::RIGHT_MARKING, 'uid' => $v['memID'], 'cd_id' => '', 'tag_id' => '', 'job_id' => '', 'role_id' => '' ]; } } } // 查询分类详情 $cate_data = $this->_d_cate->get($paper['ec_id']); // 将分类的状态赋值给试卷状态 $data['cate_status'] = $cate_data['ec_status']; // 阅卷类型 $data['marking_type'] = $param['marking_type']; // 获取最后更新时间 $data['last_time'] = MILLI_TIME; // 如果阅卷人不为空 并且是发布状态 if (!empty($right_marking) && self::PAPER_PUBLISH == $data['exam_status']) { // 阅卷人发送消息 $this->marking_send($param); } // 获取答卷扩展信息 $extend_list = $this->_d_answer_detail_extend->list_by_conds(['ep_id' => $ep_id]); $extend_list = array_combine_by_key($extend_list, 'et_id'); try { // 开始事务 $this->start_trans(); // 更新试卷表 $this->_d->update($ep_id, $data); // 删除之前的权限 $this->_d_right->delete_by_conds(['epc_id' => $ep_id, 'er_type' => self::RIGHT_PAPER]); // 删除之前的阅卷人权限 $this->_d_right->delete_by_conds(['epc_id' => $ep_id, 'er_type' => self::RIGHT_MARKING]); // 如果有权限数据 if (!empty($right)) { // 新增权限数据 $this->_d_right->insert_all($right); } // 如果有阅卷人 if (!empty($right_marking)) { // 新增阅卷 $this->_d_right->insert_all($right_marking); } // 分表oa_exam_answer_detail_extend待增数据初始化 $detail_extend_data = []; if (!empty($topic_list)) { foreach ($topic_list as $topic) { $extend_info = $extend_list[$topic['et_id']]; if (empty($extend_info)) { // 组装题目详情数据 $et_detail = [ 'et_type' => $topic['et_type'], // 题目类型(1:单选题 2:判断题 3:问答题 4:多选题,5:语音题) 'title' => $topic['title'], // 题目名称 'title_pic' => $topic['title_pic'], // 题目图片(逗号分割 'answer' => $topic['answer'], // 正确答案(多选用逗号分隔) 'answer_resolve' => $topic['answer_resolve'], // 答案解析 'answer_coverage' => $topic['answer_coverage'], // 答案覆盖率(问答题) 'match_type' => $topic['match_type'], // 是否匹配关键字(0:否 1:是) 'answer_keyword' => unserialize($topic['answer_keyword']) // 答案关键字(序列化:关键字,百分比) ]; // 组装分表数据 $detail_extend_data[] = [ 'ep_id' => $ep_id, // 试卷id 'et_id' => $topic['et_id'], // 题目id 'et_option' => $topic['options'], // 题目选项序列化 'et_detail' => serialize($et_detail) // 题目详情序列化 ]; } } } if (!empty($detail_extend_data)) { // 待插入数组去重 $detail_extend_data = $this->remove_duplicate($detail_extend_data); // 同步往分表新增数据 $this->_d_answer_detail_extend->insert_all($detail_extend_data); } // 提交事务 $this->commit(); } catch (\Think\Exception $e) { \Think\Log::record($e); // 事务回滚 $this->_set_error($e->getMessage(), $e->getCode()); $this->rollback(); E('_ERR_DATA_SAVE_FAIL'); } catch (\Exception $e) { \Think\Log::record($e); $this->_set_error($e->getMessage(), $e->getCode()); // 事务回滚 $this->rollback(); E('_ERR_DATA_SAVE_FAIL'); } // 附件处理开始 $attach_serv = new AttachOperation(); if (!empty($data['cover_id'])) { // 将图片设置为已使用 $attach_serv->insert_attach( APP_DIR, 'paper', $ep_id, ['attach_ids' => [$data['cover_id']]] ); } // 1.推送消息 2.设置定时任务 if (self::PAPER_PUBLISH == $data['exam_status']) { $ep_id = $param['ep_id']; // 是否推荐到首页feed流 if ($data['is_recommend']) { // 判断feed流权限发送 if (self::AUTH_ALL == $data['is_all']) { $feed_right = []; } else { $feed_right = $this->format_feed_data($right); } $url = rpcUrl('/Public/Rpc/Recommender/ArticleUpdate'); $data_send = [ 'app' => APP_DIR, 'dataCategoryId' => '', 'dataId' => $ep_id, 'title' => $paper['ep_name'], 'summary' => $data['intro'], 'dataType' => $paper['paper_type'] == self::SIMULATION_PAPER_TYPE ? '模拟' : '', 'endTime' => $param['end_time'], 'attachId' => $data['cover_id'], 'pic' => $this->format_cover($data['cover_id'], false), 'url' => 'Exam/Frontend/Index/Msg?ep_id=' . $ep_id, 'right' => $feed_right ]; \Com\Rpc::phprpc($url)->invoke('Index', $data_send); } else { // 删除首页feed流 $url = rpcUrl('/Public/Rpc/Recommender/ArticleDelete'); $data_send = [ 'app' => APP_DIR, 'dataCategoryId' => '', 'dataId' => $ep_id ]; \Com\Rpc::phprpc($url)->invoke('Index', $data_send); } // 如果发送消息通知 if ($data['is_pushmsg']) { $params['name'] = $paper['ep_name']; $params['description'] = $data['intro']; $params['img_id'] = $data['cover_id']; $params['is_cover_open'] = $data['is_cover_open']; $params['id'] = $ep_id; $params['begin_time'] = $data['begin_time']; $params['end_time'] = $data['end_time']; if ($paper['exam_status'] == self::PAPER_PUBLISH) { // 如果之前就是已发布的状态,此处取差集 $uids = array_diff($uids_now, $uids_old); } else { // 从草稿发布 $uids = $uids_now; } // 如果存在需要发送消息的人员 if (!empty($uids)) { sort($uids); // 用户UIDS集合赋值 $params['uids'] = $uids; // 发送及时消息 $this->new_send_msg($params); } } // 定时任务 $this->cron_add($ep_id); } return true; } /** * 编辑试卷基本信息(任务类,线下培训) * * @author:daijun * * @param array $param 传入参数 * @param array $paper 试卷信息 * * @return bool */ public function base_save_new($param = [], $paper = []) { $used_ids = []; if (!empty($used_ids)) { E('_ERR_EXAM_USED_EDIT'); } // 验证属性 if (!$data = $this->check_base_save_new($param)) { E('_EMPTY_SAVE_DATA'); } // 如果是发布动作 if ($data['exam_status'] == self::PAPER_PUBLISH) { // 试卷题目查询条件 $conditions_topic['ep_id'] = $paper['ep_id']; // 查询组装总题目数量 if ($paper['ep_type'] != self::TOPIC_RANDOM) { // 如果不是随机出题,此处查询该试卷的试题总数 $data['topic_count'] = $this->_d_snapshot->count_by_conds($conditions_topic); // 获取试卷题目列表 $topic_list = $this->_d_snapshot->list_by_conds($conditions_topic); } else { // 如果是随机出题 $rule = unserialize($paper['rule']); $data['topic_count'] = intval($rule['single_count']) + intval($rule['multiple_count']) + intval($rule['judgment_count']) + intval($rule['question_count']) + intval($rule['voice_count']); // 获取试卷题目列表 $topic_list = $this->_d_random->list_by_conds($conditions_topic); } // 发布时间 $data['publish_time'] = MILLI_TIME; } // 初始化阅卷人权限 $right_marking = []; // 重组阅卷人列表 foreach ($param['marking_list'] as $v) { // 如果阅卷人存在 if ($v['memID']) { $right_marking[] = [ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_MARKING, 'uid' => $v['memID'], 'cd_id' => '', 'tag_id' => '', 'job_id' => '', 'role_id' => '' ]; } } // 查询分类详情 $cate_data = $this->_d_cate->get($paper['ec_id']); // 将分类的状态赋值给试卷状态 $data['cate_status'] = $cate_data['ec_status']; // 阅卷类型 $data['marking_type'] = $param['marking_type']; // 获取最后更新时间 $data['last_time'] = MILLI_TIME; $data['begin_time'] = 0; $data['end_time'] = 0; $data['integral_action_type'] = 2; // 不启用积分策略 // 获取答卷扩展信息 $extend_list = $this->_d_answer_detail_extend->list_by_conds(['ep_id' => $paper['ep_id']]); $extend_list = array_combine_by_key($extend_list, 'et_id'); // 是否启用封面图片 $data['is_cover_open'] = $param['is_cover_open']; // 如果上传图片 if (!empty($param['cover_id'])) { $data['cover_id'] = $param['cover_id']; } // 阅卷人发送消息 // $this->marking_send($param); try { // 开始事务 $this->start_trans(); // 更新试卷表 $this->_d->update($param['ep_id'], $data); // 删除之前的阅卷人权限 $this->_d_right->delete_by_conds(['epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_MARKING]); // 如果有阅卷人 if (!empty($right_marking)) { // 新增阅卷 $this->_d_right->insert_all($right_marking); } // 分表oa_exam_answer_detail_extend待增数据初始化 $detail_extend_data = []; if (!empty($topic_list)) { foreach ($topic_list as $topic) { $extend_info = $extend_list[$topic['et_id']]; if (empty($extend_info)) { // 组装题目详情数据 $et_detail = [ 'et_type' => $topic['et_type'], // 题目类型(1:单选题 2:判断题 3:问答题 4:多选题,5:语音题) 'title' => $topic['title'], // 题目名称 'title_pic' => $topic['title_pic'], // 题目图片(逗号分割 'answer' => $topic['answer'], // 正确答案(多选用逗号分隔) 'answer_resolve' => $topic['answer_resolve'], // 答案解析 'answer_coverage' => $topic['answer_coverage'], // 答案覆盖率(问答题) 'match_type' => $topic['match_type'], // 是否匹配关键字(0:否 1:是) 'answer_keyword' => unserialize($topic['answer_keyword']) // 答案关键字(序列化:关键字,百分比) ]; // 组装分表数据 $detail_extend_data[] = [ 'ep_id' => $paper['ep_id'], // 试卷id 'et_id' => $topic['et_id'], // 题目id 'et_option' => $topic['options'], // 题目选项序列化 'et_detail' => serialize($et_detail) // 题目详情序列化 ]; } } } if (!empty($detail_extend_data)) { // 待插入数组去重 $detail_extend_data = $this->remove_duplicate($detail_extend_data); // 同步往分表新增数据 $this->_d_answer_detail_extend->insert_all($detail_extend_data); } // 提交事务 $this->commit(); } catch (\Think\Exception $e) { \Think\Log::record($e); // 事务回滚 $this->_set_error($e->getMessage(), $e->getCode()); $this->rollback(); E('_ERR_DATA_SAVE_FAIL'); } catch (\Exception $e) { \Think\Log::record($e); $this->_set_error($e->getMessage(), $e->getCode()); // 事务回滚 $this->rollback(); E('_ERR_DATA_SAVE_FAIL'); } // 1.定时任务 if (self::PAPER_PUBLISH == $data['exam_status']) { $ep_id = $param['ep_id']; // 定时任务 $this->cron_add_new($ep_id); } // 附件处理开始 $attach_aerv = new AttachOperation(); if (!empty($data['cover_id'])) { // 将图片设置为已使用 $attach_aerv->make_active([$data['cover_id']]); } return true; } /** * 添加试卷基本信息参数验证 * * @author:daijun * * @param array $param * * @return array|bool */ public function check_base_save_new($param = []) { $data = []; // 验证试卷ID if (empty($param['ep_id'])) { E('_EMPTY_EP_ID'); } // 验证考试时长 if (empty($param['paper_time'])) { E('_EMPTY_PAPER_TIME'); } // 验证及格分数 if (empty($param['pass_score'])) { E('_EMPTY_PASS_SCORE'); } // 验证考试说明 if (empty($param['intro'])) { E('_EMPTY_INTRO'); } // 验证试卷状态 if (empty($param['exam_status'])) { E('_EMPTY_EXAM_STATUS'); } // 如果不在指定类型中 if (!in_array($param['marking_type'], [self::MANUAL_MARKING_TYPE, self::AUTO_MARKING_TYPE])) { E('_ERR_MARKING_TYPE'); } // 赋值 if (!empty($param['cover_id'])) { $data['cover_id'] = $param['cover_id']; } $data['is_all'] = self::AUTH_ALL; $data['paper_time'] = $param['paper_time']; // 显示答案 $data['answer_resolve'] = 1; $data['pass_score'] = intval($param['pass_score']); $data['intro'] = $param['intro']; // 获取是否有权限选择阅卷类型 list($show_marking, $marking_list) = $this->get_marking($param['ep_id']); // 如果没有权限选择阅卷类型 if (!$show_marking) { // 如果选择手动阅卷 if (self::MANUAL_MARKING_TYPE == $param['marking_type']) { E('_ERR_NOT_MARKING_TYPE'); } } // 如果是手动阅卷 if (self::MANUAL_MARKING_TYPE == $param['marking_type']) { // 如果阅读人员列表不是数组 if (!is_array($param['marking_list'])) { E('_ERR_MARKING_LIST'); } // 阅卷人IDS $mem_uids = array_unique(array_column($param['marking_list'], 'memID')); // 如果阅卷人不存在 if (empty($mem_uids)) { E('_ERR_MARKING_UID'); } } $data['exam_status'] = intval($param['exam_status']); return $data; } /** * 发布试卷定时任务添加(添加任务,线下培训) * * @param array * ep_id => 试卷id * * @return bool */ public function cron_add_new($ep_id) { // 实例化定时任务类 $cron_serv = new Cron(Service::instance()); // 获取试卷基本信息 $data = $this->_d->get($ep_id); // 需要定时通知的试卷id $res_params = ['ep_id' => $ep_id]; // json参数 $json_params = json_encode($res_params); // 初始化定时任务入库id $corn_create_exam = $data['corn_create_exam'] ? $data['corn_create_exam'] : ''; // 如果抽题我随机抽题 if ($data['ep_type'] == self::TOPIC_RANDOM && empty($data['corn_create_exam'])) { // 生成考卷定时任务 $create_exam = [ 'crRemark' => 'corn_create_exam', 'crType' => 2, 'crParams' => $json_params, 'crMethod' => 'POST', 'crReqUrl' => oaUrl('Frontend/Callback/CreateExam'), // 回调地址 'crTimes' => 0, 'crCron' => '0 0/5 * * * ?', 'crMonitorExecution' => 0, 'crDescription' => '自动创建考卷' ]; $res_create_exam = $cron_serv->add($create_exam); // crId $corn_create_exam = $res_create_exam['crId']; } $this->_d->update($ep_id, ['corn_create_exam' => $corn_create_exam]); return true; } /** * 添加试卷基本信息参数验证 * * @author:daijun * * @param array $param * * @return array|bool */ public function check_base_save($param = []) { $data = []; // 验证试卷ID if (empty($param['ep_id'])) { E('_EMPTY_EP_ID'); } // 验证权限设置 if (!in_array($param['is_all'], [self::AUTH_ALL, self::NO_AUTH_ALL])) { // 权限类型必须为0和1 E('_ERR_RIGHT_TYPE'); } // 验证开始时间不能为空 if (empty($param['begin_time'])) { E('_EMPTY_BEGIN_TIME'); } // 验证开始时间不能早于当前时间 if ($param['begin_time'] <= MILLI_TIME) { E('_ERR_BEGIN_TIME'); } // 验证结束时间不能为空 if (empty($param['end_time'])) { E('_EMPTY_END_TIME'); } // 验证结束时间不能早于开始时间 if ($param['end_time'] <= $param['begin_time']) { E('_ERR_END_TIME'); } // 验证考试时长 if (($param['begin_time'] + to_milli_time($param['paper_time'] * 60)) > $param['end_time']) { E('_ERR_PAPER_TIME'); } // 验证考试时长 if (empty($param['paper_time'])) { E('_EMPTY_PAPER_TIME'); } // 验证发送提醒 if (!isset($param['is_pushmsg'])) { E('_EMPTY_PUSH_MSG'); } // 验证设置考试通知 if (!isset($param['is_notify'])) { E('_EMPTY_IS_NOTIFY'); } // 是否开启封面图片上传验证 if (!in_array($param['is_cover_open'], [self::IS_COVER_PIC_OPEN, self::IS_COVER_PIC_CLOSE])) { E('_ERR_PIC_TYPE'); } // 如果开启了考试通知,则时间设置不能为空 if ($param['is_notify']) { if (empty($param['notify_begin'])) { E('_ERR_BEGIN_NOTIFY_TIME'); } if (empty($param['notify_end'])) { E('_ERR_END_NOTIFY_TIME'); } } // 开始前通知时间 if (!empty($param['notify_begin'])) { // 考试提醒时间验证 if (($param['begin_time'] - intval(to_milli_time($param['notify_begin']) * 60)) <= MILLI_TIME) { E('_ERR_BEGIN_NOTIFY_TIME'); } } // 结束前通知时间 if (!empty($param['notify_end'])) { // 考试提醒时间验证 if (($param['end_time'] - intval(to_milli_time($param['notify_end']) * 60)) <= MILLI_TIME) { E('_ERR_END_NOTIFY_TIME'); } } // 验证开启推荐 if (!isset($param['is_recommend'])) { E('_EMPTY_IS_RECOMMEND'); } // 验证及格分数 if (empty($param['pass_score'])) { E('_EMPTY_PASS_SCORE'); } // 验证考试说明 if (empty($param['intro'])) { E('_EMPTY_INTRO'); } // 验证试卷状态 if (empty($param['exam_status'])) { E('_EMPTY_EXAM_STATUS'); } // 如果不在指定类型中 if (!in_array($param['marking_type'], [self::MANUAL_MARKING_TYPE, self::AUTO_MARKING_TYPE])) { E('_ERR_MARKING_TYPE'); } // 验证可见性设置(交卷后) $data['is_see_after_submit'] = intval($param['is_see_after_submit']); // 验证可见性设置(结束后) $data['is_see_after_over'] = intval($param['is_see_after_over']); if (self::OPEN_MAKEUP == $param['is_open_makeup']) { // 如果开启了补考,则补考次数和补考时间不能为空 if (empty($param['makeup_num']) || empty($param['makeup_start_time']) || empty($param['makeup_end_time'])) { E('_EMPTY_MAKEUP_DATA'); } if ($param['makeup_start_time'] <= $param['begin_time']) { // 如果补考开始时间大于等于考试开始时间,则抛错 E('_ERR_MAKEUP_START_TIME'); } if ($param['makeup_start_time'] >= $param['makeup_end_time']) { // 如果补考开始时间大于等于补考结束时间,则抛错 E('_ERR_MAKEUP_END_TIME'); } if ($param['makeup_start_time'] + to_milli_time($param['paper_time'] * 60) > $param['makeup_end_time']) { // 如果开始补考时间加上考试时长大于补考结束时间,则抛错 E('_ERR_MAKEUP_END_TIME_FOR_PAPER_TIME'); } } // 是否开启补考 $data['is_open_makeup'] = empty($param['is_open_makeup']) ? self::CLOSE_MAKEUP : $param['is_open_makeup']; // 补考开始时间 $data['makeup_start_time'] = empty($param['makeup_start_time']) ? 0 : $param['makeup_start_time']; // 补考结束时间 $data['makeup_end_time'] = empty($param['makeup_end_time']) ? 0 : $param['makeup_end_time']; // 补考次数 $data['makeup_num'] = intval($param['makeup_num']); // 初始化封面ID $data['cover_id'] = ''; // 如果显示封面 if (self::IS_COVER_PIC_OPEN == $param['is_cover_open']) { $data['cover_id'] = $param['cover_id']; } $data['is_cover_open'] = $param['is_cover_open']; $data['is_all'] = $param['is_all']; $data['begin_time'] = $param['begin_time']; $data['end_time'] = $param['end_time']; $data['paper_time'] = $param['paper_time']; $data['is_pushmsg'] = intval($param['is_pushmsg']); $data['is_notify'] = intval($param['is_notify']); $data['notify_begin'] = intval($param['notify_begin']); $data['notify_end'] = intval($param['notify_end']); $data['is_recommend'] = intval($param['is_recommend']); // 显示答案 $data['answer_resolve'] = 1; $data['pass_score'] = intval($param['pass_score']); $data['intro'] = $param['intro']; $data['is_upset_topic'] = empty($param['is_upset_topic']) ? self::UPSET_TOPIC_NO : $param['is_upset_topic']; // 是否打乱题目 $data['is_upset_option'] = empty($param['is_upset_option']) ? self::UPSET_OPTION_NO : $param['is_upset_option'];// 是否打乱选项 // 获取是否有权限选择阅卷类型 list($show_marking, $marking_list) = $this->get_marking($param['ep_id']); // 如果没有权限选择阅卷类型 && 选择手动阅卷 if (!$show_marking && self::MANUAL_MARKING_TYPE == $param['marking_type']) { E('_ERR_NOT_MARKING_TYPE'); } // 如果是手动阅卷 if (self::MANUAL_MARKING_TYPE == $param['marking_type']) { // 如果阅读人员列表不是数组 if (!is_array($param['marking_list'])) { E('_ERR_MARKING_LIST'); } // 阅卷人IDS $mem_uids = array_unique(array_column($param['marking_list'], 'memID')); // 如果阅卷人不存在 if (empty($mem_uids)) { E('_ERR_MARKING_UID'); } } // 是否开启匿名阅卷 $data['is_open_anonymous_marking'] = empty($param['is_open_anonymous_marking']) ? self::CLOSE_ANONYMOUS_MARKING : $param['is_open_anonymous_marking']; $data['exam_status'] = intval($param['exam_status']); // 如果不是全公司,组装right数据 $right = []; if ($data['is_all'] == self::AUTH_NOT_ALL) { // 用户集合 if (!empty($param['right']['user_list'])) { $users = array_column($param['right']['user_list'], 'memID'); $users_list = array_combine_by_key($param['right']['user_list'], 'memID'); foreach ($users as $v) { $right[] = [ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_PAPER, 'source_type' => empty($users_list[$v]['source_type']) ? 1 : $users_list[$v]['source_type'], // 权限来源 'uid' => $v, 'cd_id' => '', 'tag_id' => '', 'job_id' => '', 'role_id' => '' ]; } } // 部门集合 if (!empty($param['right']['dp_list'])) { $dps = array_column($param['right']['dp_list'], 'dpID'); foreach ($dps as $v) { $right[] = [ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_PAPER, 'source_type' => 1, // 权限来源为选择 'uid' => '', 'cd_id' => $v, 'tag_id' => '', 'job_id' => '', 'role_id' => '' ]; } } // 岗位集合 if (!empty($param['right']['job_list'])) { $jobs = array_column($param['right']['job_list'], 'jobID'); foreach ($jobs as $v) { $right[] = [ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_PAPER, 'source_type' => 1, // 权限来源为选择 'uid' => '', 'cd_id' => '', 'tag_id' => '', 'job_id' => $v, 'role_id' => '' ]; } } // 角色集合 if (!empty($param['right']['role_list'])) { $roles = array_column($param['right']['role_list'], 'roleID'); foreach ($roles as $v) { $right[] = [ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_PAPER, 'source_type' => 1, // 权限来源为选择 'uid' => '', 'cd_id' => '', 'tag_id' => '', 'job_id' => '', 'role_id' => $v ]; } } // 标签集合 if (!empty($param['right']['tag_list'])) { $tags = array_column($param['right']['tag_list'], 'tagID'); foreach ($tags as $v) { $right[] = [ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_PAPER, 'source_type' => 1, // 权限来源为选择 'uid' => '', 'cd_id' => '', 'tag_id' => $v, 'job_id' => '', 'role_id' => '' ]; } } // 权限不能都为空 if (empty($right)) { E('_EMPTY_RIGHT'); } } $data['right'] = $right; // 如果是否开启策略不符合规范 if (!in_array($param['is_open_integral'], [self::CLOSE_INTEGRAL, self::OPEN_INTEGRAL])) { E('ERR_IS_OPEN_INTEGRAL'); } // 默认积分策略ID为空 $data['integral_strategyid'] = ''; // 默认学分策略ID为空 $data['credit_strategyid'] = ''; // 初始化积分策略类型为不使用策略 $data['integral_action_type'] = self::INTEGRAL_ACTION_TYPE_NO; // 初始化学分策略类型为不使用策略 $data['credit_action_type'] = self::CREDIT_ACTION_TYPE_FALSE; // 如果开启积分策略 if (self::OPEN_INTEGRAL == $param['is_open_integral']) { // 如果积分策略为空 if (empty($param['integral_action_type'])) { E('_EMPTY_INTEGRAL_ACTION'); } // 如果策略类型不是自定义或者默认 if (!in_array($param['integral_action_type'], [self::INTEGRAL_ACTION_TYPE_DEFAULT, self::INTEGRAL_ACTION_TYPE_MY])) { E('ERR_OPEN_INTEGRAL_ACTION_TYPE'); } $data['integral_action_type'] = $param['integral_action_type']; // 如果开启的是自定义的策略,则策略ID不能为空 if (self::INTEGRAL_ACTION_TYPE_MY == $param['integral_action_type'] && empty($param['integral_strategyid'])) { E('_EMPTY_INTEGRAL_ACTIONID'); } $data['integral_strategyid'] = strval($param['integral_strategyid']); } // 如果开启学分策略 if (self::OPEN_INTEGRAL == $param['is_open_credit']) { // 如果学分策略为空 if (empty($param['credit_action_type'])) { E('_EMPTY_CREDIT_ACTION'); } // 如果策略类型不是默认或者自定义 if (!in_array($param['credit_action_type'], [self::CREDIT_ACTION_TYPE_DEFAULT, self::CREDIT_ACTION_TYPE_CUSTOMIZE])) { E('ERR_OPEN_CREDIT_ACTION_TYPE'); } $data['credit_action_type'] = $param['credit_action_type']; // 如果开启的是自定义的策略,则策略ID不能为空 if (self::CREDIT_ACTION_TYPE_CUSTOMIZE == $param['credit_action_type'] && empty($param['credit_strategyid'])) { E('_EMPTY_CREDIT_ACTIONID'); } $data['credit_strategyid'] = strval($param['credit_strategyid']); } return $data; } /** *【后台】给阅卷人发送消息 * * @author 何岳龙 * * @param array $param POST 数组 * * @return bool */ public function marking_send($param = []) { // 初始化原始 $old_marking_user = []; // 如果是手动阅卷 if (self::MANUAL_MARKING_TYPE == $param['marking_type']) { // 获取最新阅读人UIDS集合 $new_marking_user = array_unique(array_filter(array_column($param['marking_list'], 'memID'))); // 获取原始阅卷人权限 $right_list = $this->_d_right->list_by_conds([ 'epc_id' => $param['ep_id'], 'er_type' => self::RIGHT_MARKING ]); // 如果有原始数据 if (!empty($right_list)) { // 获取原始阅卷人用户UIDS集合 $old_marking_user = array_unique(array_filter(array_column($right_list, 'uid'))); } // 取查集获取发消息用户 $msg_uids = array_diff($new_marking_user, $old_marking_user); // 获取试卷信息 $paper = $this->_d->get($param['ep_id']); // 如果有待发消息用户 if (!empty($msg_uids)) { $msg = [ 'uids' => $msg_uids, 'name' => $paper['ep_name'], 'img_id' => $param['cover_id'], 'is_cover_open' => $param['is_cover_open'], 'begin_time' => $param['begin_time'], 'end_time' => $param['end_time'], 'id' => $param['ep_id'] ]; // 给阅卷人发送消息 $this->send_msg($msg, self::MARKING_MSG); } } return true; } /** * 格式化feed流权限数据 * * @author 蔡建华 * * @param array $right 权限数组 * * @return array */ protected function format_feed_data($right) { $feed_right = []; // 格式化权限字段 if (!empty($right)) { $users = array_filter(array_column($right, 'uid')); $departments = array_filter(array_column($right, 'cd_id')); $jobs = array_filter(array_column($right, 'job_id')); $roles = array_filter(array_column($right, 'role_id')); $tags = array_filter(array_column($right, 'tag_id')); sort($users); sort($departments); sort($jobs); sort($roles); sort($tags); $feed_right['users'] = !empty($users) ? $users : ''; $feed_right['departments'] = !empty($departments) ? $departments : ''; $feed_right['jobs'] = !empty($jobs) ? $jobs : ''; $feed_right['roles'] = !empty($roles) ? $roles : ''; $feed_right['tags'] = !empty($tags) ? $tags : ''; } return $feed_right; } /** * 考试通知发送 * * @param array $params POST 参数 * * @return bool */ public function new_send_msg($params = []) { // 分割成多个元素分组 $uid_groups = array_chunk($params['uids'], Msg::USER_MAX_COUNT); // 分批发送 foreach ($uid_groups as $uid_group) { $params['uids'] = $uid_group; $this->send_msg($params, self::ANSWER_COMING_MSG); } return true; } /** * 发布试卷定时任务添加 * * @param array * ep_id => 试卷id * * @return bool */ public function cron_add($ep_id) { // 实例化定时任务类 $cron_serv = new Cron(Service::instance()); // 获取试卷基本信息 $data = $this->_d->get($ep_id); // 存在定时发布 if (!empty($data['begin_corn'])) { $cron_serv->delete($data['begin_corn']); } // 存在定时提醒 if (!empty($data['end_cron'])) { $cron_serv->delete($data['end_cron']); } // 需要定时通知的试卷id $res_params = ['ep_id' => $ep_id]; // json参数 $json_params = json_encode($res_params); // 初始化定时任务入库id $begin_corn = ''; $end_cron = ''; $corn_create_exam = $data['corn_create_exam'] ? $data['corn_create_exam'] : ''; $cron_statistics = $data['cron_statistics'] ? $data['cron_statistics'] : ''; $cron_rank_id = $data['cron_rank_id'] ? $data['cron_rank_id'] : ''; // 如果开始前提醒时间不为空 if (!empty($data['notify_begin'])) { // 获取截止提醒时间 $remind_time = $data['begin_time'] - to_milli_time($data['notify_begin'] * 60); // 考试开始前提醒 $conds_remind = [ 'crRemark' => 'exam_notify_begin', 'crType' => 2, 'crParams' => $json_params, 'crMethod' => 'POST', 'crReqUrl' => oaUrl('Frontend/Callback/RemindBegin'), // 回调地址 'crTimes' => 1, 'crCron' => rgmdate((String)$remind_time, 's i G j n ? Y'), 'crMonitorExecution' => 0, 'crDescription' => '考试开始提醒' ]; // 创建定时任务 $res_remind = $cron_serv->add($conds_remind); // crId $begin_corn = $res_remind['crId']; } // 如果结束前提醒时间不为空 if (!empty($data['notify_end'])) { // 获取截止提醒时间 $remind_time = $data['end_time'] - to_milli_time($data['notify_end'] * 60); //考试结束前提醒 $conds_remind = [ 'crRemark' => 'exam_notify_end', 'crType' => 2, 'crParams' => $json_params, 'crMethod' => 'POST', 'crReqUrl' => oaUrl('Frontend/Callback/RemindEnd'), // 回调地址 'crTimes' => 1, 'crCron' => rgmdate((String)$remind_time, 's i G j n ? Y'), 'crMonitorExecution' => 0, 'crDescription' => '考试结束提醒' ]; // 创建定时任务 $res_remind = $cron_serv->add($conds_remind); // crId $end_cron = $res_remind['crId']; } // 如果抽题我随机抽题 if (self::TOPIC_RANDOM == $data['ep_type'] && empty($data['corn_create_exam'])) { // 生成考卷定时任务 $create_exam = [ 'crRemark' => 'corn_create_exam', 'crType' => 2, 'crParams' => $json_params, 'crMethod' => 'POST', 'crReqUrl' => oaUrl('Frontend/Callback/CreateExam'), // 回调地址 'crTimes' => 0, 'crCron' => '0 0/5 * * * ?', 'crMonitorExecution' => 0, 'crDescription' => '自动创建考卷' ]; $res_create_exam = $cron_serv->add($create_exam); // crId $corn_create_exam = $res_create_exam['crId']; } $this->_d->update($ep_id, [ 'begin_corn' => $begin_corn, 'end_cron' => $end_cron, 'corn_create_exam' => $corn_create_exam, ]); return true; } /** * 格式化获取试卷规则数据 * * @author daijun * * @param array $data 试卷信息 * @param array $bank_topic_total 各题库含有的各题型的总数 * @param array $topic_count_list 普通抽题数列表 * * @return array */ public function format_rule_data($data = [], $bank_topic_total = [], $topic_count_list = []) { $result = [ 'ep_name' => $data['ep_name'], 'ec_id' => intval($data['ec_id']), 'exam_type' => intval($data['exam_type']), 'paper_type' => intval($data['paper_type']), 'ep_type' => intval($data['ep_type']), 'search_type' => intval($data['search_type']), 'advanced_choose' => intval($data['advanced_choose']) ]; // 获取题库ID集合(一维数组) $eb_ids = explode(',', $data['bank_data']); // 查询题库列表 $bank_data = $this->_d_bank->list_by_conds(['eb_id' => $eb_ids], null, [], 'eb_id,eb_name,single_count,multiple_count,judgment_count,question_count,voice_count'); // 格式化组装数据 $bank_list = []; foreach ($bank_data as $v) { $bank_list[] = [ 'eb_id' => intval($v['eb_id']), 'eb_name' => $v['eb_name'], 'single_count' => intval($v['single_count']), 'multiple_count' => intval($v['multiple_count']), 'judgment_count' => intval($v['judgment_count']), 'question_count' => intval($v['question_count']), 'voice_count' => intval($v['voice_count']) ]; } $result['topic_count_list'] = $topic_count_list; $result['bank_list'] = $bank_list; $bank_topic = array_combine_by_key($bank_topic_total, 'eb_id'); // 格式化题库出题数量数据 if (!empty($data['bank_topic_data'])) { $bank_topic_data = unserialize($data['bank_topic_data']); $b_topic_data = []; foreach ($bank_topic_data as $k => $v) { $b_topic_data[] = [ 'eb_id' => intval($v['eb_id']), 'eb_name' => $v['eb_name'], 'single_count' => intval($v['single_count']), 'multiple_count' => intval($v['multiple_count']), 'judgment_count' => intval($v['judgment_count']), 'question_count' => intval($v['question_count']), 'voice_count' => intval($v['voice_count']), 'single_count_max' => $bank_topic[$v['eb_id']]['single_count'], 'multiple_count_max' => $bank_topic[$v['eb_id']]['multiple_count'], 'judgment_count_max' => $bank_topic[$v['eb_id']]['judgment_count'], 'question_count_max' => $bank_topic[$v['eb_id']]['question_count'], 'voice_count_max' => $bank_topic[$v['eb_id']]['voice_count'] ]; } $result['bank_topic_data'] = $b_topic_data; } else { $result['bank_topic_data'] = []; } // 格式化出题规则数据 if (!empty($data['rule'])) { $rule = unserialize($data['rule']); foreach ($rule as $k => $v) { $rule[$k] = intval($v); } $result['rule'] = $rule; } else { $result['rule'] = []; } // 格式化标签数据 if (!empty($data['tag_data'])) { $tag_data = unserialize($data['tag_data']); // 标签ID集合 $tag_ids = array_column($tag_data, 'etag_id'); // 查询标签列表 $tag_list = $this->_d_tag->list_by_conds(['etag_id' => $tag_ids]); $tag_list = array_combine_by_key($tag_list, 'etag_id'); // 属性ID集合 $attr_ids = []; foreach ($tag_data as $v) { $attr_ids_arr = []; if (is_array($v['attr_data'])) { $attr_ids_arr = array_column($v['attr_data'], 'attr_id'); } if (!empty($attr_ids_arr) && is_array($attr_ids_arr)) { $attr_ids = array_merge($attr_ids, $attr_ids_arr); } } // 查询属性列表 $attr_list = $this->_d_attr->list_by_conds(['attr_id' => $attr_ids]); $attr_list = array_combine_by_key($attr_list, 'attr_id'); // 循环格式化组装标签属性 $tag_format_data = []; foreach ($tag_data as $k => $v) { $tag_format_data[$k]['etag_id'] = intval($v['etag_id']); $tag_format_data[$k]['etag_name'] = $tag_list[$v['etag_id']]['tag_name']; // 循环格式化属性 $attr = []; foreach ($v['attr_data'] as $_k => $_v) { $attr[$_k]['attr_id'] = intval($_v['attr_id']); $attr[$_k]['attr_name'] = $attr_list[$_v['attr_id']]['attr_name']; } $tag_format_data[$k]['attr_data'] = $attr; } $result['tag_data'] = $tag_format_data; } else { $result['tag_data'] = []; } return $result; } /** * 格式化试卷信息 * * @author: 蔡建华 * * @param array $data 试卷信息 * @param string $uid 用户uid * * @return array */ public function format_paper_detail($data = [], $uid = '') { // 组装格式化数据 $detail = [ 'paper_type' => intval($data['paper_type']), 'exam_type' => intval($data['exam_type']), 'ep_name' => $data['ep_name'], 'total_score' => $data['total_score'], 'pass_score' => $data['pass_score'], 'topic_count' => intval($data['topic_count']), 'paper_time' => $data['paper_time'], 'begin_time' => $data['begin_time'], 'end_time' => $data['end_time'], 'join_count' => intval($data['join_count']), 'intro' => $data['intro'], 'marking_type' => intval($data['marking_type']) ]; if (self::IS_COVER_PIC_OPEN == $data['is_cover_open']) { // 开启封面图片 $detail['cover_url'] = $this->format_cover($data['cover_id']); } else { $detail['cover_url'] = ''; } // 默认未参加 $detail['is_join_test'] = 0; $makeup_tag = 0; // 补考状态(0:不可补考,1:可补考) // 常规考试 if (self::NOMAL_TYPE == $data['exam_type']) { $detail['ep_status'] = $this->paper_status($data['exam_status'], $data['begin_time'], $data['end_time']); $now_time = MILLI_TIME; if (self::EVALUATION_PAPER_TYPE == $data['paper_type'] && self::OPEN_MAKEUP == $data['is_open_makeup'] && in_array($detail['ep_status'], [self::STATUS_END, self::STATUS_STOP, self::STATUS_ING]) && $now_time < $data['makeup_end_time'] && $now_time > $data['makeup_start_time']) { // 如果是测评试卷 && 开启补考 && 是进行中,已结束或者已终止的 && 在补考时间段内 // 查询该用户参加补考的次数 $num = $this->_d_answer->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0, 'is_makeup' => self::IS_MAKEUP]); if ($data['makeup_num'] > $num && in_array($detail['ep_status'], [self::STATUS_END, self::STATUS_STOP])) { // 如果补考次数小于设定的次数,则可补考 $makeup_tag = 1; } } // 查询该用户正常参与的次数 $num = $this->_d_answer->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0, 'is_makeup' => self::IS_MAKEUP_FALSE]); if ($num == 0 && $detail['ep_status'] == self::STATUS_ING) { // 如果是进行中,并且没有交卷记录,则不可补考 $makeup_tag = 0; } if ($num) { // 已经参加过测评 $detail['is_join_test'] = 1; } // 查询该用户补考参与的次数 $num = $this->_d_answer->count_by_conds(['ep_id' => $data['ep_id'], 'uid' => $uid, 'my_time>?' => 0, 'is_makeup' => self::IS_MAKEUP]); // 计算剩余补考次数 $makeup_num = $data['makeup_num'] - $num; if ($makeup_num < 0) { $makeup_num = 0; } } else { // 任务类或线下培训:进行中 $detail['ep_status'] = self::STATUS_ING; } $detail['makeup_tag'] = $makeup_tag; $detail['is_open_makeup'] = $data['is_open_makeup']; // 是否开启补考功能:(1:开启 2:不开启) $detail['makeup_num'] = $makeup_num; // 剩余补考次数 $detail['makeup_start_time'] = $data['makeup_start_time']; // 补考开始时间 $detail['makeup_end_time'] = $data['makeup_end_time']; // 补考结束时间 return $detail; } /** * 重新抽题 * * @author:daijun * * @param int $ep_id * * @return bool */ public function extract_topic($ep_id = 0) { // 如果试卷出题规则不是随机,则按配置抽题 if (!$topic_list = $this->get_temp_list_by_epid($ep_id)) { E('_ERR_CHOICE_PAPER'); } try { // 开始事务 $this->start_trans(); // 删除快照表(即已选择的试题) $this->_d_snapshot->delete_by_conds(['ep_id' => $ep_id]); // 删除试卷备选题目信息 $this->_d_temp->delete_by_conds(['ep_id' => $ep_id]); $temp_list = []; foreach ($topic_list as $k => $v) { $temp_list[] = [ 'ep_id' => $ep_id, 'et_id' => $v['et_id'], 'score' => $v['score'], 'order_num' => $k, ]; } if (!empty($temp_list)) { // 存入备选题目列表 $this->_d_temp->insert_all($temp_list); } // 提交事务 $this->commit(); } catch (\Think\Exception $e) { \Think\Log::record($e); // 事务回滚 $this->_set_error($e->getMessage(), $e->getCode()); $this->rollback(); return false; } catch (\Exception $e) { \Think\Log::record($e); $this->_set_error($e->getMessage(), $e->getCode()); // 事务回滚 $this->rollback(); return false; } return true; } /** * 查询考试统计列表 * * @author: 英才 * * @param array $params 查询条件数组 * @param null $page_option 分页 * @param array $order_option 排序数组 * @param String $fields 查询字段 * * @return array|bool */ public function list_search_where($params, $page_option = null, $order_option = [], $fields = '*') { try { return $this->_d->list_search_where($params, $page_option, $order_option, $fields); } catch (\Exception $e) { E($e->getCode() . ':' . $e->getMessage()); return false; } } /** * 统计考试统计列表总数 * * @author: 英才 * * @param array $params 查询条件数组 * * @return array|bool */ public function count_search_where($params) { try { return $this->_d->count_search_where($params); } catch (\Exception $e) { E($e->getCode() . ":" . $e->getMessage()); return false; } } /** * 推送停止考试消息与交卷 * * @author daijun * * @param array $data 试卷信息 */ public function stop_paper($data = []) { $right_serv = new RightService(); // 去权限表查询权限信息 $right = $this->_d_right->list_by_conds(['er_type' => 0, 'epc_id' => $data['ep_id']]); // 格式化权限信息 $right_res = $right_serv->format_db_data($right); // 获取数据更新后的权限用户列表 $right_res['is_all'] = $data['is_all']; $params['name'] = $data['ep_name']; $params['description'] = $data['intro']; $params['img_id'] = $data['cover_id']; $params['is_cover_open'] = $data['is_cover_open']; $params['msg'] = $data['reason']; $params['id'] = $data['ep_id']; $params['uids'] = $right_serv->list_uids_by_right($right_res); // 如果填写了发送的文字则推送考试停止消息 if (!empty($data['reason'])) { $right_serv->send_msg($params, self::ANSWER_AHEAD_END); } // 临时避免定时任务查询或删除失败抛出错误 try { // 删除定时任务 $this->cron_delete($data); } catch (\Think\Exception $e) { \Think\Log::record($e); } } /** * 删除试卷定时任务 * * @param $data array 试卷信息 * * @return bool */ public function cron_delete($data = []) { // 实例化定时任务类 $cron_serv = new Cron(Service::instance()); // 临时避免定时任务查询或删除失败抛出错误 try { // 存在定时发布 if (!empty($data['begin_corn'])) { $cron_serv->delete($data['begin_corn']); } // 存在定时提醒 if (!empty($data['end_cron'])) { $cron_serv->delete($data['end_cron']); } // 存在试卷定时任务 if (!empty($data['corn_exam'])) { $cron_serv->delete($data['corn_exam']); } // 存在自动创建考卷的cornid if (!empty($data['corn_create_exam'])) { $cron_serv->delete($data['corn_create_exam']); } // 存在考试统计cornid if (!empty($data['cron_statistics'])) { $cron_serv->delete($data['cron_statistics']); } // 存在考试分批推送消息cornid if (!empty($data['cron_send_msg'])) { $cron_serv->delete($data['cron_send_msg']); } // 存在考试排名列表接口定时更新cornid if (!empty($data['cron_rank_id'])) { $cron_serv->delete($data['cron_rank_id']); } } catch (\Think\Exception $e) { \Think\Log::record($e); return true; } return true; } /** * 根据条件更新试卷且不修改最后更新时间 * * @author 鲜彤 * * @param $conds array 条件 * @param $data array 数据 * * @return mixed */ public function update_by_paper($conds = [], $data = []) { return $this->_d->update_by_paper($conds, $data); } /** * 随机抽取按规则抽题 * * @author caijainhua * * @param array $bank_ids 题库ID集合 * @param array $paper 试卷ID * @param array $bank_topic_data 题库题目数量设置 * @param array $rule_data 题目得分规则设置 * * @return array */ public function random_rule_topic($bank_ids = [], $paper = [], $bank_topic_data = [], $rule_data = []) { $ep_id = $paper['ep_id']; $topic_all_list = []; $topic_list_ids = $this->_d_random->list_by_conds(['ep_id' => $ep_id]); // 开启高级抽取规则值 if (self::ADVANCED_CHOOSE_OPEN == $paper['advanced_choose']) { // 题库题目属性数组 $bank_topic_attr = []; foreach ($topic_list_ids as $v) { $bank_topic_attr[$v['eb_id']][] = $v; } // 循环题库 foreach ($bank_ids as $k => $v) { // 检查是否需要在该题库抽题 $is_bank_topic = 1; // 默认需要从该题库需要抽题 // 此处查询该题库是否设置了题目 foreach ($bank_topic_data as $val) { if ($val['eb_id'] == $v) { // 计算当前题库设置测抽题数量 $t_count = $val['single_count'] + $val['multiple_count'] + $val['judgment_count'] + $val['question_count'] + $val['voice_count']; if (0 == $t_count) { $is_bank_topic = 0; } break; } } // 如果规则设置不从该题库抽题,则continue if (!$is_bank_topic) { continue; } // 查询该题库下的符合条件的题目列表 $topic_list = $bank_topic_attr[$v]; if (empty($topic_list)) { E('_ERR_DELETE_RULE_TOPIC'); } // 将题目列表按照题目类型分组返回 if (!$topic_data = $this->format_by_et_type($topic_list, $bank_topic_data, $rule_data, $v, false)) { return false; } if (!empty($topic_data) && is_array($topic_data)) { // 合并组装数组 $topic_all_list = array_merge($topic_all_list, $topic_data); } } if (empty($topic_all_list)) { E('_ERR_CHOICE_PAPER'); } } else { // 没有开启高级随机抽题,按照规则总数抽题 $topic_all_list = $this->format_simple_by_et_type( $topic_list_ids, $rule_data, false ); } // 对抽取的题目进行打乱 $topics = array_combine_by_key($topic_all_list, 'et_id'); $tp_ids = array_column($topic_all_list, 'et_id'); // 打乱ID shuffle($tp_ids); // 循环组装试题信息 $result_list = []; foreach ($tp_ids as $k => $v) { $data = $topics[$v]; $data['order_num'] = $k + 1; $result_list[] = $data; } return $result_list; } /** *【后台】将试卷信息写入缓存中 * @author 何岳龙 * * @param array $param 试卷详情信息 * @param int $exam_status 试卷发布状态 * * @return bool */ public function exam_set_cache($param = [], $exam_status = 0) { // 组装前台跳转数据 $cache_data = [ 'ep_id' => $param['ep_id'], 'exam_status' => $exam_status, 'begin_time' => $param['begin_time'], 'end_time' => $param['end_time'], 'uids' => $param['uids'], // 应推送新消息人员ID 'cron_id' => $param['cron_id'], // 应推送新消息任务ID 'join_uids' => $param['join_uids'], // 有权参与人员ID集合 ]; // 写入缓存 $cache = &Cache::instance(); $cache->set('Common.Exam_' . $param['ep_id'], $cache_data, 7200); return true; } /** * 闯关交卷 * * @param array $params 闯关参数 * @param int $uid 用户ID * * @return mixed */ public function submit_break_papers($params = [], $uid = 0) { // 判断课程ID if (!intval($params['article_id'])) { E('_EMPTY_BREAK_CID'); } // 判断用户UID if (!$uid) { E('_EMPTY_UID'); } // 闯关答题列表 $qa_list = $params['qa_list']; if (empty($qa_list)) { E('_EMPTY_ANSWER_LIST'); } $result_list = []; // 从题库获取闯关试题列表 $et_ids = array_column($qa_list, 'et_id'); $topic_list = $this->_d_topic->list_by_pks($et_ids); $error_total = 0; $right_total = 0; // 闯关实例列表为空时,默认为闯关通过(为了兼容闯关过程中,管理员在后台题库中把闯关题目给删除掉) if (empty($topic_list)) { $error_total = 0; $right_total = count($et_ids); } else { // 格式化成以题目ID为键的二维数组 $topic_list = array_combine_by_key($topic_list, 'et_id'); // 循环处理答题列表 foreach ($qa_list as $key => $val) { $topic_detail = $topic_list[$val['et_id']]; // 获取答案字符串 $answer = $this->get_break_answer($val, $topic_detail); $answer_detail = $topic_detail; $answer_detail['my_answer'] = $answer; // 判断是否正确 $score_res = $this->get_topic_score($answer_detail); if (self::MY_PASS == $score_res['is_pass']) { // 答对题目数统计 $right_total++; } else { // 答错题目数统计 $error_total++; } $result_list[] = [ 'et_index' => $key, 'et_id' => $val['et_id'], 'is_pass' => $score_res['is_pass'] ]; } } $is_pass = $error_total > 0 ? self::MY_BREAK_NO_PASS : self::MY_BREAK_PASS; unset($params['qa_list']); $data_params = @urldecode($params['params']); // 闯关参数拼接 $data = []; if ($data_params) { parse_str($data_params, $data); } // prc参数拼装 $rpc_params = [ 'uid' => $uid, 'article_id' => $params['article_id'], 'is_pass' => $error_total > 0 ? 1 : 2, // 测评是否通过(1=未通过;2=已通过) 'no_total' => intval($error_total), 'yes_total' => intval($right_total), 'article_chapter_id' => intval($params['article_chapter_id']), 'source_id' => intval($params['source_id']), 'customtask_id' => intval($params['customtask_id']), 'plan_id' => intval($params['plan_id']), 'ed_id' => intval($params['ed_id']), 'map_id' => (int)$params['map_id'], 'path_id' => (int)$params['path_id'], ]; $rpc_params = array_merge($rpc_params, $data); $rpc_url = rpcUrl('/Course/Rpc/Exam/Update'); $rpc_care = \Com\Rpc::phprpc($rpc_url)->invoke('Index', $rpc_params); if (!empty($rpc_care)) { foreach ($rpc_care as $key => $value) { $rpc_care[$key]['content'] = '积分'; } } // 闯关通过后,调用常规任务埋点 if ($is_pass == self::MY_BREAK_PASS) { $taskCenterServ = &TaskCenter::instance(); $taskCenterServ->triggerCustomtask([ 'uid' => $uid, 'customtask_id' => $params['customtask_id'], 'app_data_id' => $params['article_id'], 'action_key' => 'course_study', 'description' => '完成课程', 'app' => 'course', ]); } return [ 'is_pass' => $is_pass, 'care' => $rpc_care, 'answer_list' => $result_list ]; } /** * 获取答案字符串 * * @param array $val 答案数据 * @param array $et_detail 题目详情 * * @return array|string 答案字符串 */ public function get_break_answer($val = [], $et_detail = []) { $str_answer = ''; // 多选题 if (self::TOPIC_TYPE_MULTIPLE == $et_detail['et_type']) { if (isset($val['my_answer']) && !empty($val['my_answer'])) { // 分割答案选项 $my_answer = explode(',', $val['my_answer']); // 答案选项重新排序 sort($my_answer); // 答案选项拼接为字符串 $str_answer = implode(',', $my_answer); } } elseif (self::TOPIC_TYPE_QUESTION == $et_detail['et_type'] || self::TOPIC_TYPE_SINGLE == $et_detail['et_type'] || self::TOPIC_TYPE_JUDGMENT == $et_detail['et_type'] ) { // 问答、单选、判断题 $str_answer = strval($val['my_answer']); } return $str_answer; } /** * 题目分数正确 * * @author: 蔡建华 * * @param array $data 题目信息 * * @return array 返回 分数和答案是否正确 */ protected function get_topic_score($data = []) { $my_answer = trim($data['my_answer']); if (empty($my_answer)) { $my_score = 0; $is_pass = self::NO_MY_PASS; } else { $et_type = $data['et_type']; if (self::TOPIC_TYPE_SINGLE == $et_type || self::TOPIC_TYPE_MULTIPLE == $et_type || self::TOPIC_TYPE_JUDGMENT == $et_type) { // 如果是单选或者多选题 或者判断题 if ($my_answer == $data['answer']) { $my_score = $data['score']; $is_pass = self::MY_PASS; } else { $my_score = $data['score']; $is_pass = self::NO_MY_PASS; } } else { $answer_serv = new AnswerService(); // 如果是问答题 if (self::KEYWORD_OPEN == $data['match_type']) { $answer_keyword = unserialize($data['answer_keyword']); // 匹配关键字方式 $my_score = $answer_serv->_key_question_score($answer_keyword, $my_answer, $data['score']); // 得满分算回答正确,否则为回答错误 if ($my_score == $data['score']) { $is_pass = self::MY_PASS; } else { $is_pass = self::NO_MY_PASS; } } else { // 覆盖率方式 $is_pass = $answer_serv->_coverage_question_score($data['answer'], $data['answer_coverage'], $my_answer); $my_score = $is_pass === true ? $data['score'] : 0; // 得满分算回答正确,否则为回答错误 if ($my_score == $data['score']) { $is_pass = self::MY_PASS; } else { $is_pass = self::NO_MY_PASS; } } } } return [ 'is_pass' => $is_pass, 'my_score' => $my_score ]; } }