QuestionService.php 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: hanl
  5. * Date: 2018/6/8
  6. * Time: 10:13
  7. */
  8. namespace App\Services;
  9. use DB;
  10. use Excel;
  11. use App\Common\Api;
  12. use App\Models\Questions;
  13. use App\Models\Categorys;
  14. use App\Models\Configs;
  15. use Illuminate\Support\Facades\Log;
  16. use Illuminate\Support\Facades\Redis;
  17. class QuestionService
  18. {
  19. public function __construct($configs = [])
  20. {
  21. }
  22. const QUESTION_UPLOAD_FAIL = 'bsb_question_upload_fail:';
  23. /**
  24. * 获取题库详情
  25. * @param $activity_id
  26. * @param $corp_id
  27. * @param $category_id
  28. * @return array|mixed
  29. */
  30. public static function getCategoryInfo($activity_id, $category_id){
  31. $res = Categorys::where('activity_id', $activity_id)->where('category_id', $category_id)->first();
  32. if($res){
  33. $res = collect($res)->toArray();
  34. $res = Api::dateFormat($res);
  35. }
  36. return $res;
  37. }
  38. /**
  39. * 添加题库
  40. * @param $params
  41. * @return array
  42. */
  43. public static function saveCategory($params){
  44. $act_info = ActivityService::getActivityInfo($params['activity_id'], $params['corp_id']);
  45. if(!$act_info){
  46. return Api::arr(config('code.activity_not_exist'), trans('msg.activity_not_exist'));
  47. }
  48. $query = [];
  49. $tmp = explode("-",$params["category_name"]);
  50. Log::info("category_name:".$params["category_name"]);
  51. Log::info(json_encode($tmp,256));
  52. $query['category_name'] = $tmp[0];
  53. if (!empty($tmp[1]) && $tmp[1] == "直接雇员") {
  54. $query["direct_employees"] = 1;
  55. } else {
  56. $query["direct_employees"] = 0;
  57. }
  58. $query['update_time'] = time();
  59. if(!empty($params['category_id']) && isset($params['category_id'])){
  60. $res = Categorys::where('activity_id', $params['activity_id'])->where('corp_id', $params['corp_id'])->where('category_id', $params['category_id'])->update($query);
  61. $category_id = $params['category_id'];
  62. } else {
  63. $query['corp_id'] = $params['corp_id'];
  64. $query['activity_id'] = $params['activity_id'];
  65. $query['create_time'] = time();
  66. $res = Categorys::insertGetId($query);
  67. $category_id = $res;
  68. }
  69. if($res){
  70. $data['category_id'] = $category_id;
  71. return Api::arr(config('code.success'), trans('msg.success'), $data);
  72. } else {
  73. return Api::arr(config('code.sql_error'), trans('msg.sql_error'));
  74. }
  75. }
  76. /**
  77. * 获取题库列表
  78. * @param $params
  79. * @return array
  80. */
  81. public static function getCategoryList($params){
  82. $act_info = ActivityService::getActivityInfo($params['activity_id'], $params['corp_id']);
  83. if(!$act_info){
  84. return Api::arr(config('code.activity_not_exist'), trans('msg.activity_not_exist'));
  85. }
  86. $categorys = Categorys::where('corp_id', $params['corp_id'])->where('activity_id', $params['activity_id'])->get()->toArray();
  87. foreach ($categorys as $k=>$v) {
  88. $question_num = Questions::where('category_id', $v['category_id'])
  89. ->where('corp_id', $params['corp_id'])
  90. ->where('activity_id', $params['activity_id'])
  91. ->where('status', 1) //题目状态 0 无效 1有效
  92. ->count();
  93. $v['questionNum'] = $question_num?$question_num:0;
  94. //时间格式化
  95. $categorys[$k] = Api::dateFormat($v);
  96. }
  97. return Api::arr(config('code.success'), trans('msg.success'), $categorys);
  98. }
  99. /**
  100. * 计算多个题库内的题目总数
  101. */
  102. public static function getQuestionsCount($categorys, $corp_id, $activity_id){
  103. $category = explode(',', $categorys);
  104. $count = 0;
  105. foreach($category as $k=>$v){
  106. $category_count = Questions::where('corp_id', $corp_id)->where('activity_id', $activity_id)->where('category_id', $v)->count();
  107. $count += $category_count;
  108. }
  109. return $count;
  110. }
  111. /**
  112. * 答题设置
  113. * @param $params
  114. * @return array
  115. */
  116. public static function questionConfigDetail($params){
  117. //闯关、必答、PK 题库不能为空 必选项
  118. if(empty($params['must_category_ids']) || empty($params['blockade_category_ids']) || empty($params['pk_category_ids'])){
  119. return Api::arr(config('code.params_error'), trans('msg.category_ids_null'));
  120. }
  121. $must_category_ids = $params['must_category_ids'];
  122. $blockade_category_ids = $params['blockade_category_ids'];
  123. $pk_category_ids = $params['pk_category_ids'];
  124. //计算选中的题库内题目总数
  125. $must_count = self::getQuestionsCount($must_category_ids, $params['corp_id'], $params['activity_id']);
  126. $blockade_count = self::getQuestionsCount($blockade_category_ids, $params['corp_id'], $params['activity_id']);
  127. $pk_count = self::getQuestionsCount($pk_category_ids, $params['corp_id'], $params['activity_id']);
  128. //题目数不能大于选中的题库内题目的总数
  129. if ($must_count < $params['must_question_num'] || $blockade_count < $params['blockade_question_num'] || $pk_count < $params['pk_question_num']) {
  130. return Api::arr(config('code.params_error'), trans('msg.num_greater_category'));
  131. }
  132. //允许答错的题目数不能大于总题目数
  133. if ($params['must_answer_wrong_num'] > $params['must_question_num'] || $params['blockade_answer_wrong_num'] > $params['blockade_question_num']) {
  134. return Api::arr(config('code.params_error'), trans('msg.wrong_greater_num'));
  135. }
  136. //获取配置项信息
  137. $config_info = Configs::where('corp_id', $params['corp_id'])->where('activity_id', $params['activity_id'])->first();
  138. if(!$config_info){
  139. return Api::arr(config('code.activity_not_exist'), trans('msg.activity_not_exist'));
  140. }
  141. //配置项入库
  142. $config_info = collect($config_info)->toArray();
  143. $extend_configs = json_decode($config_info['extend_configs'], true);
  144. $extend_configs['must_question_num'] = $params['must_question_num'];
  145. $extend_configs['must_answer_wrong_num'] = $params['must_answer_wrong_num'];
  146. $extend_configs['must_answer_success_km'] = $params['must_answer_success_km'];
  147. $extend_configs['blockade_question_num'] = $params['blockade_question_num'];
  148. $extend_configs['blockade_answer_wrong_num'] = $params['blockade_answer_wrong_num'];
  149. $extend_configs['blockade_answer_success_km'] = $params['blockade_answer_success_km'];
  150. $extend_configs['pk_question_num'] = $params['pk_question_num'];
  151. $extend_configs['pk_last_double'] = $params['pk_last_double'];
  152. $extend_configs['pk_kms'] = $params['pk_kms'];
  153. $extend_configs['must_category_ids'] = $must_category_ids;
  154. $extend_configs['blockade_category_ids'] = $blockade_category_ids;
  155. $extend_configs['pk_category_ids'] = $pk_category_ids;
  156. $extend_configs['top_prize_open'] = $params['top_prize_open'];
  157. $extend_configs['top_prize_text'] = $params['top_prize_text'];
  158. $extend_configs['top_prize_num'] = $params['top_prize_num'];
  159. $extend_configs['must_answer_count_down'] = isset($params['must_answer_count_down']) && $params['must_answer_count_down']>=5?$params['must_answer_count_down']:10;
  160. $extend_configs['blockade_answer_count_down'] = isset($params['blockade_answer_count_down']) && $params['blockade_answer_count_down']>=5?$params['blockade_answer_count_down']:10;
  161. $extend_configs = json_encode($extend_configs);
  162. $res_config = Configs::where('corp_id', $params['corp_id'])->where('activity_id', $params['activity_id'])->update(['extend_configs' => $extend_configs, 'update_time' => time()]);
  163. return Api::arr(config('code.success'), trans('msg.success'));
  164. }
  165. /**
  166. * 修改/新增题目
  167. * @param $params
  168. * @return array
  169. */
  170. public static function questionModify($params){
  171. $query = [];
  172. $question_options= [];
  173. $answers = [];
  174. if(!empty($params['option_a'])){
  175. $question_options['A'] = Api::trimAll($params['option_a']);
  176. $answers[] = 'A';
  177. }
  178. if(!empty($params['option_b'])){
  179. $question_options['B'] = Api::trimAll($params['option_b']);
  180. $answers[] = 'B';
  181. }
  182. if(!empty($params['option_c'])){
  183. $question_options['C'] = Api::trimAll($params['option_c']);
  184. $answers[] = 'C';
  185. }
  186. if(!empty($params['option_d'])){
  187. $question_options['D'] = Api::trimAll($params['option_d']);
  188. $answers[] = 'D';
  189. }
  190. $correct_answer = self::answerChange($params['correct_answer']);
  191. //题目和答案不可为空
  192. if(empty(Api::trimAll($params['question_title'])) || empty($correct_answer)){
  193. return Api::arr(config('code.params_error'), trans('msg.title_answer_null'));
  194. }
  195. //选项最少需要两个
  196. if(count($question_options)<2){
  197. return Api::arr(config('code.params_error'), trans('msg.option_error'));
  198. }
  199. //答案必须在已有可用选项当中
  200. foreach($correct_answer as $k=>$v){
  201. if(!in_array($v, $answers)){
  202. return Api::arr(config('code.params_error'), trans('msg.answer_error'));
  203. }
  204. }
  205. //参数组装
  206. $query['question_title'] = Api::trimAll($params['question_title']);
  207. $query['question_options'] = serialize($question_options);
  208. $query['correct_answer'] = implode('', $correct_answer);
  209. $query['question_type'] = count($correct_answer) > 1 ? 2 : 1; //题目类型 单选/多选
  210. $query['category_id'] = intval($params['category_id']);
  211. $query['update_time'] = time();
  212. //题目ID存在则修改题目,不存在则新增题目
  213. if(isset($params['question_id']) && $params['question_id'] != 0){
  214. $question = Questions::where('activity_id', $params['activity_id'])->where('corp_id', $params['corp_id'])->where('question_id', $params['question_id'])->first();
  215. if(!$question){
  216. return Api::arr(config('code.question_not_exist'), trans('msg.question_not_exist'));
  217. }
  218. $res = Questions::where('activity_id', $params['activity_id'])->where('corp_id', $params['corp_id'])->where('question_id', $params['question_id'])->update($query);
  219. if(!$res){
  220. return Api::arr(config('code.sql_error'), trans('msg.sql_error'));
  221. }
  222. } else {
  223. $query['corp_id'] = $params['corp_id'];
  224. $query['activity_id'] = $params['activity_id'];
  225. $query['create_time'] = time();
  226. $res = Questions::insert($query);
  227. if(!$res){
  228. return Api::arr(config('code.sql_error'), trans('msg.sql_error'));
  229. }
  230. }
  231. return Api::arr(config('code.success'), trans('msg.success'));
  232. }
  233. /**
  234. * 获取题库内题目数量
  235. * @param $corp_id
  236. * @param $activity_id
  237. * @return array
  238. */
  239. public static function getCategoryCount($corp_id, $activity_id){
  240. $categorys = Categorys::where('activity_id', $activity_id)->where('corp_id', $corp_id)->get()->toArray();
  241. $data = [];
  242. if($categorys) {
  243. foreach ($categorys as $category) {
  244. $data[$category['category_name']] = Questions::where('activity_id', $activity_id)->where('corp_id', $corp_id)->where('category_id', $category['category_id'])->count();
  245. }
  246. }
  247. return $data;
  248. }
  249. /**
  250. * 新增题库及题目上传
  251. * @param $data
  252. * @param $params
  253. * @return array|string
  254. */
  255. public static function questionUpload($data, $params){
  256. if(empty($data)){
  257. return Api::arr(config('code.fail'), trans('msg.question_not_exist'));
  258. }
  259. //添加题库
  260. $categoryRes = self::saveCategory($params);
  261. if($categoryRes['code'] != config('code.success')){
  262. return Api::arr(config('code.fail'), trans('msg.category_add_fail'));
  263. }
  264. $category_id = $categoryRes['data']['category_id'];
  265. DB::beginTransaction();
  266. $errors = [];
  267. $error_num = 0;
  268. foreach ($data as $k => $v) {
  269. $question_options = array();
  270. $question_title = $v[0];
  271. $answers = array();
  272. if(!empty($v[1]) || $v[1] == '0'){
  273. $question_options['A'] = $v[1];
  274. $answers[] = 'A';
  275. }
  276. if(!empty($v[2]) || $v[2] == '0'){
  277. $question_options['B'] = $v[2];
  278. $answers[] = 'B';
  279. }
  280. if(!empty($v[3]) || $v[3] == '0'){
  281. $question_options['C'] = $v[3];
  282. $answers[] = 'C';
  283. }
  284. if(!empty($v[4]) || $v[4] == '0'){
  285. $question_options['D'] = $v[4];
  286. $answers[] = 'D';
  287. }
  288. $correct_answer = $v[5];
  289. $correct_answer = self::answerChange($correct_answer);
  290. $query = array();
  291. $query['corp_id'] = $params['corp_id'];
  292. $query['activity_id'] = $params['activity_id'];
  293. $query['category_id'] = $category_id;
  294. $query['question_title'] = $question_title;
  295. //答案单选 答案不能为空 题目不能为空 选项最少两个 答案不在选项中 都视为错误题目
  296. if(empty($correct_answer)){
  297. $v[6] = '答案为空';
  298. $errors[] = $v;
  299. $error_num += 1;
  300. continue;
  301. }
  302. if(empty($query['question_title'])){
  303. $v[6] = '题目为空';
  304. $errors[] = $v;
  305. $error_num += 1;
  306. continue;
  307. }
  308. if(count($answers) < 2){
  309. $v[6] = '选项少于2个';
  310. $errors[] = $v;
  311. $error_num += 1;
  312. continue;
  313. }
  314. //支持多选
  315. $correct_type = 0;
  316. foreach($correct_answer as $key=>$val){
  317. if(!in_array($val, $answers)){
  318. $v[6] = '答案中选项为空或不存在';
  319. $errors[] = $v;
  320. $error_num += 1;
  321. $correct_type = 1;
  322. continue;
  323. }
  324. }
  325. if($correct_type == 1){
  326. continue;
  327. }
  328. $query['correct_answer'] = implode('', $correct_answer);
  329. $query['question_type'] = count($correct_answer) > 1 ? 2 : 1;
  330. $query['question_options'] = serialize($question_options); //序列化
  331. $query['create_time'] = time();
  332. $query['update_time'] = time();
  333. $res = Questions::insert($query);
  334. if (!$res) {
  335. DB::rollBack();
  336. return Api::arr(config('code.sql_error'), trans('msg.sql_error'));
  337. }
  338. }
  339. DB::commit();
  340. //错误记录存入redis
  341. Redis::set(self::QUESTION_UPLOAD_FAIL.$params['activity_id'], json_encode($errors),'EX',3600);
  342. $res = [];
  343. $res['total'] = count($data);
  344. $res['error_num'] = $error_num;
  345. $res['success_num'] = $res['total'] - $error_num;
  346. $res['category_id'] = $category_id;
  347. return Api::arr(config('code.success'), trans('msg.success'), $res);
  348. }
  349. /**
  350. * 删除题目
  351. * 2018-06-22 10:32:00
  352. * @param $params
  353. * @return array
  354. */
  355. public static function delQuestion($params){
  356. //题目信息
  357. $questionInfo = Questions::where('activity_id', $params['activity_id'])
  358. ->where('corp_id', $params['corp_id'])
  359. ->where('category_id', $params['category_id'])
  360. ->where('question_id', $params['question_id'])
  361. ->where('status', 1)
  362. ->first();
  363. if(!$questionInfo){
  364. return Api::arr(config('code.question_not_exist'), trans('msg.question_not_exist'));
  365. }
  366. //删除题目
  367. $res = Questions::where('activity_id', $params['activity_id'])
  368. ->where('corp_id', $params['corp_id'])
  369. ->where('category_id', $params['category_id'])
  370. ->where('question_id', $params['question_id'])
  371. ->where('status', 1)
  372. ->delete();
  373. if($res){
  374. return Api::arr(config('code.success'), trans('msg.success'));
  375. } else {
  376. return Api::arr(config('code.sql_error'), trans('msg.sql_error'));
  377. }
  378. }
  379. /**
  380. * 下载上传失败的题目数据
  381. * @param $data
  382. */
  383. public static function downloadQuestionExcel($data){
  384. $cellData = [
  385. ['题目标题', 'A选项', 'B选项', 'C选项', 'D选项', '答案', '错误原因'],
  386. ['填写规则'],
  387. ];
  388. foreach($cellData as $k=>$v){
  389. array_unshift($data, $v);
  390. }
  391. Excel::create('题目导入错误数据'.date('mdHi'),function($excel) use ($data){
  392. $excel->sheet('题目信息', function($sheet) use ($data){
  393. $tot = count($data) ;
  394. $sheet->setWidth(array(
  395. 'A' => 50,
  396. 'B' => 15,
  397. 'C' => 15,
  398. 'D' => 15,
  399. 'E' => 15,
  400. 'F' => 15,
  401. 'G' => 15,
  402. ))->rows($data)->setFontSize(12);
  403. //设置第一行的表格格式
  404. $sheet->setStyle([
  405. 'font' => [
  406. 'size' => 12,
  407. 'bold' => false,
  408. ]
  409. ])
  410. ->mergeCells('A1:G1') //合并单元格
  411. ->setHeight(1, 100) //设置行高
  412. ->setHeight(2, 30)
  413. ->cells('A1:G1', function($cells){ //设置单元格格式
  414. $cells->setValignment('center'); //文本对齐方式
  415. $cells->setFontWeight('bold'); //粗体
  416. })
  417. ->cells('A2:G2', function($cells){
  418. $cells->setValignment('center');
  419. $cells->setBackground('#92D050');
  420. $cells->setFontWeight('bold');
  421. $cells->setFontSize(10);
  422. $cells->setAlignment('center');
  423. })
  424. ->cells('B2:G'.$tot, function($cells) {
  425. $cells->setAlignment('center');
  426. })
  427. ->cells('G3:G'.$tot, function($cells) {
  428. $cells->setFontColor('#ff0000');
  429. $cells->setFontWeight('bold');
  430. });
  431. });
  432. })->export('xls');
  433. }
  434. /**
  435. * 添加默认题库
  436. * @param $activity_id
  437. * @return array
  438. */
  439. public static function addDefault($activity_id){
  440. $activity_info = ActivityService::getActivityInfo($activity_id);
  441. if(!$activity_info){
  442. return Api::arr(config('code.fail'), trans('msg.activity_not_exist'));
  443. }
  444. $default_questions = Questions::where('corp_id', 0)->where('activity_id', 0)->get()->toArray();
  445. $query_category = [];
  446. $query_category['corp_id'] = $activity_info['corp_id'];
  447. $query_category['activity_id'] = $activity_id;
  448. $query_category['category_name'] = '样例题库';
  449. $query_category['create_time'] = time();
  450. $query_category['update_time'] = time();
  451. $res = Categorys::insertGetId($query_category);
  452. if(!$res){
  453. return Api::arr(config('code.sql_error'), trans('msg.sql_error'));
  454. }
  455. foreach($default_questions as $k=>$v){
  456. $query_question = [];
  457. $query_question['corp_id'] = $activity_info['corp_id'];
  458. $query_question['activity_id'] = $activity_id;
  459. $query_question['question_title'] = $v['question_title'];
  460. $query_question['category_id'] = $res;
  461. $query_question['question_options'] = $v['question_options'];
  462. $query_question['correct_answer'] = $v['correct_answer'];
  463. $query_question['create_time'] = time();
  464. $query_question['update_time'] = time();
  465. Questions::insert($query_question);
  466. }
  467. $data['category_id'] = $res;
  468. return Api::arr(config('code.success'), trans('msg.success'), $data);
  469. }
  470. /**
  471. * 删除题库(活动委发布状态才可删除)
  472. * @param $activity_id
  473. * @param $category_id
  474. * @return array
  475. */
  476. public static function delCategory($activity_id, $category_id){
  477. $activityInfo = ActivityService::getActivityInfo($activity_id);
  478. if(!$activityInfo){
  479. return Api::arr(config('code.fail'), trans('msg.activity_not_exist'));
  480. }
  481. if($activityInfo['is_complete_set'] == 1){
  482. return Api::arr(config('code.fail'), '活动已发布,无法删除题库');
  483. }
  484. $categoryInfo = Categorys::where('category_id', $category_id)->first();
  485. if(!$categoryInfo){
  486. return Api::arr(config('code.fail'), trans('msg.category_not_exist'));
  487. }
  488. Categorys::where('category_id', $category_id)->delete();
  489. $questions = Questions::where('category_id', $category_id)->get();
  490. foreach($questions as $k=>$v){
  491. Questions::where('question_id', $v['question_id'])->delete();
  492. }
  493. return Api::arr(config('code.success'), trans('msg.success'));
  494. }
  495. /**
  496. * 题目答案转换
  497. * @param $correct_answer
  498. * @return array|string
  499. */
  500. public static function answerChange($correct_answer){
  501. //去除中文字并转换大写
  502. $arr = strtoupper(preg_replace('/([\x80-\xff]*)/i','',$correct_answer));
  503. //拆分单个选项并去除重复选项
  504. $arr = array_unique(str_split($arr));
  505. //排序
  506. sort($arr);
  507. return $arr;
  508. }
  509. }
  510. ?>