Compare commits

...

12 Commits

Binary file not shown.

Binary file not shown.

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/study-main.iml" filepath="$PROJECT_DIR$/.idea/study-main.iml" />
</modules>
</component>
</project>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="290e4dd4-d42f-4971-907f-b8342fb26081" name="更改" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="336DDmeWCR6med2OBKWubyWvHE4" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-fbdcb00ec9e3-intellij.indexing.shared.core-IU-251.27812.49" />
<option value="bundled-js-predefined-d6986cc7102b-09060db00ec0-JavaScript-IU-251.27812.49" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="290e4dd4-d42f-4971-907f-b8342fb26081" name="更改" comment="" />
<created>1758628510738</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1758628510738</updated>
<workItem from="1758628512648" duration="6000" />
<workItem from="1760962965734" duration="3000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>

@ -0,0 +1,847 @@
/*
Navicat Premium Data Transfer
Source Server : 127.0.0.1-
Source Server Type : MySQL
Source Server Version : 80028 (8.0.28)
Source Host : localhost:3306
Source Schema : yf_exam_lite
Target Server Type : MySQL
Target Server Version : 80028 (8.0.28)
File Encoding : 65001
Date: 20/02/2023 10:38:52
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for el_exam
-- ----------------------------
DROP TABLE IF EXISTS `el_exam`;
CREATE TABLE `el_exam` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`title` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试名称',
`content` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '考试描述',
`open_type` int NOT NULL DEFAULT '1' COMMENT '1公开2部门3定员',
`state` int NOT NULL DEFAULT '0' COMMENT '考试状态',
`time_limit` tinyint NOT NULL DEFAULT '0' COMMENT '是否限时',
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`total_score` int NOT NULL DEFAULT '0' COMMENT '总分数',
`total_time` int NOT NULL DEFAULT '0' COMMENT '总时长(分钟)',
`qualify_score` int NOT NULL DEFAULT '0' COMMENT '及格分数',
PRIMARY KEY (`id`) USING BTREE,
KEY `open_type` (`open_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试主表';
-- ----------------------------
-- Records of el_exam
-- ----------------------------
BEGIN;
INSERT INTO `el_exam` (`id`, `title`, `content`, `open_type`, `state`, `time_limit`, `start_time`, `end_time`, `create_time`, `update_time`, `total_score`, `total_time`, `qualify_score`) VALUES ('1587621704140427265', '【云帆演示】考试', '【云帆演示】考试', 1, 0, 0, NULL, NULL, '2022-11-02 09:44:46', '2022-11-02 09:44:46', 100, 30, 60);
COMMIT;
-- ----------------------------
-- Table structure for el_exam_depart
-- ----------------------------
DROP TABLE IF EXISTS `el_exam_depart`;
CREATE TABLE `el_exam_depart` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'ID',
`exam_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '考试ID',
`depart_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门ID',
PRIMARY KEY (`id`) USING BTREE,
KEY `exam_id` (`exam_id`),
KEY `depart_id` (`depart_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试部门';
-- ----------------------------
-- Records of el_exam_depart
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for el_exam_repo
-- ----------------------------
DROP TABLE IF EXISTS `el_exam_repo`;
CREATE TABLE `el_exam_repo` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`exam_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试ID',
`repo_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题库ID',
`radio_count` int NOT NULL DEFAULT '0' COMMENT '单选题数量',
`radio_score` int NOT NULL DEFAULT '0' COMMENT '单选题分数',
`multi_count` int NOT NULL DEFAULT '0' COMMENT '多选题数量',
`multi_score` int NOT NULL DEFAULT '0' COMMENT '多选题分数',
`judge_count` int NOT NULL DEFAULT '0' COMMENT '判断题数量',
`judge_score` int NOT NULL DEFAULT '0' COMMENT '判断题分数',
`saq_count` int NOT NULL DEFAULT '0' COMMENT '简答题数量',
`saq_score` int NOT NULL DEFAULT '0' COMMENT '简答题分数',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `exam_repo_id` (`exam_id`,`repo_id`),
KEY `rule_id` (`exam_id`) USING BTREE,
KEY `repo_id` (`repo_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试题库';
-- ----------------------------
-- Records of el_exam_repo
-- ----------------------------
BEGIN;
INSERT INTO `el_exam_repo` (`id`, `exam_id`, `repo_id`, `radio_count`, `radio_score`, `multi_count`, `multi_score`, `judge_count`, `judge_score`, `saq_count`, `saq_score`) VALUES ('1627496519370297345', '1587621704140427265', '1587622451624120321', 2, 10, 6, 10, 2, 10, 0, 0);
COMMIT;
-- ----------------------------
-- Table structure for el_paper
-- ----------------------------
DROP TABLE IF EXISTS `el_paper`;
CREATE TABLE `el_paper` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷ID',
`user_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
`depart_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门ID',
`exam_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '规则ID',
`title` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试标题',
`total_time` int NOT NULL DEFAULT '0' COMMENT '考试时长',
`user_time` int NOT NULL DEFAULT '0' COMMENT '用户时长',
`total_score` int NOT NULL DEFAULT '0' COMMENT '试卷总分',
`qualify_score` int NOT NULL DEFAULT '0' COMMENT '及格分',
`obj_score` int NOT NULL DEFAULT '0' COMMENT '客观分',
`subj_score` int NOT NULL DEFAULT '0' COMMENT '主观分',
`user_score` int NOT NULL COMMENT '用户得分',
`has_saq` tinyint NOT NULL DEFAULT '0' COMMENT '是否包含简答题',
`state` int NOT NULL DEFAULT '1' COMMENT '试卷状态',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`limit_time` datetime DEFAULT NULL COMMENT '截止时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `user_id` (`user_id`) USING BTREE,
KEY `exam_id` (`exam_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录';
-- ----------------------------
-- Records of el_paper
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for el_paper_qu
-- ----------------------------
DROP TABLE IF EXISTS `el_paper_qu`;
CREATE TABLE `el_paper_qu` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`paper_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷ID',
`qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID',
`qu_type` int NOT NULL COMMENT '题目类型',
`answered` tinyint NOT NULL DEFAULT '0' COMMENT '是否已答',
`answer` varchar(5000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '主观答案',
`sort` int NOT NULL DEFAULT '0' COMMENT '问题排序',
`score` int NOT NULL DEFAULT '0' COMMENT '单题分分值',
`actual_score` int NOT NULL DEFAULT '0' COMMENT '实际得分(主观题)',
`is_right` tinyint NOT NULL DEFAULT '0' COMMENT '是否答对',
PRIMARY KEY (`id`) USING BTREE,
KEY `paper_id` (`paper_id`) USING BTREE,
KEY `qu_id` (`qu_id`) USING BTREE,
KEY `paper_qu_id` (`paper_id`,`qu_id`) USING BTREE,
KEY `sort` (`sort`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录考题';
-- ----------------------------
-- Records of el_paper_qu
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for el_paper_qu_answer
-- ----------------------------
DROP TABLE IF EXISTS `el_paper_qu_answer`;
CREATE TABLE `el_paper_qu_answer` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '自增ID',
`paper_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷ID',
`answer_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '回答项ID',
`qu_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID',
`is_right` tinyint NOT NULL DEFAULT '0' COMMENT '是否正确项',
`checked` tinyint NOT NULL DEFAULT '0' COMMENT '是否选中',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序',
`abc` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '选项标签',
PRIMARY KEY (`id`) USING BTREE,
KEY `paper_id` (`paper_id`) USING BTREE,
KEY `qu_id` (`qu_id`) USING BTREE,
KEY `paper_qu_id` (`paper_id`,`qu_id`) USING BTREE,
KEY `sort` (`sort`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录答案';
-- ----------------------------
-- Records of el_paper_qu_answer
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for el_qu
-- ----------------------------
DROP TABLE IF EXISTS `el_qu`;
CREATE TABLE `el_qu` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID',
`qu_type` int NOT NULL COMMENT '题目类型',
`level` int NOT NULL DEFAULT '1' COMMENT '1普通,2较难',
`image` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '题目图片',
`content` varchar(2000) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目内容',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '题目备注',
`analysis` varchar(2000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '整题解析',
PRIMARY KEY (`id`) USING BTREE,
KEY `qu_type` (`qu_type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='试题主表';
-- ----------------------------
-- Records of el_qu
-- ----------------------------
BEGIN;
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642263625729', 1, 1, '', '五个答案中哪个是最好的类比?女儿对于父亲相当于侄女对于', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642393649153', 1, 1, '', '五个答案中哪个是最好的类比?皮对于树相当于鳞对于', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642473340930', 1, 1, '', '火车守车(车尾)长6.4米。机车的长度等于守车的长加上半节车厢的长。车厢长度等于守车长加上机车长。火车的机车、车厢、守车共长多少米?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642557227009', 1, 1, '', '角对于元相当于小时对于', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642641113089', 1, 1, '', '如果把这个大立方体的六个面全部涂上黑色然后按图中虚线把它切成36个小方块两面有黑色的小方块有多少个', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642699833345', 1, 1, '', '找出与众不同的一个:', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642762747906', 2, 1, '', '以下哪些是中国的特别行政区?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642838245378', 2, 1, '', '中国东北三省是指()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642909548545', 3, 1, '', '咖啡的故乡是非洲吗?', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622642976657410', 3, 1, '', '世界上最长的山脉安第斯山脉', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643039571970', 1, 1, '', '西亚波斯湾沿岸比较富裕,其原因是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '西亚波斯湾沿岸国家比较富裕,是因为大量出口石油资源.');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643102486530', 1, 1, '', '我国最北面的城市是哪个()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '漠河市,隶属黑龙江省大兴安岭地区。 [1] 地处黑龙江省北部。西与内蒙古自治区呼伦贝尔市额尔古纳市为邻南与内蒙古自治区根河市和呼中区交界东与塔河县接壤北隔黑龙江与俄罗斯外贝加尔边疆区原赤塔州和阿穆尔州相望是中国最北端的县级行政区地势南高北低南北呈坡降趋势属于寒温带大陆性季风气候。下辖6个镇总面积18427平方千米。 [2] 根据第七次人口普查数据截至2020年11月1日零时漠河市常住人口为54036人。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643173789698', 1, 1, '', '人们把社会生产的各个部门划分为三类产业,下列属于第三产业的是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '通常人们把生产的各部门划分为三类产业.农业是第一产业,包括种植业、林业、畜牧业、渔业等;工业和建筑业是第二产业;流通部门和服务部门是第三产业');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643240898561', 1, 1, '', '亚洲人中分布不是很均匀,其中人中较稀疏的部分是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '人口稠密地区都处在中低纬度、近海、平原地区,亚洲人口稀疏的地区在纬度位置较高的北亚,沙漠广布的西亚及气候干旱的中亚地区.');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643312201730', 1, 1, '', '亚洲各国经济发展不平衡,下列国家中人均国民生产总值最高的是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '本题还可以考查人口人口超亿的国家有中国、印度、印度尼西亚、巴基斯坦、日本、孟加拉国等6个各大洲人口排序亚非欧南北美大洋洲各大洲人口增长率非南美亚大洋北美欧著名的民族文化亚洲三个人类文明发祥地华夏文化(黄河——长江中下游地区)、印度河流域文化、两河流域文化(由幼法拉底河和底格里斯河冲积而成的美索不达米亚平原地区);还有恒河文化、阿拉伯文化等。亚洲只有日本属于发达国家。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643396087810', 1, 1, '', '地球上0度经线和0度纬线的长度相比', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '0度经线和0度纬线相比()0度纬线的长度是0度经线的2倍。 因为经线是半圆,赤道是大圆。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643467390977', 1, 1, '', '下列国家人口超过1亿的南亚国家是', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '世界人口超过一亿的国家从多到少依次为中国、印度、美国、印度尼西亚、巴西、巴基斯坦、俄罗斯、孟加拉国、尼日利亚、日本和墨西哥共有11个其中属于南亚的是印度、巴基斯坦和孟加拉国');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643551277058', 1, 1, '', '世界上跨东西、南北距离最长的大洲是()', '2022-11-02 09:48:29', '2022-11-02 09:48:29', '', '在全球的七大洲中,亚洲是世界上面积最大的一洲,也是南北跨纬度最多,热量差异大; 东西跨经度仅次于南极洲,东西距离最长的大洲;大洋洲是世界上最小的一洲 考点:本题主要考查大洲和大洋。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643626774530', 1, 1, '', '地球公转会产生()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '地球自转产生的现象有: 1、昼夜更替现象向着太阳的半球,是白天,背着太阳的半球,是黑夜; 2、南北半球的地转偏向力引起的各种运动旋转现象 3、地球上不同经度的地方,有不同的地方时经度每隔15度,地方时相差一小时; 4、东西部地区的时间差现象生物作息规律现象 5、对地球形状的影响.地球自转所产生的惯性离心力,使得地球由两级向赤道逐渐膨胀,成为目前略扁的旋转椭球体。 地球公转产生的现象有: 1、根据太阳高度的差异,划分出五带:北寒、北温、热带、南温、南寒; 2、根据获得热量多少的时间差异,划分出四季:春、夏、秋、冬; 3、昼夜长短的变化现象 4、天象位置的变化生物生长规律现象. 5、正午太阳高度的变化夏至日太阳直射北回归线全球正午太阳高度从北回归线向南北两侧逐渐递减二分日太阳直射赤道全球正午太阳高度从赤道向两极递减全球昼夜平分冬至日太阳直射南回归线全球正午高度从南回归线向南北两侧逐渐递减南回归线及其以南的地区正午太阳高度达到最大值北半球各纬度正午太阳高度达到最小值。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643693883394', 2, 1, '', '南极旅游的兴起,请下列哪些因素有关()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643760992258', 2, 1, '', '交通运输线路(公路、铁路),选址的原因有()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643823906818', 2, 1, 'http://localhost:8201/upload/file/2022/11/07/1589528183307354113.jpg', '地球自转产生的现象有()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643895209985', 2, 1, '', '地球公转产生的现象有()', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622643979096066', 3, 1, '', '地球上出现的潮汐是由于地月吸引力。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644054593538', 3, 1, '', '被称为“万园之园”的我国古典园林是颐和园。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644134285314', 3, 1, '', '世界最长的山脉是安第斯山脉。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644201394178', 3, 1, '', '仪表显示当前发动机转速是6000转克/分钟。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '仪表显示当前车速是20公里/小时。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644281085953', 3, 1, '', '仪表显示当前冷却液的温度是90°C。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '水温表:指示发动机冷却液的温度,单位为摄氏度(C)。水温表指针所指的位置显示当前冷却液的温度。\n如图所示指针指的位置是90表示当前冷却液的温度是9o°C。');
INSERT INTO `el_qu` (`id`, `qu_type`, `level`, `image`, `content`, `create_time`, `update_time`, `remark`, `analysis`) VALUES ('1587622644356583425', 1, 1, '', '我国面积最大的湖泊是()。', '2022-11-02 09:48:30', '2022-11-02 09:48:30', '', '青海湖是我国最大的湖泊它是一个咸水湖面积约4450平方公里说起来也不小了但是如果把它放到全世界范围来看的话青海湖实际上并非大型湖泊单就面积而言在全世界排名第34位和我国国土面积世界第三的位置很不相称世界最大湖泊里海的面积38万平方公里就相当于84个青海湖。');
COMMIT;
-- ----------------------------
-- Table structure for el_qu_answer
-- ----------------------------
DROP TABLE IF EXISTS `el_qu_answer`;
CREATE TABLE `el_qu_answer` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '答案ID',
`qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '问题ID',
`is_right` tinyint NOT NULL DEFAULT '0' COMMENT '是否正确',
`image` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '选项图片',
`content` varchar(5000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '答案内容',
`analysis` varchar(5000) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '答案分析',
PRIMARY KEY (`id`) USING BTREE,
KEY `qu_id` (`qu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='试题答案选项';
-- ----------------------------
-- Records of el_qu_answer
-- ----------------------------
BEGIN;
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642309763073', '1587622642263625729', 0, '', '母亲', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151681', '1587622642263625729', 0, '', '哥哥', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151682', '1587622642263625729', 0, '', '侄子', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151683', '1587622642263625729', 0, '', '表兄', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642318151684', '1587622642263625729', 1, '', '叔叔', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037761', '1587622642393649153', 0, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037762', '1587622642393649153', 0, '', '大海', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037763', '1587622642393649153', 0, '', '渔夫', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037764', '1587622642393649153', 1, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642402037765', '1587622642393649153', 0, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118145', '1587622642473340930', 0, '', '25.6米', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118146', '1587622642473340930', 0, '', '36米', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118147', '1587622642473340930', 1, '', '51.2米', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118148', '1587622642473340930', 0, '', '64.4米', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642490118149', '1587622642473340930', 0, '', '76.2米', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809922', '1587622642557227009', 1, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809923', '1587622642557227009', 0, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809924', '1587622642557227009', 0, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809925', '1587622642557227009', 0, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642569809926', '1587622642557227009', 0, '', '', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501697', '1587622642641113089', 0, '', '10', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501698', '1587622642641113089', 0, '', '12', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501699', '1587622642641113089', 1, '', '16', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501700', '1587622642641113089', 0, '', '20', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642649501701', '1587622642641113089', 0, '', '8', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221953', '1587622642699833345', 0, '', '西安', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221954', '1587622642699833345', 0, '', '郑州', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221955', '1587622642699833345', 1, '', '哈尔滨', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221956', '1587622642699833345', 0, '', '昆明', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642708221957', '1587622642699833345', 0, '', '南昌', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642771136513', '1587622642762747906', 1, '', '香港', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642775330818', '1587622642762747906', 1, '', '澳门', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642775330819', '1587622642762747906', 0, '', '珠海', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642775330820', '1587622642762747906', 0, '', '重庆', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633985', '1587622642838245378', 1, '', '黑龙江省', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633986', '1587622642838245378', 1, '', '吉林省', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633987', '1587622642838245378', 1, '', '辽宁省', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642846633988', '1587622642838245378', 0, '', '河北省', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642917937153', '1587622642909548545', 1, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642917937154', '1587622642909548545', 0, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642985046018', '1587622642976657410', 1, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622642989240321', '1587622642976657410', 0, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643047960578', '1587622643039571970', 0, '', '工亚基础雄厚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643047960579', '1587622643039571970', 1, '', '拥有丰富的石油资源', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643047960580', '1587622643039571970', 0, '', '第三产业在国内生产总值中的比重大', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643052154882', '1587622643039571970', 0, '', '大力发展出口加工工业', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875137', '1587622643102486530', 0, '', '铁岭', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875138', '1587622643102486530', 0, '', '齐齐哈尔', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875139', '1587622643102486530', 0, '', '沈阳', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643110875140', '1587622643102486530', 1, '', '漠河', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178305', '1587622643173789698', 0, '', '工业', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178306', '1587622643173789698', 0, '', '建筑业', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178307', '1587622643173789698', 1, '', '服务业', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643182178308', '1587622643173789698', 0, '', '农业', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643249287170', '1587622643240898561', 0, '', '东亚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643253481474', '1587622643240898561', 0, '', '东南亚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643253481475', '1587622643240898561', 0, '', '南亚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643253481476', '1587622643240898561', 1, '', '西亚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784642', '1587622643312201730', 0, '', '韩国', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784643', '1587622643312201730', 0, '', '新加坡', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784644', '1587622643312201730', 1, '', '日本', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643324784645', '1587622643312201730', 0, '', '马来西亚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476417', '1587622643396087810', 1, '', '0度纬线稍长', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476418', '1587622643396087810', 0, '', '0度经线稍长', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476419', '1587622643396087810', 0, '', '相等', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643404476420', '1587622643396087810', 0, '', '0度经线不到0度纬线的1/2', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973889', '1587622643467390977', 1, '', '孟加拉国', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973890', '1587622643467390977', 0, '', '印度尼西亚', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973891', '1587622643467390977', 0, '', '日本', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643479973892', '1587622643467390977', 0, '', '韩国', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859970', '1587622643551277058', 0, '', '非洲', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859971', '1587622643551277058', 0, '', '北美洲', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859972', '1587622643551277058', 0, '', '大洋洲', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643563859973', '1587622643551277058', 1, '', '亚洲', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643635163138', '1587622643626774530', 1, '', '四季现象', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643639357441', '1587622643626774530', 0, '', '昼夜交替', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643639357442', '1587622643626774530', 0, '', '时间差异', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643639357443', '1587622643626774530', 0, '', '日期差异', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272001', '1587622643693883394', 1, '', '人们的求知、探秘和猎奇欲望的增长', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272002', '1587622643693883394', 1, '', '交通工具的发展', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272003', '1587622643693883394', 0, '', '促使不同国家的地区的文化交增进友谊', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643702272004', '1587622643693883394', 1, '', '科学技术的进步', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643765186561', '1587622643760992258', 1, '', '等高线稀疏', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643769380865', '1587622643760992258', 1, '', '地形坡度和缓', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643769380866', '1587622643760992258', 0, '', '隧道多', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643769380867', '1587622643760992258', 0, '', '地形复杂', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295426', '1587622643823906818', 1, 'http://localhost:8201/upload/file/2022/11/07/1589528211451133954.jpg', '南北半球的地转偏向力引起的各种运动旋转现象;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295427', '1587622643823906818', 1, '', '地球上不同经度的地方,有不同的地方时经度每隔15度,地方时相差一小时;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295428', '1587622643823906818', 1, 'http://localhost:8201/upload/file/2022/11/07/1589528315486650369.jpeg', '东西部地区的时间差现象,生物作息规律现象;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295429', '1587622643823906818', 1, '', '对地球形状的影响.地球自转所产生的惯性离心力,使得地球由两级向赤道逐渐膨胀,成为目前略扁的旋转椭球体。', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643832295430', '1587622643823906818', 1, '', '昼夜更替现象,向着太阳的半球,是白天,背着太阳的半球,是黑夜;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598594', '1587622643895209985', 1, '', '根据太阳高度的差异,划分出五带:北寒、北温、热带、南温、南寒;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598595', '1587622643895209985', 1, '', '根据获得热量多少的时间差异,划分出四季:春、夏、秋、冬;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598596', '1587622643895209985', 1, '', '昼夜长短的变化现象;', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643903598597', '1587622643895209985', 1, '', '正午太阳高度的变化;夏至日太阳直射北回归线,全球正午太阳高度从北回归线向南北两侧逐渐递减;二分日太阳直射赤道,全球正午太阳高度从赤道向两极递减,全球昼夜平分;冬至日太阳直射南回归线,全球正午高度从南回归线向南北两侧逐渐递减,南回归线及其以南的地区正午太阳高度达到最大值,北半球各纬度正午太阳高度达到最小值。', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643983290369', '1587622643979096066', 1, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622643987484674', '1587622643979096066', 0, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644058787842', '1587622644054593538', 0, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644062982146', '1587622644054593538', 1, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644138479618', '1587622644134285314', 1, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644142673922', '1587622644134285314', 0, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644209782786', '1587622644201394178', 0, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644209782787', '1587622644201394178', 1, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644289474562', '1587622644281085953', 1, '', '正确', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644289474563', '1587622644281085953', 0, '', '错误', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644364972034', '1587622644356583425', 1, '', '青海湖', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644364972035', '1587622644356583425', 0, '', '太湖', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644364972036', '1587622644356583425', 0, '', '洞庭湖', '');
INSERT INTO `el_qu_answer` (`id`, `qu_id`, `is_right`, `image`, `content`, `analysis`) VALUES ('1587622644369166338', '1587622644356583425', 0, '', '鄱阳湖', '');
COMMIT;
-- ----------------------------
-- Table structure for el_qu_repo
-- ----------------------------
DROP TABLE IF EXISTS `el_qu_repo`;
CREATE TABLE `el_qu_repo` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,
`qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '试题',
`repo_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '归属题库',
`qu_type` int NOT NULL DEFAULT '0' COMMENT '题目类型',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序',
PRIMARY KEY (`id`) USING BTREE,
KEY `qu_id` (`qu_id`) USING BTREE,
KEY `repo_id` (`repo_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='试题题库关联';
-- ----------------------------
-- Records of el_qu_repo
-- ----------------------------
BEGIN;
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670122192897', '1587622643760992258', '1587622451624120321', 2, 1);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670143164418', '1587622644281085953', '1587622451624120321', 3, 2);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670159941633', '1587622643626774530', '1587622451624120321', 1, 3);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670180913153', '1587622644134285314', '1587622451624120321', 3, 4);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670201884673', '1587622643979096066', '1587622451624120321', 3, 5);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670243827714', '1587622644356583425', '1587622451624120321', 1, 6);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670268993537', '1587622643693883394', '1587622451624120321', 2, 7);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670315130881', '1587622644201394178', '1587622451624120321', 3, 8);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670340296706', '1587622644054593538', '1587622451624120321', 3, 9);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670361268226', '1587622643895209985', '1587622451624120321', 2, 10);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670382239745', '1587622642641113089', '1587622451624120321', 1, 11);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670411599873', '1587622643102486530', '1587622451624120321', 1, 12);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670436765698', '1587622642473340930', '1587622451624120321', 1, 13);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670461931522', '1587622642976657410', '1587622451624120321', 3, 14);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670491291650', '1587622642263625729', '1587622451624120321', 1, 15);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670520651778', '1587622643467390977', '1587622451624120321', 1, 16);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670545817602', '1587622642838245378', '1587622451624120321', 2, 17);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670575177730', '1587622643312201730', '1587622451624120321', 1, 18);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670604537858', '1587622642699833345', '1587622451624120321', 1, 19);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670638092289', '1587622643173789698', '1587622451624120321', 1, 20);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670667452417', '1587622642557227009', '1587622451624120321', 1, 21);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670696812545', '1587622643039571970', '1587622451624120321', 1, 22);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670721978369', '1587622643551277058', '1587622451624120321', 1, 23);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670751338497', '1587622642393649153', '1587622451624120321', 1, 24);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670776504322', '1587622642909548545', '1587622451624120321', 3, 25);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670810058754', '1587622643396087810', '1587622451624120321', 1, 26);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670843613186', '1587622642762747906', '1587622451624120321', 2, 27);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1587622670872973313', '1587622643240898561', '1587622451624120321', 1, 28);
INSERT INTO `el_qu_repo` (`id`, `qu_id`, `repo_id`, `qu_type`, `sort`) VALUES ('1589528323552296962', '1587622643823906818', '1587622451624120321', 2, 29);
COMMIT;
-- ----------------------------
-- Table structure for el_repo
-- ----------------------------
DROP TABLE IF EXISTS `el_repo`;
CREATE TABLE `el_repo` (
`id` varchar(64) CHARACTER SET utf8 NOT NULL COMMENT '题库ID',
`code` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '题库编号',
`title` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '题库名称',
`remark` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '题库备注',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='题库信息';
-- ----------------------------
-- Records of el_repo
-- ----------------------------
BEGIN;
INSERT INTO `el_repo` (`id`, `code`, `title`, `remark`, `create_time`, `update_time`) VALUES ('1587622451624120321', '', '【云帆】演示题库', '【云帆】演示题库', '2022-11-02 09:47:44', '2022-11-02 09:47:44');
COMMIT;
-- ----------------------------
-- Table structure for el_user_book
-- ----------------------------
DROP TABLE IF EXISTS `el_user_book`;
CREATE TABLE `el_user_book` (
`id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`exam_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试ID',
`user_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
`qu_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目ID',
`create_time` datetime DEFAULT NULL COMMENT '加入时间',
`update_time` datetime DEFAULT NULL COMMENT '最近错误时间',
`wrong_count` int NOT NULL COMMENT '错误时间',
`title` varchar(1000) COLLATE utf8mb4_general_ci NOT NULL COMMENT '题目标题',
`sort` int NOT NULL COMMENT '错题序号',
PRIMARY KEY (`id`) USING BTREE,
KEY `user_id` (`user_id`),
KEY `sort` (`sort`),
KEY `exam_id` (`exam_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='错题本';
-- ----------------------------
-- Records of el_user_book
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for el_user_exam
-- ----------------------------
DROP TABLE IF EXISTS `el_user_exam`;
CREATE TABLE `el_user_exam` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL,
`user_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
`exam_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '考试ID',
`try_count` int NOT NULL DEFAULT '1' COMMENT '考试次数',
`max_score` int NOT NULL DEFAULT '0' COMMENT '最高分数',
`passed` tinyint NOT NULL DEFAULT '0' COMMENT '是否通过',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `user_id` (`user_id`,`exam_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='考试记录';
-- ----------------------------
-- Records of el_user_exam
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_BLOB_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;
CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`BLOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_BLOB_TRIGGERS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_CALENDARS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_CALENDARS`;
CREATE TABLE `QRTZ_CALENDARS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`CALENDAR_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`CALENDAR` blob NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_CALENDARS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_CRON_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;
CREATE TABLE `QRTZ_CRON_TRIGGERS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`CRON_EXPRESSION` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TIME_ZONE_ID` varchar(80) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_CRON_TRIGGERS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_FIRED_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;
CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`ENTRY_ID` varchar(95) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`INSTANCE_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`FIRED_TIME` bigint NOT NULL,
`SCHED_TIME` bigint NOT NULL,
`PRIORITY` int NOT NULL,
`STATE` varchar(16) COLLATE utf8mb4_general_ci NOT NULL,
`JOB_NAME` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL,
`JOB_GROUP` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL,
`IS_NONCONCURRENT` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL,
`REQUESTS_RECOVERY` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_FIRED_TRIGGERS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_JOB_DETAILS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;
CREATE TABLE `QRTZ_JOB_DETAILS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`JOB_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`JOB_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`DESCRIPTION` varchar(250) COLLATE utf8mb4_general_ci DEFAULT NULL,
`JOB_CLASS_NAME` varchar(250) COLLATE utf8mb4_general_ci NOT NULL,
`IS_DURABLE` varchar(1) COLLATE utf8mb4_general_ci NOT NULL,
`IS_NONCONCURRENT` varchar(1) COLLATE utf8mb4_general_ci NOT NULL,
`IS_UPDATE_DATA` varchar(1) COLLATE utf8mb4_general_ci NOT NULL,
`REQUESTS_RECOVERY` varchar(1) COLLATE utf8mb4_general_ci NOT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_JOB_DETAILS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_LOCKS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_LOCKS`;
CREATE TABLE `QRTZ_LOCKS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`LOCK_NAME` varchar(40) COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_LOCKS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;
CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_PAUSED_TRIGGER_GRPS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_SCHEDULER_STATE
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;
CREATE TABLE `QRTZ_SCHEDULER_STATE` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`INSTANCE_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`LAST_CHECKIN_TIME` bigint NOT NULL,
`CHECKIN_INTERVAL` bigint NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_SCHEDULER_STATE
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('examScheduler', 'MacBook-Pro-16.local1676860344454', 1676860726631, 10000);
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_SIMPLE_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;
CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`REPEAT_COUNT` bigint NOT NULL,
`REPEAT_INTERVAL` bigint NOT NULL,
`TIMES_TRIGGERED` bigint NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_SIMPLE_TRIGGERS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_SIMPROP_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;
CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`STR_PROP_1` varchar(512) COLLATE utf8mb4_general_ci DEFAULT NULL,
`STR_PROP_2` varchar(512) COLLATE utf8mb4_general_ci DEFAULT NULL,
`STR_PROP_3` varchar(512) COLLATE utf8mb4_general_ci DEFAULT NULL,
`INT_PROP_1` int DEFAULT NULL,
`INT_PROP_2` int DEFAULT NULL,
`LONG_PROP_1` bigint DEFAULT NULL,
`LONG_PROP_2` bigint DEFAULT NULL,
`DEC_PROP_1` decimal(13,4) DEFAULT NULL,
`DEC_PROP_2` decimal(13,4) DEFAULT NULL,
`BOOL_PROP_1` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL,
`BOOL_PROP_2` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_SIMPROP_TRIGGERS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for QRTZ_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_TRIGGERS`;
CREATE TABLE `QRTZ_TRIGGERS` (
`SCHED_NAME` varchar(120) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`JOB_NAME` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`JOB_GROUP` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,
`DESCRIPTION` varchar(250) COLLATE utf8mb4_general_ci DEFAULT NULL,
`NEXT_FIRE_TIME` bigint DEFAULT NULL,
`PREV_FIRE_TIME` bigint DEFAULT NULL,
`PRIORITY` int DEFAULT NULL,
`TRIGGER_STATE` varchar(16) COLLATE utf8mb4_general_ci NOT NULL,
`TRIGGER_TYPE` varchar(8) COLLATE utf8mb4_general_ci NOT NULL,
`START_TIME` bigint NOT NULL,
`END_TIME` bigint DEFAULT NULL,
`CALENDAR_NAME` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL,
`MISFIRE_INSTR` smallint DEFAULT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `SCHED_NAME` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of QRTZ_TRIGGERS
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for sys_config
-- ----------------------------
DROP TABLE IF EXISTS `sys_config`;
CREATE TABLE `sys_config` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`site_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '系统名称',
`front_logo` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '前端LOGO',
`back_logo` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '后台LOGO',
`copy_right` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '版权信息',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`create_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建人',
`update_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '修改人',
`data_flag` int DEFAULT '0' COMMENT '数据标识',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统设置';
-- ----------------------------
-- Records of sys_config
-- ----------------------------
BEGIN;
INSERT INTO `sys_config` (`id`, `site_name`, `front_logo`, `back_logo`, `copy_right`, `create_time`, `update_time`, `create_by`, `update_by`, `data_flag`) VALUES ('1', '云帆在线培训考试系统', NULL, NULL, NULL, '2020-12-03 16:51:30', '2020-12-03 16:51:30', '', '', 1);
COMMIT;
-- ----------------------------
-- Table structure for sys_depart
-- ----------------------------
DROP TABLE IF EXISTS `sys_depart`;
CREATE TABLE `sys_depart` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`dept_type` int NOT NULL DEFAULT '1' COMMENT '1公司2部门',
`parent_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属上级',
`dept_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门名称',
`dept_code` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门编码',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='部门信息';
-- ----------------------------
-- Records of sys_depart
-- ----------------------------
BEGIN;
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302853644578000898', 1, '0', '北京云帆互联科技有限公司', 'A01', 1);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302855940200284161', 1, '1302855776496599041', '后端组', 'A01A01A01', 2);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302855994843676674', 1, '1302855776496599041', '前端组', 'A01A01A02', 1);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856017283203073', 1, '1302855776496599041', '产品组', 'A01A01A03', 3);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856084475953154', 1, '1302855776496599041', '测试组', 'A01A01A05', 5);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856266567467010', 1, '1302855896415944705', '客户一组', 'A01A05A01', 1);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1302856320602685442', 1, '1302855896415944705', '客服二组', 'A01A05A02', 2);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1318103313740320770', 1, '1302853644578000898', '技术部', 'A01A01', 1);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1318103339229106178', 1, '1302853644578000898', '人事部', 'A01A02', 2);
INSERT INTO `sys_depart` (`id`, `dept_type`, `parent_id`, `dept_name`, `dept_code`, `sort`) VALUES ('1318103362494910465', 1, '1302853644578000898', '财务部', 'A01A03', 3);
COMMIT;
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色ID',
`role_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色';
-- ----------------------------
-- Records of sys_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_role` (`id`, `role_name`) VALUES ('sa', '超级管理员');
INSERT INTO `sys_role` (`id`, `role_name`) VALUES ('student', '学员');
COMMIT;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`user_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
`real_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '真实姓名',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码',
`salt` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码盐',
`role_ids` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色列表',
`depart_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '部门ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`state` int NOT NULL DEFAULT '0' COMMENT '状态',
`data_flag` int NOT NULL DEFAULT '0' COMMENT '0正常,1隐藏',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='管理用户';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` (`id`, `user_name`, `real_name`, `password`, `salt`, `role_ids`, `depart_id`, `create_time`, `update_time`, `state`, `data_flag`) VALUES ('10001', 'admin', '超管A', '06681cd08837b21adf6b5ef9279d403d', 'XoFFuS', 'sa', '1318103313740320770', '2020-04-20 13:51:03', '2020-04-20 13:51:03', 0, 0);
INSERT INTO `sys_user` (`id`, `user_name`, `real_name`, `password`, `salt`, `role_ids`, `depart_id`, `create_time`, `update_time`, `state`, `data_flag`) VALUES ('1252125239901696002', 'person', '张三', '6dfdd6761a3e8319719f32abb9aeae9c', 'tZCjLq', 'student', '1318103339229106178', '2020-04-20 14:41:35', '2020-04-20 14:41:35', 0, 0);
COMMIT;
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
`user_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID',
`role_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户角色';
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1318103579445284865', '10001', 'sa');
INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1318128865264132097', '1252125239901696002', 'student');
INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1587574421424279555', '1587574421424279554', 'student');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

@ -0,0 +1,4 @@
1、把运行包中的exam-api.jar和application-local.yml复制到本目录
2、运行命令注册为Windows系统服务yf-exam.exe install
3、在服务中即可找到名称为CloudExam的服务启动即可告别黑窗烦恼
注:本功能适合.NetFramework 4.0版本,如果无法注册,请检查环境是否匹配

@ -0,0 +1,85 @@
# 独立配置文件可以拿到jar外面跑
spring:
application:
name: yf-exam-lite
profiles:
active: dev
main:
allow-bean-definition-overriding: true
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yf_exam?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: root
password: root
# druid相关配置
druid:
max-active: 5000
initial-size: 20
min-idle: 5
async-init: true
# 监控统计
filters: stat,wall
filter:
stat:
log-slow-sql: true
slow-sql-millis: 5000
wall:
config:
create-table-allow: false
alter-table-allow: false
drop-table-allow: false
truncate-allow: false
# 定时任务配置
quartz:
# 数据库方式
job-store-type: jdbc
# quartz 相关属性配置
properties:
org:
quartz:
scheduler:
instanceName: examScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
clusterCheckinInterval: 10000
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
server:
port: 8101
# 启用服务端压缩
compression:
enabled: true
min-response-size: 10
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# 文件上传配置
conf:
upload:
# 物理文件存储位置,以/结束windows已正斜杠d:/exam-upload/
dir: d:/upload/
# 访问地址,注意不要去除/upload/file/,此节点为虚拟标识符
# 如http://localhost:8101/upload/file/exam.jpg对应物理文件为/data/upload/exam.jpg
url: http://localhost:8101/upload/file/
# 允许上传的文件后缀
allow-extensions: jpg,jpeg,png
# 开启文档
swagger:
enable: true
logging:
level:
root: debug
path: logs/${spring.application.name}/

@ -0,0 +1 @@
2025-09-19 21:29:38,735 DEBUG - Starting WinSW in service mode

@ -0,0 +1,8 @@
<service>
<id>yf-exam</id>
<name>CloudExam</name>
<description>云帆在线考试系统服务运行在http://localhost:8101</description>
<executable>java</executable>
<arguments>-jar exam-api.jar --spring.config.location=application-local.yml</arguments>
<log mode="roll"></log>
</service>

File diff suppressed because one or more lines are too long

@ -0,0 +1,214 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yfhl</groupId>
<artifactId>exam-api</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>exam-api</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<fastjson.version>2.0.24</fastjson.version>
<oss.version>3.7.0</oss.version>
<aliyun.sdk.version>4.1.1</aliyun.sdk.version>
<swagger.version>2.9.2</swagger.version>
<dozer.version>5.5.1</dozer.version>
<apache.commons.version>3.8</apache.commons.version>
<mysql.driver.version>8.0.11</mysql.driver.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<lombok.version>1.18.4</lombok.version>
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
<alicloud.version>2.1.1.RELEASE</alicloud.version>
<poi.version>3.9</poi.version>
<log4j2.version>2.17.2</log4j2.version>
</properties>
<dependencies>
<!-- WEB支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring quartz依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>${dozer.version}</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.3</version>
</dependency>
<!-- poi office -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
<!-- Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<defaultGoal>compile</defaultGoal>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,53 @@
package com.yf.exam;
import com.yf.exam.core.api.utils.JsonConverter;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
/**
* 线
* @author bool
* @email 18365918@qq.com
* @date 2020-03-04 19:41
*/
@Log4j2
@SpringBootApplication
public class ExamApplication implements WebMvcConfigurer {
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext application = SpringApplication.run(ExamApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
// 未配置默认空白
if(path == null){
path = "";
}
log.info("\n----------------------------------------------------------\n\t" +
"云帆考试系统启动成功,访问路径如下:\n\t" +
"本地路径: \t\thttp://localhost:" + port + path + "/\n\t" +
"网络地址: \thttp://" + ip + ":" + port + path + "/\n\t" +
"API文档: \t\thttp://" + ip + ":" + port + path + "/doc.html\n" +
"----------------------------------------------------------");
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//保留原有converter,把新增fastConverter插入集合头,保证优先级
converters.add(0, JsonConverter.fastConverter());
}
}

@ -0,0 +1,15 @@
package com.yf.exam.ability;
/**
*
* @author bool
*/
public class Constant {
/**
*
*/
public static final String FILE_PREFIX = "/upload/file/";
}

@ -0,0 +1,13 @@
package com.yf.exam.ability.job.enums;
/**
*
* @author van
*/
public interface JobGroup {
/**
*
*/
String SYSTEM = "system";
}

@ -0,0 +1,14 @@
package com.yf.exam.ability.job.enums;
/**
*
* @author bool
*/
public interface JobPrefix {
/**
*
*/
String BREAK_EXAM = "break_exam_";
}

@ -0,0 +1,53 @@
package com.yf.exam.ability.job.service;
/**
*
* @author bool
* @date 2020/11/29 2:17
*/
public interface JobService {
/**
*
*/
String TASK_DATA = "taskData";
/**
*
* @param jobClass
* @param jobName
* @param cron
* @param data
*/
void addCronJob(Class jobClass, String jobName, String cron, String data);
/**
*
* @param jobClass
* @param jobName
* @param data
*/
void addCronJob(Class jobClass, String jobName, String data);
/**
*
* @param jobName
* @param jobGroup
*/
void pauseJob(String jobName, String jobGroup);
/**
*
* @param triggerName
* @param triggerGroup
*/
void resumeJob(String triggerName, String triggerGroup);
/**
* job
* @param jobName
* @param jobGroup
*/
void deleteJob(String jobName, String jobGroup);
}

@ -0,0 +1,123 @@
package com.yf.exam.ability.job.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yf.exam.ability.job.enums.JobGroup;
import com.yf.exam.ability.job.service.JobService;
import lombok.extern.log4j.Log4j2;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* @author bool
*/
@Log4j2
@Service
public class JobServiceImpl implements JobService {
/**
* Quartz
*/
private Scheduler scheduler;
/**
*
* @param schedulerFactoryBean
*/
public JobServiceImpl(@Autowired SchedulerFactoryBean schedulerFactoryBean) {
scheduler = schedulerFactoryBean.getScheduler();
}
@Override
public void addCronJob(Class jobClass, String jobName, String cron, String data) {
String jobGroup = JobGroup.SYSTEM;
// 自动命名
if(StringUtils.isEmpty(jobName)){
jobName = jobClass.getSimpleName().toUpperCase() + "_"+IdWorker.getIdStr();
}
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail != null) {
log.info("++++++++++任务:{} 已存在", jobName);
this.deleteJob(jobName, jobGroup);
}
log.info("++++++++++构建任务:{},{},{},{},{} ", jobClass.toString(), jobName, jobGroup, cron, data);
//构建job信息
jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
//用JopDataMap来传递数据
jobDetail.getJobDataMap().put(TASK_DATA, data);
//按新的cronExpression表达式构建一个新的trigger
Trigger trigger = null;
// 有表达式的按表达式
if(!StringUtils.isEmpty(cron)){
log.info("+++++表达式执行:"+ JSON.toJSONString(jobDetail));
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
}else{
// 无表达式则立即执行
log.info("+++++立即执行:"+ JSON.toJSONString(jobDetail));
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().build();
}
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void addCronJob(Class jobClass, String jobName, String data) {
// 立即执行任务
this.addCronJob(jobClass, jobName, null, data);
}
@Override
public void pauseJob(String jobName, String jobGroup) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
scheduler.pauseTrigger(triggerKey);
log.info("++++++++++暂停任务:{}", jobName);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void resumeJob(String jobName, String jobGroup) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
scheduler.resumeTrigger(triggerKey);
log.info("++++++++++重启任务:{}", jobName);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void deleteJob(String jobName, String jobGroup) {
try {
JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
scheduler.deleteJob(jobKey);
log.info("++++++++++删除任务:{}", jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

@ -0,0 +1,29 @@
package com.yf.exam.ability.shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import javax.servlet.Filter;
import java.util.Map;
/**
* URL
* 400https://youdomain.com/upload/file/云帆考试系统用户手册.pdf
* @author van
*/
public class CNFilterFactoryBean extends ShiroFilterFactoryBean {
@Override
protected FilterChainManager createFilterChainManager() {
FilterChainManager manager = super.createFilterChainManager();
// URL携带中文400servletPath中文校验bug
Map<String, Filter> filterMap = manager.getFilters();
Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
if (invalidRequestFilter instanceof InvalidRequestFilter) {
((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
}
return manager;
}
}

@ -0,0 +1,131 @@
package com.yf.exam.ability.shiro;
import com.yf.exam.ability.shiro.jwt.JwtToken;
import com.yf.exam.ability.shiro.jwt.JwtUtils;
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
import com.yf.exam.modules.sys.user.service.SysUserRoleService;
import com.yf.exam.modules.sys.user.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
/**
*
* @author bool
*/
@Component
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
@Autowired
@Lazy
private SysUserService sysUserService;
@Autowired
@Lazy
private SysUserRoleService sysUserRoleService;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
*
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userId = null;
if (principals != null) {
SysUserLoginDTO user = (SysUserLoginDTO) principals.getPrimaryPrincipal();
userId = user.getId();
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 查找用户角色
List<String> roles = sysUserRoleService.listRoles(userId);
info.setRoles(new HashSet<>(roles));
log.info("++++++++++校验详细权限完成");
return info;
}
/**
*
* @param auth
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
if (token == null) {
throw new AuthenticationException("token为空!");
}
// 校验token有效性
SysUserLoginDTO user = this.checkToken(token);
return new SimpleAuthenticationInfo(user, token, getName());
}
/**
* Token
* @param token
* @return
* @throws AuthenticationException
*/
public SysUserLoginDTO checkToken(String token) throws AuthenticationException {
// 查询用户信息
log.debug("++++++++++校验用户token "+ token);
// 从token中获取用户名
String username = JwtUtils.getUsername(token);
log.debug("++++++++++用户名: "+ username);
if (username == null) {
throw new AuthenticationException("无效的token");
}
// 查找登录用户对象
SysUserLoginDTO user = sysUserService.token(token);
// 校验token是否失效
if (!JwtUtils.verify(token, username)) {
throw new AuthenticationException("登陆失效,请重试登陆!");
}
return user;
}
/**
*
* @param principals
*/
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
}

@ -0,0 +1,53 @@
package com.yf.exam.ability.shiro.aop;
import com.yf.exam.ability.shiro.jwt.JwtToken;
import com.yf.exam.aspect.utils.InjectUtils;
import com.yf.exam.modules.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author bool
*/
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
/**
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
// 写出统一错误信息
InjectUtils.restError((HttpServletResponse) response);
return false;
}
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(Constant.TOKEN);
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登入如果错误他会抛出异常并被捕获
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功返回true
return true;
}
}

@ -0,0 +1,33 @@
package com.yf.exam.ability.shiro.jwt;
import lombok.Data;
import org.apache.shiro.authc.AuthenticationToken;
/**
* @author bool
*/
@Data
public class JwtToken implements AuthenticationToken {
private static final long serialVersionUID = 1L;
/**
* JWTtoken
*/
private String token;
public JwtToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}

@ -0,0 +1,99 @@
package com.yf.exam.ability.shiro.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.yf.exam.core.utils.file.Md5Util;
import java.util.Calendar;
import java.util.Date;
/**
* JWT
* @author bool
*/
public class JwtUtils {
/**
* 24
*/
private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000;
/**
*
* @param token
* @param username
* @return
*/
public static boolean verify(String token, String username) {
try {
// 根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username));
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
// 效验TOKEN
verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* Token
* @param token
* @return
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* JWT Token
* @param username
* @return
*/
public static String sign(String username) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username));
// 附带username信息
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date).sign(algorithm);
}
/**
* JWT
* @param userName
* @return
*/
private static String encryptSecret(String userName){
// 一个简单的登录规则,用户名+当前月份为加密串,意思每个月会变,要重新登录
// 可自行修改此规则
Calendar cl = Calendar.getInstance();
cl.setTimeInMillis(System.currentTimeMillis());
StringBuffer sb = new StringBuffer(userName)
.append("&")
.append(cl.get(Calendar.MONTH));
// 获取MD5
String secret = Md5Util.md5(sb.toString());
return Md5Util.md5(userName + "&" + secret);
}
}

@ -0,0 +1,32 @@
package com.yf.exam.ability.upload.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
*
* @author van
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "conf.upload")
public class UploadConfig {
/**
* 访
*/
private String url;
/**
*
*/
private String dir;
/**
*
*/
private String [] allowExtensions;
}

@ -0,0 +1,57 @@
package com.yf.exam.ability.upload.controller;
import com.yf.exam.ability.Constant;
import com.yf.exam.ability.upload.dto.UploadReqDTO;
import com.yf.exam.ability.upload.dto.UploadRespDTO;
import com.yf.exam.ability.upload.service.UploadService;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author bool
*/
@Log4j2
@Api(tags = {"文件上传"})
@RestController
public class UploadController extends BaseController {
@Autowired
private UploadService uploadService;
/**
*
* @param reqDTO
* @return
*/
@PostMapping("/common/api/file/upload")
@ApiOperation(value = "文件上传", notes = "此接口较为特殊参数都通过表单方式提交而非JSON")
public ApiRest<UploadRespDTO> upload(@ModelAttribute UploadReqDTO reqDTO) {
// 上传并返回URL
UploadRespDTO respDTO = uploadService.upload(reqDTO);
return super.success(respDTO);
}
/**
*
* @param request
* @param response
*/
@GetMapping(Constant.FILE_PREFIX+"**")
@ApiOperation(value = "文件下载", notes = "文件下载")
public void download(HttpServletRequest request, HttpServletResponse response) {
uploadService.download(request, response);
}
}

@ -0,0 +1,22 @@
package com.yf.exam.ability.upload.dto;
import com.yf.exam.core.api.dto.BaseDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/**
*
* @author
* @date 2019-12-26 17:54
*/
@Data
@ApiModel(value="文件上传参数", description="文件上传参数")
public class UploadReqDTO extends BaseDTO {
@ApiModelProperty(value = "上传文件内容", required=true)
private MultipartFile file;
}

@ -0,0 +1,23 @@
package com.yf.exam.ability.upload.dto;
import com.yf.exam.core.api.dto.BaseDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @author bool
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value="文件上传响应", description="文件上传响应")
public class UploadRespDTO extends BaseDTO {
@ApiModelProperty(value = "上传后的完整的URL地址", required=true)
private String url;
}

@ -0,0 +1,30 @@
package com.yf.exam.ability.upload.service;
import com.yf.exam.ability.upload.dto.UploadReqDTO;
import com.yf.exam.ability.upload.dto.UploadRespDTO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* OSS
* @author bool
* @date 2019-07-12 16:45
*/
public interface UploadService {
/**
*
* @param reqDTO
* @return
*/
UploadRespDTO upload(UploadReqDTO reqDTO);
/**
*
* @param request
* @param response
*/
void download(HttpServletRequest request, HttpServletResponse response);
}

@ -0,0 +1,135 @@
package com.yf.exam.ability.upload.service.impl;
import com.yf.exam.ability.Constant;
import com.yf.exam.ability.upload.config.UploadConfig;
import com.yf.exam.ability.upload.dto.UploadReqDTO;
import com.yf.exam.ability.upload.dto.UploadRespDTO;
import com.yf.exam.ability.upload.service.UploadService;
import com.yf.exam.ability.upload.utils.FileUtils;
import com.yf.exam.core.exception.ServiceException;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author bool
* @date 2019-07-30 21:02
*/
@Log4j2
@Service
public class UploadServiceImpl implements UploadService {
@Autowired
private UploadConfig conf;
@Override
public UploadRespDTO upload(UploadReqDTO reqDTO) {
// 文件内容
MultipartFile file = reqDTO.getFile();
// 验证文件后缀
boolean allow = FilenameUtils.isExtension(file.getOriginalFilename(), conf.getAllowExtensions());
if(!allow){
throw new ServiceException("文件类型不允许上传!");
}
// 上传文件夹
String fileDir = conf.getDir();
// 真实物理地址
String fullPath;
try {
// 新文件
String filePath = FileUtils.processPath(file);
// 文件保存地址
fullPath = fileDir + filePath;
// 创建文件夹
FileUtils.checkDir(fullPath);
// 上传文件
FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(fullPath));
return this.generateResult(filePath);
} catch (IOException e) {
e.printStackTrace();
throw new ServiceException("文件上传失败:"+e.getMessage());
}
}
@Override
public void download(HttpServletRequest request, HttpServletResponse response) {
// 获取真实的文件路径
String filePath = this.getRealPath(request.getRequestURI());
// 处理中文问题
try {
filePath = URLDecoder.decode(filePath, "utf-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
System.out.println("++++完整路径为:"+filePath);
try {
FileUtils.writeRange(request, response, filePath);
} catch (IOException e) {
response.setStatus(404);
log.error("预览文件失败" + e.getMessage());
}
}
/**
*
* @param fileName
* @return
*/
private UploadRespDTO generateResult(String fileName) {
//获取加速域名
String domain = conf.getUrl();
// 返回结果
return new UploadRespDTO(domain + fileName);
}
/**
*
* @param uri
* @return
*/
public String getRealPath(String uri){
String regx = Constant.FILE_PREFIX+"(.*)";
// 查找全部变量
Pattern pattern = Pattern.compile(regx);
Matcher m = pattern.matcher(uri);
if (m.find()) {
String str = m.group(1);
return conf.getDir() + str;
}
return null;
}
}

@ -0,0 +1,172 @@
package com.yf.exam.ability.upload.utils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yf.exam.core.utils.DateUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
/**
*
* @author bool
*/
public class FileUtils {
/**
*
*/
private static final String SUFFIX_SPLIT = ".";
/**
* 线线
* @param request
* @param response
* @param filePath
* @throws IOException
*/
public static void writeRange(HttpServletRequest request,
HttpServletResponse response, String filePath) throws IOException {
// 读取文件
File file = new File(filePath);
//只读模式
RandomAccessFile randomFile = new RandomAccessFile(file, "r");
long contentLength = randomFile.length();
String range = request.getHeader("Range");
int start = 0, end = 0;
if (range != null && range.startsWith("bytes=")) {
String[] values = range.split("=")[1].split("-");
start = Integer.parseInt(values[0]);
if (values.length > 1) {
end = Integer.parseInt(values[1]);
}
}
int requestSize;
if (end != 0 && end > start) {
requestSize = end - start + 1;
} else {
requestSize = Integer.MAX_VALUE;
}
byte[] buffer = new byte[128];
response.setContentType(MediaUtils.getContentType(filePath));
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("ETag", file.getName());
response.setHeader("Last-Modified", new Date().toString());
//第一次请求只返回content length来让客户端请求多次实际数据
if (range == null) {
response.setHeader("Content-length", contentLength + "");
} else {
//以后的多次以断点续传的方式来返回视频数据
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
long requestStart = 0, requestEnd = 0;
String[] ranges = range.split("=");
if (ranges.length > 1) {
String[] rangeData = ranges[1].split("-");
requestStart = Integer.parseInt(rangeData[0]);
if (rangeData.length > 1) {
requestEnd = Integer.parseInt(rangeData[1]);
}
}
long length;
if (requestEnd > 0) {
length = requestEnd - requestStart + 1;
response.setHeader("Content-length", "" + length);
response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength);
} else {
length = contentLength - requestStart;
response.setHeader("Content-length", "" + length);
response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength);
}
}
ServletOutputStream out = response.getOutputStream();
int needSize = requestSize;
randomFile.seek(start);
while (needSize > 0) {
int len = randomFile.read(buffer);
if (needSize < buffer.length) {
out.write(buffer, 0, needSize);
} else {
out.write(buffer, 0, len);
if (len < buffer.length) {
break;
}
}
needSize -= buffer.length;
}
randomFile.close();
out.close();
}
/**
*
* @param fileName
* @return
*/
public static String renameFile(String fileName) {
//没有后缀名不处理
if (!fileName.contains(SUFFIX_SPLIT)) {
return fileName;
}
//文件后缀
String extension = FilenameUtils.getExtension(fileName);
//以系统时间命名
return IdWorker.getIdStr() + "."+ extension;
}
/**
* 2021/01/01/xxx.jpg
* @param file
* @return
*/
public static String processPath(MultipartFile file){
// 创建OSSClient实例。
String fileName = file.getOriginalFilename();
// 需要重命名
fileName = renameFile(fileName);
//获得上传的文件夹
String dir = DateUtils.formatDate(new Date(), "yyyy/MM/dd/");
return new StringBuffer(dir).append(fileName).toString();
}
/**
*
* @param fileName
* @return
*/
public static void checkDir(String fileName){
int index = fileName.lastIndexOf("/");
if(index == -1){
return;
}
File file = new File(fileName.substring(0,index));
if(!file.exists()){
file.mkdirs();
}
}
}

@ -0,0 +1,47 @@
package com.yf.exam.ability.upload.utils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author bool
* @date 2019-11-14 16:21
*/
public class MediaUtils {
public static final Map<String, String> MEDIA_MAP = new HashMap(){
{
//本来是pdf的
put(".pdf", "application/pdf");
//视频
put(".mp4", "video,video/mp4");
}
};
/**
*
* @param filePath
* @return
*/
public static String getContentType(String filePath){
if(!StringUtils.isBlank(filePath)
&& filePath.indexOf(".")!=-1) {
// 后缀转换成小写
String suffix = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
if (MEDIA_MAP.containsKey(suffix)) {
return MEDIA_MAP.get(suffix);
}
}
return "application/octet-stream";
}
}

@ -0,0 +1,317 @@
package com.yf.exam.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.yf.exam.core.annon.Dict;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.utils.Reflections;
import com.yf.exam.modules.sys.system.service.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* AOP
*
* @author bool
*/
@Aspect
@Component
@Slf4j
public class DictAspect {
@Autowired
private SysDictService sysDictService;
/**
* Controller
* @param pjp
* @return
* @throws Throwable
*/
@Around("execution(public * com.yf.exam..*.*Controller.*(..))")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
return this.translate(pjp);
}
/**
* BaseDictService
*
* @param pjp
* @return
* @throws Throwable
*/
public Object translate(ProceedingJoinPoint pjp) throws Throwable {
// 处理字典
return this.parseAllDictText(pjp.proceed());
}
/**
*
*
* @param result
*/
private Object parseAllDictText(Object result) {
// 非ApiRest类型不处理
if (result instanceof ApiRest) {
parseFullDictText(result);
}
return result;
}
/**
*
*
* @param result
*/
private void parseFullDictText(Object result) {
try {
Object rest = ((ApiRest) result).getData();
// 不处理普通数据类型
if (rest == null || this.isBaseType(rest.getClass())) {
return;
}
// 分页的
if (rest instanceof IPage) {
List<Object> items = new ArrayList<>(16);
for (Object record : ((IPage) rest).getRecords()) {
Object item = this.parseObject(record);
items.add(item);
}
((IPage) rest).setRecords(items);
return;
}
// 数据列表的
if (rest instanceof List) {
List<Object> items = new ArrayList<>();
for (Object record : ((List) rest)) {
Object item = this.parseObject(record);
items.add(item);
}
// 重新回写值
((ApiRest) result).setData(items);
return;
}
// 处理单对象
Object item = this.parseObject(((ApiRest) result).getData());
((ApiRest) result).setData(item);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
*
* @param record
* @return
*/
public Object parseObject(Object record) {
if (record == null) {
return null;
}
// 不处理普通数据类型
if (this.isBaseType(record.getClass())) {
return record;
}
// 转换JSON字符
String json = JSON.toJSONString(record);
JSONObject item = JSONObject.parseObject(json);
for (Field field : Reflections.getAllFields(record)) {
// 如果是List类型
if (List.class.isAssignableFrom(field.getType())) {
try {
List list = this.processList(field, item.getObject(field.getName(), List.class));
item.put(field.getName(), list);
continue;
} catch (Exception e) {
e.printStackTrace();
}
continue;
}
// 处理普通字段
if (field.getAnnotation(Dict.class) != null) {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
String key = String.valueOf(item.get(field.getName()));
//翻译字典值对应的txt
String textValue = this.translateDictValue(code, text, table, key);
if (StringUtils.isEmpty(textValue)) {
textValue = "";
}
item.put(field.getName() + "_dictText", textValue);
continue;
}
//日期格式转换
if ("java.util.Date".equals(field.getType().getName()) && item.get(field.getName()) != null) {
// 获取注解
JsonFormat ann = field.getAnnotation(JsonFormat.class);
// 格式化方式
SimpleDateFormat fmt;
// 使用注解指定的
if (ann != null && !StringUtils.isEmpty(ann.pattern())) {
fmt = new SimpleDateFormat(ann.pattern());
} else {
// 默认时间样式
fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
item.put(field.getName(), fmt.format(new Date((Long) item.get(field.getName()))));
continue;
}
}
return item;
}
/**
* List
*
* @param field
* @return
*/
private List<Object> processList(Field field, List list) {
// 空判断
if (list == null || list.size() == 0) {
return new ArrayList<>();
}
// 获得List属性的真实类
Type genericType = field.getGenericType();
Class<?> actualType = null;
if (genericType instanceof ParameterizedType) {
// 尝试获取数据类型
ParameterizedType pt = (ParameterizedType) genericType;
try {
actualType = (Class) pt.getActualTypeArguments()[0];
}catch (Exception e){
return list;
}
}
// 常规列表无需处理
if (isBaseType(actualType)) {
return list;
}
// 返回列表
List<Object> result = new ArrayList<>(16);
for (int i = 0; i < list.size(); i++) {
// 创建实例-->赋值-->字典处理
Object data = list.get(i);
try {
data = JSON.parseObject(JSON.toJSONString(data), actualType);
}catch (Exception e){
// 转换出错不处理
}
// 处理后的数据
Object pds = this.parseObject(data);
result.add(pds);
}
return result;
}
/**
*
*
* @param code
* @param text
* @param table
* @param key
* @return
*/
private String translateDictValue(String code, String text, String table, String key) {
if (StringUtils.isEmpty(key)) {
return null;
}
try {
// 翻译值
String dictText = null;
if (!StringUtils.isEmpty(table)) {
dictText = sysDictService.findDict(table, text, code, key.trim());
}
if (!StringUtils.isEmpty(dictText)) {
return dictText;
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
*
*
* @param clazz
* @return
*/
private boolean isBaseType(Class clazz) {
// 基础数据类型
if (clazz.equals(java.lang.Integer.class) ||
clazz.equals(java.lang.Byte.class) ||
clazz.equals(java.lang.Long.class) ||
clazz.equals(java.lang.Double.class) ||
clazz.equals(java.lang.Float.class) ||
clazz.equals(java.lang.Character.class) ||
clazz.equals(java.lang.Short.class) ||
clazz.equals(java.lang.Boolean.class)) {
return true;
}
// String类型
if (clazz.equals(java.lang.String.class)) {
return true;
}
// 数字
if (clazz.equals(java.lang.Number.class)) {
return true;
}
return false;
}
}

@ -0,0 +1,144 @@
package com.yf.exam.aspect.mybatis;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
import lombok.extern.log4j.Log4j2;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.shiro.SecurityUtils;
import java.io.StringReader;
import java.sql.Connection;
import java.util.Properties;
/**
* ID
* PaginationInterceptor
* @author bool
*/
@Log4j2
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),})
public class QueryInterceptor extends PaginationInterceptor implements Interceptor {
/**
* ID
*/
private static final String USER_FILTER = "{{userId}}";
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
//sql语句类型
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 只过滤查询的
if (SqlCommandType.SELECT == sqlCommandType) {
// 获得原始SQL
String sql = statementHandler.getBoundSql().getSql();
// 不处理
if(!sql.contains(USER_FILTER)){
return super.intercept(invocation);
}
// 处理SQL语句
String outSql = this.parseSql(sql);
// 设置SQL
metaObject.setValue("delegate.boundSql.sql", outSql);
// 再分页
return super.intercept(invocation);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
*
* @return
*/
private SysUserLoginDTO getLoginUser() {
try {
return SecurityUtils.getSubject().getPrincipal() != null ? (SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal() : null;
} catch (Exception e) {
return null;
}
}
/**
* ID
* @param sql
* @return
*/
private String processUserId(String sql) {
// 当前用户
SysUserLoginDTO user = this.getLoginUser();
String userId = user.getId();
if(StringUtils.isNotBlank(userId)){
return sql.replace(USER_FILTER, userId);
}
return null;
}
/**
*
* @param src
* @return
*/
private String parseSql(String src) {
CCJSqlParserManager parserManager = new CCJSqlParserManager();
try {
Select select = (Select) parserManager.parse(new StringReader(src));
PlainSelect selectBody = (PlainSelect) select.getSelectBody();
// 过滤客户
String sql = selectBody.toString();
// 过滤用户ID
sql = this.processUserId(sql);
// 获得SQL
return sql;
} catch (Exception e) {
e.printStackTrace();
}
return src;
}
}

@ -0,0 +1,80 @@
package com.yf.exam.aspect.mybatis;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.Objects;
import java.util.Properties;
/**
*
* @author bool
*/
@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class UpdateInterceptor extends AbstractSqlParserHandler implements Interceptor {
/**
*
*/
private static final String CREATE_TIME = "createTime";
/**
*
*/
private static final String UPDATE_TIME = "updateTime";
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
// SQL操作命令
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 获取新增或修改的对象参数
Object parameter = invocation.getArgs()[1];
// 获取对象中所有的私有成员变量(对应表字段)
Field[] declaredFields = parameter.getClass().getDeclaredFields();
if (parameter.getClass().getSuperclass() != null) {
Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields();
declaredFields = ArrayUtils.addAll(declaredFields, superField);
}
String fieldName = null;
for (Field field : declaredFields) {
fieldName = field.getName();
if (Objects.equals(CREATE_TIME, fieldName)) {
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
field.setAccessible(true);
field.set(parameter, new Timestamp(System.currentTimeMillis()));
}
}
if (Objects.equals(UPDATE_TIME, fieldName)) {
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
field.setAccessible(true);
field.set(parameter, new Timestamp(System.currentTimeMillis()));
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}

@ -0,0 +1,99 @@
package com.yf.exam.aspect.utils;
import com.alibaba.fastjson.JSON;
import com.yf.exam.core.api.ApiError;
import com.yf.exam.core.api.ApiRest;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
/**
*
* @author bool
* @date 2019-07-17 09:32
*/
@Log4j2
@Component
public class InjectUtils {
/**
*
*
* @param object
* @param value
* @param fields
* @throws Exception
*/
public void setValue(Object object, Object value, String... fields) throws Exception {
//设置同类的属性
for (String fieldName : fields) {
//获取当前
Field field = this.getFiled(object.getClass(), fieldName);
if(field == null){
continue;
}
field.setAccessible(true);
field.set(object, value);
}
}
/**
*
*
* @param clazz
* @param fieldName
*/
private Field getFiled(Class clazz, String fieldName) {
System.out.println("注入的类:"+clazz.toString());
//是否具有包含关系
try {
//获取当前类的属性
return clazz.getDeclaredField(fieldName);
}catch (Exception e){
log.error(clazz.toString() + ": not exist field, try superclass " + fieldName);
//如果为空且存在父类,则往上找
if(clazz.getSuperclass()!=null){
return this.getFiled(clazz.getSuperclass(), fieldName);
}
return null;
}
}
/**
*
* @param response
* @throws IOException
*/
public static void restError(HttpServletResponse response) {
try {
//固定错误
ApiRest apiRest = new ApiRest(ApiError.ERROR_10010002);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSON.toJSONString(apiRest));
response.getWriter().close();
}catch (IOException e){
}
}
}

@ -0,0 +1,35 @@
package com.yf.exam.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
*
* @author bool
* @date 2019-08-13 17:28
*/
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(CorsConfiguration.ALL);
config.addAllowedHeader(CorsConfiguration.ALL);
config.addAllowedMethod(CorsConfiguration.ALL);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}

@ -0,0 +1,28 @@
package com.yf.exam.config;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;
import javax.servlet.MultipartConfigElement;
/**
*
* @author bool
* @date 2019-07-29 16:23
*/
@Configuration
public class MultipartConfig {
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
// 单个数据大小
factory.setMaxFileSize(DataSize.ofMegabytes(5000L));
/// 总上传数据大小
factory.setMaxRequestSize(DataSize.ofMegabytes(5000L));
return factory.createMultipartConfig();
}
}

@ -0,0 +1,37 @@
package com.yf.exam.config;
import com.yf.exam.aspect.mybatis.QueryInterceptor;
import com.yf.exam.aspect.mybatis.UpdateInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Mybatis
*
* @author bool
*/
@Configuration
@MapperScan("com.yf.exam.modules.**.mapper")
public class MybatisConfig {
/**
*
*/
@Bean
public QueryInterceptor queryInterceptor() {
QueryInterceptor query = new QueryInterceptor();
query.setLimit(-1L);
return query;
}
/**
*
*/
@Bean
public UpdateInterceptor updateInterceptor() {
return new UpdateInterceptor();
}
}

@ -0,0 +1,77 @@
package com.yf.exam.config;
import lombok.extern.log4j.Log4j2;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
*
* @author bool
*/
@Log4j2
@Configuration
@EnableScheduling
@EnableAsync
public class ScheduledConfig implements SchedulingConfigurer, AsyncConfigurer {
/**
* 使线
* @return
*/
@Bean(destroyMethod = "shutdown", name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(600);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
/**
* 线
* @return
*/
@Bean(name = "asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setQueueCapacity(1000);
executor.setKeepAliveSeconds(600);
executor.setMaxPoolSize(20);
executor.setThreadNamePrefix("taskExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler taskScheduler = taskScheduler();
scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
}
@Override
public Executor getAsyncExecutor() {
return asyncExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
log.error("异步任务执行出现异常, message {}, emthod {}, params {}", throwable, method, objects);
};
}
}

@ -0,0 +1,127 @@
package com.yf.exam.config;
import com.yf.exam.ability.shiro.CNFilterFactoryBean;
import com.yf.exam.ability.shiro.ShiroRealm;
import com.yf.exam.ability.shiro.aop.JwtFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiro
* @author bool
*/
@Slf4j
@Configuration
public class ShiroConfig {
/**
* Filter Chain
*
* 1URLFilter使
* 2
* 3permsroles
*/
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new CNFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器
Map<String, String> map = new LinkedHashMap<>();
// 需要排除的一些接口
map.put("/exam/api/sys/user/login", "anon");
map.put("/exam/api/sys/user/reg", "anon");
map.put("/exam/api/sys/user/quick-reg", "anon");
// 获取网站基本信息
map.put("/exam/api/sys/config/detail", "anon");
// 文件读取
map.put("/upload/file/**", "anon");
map.put("/", "anon");
map.put("/v2/**", "anon");
map.put("/doc.html", "anon");
map.put("/**/*.js", "anon");
map.put("/**/*.css", "anon");
map.put("/**/*.html", "anon");
map.put("/**/*.svg", "anon");
map.put("/**/*.pdf", "anon");
map.put("/**/*.jpg", "anon");
map.put("/**/*.png", "anon");
map.put("/**/*.ico", "anon");
// 字体
map.put("/**/*.ttf", "anon");
map.put("/**/*.woff", "anon");
map.put("/**/*.woff2", "anon");
map.put("/druid/**", "anon");
map.put("/swagger-ui.html", "anon");
map.put("/swagger**/**", "anon");
map.put("/webjars/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
filterMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(filterMap);
map.put("/**", "jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
/**
*
* @return
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
return defaultAdvisorAutoProxyCreator;
}
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}

@ -0,0 +1,65 @@
package com.yf.exam.config;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import io.swagger.annotations.ApiOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Collections;
/**
* Swagger
* @author bool
* @date 2020/8/19 20:53
*/
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
@ConfigurationProperties(prefix = "swagger")
public class SwaggerConfig {
@Bean
public Docket examApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("考试模块接口")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.ant("/exam/api/**"))
.build()
.securitySchemes(Collections.singletonList(securityScheme()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("考试系统接口")
.description("考试系统接口")
.contact(new Contact("Van", "https://exam.yfhl.net", "18365918@qq.com"))
.version("1.0.0")
.build();
}
/**
*
* @return
*/
@Bean
SecurityScheme securityScheme() {
return new ApiKey("token", "token", "header");
}
}

@ -0,0 +1,21 @@
package com.yf.exam.core.annon;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author bool
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
String dicCode();
String dicText() default "";
String dictTable() default "";
}

@ -0,0 +1,67 @@
package com.yf.exam.core.api;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
*
* 使
* @author bool
* @date 2019-06-14 21:15
*/
@NoArgsConstructor
@AllArgsConstructor
public enum ApiError implements Serializable {
/**
*
*/
ERROR_10010001("参数不全或类型错误!"),
ERROR_10010002("您还未登录,请先登录!"),
ERROR_10010003("数据不存在!"),
ERROR_10010012("图形验证码错误!"),
ERROR_10010013("短信验证码错误!"),
ERROR_10010014("不允许重复评论!"),
/**
*
*/
ERROR_20010001("试题被删除,无法继续考试!"),
ERROR_20010002("您有正在进行的考试!"),
ERROR_90010001("账号不存在,请确认!"),
ERROR_90010002("账号或密码错误!"),
ERROR_90010003("至少要包含一个角色!"),
ERROR_90010004("管理员账号无法修改!"),
ERROR_90010005("账号被禁用,请联系管理员!"),
ERROR_90010006("活动用户不足,无法开启竞拍!"),
ERROR_90010007("旧密码不正确,请确认!"),
ERROR_60000001("数据不存在!");
public String msg;
/**
* Markdown
* @param args
*/
public static void main(String[] args) {
for (ApiError e : ApiError.values()) {
System.out.println("'"+e.name().replace("ERROR_", "")+"':'"+e.msg+"',");
}
}
/**
*
* @return
*/
public Integer getCode(){
return Integer.parseInt(this.name().replace("ERROR_", ""));
}
}

@ -0,0 +1,64 @@
package com.yf.exam.core.api;
import com.yf.exam.core.api.ApiError;
import com.yf.exam.core.exception.ServiceException;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @author bool
* @date 2018/11/20 09:48
*/
@Data
@NoArgsConstructor
@ApiModel(value="接口响应", description="接口响应")
public class ApiRest<T>{
/**
*
*/
@ApiModelProperty(value = "响应消息")
private String msg;
/**
*
*/
@ApiModelProperty(value = "响应代码,0为成功,1为失败", required = true)
private Integer code;
/**
* body
*/
@ApiModelProperty(value = "响应内容")
protected T data;
/**
*
* @return
*/
public boolean isSuccess(){
return code.equals(0);
}
/**
*
* @param error
*/
public ApiRest(ServiceException error){
this.code = error.getCode();
this.msg = error.getMsg();
}
/**
*
* @param error
*/
public ApiRest(ApiError error){
this.code = error.getCode();
this.msg = error.msg;
}
}

@ -0,0 +1,154 @@
package com.yf.exam.core.api.controller;
import com.yf.exam.core.api.ApiError;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.exception.ServiceException;
/**
*
* @author Dav
*/
public class BaseController {
/**
*
*/
private static final Integer CODE_SUCCESS = 0;
private static final String MSG_SUCCESS = "操作成功!";
/**
*
*/
private static final Integer CODE_FAILURE = 1;
private static final String MSG_FAILURE = "请求失败!";
/**
*
* @param code
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> message(Integer code, String message, T data){
ApiRest<T> response = new ApiRest<>();
response.setCode(code);
response.setMsg(message);
if(data!=null) {
response.setData(data);
}
return response;
}
/**
*
* @param <T>
* @return
*/
protected <T> ApiRest<T> success(){
return message(0, "请求成功!", null);
}
/**
*
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> success(String message, T data){
return message(CODE_SUCCESS, message, data);
}
/**
*
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> success(T data){
return message(CODE_SUCCESS, MSG_SUCCESS, data);
}
/**
*
* @param code
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(Integer code, String message, T data){
return message(code, message, data);
}
/**
*
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(String message, T data){
return message(CODE_FAILURE, message, data);
}
/**
*
* @param message
* @return
*/
protected <T> ApiRest<T> failure(String message){
return message(CODE_FAILURE, message, null);
}
/**
*
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(T data){
return message(CODE_FAILURE, MSG_FAILURE, data);
}
/**
*
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(){
return message(CODE_FAILURE, MSG_FAILURE, null);
}
/**
*
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(ApiError error, T data){
return message(error.getCode(), error.msg, data);
}
/**
*
* @param ex
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(ServiceException ex){
ApiRest<T> apiRest = message(ex.getCode(), ex.getMsg(), null);
return apiRest;
}
}

@ -0,0 +1,15 @@
package com.yf.exam.core.api.dto;
import lombok.Data;
import java.io.Serializable;
/**
*
* @author dav
* @date 2019/3/16 15:56
*/
@Data
public class BaseDTO implements Serializable {
}

@ -0,0 +1,28 @@
package com.yf.exam.core.api.dto;
import com.yf.exam.core.api.dto.BaseDTO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
* ID
* </p>
*
* @author
* @since 2019-04-20 12:15
*/
@Data
@ApiModel(value="主键通用请求类", description="主键通用请求类")
public class BaseIdReqDTO extends BaseDTO {
@ApiModelProperty(value = "主键ID", required=true)
private String id;
@JsonIgnore
private String userId;
}

@ -0,0 +1,26 @@
package com.yf.exam.core.api.dto;
import com.yf.exam.core.api.dto.BaseDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
*
* </p>
*
* @author
* @since 2019-04-20 12:15
*/
@Data
@ApiModel(value="主键通用响应类", description="主键通用响应类")
@AllArgsConstructor
@NoArgsConstructor
public class BaseIdRespDTO extends BaseDTO {
@ApiModelProperty(value = "主键ID", required=true)
private String id;
}

@ -0,0 +1,26 @@
package com.yf.exam.core.api.dto;
import com.yf.exam.core.api.dto.BaseDTO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* ID
* @author bool
* @date 2019-08-01 19:07
*/
@Data
@ApiModel(value="删除参数", description="删除参数")
public class BaseIdsReqDTO extends BaseDTO {
@JsonIgnore
private String userId;
@ApiModelProperty(value = "要删除的ID列表", required = true)
private List<String> ids;
}

@ -0,0 +1,32 @@
package com.yf.exam.core.api.dto;
import com.yf.exam.core.api.dto.BaseDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2019-04-20 12:15
*/
@Data
@ApiModel(value="通用状态请求类", description="通用状态请求类")
@AllArgsConstructor
@NoArgsConstructor
public class BaseStateReqDTO extends BaseDTO {
@ApiModelProperty(value = "要修改对象的ID列表", required=true)
private List<String> ids;
@ApiModelProperty(value = "通用状态0为正常1为禁用", required=true)
private Integer state;
}

@ -0,0 +1,47 @@
package com.yf.exam.core.api.dto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
* @param <T>
* @author bool
*/
@ApiModel(value="分页参数", description="分页参数")
@Data
public class PagingReqDTO<T> {
@ApiModelProperty(value = "当前页码", required = true, example = "1")
private Integer current;
@ApiModelProperty(value = "每页数量", required = true, example = "10")
private Integer size;
@ApiModelProperty(value = "查询参数")
private T params;
@ApiModelProperty(value = "排序字符")
private String orderBy;
@JsonIgnore
@ApiModelProperty(value = "当前用户的ID")
private String userId;
/**
* MyBatis
* @return
*/
public Page toPage(){
Page page = new Page();
page.setCurrent(this.current);
page.setSize(this.size);
return page;
}
}

@ -0,0 +1,30 @@
package com.yf.exam.core.api.dto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
*
* @author bool
* @date 2019-07-20 15:17
* @param <T>
*/
public class PagingRespDTO<T> extends Page<T> {
/**
*
* @return
*/
@Override
public long getPages() {
if (this.getSize() == 0L) {
return 0L;
} else {
long pages = this.getTotal() / this.getSize();
if (this.getTotal() % this.getSize() != 0L) {
++pages;
}
return pages;
}
}
}

@ -0,0 +1,48 @@
package com.yf.exam.core.api.utils;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* JSON
* @author dav
* @date 2018/9/11 19:30
*/
public class JsonConverter {
/**
* FastJson
*
* @return
*/
public static HttpMessageConverter fastConverter() {
// 定义一个convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 添加FastJson的配置信息
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 默认转换器
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,
SerializerFeature.WriteNullNumberAsZero,
SerializerFeature.MapSortField,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.WriteNullListAsEmpty);
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
// 处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
// 在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
return fastConverter;
}
}

@ -0,0 +1,19 @@
package com.yf.exam.core.enums;
/**
*
*
* @author bool
* @date 2019-09-17 17:57
*/
public interface CommonState {
/**
*
*/
Integer NORMAL = 0;
/**
*
*/
Integer ABNORMAL = 1;
}

@ -0,0 +1,18 @@
package com.yf.exam.core.enums;
/**
*
* @author bool
*/
public interface OpenType {
/**
*
*/
Integer OPEN = 1;
/**
*
*/
Integer DEPT_OPEN = 2;
}

@ -0,0 +1,51 @@
package com.yf.exam.core.exception;
import com.yf.exam.core.api.ApiError;
import com.yf.exam.core.api.ApiRest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ServiceException extends RuntimeException{
/**
*
*/
private Integer code;
/**
*
*/
private String msg;
/**
*
* @param apiRest
*/
public ServiceException(ApiRest apiRest){
this.code = apiRest.getCode();
this.msg = apiRest.getMsg();
}
/**
*
* @param apiError
*/
public ServiceException(ApiError apiError){
this.code = apiError.getCode();
this.msg = apiError.msg;
}
/**
*
* @param msg
*/
public ServiceException(String msg){
this.code = 1;
this.msg = msg;
}
}

@ -0,0 +1,46 @@
package com.yf.exam.core.exception;
import com.yf.exam.core.api.ApiRest;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
/**
*
* @author bool
* @date 2019-06-21 19:27
*/
@RestControllerAdvice
public class ServiceExceptionHandler {
/**
* @RequestMapping
* @param binder
*/
@InitBinder
public void initWebBinder(WebDataBinder binder){
}
/**
* Model使@RequestMapping
* @param model
*/
@ModelAttribute
public void addAttribute(Model model) {
}
/**
* ServiceException
* @param e
* @return
*/
@ExceptionHandler({com.yf.exam.core.exception.ServiceException.class})
@ResponseStatus(HttpStatus.OK)
public ApiRest serviceExceptionHandler(ServiceException e) {
return new ApiRest(e);
}
}

@ -0,0 +1,59 @@
package com.yf.exam.core.utils;
import org.dozer.DozerBeanMapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Dozer, Bean<->BeanMapper.:
*
* 1. Mapper.
* 2. .
* 3. Collection.
* 4. BAB.
*
*/
public class BeanMapper {
/**
* Dozer, DozerMapper.
*/
private static DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
/**
* Dozer.
*/
public static <T> T map(Object source, Class<T> destinationClass) {
return dozerBeanMapper.map(source, destinationClass);
}
/**
* DozerCollection.
*/
public static <T> List<T> mapList(Iterable<?> sourceList, Class<T> destinationClass) {
List<T> destinationList = new ArrayList();
for (Object sourceObject : sourceList) {
T destinationObject = dozerBeanMapper.map(sourceObject, destinationClass);
destinationList.add(destinationObject);
}
return destinationList;
}
/**
* DozerAB.
*/
public static void copy(Object source, Object destinationObject) {
if(source!=null) {
dozerBeanMapper.map(source, destinationObject);
}
}
public static <T, S> List<T> mapList(Collection<S> source, Function<? super S, ? extends T> mapper) {
return source.stream().map(mapper).collect(Collectors.toList());
}
}

@ -0,0 +1,31 @@
package com.yf.exam.core.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* quartz
* @author bool
* @date 2020/11/29 3:00
*/
public class CronUtils {
/**
*
*/
private static final String DATE_FORMAT = "ss mm HH dd MM ? yyyy";
/**
*
* @param date
* @return
*/
public static String dateToCron(final Date date){
SimpleDateFormat fmt = new SimpleDateFormat(DATE_FORMAT);
String formatTimeStr = "";
if (date != null) {
formatTimeStr = fmt.format(date);
}
return formatTimeStr;
}
}

@ -0,0 +1,103 @@
package com.yf.exam.core.utils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
*
* ClassName: DateUtils <br/>
* date: 20181213 6:34:02 <br/>
*
* @author Bool
* @version
*/
public class DateUtils {
/**
*
* calcExpDays: <br/>
* @author Bool
* @param userCreateTime
* @return
* @since JDK 1.6
*/
public static int calcExpDays(Date userCreateTime){
Calendar start = Calendar.getInstance();
start.setTime(userCreateTime);
Calendar now = Calendar.getInstance();
now.setTime(new Date());
long l = now.getTimeInMillis() - start.getTimeInMillis();
int days = new Long(l / (1000 * 60 * 60 * 24)).intValue();
return days;
}
/**
*
* dateNow:. <br/>
* @author Bool
* @param format
* @return
*/
public static String dateNow(String format) {
SimpleDateFormat fmt = new SimpleDateFormat(format);
Calendar c = new GregorianCalendar();
return fmt.format(c.getTime());
}
/**
* formatDate: <br/>
* @author Bool
* @param time
* @param format
* @return
*/
public static String formatDate(Date time, String format) {
SimpleDateFormat fmt = new SimpleDateFormat(format);
return fmt.format(time.getTime());
}
/**
* parseDate:使yyyy-MM-dd HH:mm:ss
* @author Bool
* @param date
* @return
*/
public static Date parseDate(String date) {
return parseDate(date, "yyyy-MM-dd HH:mm:ss");
}
/**
*
* parseDate:使
* @author Bool
* @param date
* @param pattern
* @return
*/
public static Date parseDate(String date, String pattern) {
if (pattern==null) {
pattern = "yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat fmt = new SimpleDateFormat(pattern);
try {
return fmt.parse(date);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}

@ -0,0 +1,65 @@
package com.yf.exam.core.utils;
import javax.servlet.http.HttpServletRequest;
/**
* IPIP
* ClassName: IpUtils <br/>
* date: 2018213 7:27:52 <br/>
*
* @author Bool
* @version
*/
public class IpUtils {
/**
*
* getClientIp:IP
* @author Bool
* @param request
* @return
*/
public static String extractClientIp(HttpServletRequest request) {
String ip = null;
//X-Forwarded-ForSquid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IPapache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IPweblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//X-Real-IPnginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
//有些网络通过多层代理那么获取到的ip就会有多个一般都是通过逗号,分割开来并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
//还是不能获取到最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}

@ -0,0 +1,324 @@
/**
* Copyright (c) 2005-2012 springside.org.cn
*/
package com.yf.exam.core.utils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.util.Assert;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* .
* getter/setter, 访, , Class, AOP.
* @author calvin
* @version 2016-01-15
*/
@Log4j2
public class Reflections {
private static final String SETTER_PREFIX = "set";
private static final String GETTER_PREFIX = "get";
private static final String CGLIB_CLASS_SEPARATOR = "$$";
/**
*
*
* @param object
* @return
*/
public static Field[] getAllFields(Object object) {
Class<?> clazz = object.getClass();
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fields;
}
/**
* Getter.
* ..
*/
public static Object invokeGetter(Object obj, String propertyName) {
Object object = obj;
for (String name : StringUtils.split(propertyName, ".")){
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}
return object;
}
/**
* Setter,
* ..
*/
public static void invokeSetter(Object obj, String propertyName, Object value) {
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i=0; i<names.length; i++){
if(i<names.length-1){
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}else{
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
invokeMethodByName(object, setterMethodName, new Object[] { value });
}
}
}
/**
* , private/protected, getter.
*/
public static Object getFieldValue(final Object obj, final String fieldName) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}
Object result = null;
try {
result = field.get(obj);
} catch (IllegalAccessException e) {
log.error("不可能抛出的异常{}", e.getMessage());
}
return result;
}
/**
* , private/protected, setter.
*/
public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}
try {
field.set(obj, value);
} catch (IllegalAccessException e) {
log.error("不可能抛出的异常:{}", e.getMessage());
}
}
/**
* , private/protected.
* 使getAccessibleMethod()Method.
* +
*/
public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args) {
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
}
try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}
/**
* , private/protected
* 使getAccessibleMethodByName()Method.
*
*/
public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
Method method = getAccessibleMethodByName(obj, methodName);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
}
try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}
/**
* , DeclaredField, 访.
*
* Object, null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
} catch (NoSuchFieldException e) {//NOSONAR
// Field不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}
/**
* , DeclaredMethod,访.
* Object, null.
* +
*
* . 使Method,Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
try {
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
} catch (NoSuchMethodException e) {
// Method不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}
/**
* , DeclaredMethod,访.
* Object, null.
*
*
* . 使Method,Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
makeAccessible(method);
return method;
}
}
}
return null;
}
/**
* private/protectedpublicJDKSecurityManager
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
}
}
/**
* private/protectedpublicJDKSecurityManager
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}
/**
* , Class,
* , Object.class.
* eg.
* public UserDao extends HibernateDao<User>
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}
/**
* , Class.
* , Object.class.
*
* public UserDao extends HibernateDao<User,Long>
*
* @param clazz clazz The class to introspect
* @param index the Index of the generic ddeclaration,start from 0.
* @return the index generic declaration, or Object.class if cannot be determined
*/
public static Class getClassGenricType(final Class clazz, final int index) {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class)) {
log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class) params[index];
}
public static Class<?> getUserClass(Object instance) {
Assert.notNull(instance, "Instance must not be null");
Class clazz = instance.getClass();
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass)) {
return superClass;
}
}
return clazz;
}
/**
* checked exceptionunchecked exception.
*/
public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException) {
return new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(((InvocationTargetException) e).getTargetException());
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
return new RuntimeException("Unexpected Checked Exception.", e);
}
}

@ -0,0 +1,32 @@
package com.yf.exam.core.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Spring
*
* @author bool
* @date 2019-12-09 15:55
*/
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static <T> T getBean(Class<T> tClass) {
return applicationContext.getBean(tClass);
}
public static <T> T getBean(String name, Class<T> type) {
return applicationContext.getBean(name, type);
}
}

@ -0,0 +1,39 @@
package com.yf.exam.core.utils;
import java.util.Map;
/**
*
* @author bool
* @date 2019-05-15 11:40
*/
public class StringUtils {
/**
*
* @param str
* @return
*/
public static boolean isBlank(String str){
return str==null || "".equals(str);
}
/**
* MAPxml<xml><key>value</key>...</xml>
* @param params
* @return
*/
public static String mapToXml(Map<String, String> params){
StringBuffer sb = new StringBuffer("<xml>");
for(String key:params.keySet()){
sb.append("<")
.append(key).append(">")
.append(params.get(key))
.append("</").append(key).append(">");
}
sb.append("</xml>");
return sb.toString();
}
}

@ -0,0 +1,402 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.yf.exam.core.utils.excel;
import com.google.common.collect.Lists;
import com.yf.exam.core.utils.Reflections;
import com.yf.exam.core.utils.excel.annotation.ExcelField;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* ExcelXLSX @see org.apache.poi.ss.SpreadsheetVersion
* @author jeeplus
* @version 2016-04-21
*/
public class ExportExcel {
private static Logger log = LoggerFactory.getLogger(ExportExcel.class);
/**
*
*/
private SXSSFWorkbook wb;
/**
*
*/
private Sheet sheet;
/**
*
*/
private Map<String, CellStyle> styles;
/**
*
*/
private int rownum;
/**
* Object[]{ ExcelField, Field/Method }
*/
List<Object[]> annotationList = Lists.newArrayList();
/**
*
* @param title
* @param cls annotation.ExportField
*/
public ExportExcel(String title, Class<?> cls){
this(title, cls, 1);
}
/**
*
* @param title
* @param cls annotation.ExportField
* @param type 1:2
* @param groups
*/
public ExportExcel(String title, Class<?> cls, int type, int... groups){
// Get annotation field
Field[] fs = cls.getDeclaredFields();
for (Field f : fs){
ExcelField ef = f.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==type)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, f});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, f});
}
}
}
// Get annotation method
Method[] ms = cls.getDeclaredMethods();
for (Method m : ms){
ExcelField ef = m.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==type)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, m});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, m});
}
}
}
// Field sorting
Collections.sort(annotationList, new Comparator<Object[]>() {
@Override
public int compare(Object[] o1, Object[] o2) {
return new Integer(((ExcelField)o1[0]).sort()).compareTo(
new Integer(((ExcelField)o2[0]).sort()));
}
});
// Initialize
List<String> headerList = Lists.newArrayList();
for (Object[] os : annotationList){
String t = ((ExcelField)os[0]).title();
// 如果是导出,则去掉注释
if (type==1){
String[] ss = StringUtils.split(t, "**", 2);
if (ss.length==2){
t = ss[0];
}
}
headerList.add(t);
}
initialize(title, headerList);
}
/**
*
* @param title
* @param headerList
*/
private void initialize(String title, List<String> headerList) {
this.wb = new SXSSFWorkbook(500);
this.sheet = wb.createSheet("Export");
this.styles = createStyles(wb);
// Create title
if (StringUtils.isNotBlank(title)){
Row titleRow = sheet.createRow(rownum++);
titleRow.setHeightInPoints(30);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(),
titleRow.getRowNum(), titleRow.getRowNum(), headerList.size()-1));
}
// Create header
if (headerList == null){
throw new RuntimeException("headerList not null!");
}
Row headerRow = sheet.createRow(rownum++);
headerRow.setHeightInPoints(16);
for (int i = 0; i < headerList.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellStyle(styles.get("header"));
String[] ss = StringUtils.split(headerList.get(i), "**", 2);
if (ss.length==2){
cell.setCellValue(ss[0]);
Comment comment = this.sheet.createDrawingPatriarch().createCellComment(
new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6));
comment.setString(new XSSFRichTextString(ss[1]));
cell.setCellComment(comment);
}else{
cell.setCellValue(headerList.get(i));
}
sheet.autoSizeColumn(i);
}
for (int i = 0; i < headerList.size(); i++) {
int colWidth = sheet.getColumnWidth(i)*2;
sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth);
}
log.debug("Initialize success.");
}
/**
*
* @param wb
* @return
*/
private Map<String, CellStyle> createStyles(Workbook wb) {
Map<String, CellStyle> styles = new HashMap<>(16);
CellStyle style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
Font titleFont = wb.createFont();
titleFont.setFontName("Arial");
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
style.setFont(titleFont);
styles.put("title", style);
style = wb.createCellStyle();
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setBorderRight(CellStyle.BORDER_THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(CellStyle.BORDER_THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(CellStyle.BORDER_THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(CellStyle.BORDER_THIN);
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
style.setFont(dataFont);
styles.put("data", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(CellStyle.ALIGN_LEFT);
styles.put("data1", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(CellStyle.ALIGN_CENTER);
styles.put("data2", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(CellStyle.ALIGN_RIGHT);
styles.put("data3", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
// style.setWrapText(true);
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
Font headerFont = wb.createFont();
headerFont.setFontName("Arial");
headerFont.setFontHeightInPoints((short) 10);
headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
headerFont.setColor(IndexedColors.WHITE.getIndex());
style.setFont(headerFont);
styles.put("header", style);
return styles;
}
/**
*
* @return
*/
public Row addRow(){
return sheet.createRow(rownum++);
}
/**
*
* @param row
* @param column
* @param val
* @return
*/
public Cell addCell(Row row, int column, Object val){
return this.addCell(row, column, val, 0, Class.class);
}
/**
*
* @param row
* @param column
* @param val
* @param align 123
* @return
*/
public Cell addCell(Row row, int column, Object val, int align, Class<?> fieldType){
Cell cell = row.createCell(column);
CellStyle style = styles.get("data"+(align>=1&&align<=3?align:""));
try {
if (val == null){
cell.setCellValue("");
} else if (val instanceof String) {
cell.setCellValue((String) val);
} else if (val instanceof Integer) {
cell.setCellValue((Integer) val);
} else if (val instanceof Long) {
cell.setCellValue((Long) val);
} else if (val instanceof Double) {
cell.setCellValue((Double) val);
} else if (val instanceof Float) {
cell.setCellValue((Float) val);
} else if (val instanceof Date) {
DataFormat format = wb.createDataFormat();
style.setDataFormat(format.getFormat("yyyy-MM-dd"));
cell.setCellValue((Date) val);
} else {
if (fieldType != Class.class){
cell.setCellValue((String)fieldType.getMethod("setValue", Object.class).invoke(null, val));
}else{
cell.setCellValue((String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+val.getClass().getSimpleName()+"Type")).getMethod("setValue", Object.class).invoke(null, val));
}
}
} catch (Exception ex) {
log.info("Set cell value ["+row.getRowNum()+","+column+"] error: " + ex.toString());
cell.setCellValue(val.toString());
}
cell.setCellStyle(style);
return cell;
}
/**
* annotation.ExportField
* @return list
*/
public <E> ExportExcel setDataList(List<E> list){
for (E e : list){
int colunm = 0;
Row row = this.addRow();
StringBuilder sb = new StringBuilder();
for (Object[] os : annotationList){
ExcelField ef = (ExcelField)os[0];
Object val = null;
try{
if (StringUtils.isNotBlank(ef.value())){
val = Reflections.invokeGetter(e, ef.value());
}else{
if (os[1] instanceof Field){
val = Reflections.invokeGetter(e, ((Field)os[1]).getName());
}else if (os[1] instanceof Method){
val = Reflections.invokeMethod(e, ((Method)os[1]).getName(), new Class[] {}, new Object[] {});
}
}
}catch(Exception ex) {
log.info(ex.toString());
val = "";
}
this.addCell(row, colunm++, val, ef.align(), ef.fieldType());
sb.append(val + ", ");
}
log.debug("Write success: ["+row.getRowNum()+"] "+sb.toString());
}
return this;
}
/**
*
* @param os
*/
public ExportExcel write(OutputStream os) throws IOException{
wb.write(os);
return this;
}
/**
*
* @param fileName
*/
public ExportExcel write(HttpServletResponse response, String fileName) throws IOException{
response.reset();
response.setHeader("Access-Control-Allow-Origin", "*");
response.setContentType("application/octet-stream; charset=utf-8");
response.addHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode(fileName, "utf-8"));
write(response.getOutputStream());
return this;
}
/**
*
*/
public ExportExcel dispose(){
wb.dispose();
return this;
}
}

@ -0,0 +1,303 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.yf.exam.core.utils.excel;
import com.google.common.collect.Lists;
import com.yf.exam.core.utils.Reflections;
import com.yf.exam.core.utils.excel.annotation.ExcelField;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/**
* ExcelXLSXLSX
* @author jeeplus
* @version 2016-03-10
*/
public class ImportExcel {
private static Logger log = LoggerFactory.getLogger(ImportExcel.class);
/**
*
*/
private Workbook wb;
/**
*
*/
private Sheet sheet;
/**
*
*/
private int headerNum;
/**
*
* @param multipartFile
* @param headerNum =+1
* @param sheetIndex
* @throws InvalidFormatException
* @throws IOException
*/
public ImportExcel(MultipartFile multipartFile, int headerNum, int sheetIndex)
throws InvalidFormatException, IOException {
this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex);
}
/**
*
* @param is
* @param headerNum =+1
* @param sheetIndex
* @throws InvalidFormatException
* @throws IOException
*/
public ImportExcel(String fileName, InputStream is, int headerNum, int sheetIndex)
throws IOException {
if (StringUtils.isBlank(fileName)){
throw new RuntimeException("导入文档为空!");
}else if(fileName.toLowerCase().endsWith("xls")){
this.wb = new HSSFWorkbook(is);
}else if(fileName.toLowerCase().endsWith("xlsx")){
this.wb = new XSSFWorkbook(is);
}else{
throw new RuntimeException("文档格式不正确!");
}
if (this.wb.getNumberOfSheets()<sheetIndex){
throw new RuntimeException("文档中没有工作表!");
}
this.sheet = this.wb.getSheetAt(sheetIndex);
this.headerNum = headerNum;
log.debug("Initialize success.");
}
/**
*
* @param rownum
* @return
*/
public Row getRow(int rownum){
return this.sheet.getRow(rownum);
}
/**
*
* @return
*/
public int getDataRowNum(){
return headerNum+1;
}
/**
*
* @return
*/
public int getLastDataRowNum(){
return this.sheet.getLastRowNum()+headerNum;
}
/**
*
* @param row
* @param column
* @return
*/
public Object getCellValue(Row row, int column) {
Object val = "";
try {
Cell cell = row.getCell(column);
if (cell != null) {
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
// 当excel 中的数据为数值或日期是需要特殊处理
if (HSSFDateUtil.isCellDateFormatted(cell)) {
double d = cell.getNumericCellValue();
Date date = HSSFDateUtil.getJavaDate(d);
SimpleDateFormat dformat = new SimpleDateFormat(
"yyyy-MM-dd");
val = dformat.format(date);
} else {
NumberFormat nf = NumberFormat.getInstance();
nf.setGroupingUsed(false);// true时的格式1,234,567,890
val = nf.format(cell.getNumericCellValue());// 数值类型的数据为double所以需要转换一下
}
} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
val = cell.getStringCellValue();
} else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
val = cell.getCellFormula();
} else if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
val = cell.getBooleanCellValue();
} else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
val = cell.getErrorCellValue();
}
}
} catch (Exception e) {
return val;
}
return val;
}
/**
*
* @param cls
* @param groups
*/
public <E> List<E> getDataList(Class<E> cls, int... groups) throws InstantiationException, IllegalAccessException{
List<Object[]> annotationList = Lists.newArrayList();
// Get annotation field
Field[] fs = cls.getDeclaredFields();
for (Field f : fs){
ExcelField ef = f.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==2)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, f});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, f});
}
}
}
// Get annotation method
Method[] ms = cls.getDeclaredMethods();
for (Method m : ms){
ExcelField ef = m.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==2)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, m});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, m});
}
}
}
// Field sorting
Collections.sort(annotationList, new Comparator<Object[]>() {
@Override
public int compare(Object[] o1, Object[] o2) {
return new Integer(((ExcelField)o1[0]).sort()).compareTo(
new Integer(((ExcelField)o2[0]).sort()));
}
});
// Get excel data
List<E> dataList = Lists.newArrayList();
for (int i = this.getDataRowNum(); i < this.getLastDataRowNum(); i++) {
E e = (E)cls.newInstance();
int column = 0;
Row row = this.getRow(i);
StringBuilder sb = new StringBuilder();
for (Object[] os : annotationList){
Object val = this.getCellValue(row, column++);
if (val != null){
ExcelField ef = (ExcelField)os[0];
// Get param type and type cast
Class<?> valType = Class.class;
if (os[1] instanceof Field){
valType = ((Field)os[1]).getType();
}else if (os[1] instanceof Method){
Method method = ((Method)os[1]);
if ("get".equals(method.getName().substring(0, 3))){
valType = method.getReturnType();
}else if("set".equals(method.getName().substring(0, 3))){
valType = ((Method)os[1]).getParameterTypes()[0];
}
}
//log.debug("Import value type: ["+i+","+column+"] " + valType);
try {
//如果导入的java对象需要在这里自己进行变换。
if (valType == String.class){
String s = String.valueOf(val.toString());
if(StringUtils.endsWith(s, ".0")){
val = StringUtils.substringBefore(s, ".0");
}else{
val = String.valueOf(val.toString());
}
}else if (valType == Integer.class){
val = Double.valueOf(val.toString()).intValue();
}else if (valType == Long.class){
val = Double.valueOf(val.toString()).longValue();
}else if (valType == Double.class){
val = Double.valueOf(val.toString());
}else if (valType == Float.class){
val = Float.valueOf(val.toString());
}else if (valType == Date.class){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
val=sdf.parse(val.toString());
}else{
if (ef.fieldType() != Class.class){
val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val.toString());
}else{
val = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+valType.getSimpleName()+"Type")).getMethod("getValue", String.class).invoke(null, val.toString());
}
}
} catch (Exception ex) {
log.info("Get cell value ["+i+","+column+"] error: " + ex.toString());
val = null;
}
// set entity value
if (os[1] instanceof Field){
Reflections.invokeSetter(e, ((Field)os[1]).getName(), val);
}else if (os[1] instanceof Method){
String mthodName = ((Method)os[1]).getName();
if ("get".equals(mthodName.substring(0, 3))){
mthodName = "set"+StringUtils.substringAfter(mthodName, "get");
}
Reflections.invokeMethod(e, mthodName, new Class[] {valType}, new Object[] {val});
}
}
sb.append(val+", ");
}
dataList.add(e);
log.debug("Read success: ["+i+"] "+sb.toString());
}
return dataList;
}
}

@ -0,0 +1,59 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.yf.exam.core.utils.excel.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel
* @author jeeplus
* @version 2016-03-10
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelField {
/**
* get.area.nameoffice.name
*/
String value() default "";
/**
* ****
*/
String title();
/**
* 012
*/
int type() default 0;
/**
* 0123
*/
int align() default 0;
/**
*
*/
int sort() default 0;
/**
* type
*/
String dictType() default "";
/**
*
*/
Class<?> fieldType() default Class.class;
/**
*
*/
int[] groups() default {};
}

@ -0,0 +1,56 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.yf.exam.core.utils.excel.fieldtype;
import com.google.common.collect.Lists;
import com.yf.exam.core.utils.StringUtils;
import java.util.List;
/**
*
* @author jeeplus
* @version 2016-5-29
*/
public class ListType {
/**
*
*/
public static Object getValue(String val) {
List<String> list = Lists.newArrayList();
if(!StringUtils.isBlank(val)) {
for (String s : val.split(",")) {
list.add(s);
}
}
return list;
}
/**
*
*/
public static String setValue(Object val) {
if (val != null){
List<String> list = (List<String>)val;
StringBuffer sb = null;
for (String item: list){
if(StringUtils.isBlank(item)){
continue;
}
if(sb == null){
sb = new StringBuffer(item);
}else{
sb.append(",").append(item);
}
}
if(sb!=null) {
return sb.toString().replace("[]", "");
}
}
return "";
}
}

@ -0,0 +1,37 @@
package com.yf.exam.core.utils.file;
import java.security.MessageDigest;
/**
* MD5
* ClassName: MD5Util <br/>
* date: 2018113 6:54:53 <br/>
*
* @author Bool
* @version
*/
public class Md5Util {
/**
* MD5
* @param str
* @return
*/
public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(str.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString();
}catch(Exception e) {
return null;
}
}
}

@ -0,0 +1,57 @@
package com.yf.exam.core.utils.passwd;
import com.yf.exam.core.utils.file.Md5Util;
import org.apache.commons.lang3.RandomStringUtils;
/**
*
* ClassName: PassGenerator <br/>
* date: 20171213 7:13:03 <br/>
*
* @author Bool
* @version
*/
public class PassHandler {
/**
* checkPass:
* @author Bool
* @param inputPass
* @param salt
* @param pass MD5
* @return
*/
public static boolean checkPass(String inputPass , String salt , String pass){
String pwdMd5 = Md5Util.md5(inputPass);
return Md5Util.md5(pwdMd5 + salt).equals(pass);
}
/**
*
* buildPassword:
* @author Bool
* @param inputPass
* @return PassInfo
*/
public static PassInfo buildPassword(String inputPass) {
//产生一个6位数的随机码
String salt = RandomStringUtils.randomAlphabetic(6);
//加密后的密码
String encryptPassword = Md5Util.md5(Md5Util.md5(inputPass)+salt);
//返回对象
return new PassInfo(salt,encryptPassword);
}
public static void main(String[] args) {
PassInfo info = buildPassword("190601");
System.out.println(info.getPassword());
System.out.println(info.getSalt());
}
}

@ -0,0 +1,38 @@
package com.yf.exam.core.utils.passwd;
/**
*
* ClassName: PassInfo <br/>
* date: 2018213 7:13:50 <br/>
*
* @author Bool
* @version
*/
public class PassInfo {
//密码随机串码
private String salt;
//MD5后的密码
private String password;
public PassInfo(String salt, String password) {
super();
this.salt = salt;
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

@ -0,0 +1,13 @@
package com.yf.exam.modules;
/**
*
* @author bool
*/
public class Constant {
/**
*
*/
public static final String TOKEN = "token";
}

@ -0,0 +1,151 @@
package com.yf.exam.modules.exam.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.api.dto.BaseIdReqDTO;
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
import com.yf.exam.core.api.dto.BaseStateReqDTO;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
import com.yf.exam.modules.exam.entity.Exam;
import com.yf.exam.modules.exam.service.ExamService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Api(tags={"考试"})
@RestController
@RequestMapping("/exam/api/exam/exam")
public class ExamController extends BaseController {
@Autowired
private ExamService baseService;
/**
*
* @param reqDTO
* @return
*/
@RequiresRoles("sa")
@ApiOperation(value = "添加或修改")
@RequestMapping(value = "/save", method = { RequestMethod.POST})
public ApiRest save(@RequestBody ExamSaveReqDTO reqDTO) {
//复制参数
baseService.save(reqDTO);
return super.success();
}
/**
*
* @param reqDTO
* @return
*/
@RequiresRoles("sa")
@ApiOperation(value = "批量删除")
@RequestMapping(value = "/delete", method = { RequestMethod.POST})
public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
//根据ID删除
baseService.removeByIds(reqDTO.getIds());
return super.success();
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "查找详情")
@RequestMapping(value = "/detail", method = { RequestMethod.POST})
public ApiRest<ExamSaveReqDTO> find(@RequestBody BaseIdReqDTO reqDTO) {
ExamSaveReqDTO dto = baseService.findDetail(reqDTO.getId());
return super.success(dto);
}
/**
*
* @param reqDTO
* @return
*/
@RequiresRoles("sa")
@ApiOperation(value = "查找详情")
@RequestMapping(value = "/state", method = { RequestMethod.POST})
public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) {
QueryWrapper<Exam> wrapper = new QueryWrapper<>();
wrapper.lambda().in(Exam::getId, reqDTO.getIds());
Exam exam = new Exam();
exam.setState(reqDTO.getState());
exam.setUpdateTime(new Date());
baseService.update(exam, wrapper);
return super.success();
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "考试视角")
@RequestMapping(value = "/online-paging", method = { RequestMethod.POST})
public ApiRest<IPage<ExamOnlineRespDTO>> myPaging(@RequestBody PagingReqDTO<ExamDTO> reqDTO) {
//分页查询并转换
IPage<ExamOnlineRespDTO> page = baseService.onlinePaging(reqDTO);
return super.success(page);
}
/**
*
* @param reqDTO
* @return
*/
@RequiresRoles("sa")
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<ExamDTO>> paging(@RequestBody PagingReqDTO<ExamDTO> reqDTO) {
//分页查询并转换
IPage<ExamDTO> page = baseService.paging(reqDTO);
return super.success(page);
}
/**
*
* @param reqDTO
* @return
*/
@RequiresRoles("sa")
@ApiOperation(value = "待阅试卷")
@RequestMapping(value = "/review-paging", method = { RequestMethod.POST})
public ApiRest<IPage<ExamReviewRespDTO>> reviewPaging(@RequestBody PagingReqDTO<ExamDTO> reqDTO) {
//分页查询并转换
IPage<ExamReviewRespDTO> page = baseService.reviewPaging(reqDTO);
return super.success(page);
}
}

@ -0,0 +1,101 @@
package com.yf.exam.modules.exam.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.yf.exam.modules.paper.enums.ExamState;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Data
@ApiModel(value="考试", description="考试")
public class ExamDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID", required=true)
private String id;
@ApiModelProperty(value = "考试名称", required=true)
private String title;
@ApiModelProperty(value = "考试描述", required=true)
private String content;
@ApiModelProperty(value = "1公开2部门3定员", required=true)
private Integer openType;
@ApiModelProperty(value = "考试状态", required=true)
private Integer state;
@ApiModelProperty(value = "是否限时", required=true)
private Boolean timeLimit;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty(value = "开始时间", required=true)
private Date startTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty(value = "结束时间", required=true)
private Date endTime;
@ApiModelProperty(value = "创建时间", required=true)
private Date createTime;
@ApiModelProperty(value = "更新时间", required=true)
private Date updateTime;
@ApiModelProperty(value = "总分数", required=true)
private Integer totalScore;
@ApiModelProperty(value = "总时长(分钟)", required=true)
private Integer totalTime;
@ApiModelProperty(value = "及格分数", required=true)
private Integer qualifyScore;
/**
*
* @return
*/
public Integer getState(){
if(this.timeLimit!=null && this.timeLimit){
if(System.currentTimeMillis() < startTime.getTime() ){
return ExamState.READY_START;
}
if(System.currentTimeMillis() > endTime.getTime()){
return ExamState.OVERDUE;
}
if(System.currentTimeMillis() > startTime.getTime()
&& System.currentTimeMillis() < endTime.getTime()
&& !ExamState.DISABLED.equals(this.state)){
return ExamState.ENABLE;
}
}
return this.state;
}
}

@ -0,0 +1,33 @@
package com.yf.exam.modules.exam.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-03 17:24
*/
@Data
@ApiModel(value="考试部门", description="考试部门")
public class ExamDepartDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID", required=true)
private String id;
@ApiModelProperty(value = "考试ID", required=true)
private String examId;
@ApiModelProperty(value = "部门ID", required=true)
private String departId;
}

@ -0,0 +1,51 @@
package com.yf.exam.modules.exam.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-05 11:14
*/
@Data
@ApiModel(value="考试题库", description="考试题库")
public class ExamRepoDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID", required=true)
private String id;
@ApiModelProperty(value = "考试ID", required=true)
private String examId;
@ApiModelProperty(value = "题库ID", required=true)
private String repoId;
@ApiModelProperty(value = "单选题数量", required=true)
private Integer radioCount;
@ApiModelProperty(value = "单选题分数", required=true)
private Integer radioScore;
@ApiModelProperty(value = "多选题数量", required=true)
private Integer multiCount;
@ApiModelProperty(value = "多选题分数", required=true)
private Integer multiScore;
@ApiModelProperty(value = "判断题数量", required=true)
private Integer judgeCount;
@ApiModelProperty(value = "判断题分数", required=true)
private Integer judgeScore;
}

@ -0,0 +1,32 @@
package com.yf.exam.modules.exam.dto.ext;
import com.yf.exam.modules.exam.dto.ExamRepoDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-05 11:14
*/
@Data
@ApiModel(value="考试题库扩展响应类", description="考试题库扩展响应类")
public class ExamRepoExtDTO extends ExamRepoDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "单选题总量", required=true)
private Integer totalRadio;
@ApiModelProperty(value = "多选题总量", required=true)
private Integer totalMulti;
@ApiModelProperty(value = "判断题总量", required=true)
private Integer totalJudge;
}

@ -0,0 +1,32 @@
package com.yf.exam.modules.exam.dto.request;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Data
@ApiModel(value="考试保存请求类", description="考试保存请求类")
public class ExamSaveReqDTO extends ExamDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "题库列表", required=true)
private List<ExamRepoExtDTO> repoList;
@ApiModelProperty(value = "考试部门列表", required=true)
private List<String> departIds;
}

@ -0,0 +1,22 @@
package com.yf.exam.modules.exam.dto.response;
import com.yf.exam.modules.exam.dto.ExamDTO;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Data
@ApiModel(value="在线考试分页响应类", description="在线考试分页响应类")
public class ExamOnlineRespDTO extends ExamDTO {
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,31 @@
package com.yf.exam.modules.exam.dto.response;
import com.yf.exam.modules.exam.dto.ExamDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Data
@ApiModel(value="阅卷分页响应类", description="阅卷分页响应类")
public class ExamReviewRespDTO extends ExamDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "考试人数", required=true)
private Integer examUser;
@ApiModelProperty(value = "待阅试卷", required=true)
private Integer unreadPaper;
}

@ -0,0 +1,100 @@
package com.yf.exam.modules.exam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Data
@TableName("el_exam")
public class Exam extends Model<Exam> {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
*
*/
private String title;
/**
*
*/
private String content;
/**
* 123
*/
@TableField("open_type")
private Integer openType;
/**
*
*/
private Integer state;
/**
*
*/
@TableField("time_limit")
private Boolean timeLimit;
/**
*
*/
@TableField("start_time")
private Date startTime;
/**
*
*/
@TableField("end_time")
private Date endTime;
/**
*
*/
@TableField("create_time")
private Date createTime;
/**
*
*/
@TableField("update_time")
private Date updateTime;
/**
*
*/
@TableField("total_score")
private Integer totalScore;
/**
*
*/
@TableField("total_time")
private Integer totalTime;
/**
*
*/
@TableField("qualify_score")
private Integer qualifyScore;
}

@ -0,0 +1,42 @@
package com.yf.exam.modules.exam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-03 17:24
*/
@Data
@TableName("el_exam_depart")
public class ExamDepart extends Model<ExamDepart> {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
@TableField("exam_id")
private String examId;
/**
* ID
*/
@TableField("depart_id")
private String departId;
}

@ -0,0 +1,78 @@
package com.yf.exam.modules.exam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-05 11:14
*/
@Data
@TableName("el_exam_repo")
public class ExamRepo extends Model<ExamRepo> {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
@TableField("exam_id")
private String examId;
/**
* ID
*/
@TableField("repo_id")
private String repoId;
/**
*
*/
@TableField("radio_count")
private Integer radioCount;
/**
*
*/
@TableField("radio_score")
private Integer radioScore;
/**
*
*/
@TableField("multi_count")
private Integer multiCount;
/**
*
*/
@TableField("multi_score")
private Integer multiScore;
/**
*
*/
@TableField("judge_count")
private Integer judgeCount;
/**
*
*/
@TableField("judge_score")
private Integer judgeScore;
}

@ -0,0 +1,15 @@
package com.yf.exam.modules.exam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yf.exam.modules.exam.entity.ExamDepart;
/**
* <p>
* Mapper
* </p>
*
* @author
* @since 2020-09-03 17:24
*/
public interface ExamDepartMapper extends BaseMapper<ExamDepart> {
}

@ -0,0 +1,45 @@
package com.yf.exam.modules.exam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
import com.yf.exam.modules.exam.entity.Exam;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
public interface ExamMapper extends BaseMapper<Exam> {
/**
*
* @param page
* @param query
* @return
*/
IPage<ExamDTO> paging(Page page, @Param("query") ExamDTO query);
/**
*
* @param page
* @param query
* @return
*/
IPage<ExamReviewRespDTO> reviewPaging(Page page, @Param("query") ExamDTO query);
/**
* 线-
* @param page
* @param query
* @return
*/
IPage<ExamOnlineRespDTO> online(Page page, @Param("query") ExamDTO query);
}

@ -0,0 +1,26 @@
package com.yf.exam.modules.exam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
import com.yf.exam.modules.exam.entity.ExamRepo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* Mapper
* </p>
*
* @author
* @since 2020-09-05 11:14
*/
public interface ExamRepoMapper extends BaseMapper<ExamRepo> {
/**
*
* @param examId
* @return
*/
List<ExamRepoExtDTO> listByExam(@Param("examId") String examId);
}

@ -0,0 +1,32 @@
package com.yf.exam.modules.exam.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yf.exam.modules.exam.entity.ExamDepart;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-03 17:24
*/
public interface ExamDepartService extends IService<ExamDepart> {
/**
*
* @param examId
* @param departs
*/
void saveAll(String examId, List<String> departs);
/**
*
* @param examId
* @return
*/
List<String> listByExam(String examId);
}

@ -0,0 +1,40 @@
package com.yf.exam.modules.exam.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
import com.yf.exam.modules.exam.entity.ExamRepo;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-05 11:14
*/
public interface ExamRepoService extends IService<ExamRepo> {
/**
*
* @param examId
* @param list
*/
void saveAll(String examId, List<ExamRepoExtDTO> list);
/**
*
* @param examId
* @return
*/
List<ExamRepoExtDTO> listByExam(String examId);
/**
*
* @param examId
*/
void clear(String examId);
}

@ -0,0 +1,64 @@
package com.yf.exam.modules.exam.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
import com.yf.exam.modules.exam.entity.Exam;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
public interface ExamService extends IService<Exam> {
/**
*
* @param reqDTO
*/
void save(ExamSaveReqDTO reqDTO);
/**
*
* @param id
* @return
*/
ExamSaveReqDTO findDetail(String id);
/**
* --
* @param id
* @return
*/
ExamDTO findById(String id);
/**
*
* @param reqDTO
* @return
*/
IPage<ExamDTO> paging(PagingReqDTO<ExamDTO> reqDTO);
/**
* 线-
* @param reqDTO
* @return
*/
IPage<ExamOnlineRespDTO> onlinePaging(PagingReqDTO<ExamDTO> reqDTO);
/**
*
* @param reqDTO
* @return
*/
IPage<ExamReviewRespDTO> reviewPaging(PagingReqDTO<ExamDTO> reqDTO);
}

@ -0,0 +1,66 @@
package com.yf.exam.modules.exam.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.modules.exam.entity.ExamDepart;
import com.yf.exam.modules.exam.mapper.ExamDepartMapper;
import com.yf.exam.modules.exam.service.ExamDepartService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-03 17:24
*/
@Service
public class ExamDepartServiceImpl extends ServiceImpl<ExamDepartMapper, ExamDepart> implements ExamDepartService {
@Override
public void saveAll(String examId, List<String> departs) {
// 先删除
QueryWrapper<ExamDepart> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(ExamDepart::getExamId, examId);
this.remove(wrapper);
// 再增加
if(CollectionUtils.isEmpty(departs)){
throw new ServiceException(1, "请至少选择选择一个部门!!");
}
List<ExamDepart> list = new ArrayList<>();
for(String id: departs){
ExamDepart depart = new ExamDepart();
depart.setDepartId(id);
depart.setExamId(examId);
list.add(depart);
}
this.saveBatch(list);
}
@Override
public List<String> listByExam(String examId) {
// 先删除
QueryWrapper<ExamDepart> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(ExamDepart::getExamId, examId);
List<ExamDepart> list = this.list(wrapper);
List<String> ids = new ArrayList<>();
if(!CollectionUtils.isEmpty(list)){
for(ExamDepart item: list){
ids.add(item.getDepartId());
}
}
return ids;
}
}

@ -0,0 +1,67 @@
package com.yf.exam.modules.exam.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.core.utils.BeanMapper;
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
import com.yf.exam.modules.exam.entity.ExamRepo;
import com.yf.exam.modules.exam.mapper.ExamRepoMapper;
import com.yf.exam.modules.exam.service.ExamRepoService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-05 11:14
*/
@Service
public class ExamRepoServiceImpl extends ServiceImpl<ExamRepoMapper, ExamRepo> implements ExamRepoService {
@Transactional(rollbackFor = Exception.class)
@Override
public void saveAll(String examId, List<ExamRepoExtDTO> list) {
// 先删除
QueryWrapper<ExamRepo> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(ExamRepo::getExamId, examId);
this.remove(wrapper);
// 再增加
if(CollectionUtils.isEmpty(list)){
throw new ServiceException(1, "必须选择题库!");
}
List<ExamRepo> repos = BeanMapper.mapList(list, ExamRepo.class);
for(ExamRepo item: repos){
item.setExamId(examId);
item.setId(IdWorker.getIdStr());
}
this.saveBatch(repos);
}
@Override
public List<ExamRepoExtDTO> listByExam(String examId) {
return baseMapper.listByExam(examId);
}
@Override
public void clear(String examId) {
// 先删除
QueryWrapper<ExamRepo> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(ExamRepo::getExamId, examId);
this.remove(wrapper);
}
}

@ -0,0 +1,194 @@
package com.yf.exam.modules.exam.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.core.enums.OpenType;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.core.utils.BeanMapper;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.ExamRepoDTO;
import com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO;
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
import com.yf.exam.modules.exam.entity.Exam;
import com.yf.exam.modules.exam.mapper.ExamMapper;
import com.yf.exam.modules.exam.service.ExamDepartService;
import com.yf.exam.modules.exam.service.ExamRepoService;
import com.yf.exam.modules.exam.service.ExamService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-07-25 16:18
*/
@Service
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService {
@Autowired
private ExamRepoService examRepoService;
@Autowired
private ExamDepartService examDepartService;
@Override
public void save(ExamSaveReqDTO reqDTO) {
// ID
String id = reqDTO.getId();
if(StringUtils.isBlank(id)){
id = IdWorker.getIdStr();
}
//复制参数
Exam entity = new Exam();
// 计算分值
this.calcScore(reqDTO);
// 复制基本数据
BeanMapper.copy(reqDTO, entity);
entity.setId(id);
// 修复状态
if (reqDTO.getTimeLimit()!=null
&& !reqDTO.getTimeLimit()
&& reqDTO.getState()!=null
&& reqDTO.getState() == 2) {
entity.setState(0);
} else {
entity.setState(reqDTO.getState());
}
// 题库组卷
try {
examRepoService.saveAll(id, reqDTO.getRepoList());
}catch (DuplicateKeyException e){
throw new ServiceException(1, "不能选择重复的题库!");
}
// 开放的部门
if(OpenType.DEPT_OPEN.equals(reqDTO.getOpenType())){
examDepartService.saveAll(id, reqDTO.getDepartIds());
}
this.saveOrUpdate(entity);
}
@Override
public ExamSaveReqDTO findDetail(String id) {
ExamSaveReqDTO respDTO = new ExamSaveReqDTO();
Exam exam = this.getById(id);
BeanMapper.copy(exam, respDTO);
// 考试部门
List<String> departIds = examDepartService.listByExam(id);
respDTO.setDepartIds(departIds);
// 题库
List<ExamRepoExtDTO> repos = examRepoService.listByExam(id);
respDTO.setRepoList(repos);
return respDTO;
}
@Override
public ExamDTO findById(String id) {
ExamDTO respDTO = new ExamDTO();
Exam exam = this.getById(id);
BeanMapper.copy(exam, respDTO);
return respDTO;
}
@Override
public IPage<ExamDTO> paging(PagingReqDTO<ExamDTO> reqDTO) {
//创建分页对象
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
//转换结果
IPage<ExamDTO> pageData = baseMapper.paging(page, reqDTO.getParams());
return pageData;
}
@Override
public IPage<ExamOnlineRespDTO> onlinePaging(PagingReqDTO<ExamDTO> reqDTO) {
// 创建分页对象
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
// 查找分页
IPage<ExamOnlineRespDTO> pageData = baseMapper.online(page, reqDTO.getParams());
return pageData;
}
@Override
public IPage<ExamReviewRespDTO> reviewPaging(PagingReqDTO<ExamDTO> reqDTO) {
// 创建分页对象
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
// 查找分页
IPage<ExamReviewRespDTO> pageData = baseMapper.reviewPaging(page, reqDTO.getParams());
return pageData;
}
/**
*
* @param reqDTO
*/
private void calcScore(ExamSaveReqDTO reqDTO){
// 主观题分数
int objScore = 0;
// 题库组卷
List<ExamRepoExtDTO> repoList = reqDTO.getRepoList();
for(ExamRepoDTO item: repoList){
if(item.getRadioCount()!=null
&& item.getRadioCount()>0
&& item.getRadioScore()!=null
&& item.getRadioScore()>0){
objScore+=item.getRadioCount()*item.getRadioScore();
}
if(item.getMultiCount()!=null
&& item.getMultiCount()>0
&& item.getMultiScore()!=null
&& item.getMultiScore()>0){
objScore+=item.getMultiCount()*item.getMultiScore();
}
if(item.getJudgeCount()!=null
&& item.getJudgeCount()>0
&& item.getJudgeScore()!=null
&& item.getJudgeScore()>0){
objScore+=item.getJudgeCount()*item.getJudgeScore();
}
}
reqDTO.setTotalScore(objScore);
}
}

@ -0,0 +1,156 @@
package com.yf.exam.modules.paper.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.api.dto.BaseIdReqDTO;
import com.yf.exam.core.api.dto.BaseIdRespDTO;
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.core.utils.BeanMapper;
import com.yf.exam.modules.paper.dto.PaperDTO;
import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO;
import com.yf.exam.modules.paper.dto.request.PaperAnswerDTO;
import com.yf.exam.modules.paper.dto.request.PaperCreateReqDTO;
import com.yf.exam.modules.paper.dto.request.PaperListReqDTO;
import com.yf.exam.modules.paper.dto.request.PaperQuQueryDTO;
import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO;
import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO;
import com.yf.exam.modules.paper.dto.response.PaperListRespDTO;
import com.yf.exam.modules.paper.entity.Paper;
import com.yf.exam.modules.paper.service.PaperService;
import com.yf.exam.modules.user.UserUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-05-25 16:33
*/
@Api(tags={"试卷"})
@RestController
@RequestMapping("/exam/api/paper/paper")
public class PaperController extends BaseController {
@Autowired
private PaperService baseService;
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<PaperListRespDTO>> paging(@RequestBody PagingReqDTO<PaperListReqDTO> reqDTO) {
//分页查询并转换
IPage<PaperListRespDTO> page = baseService.paging(reqDTO);
return super.success(page);
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "创建试卷")
@RequestMapping(value = "/create-paper", method = { RequestMethod.POST})
public ApiRest<BaseIdRespDTO> save(@RequestBody PaperCreateReqDTO reqDTO) {
//复制参数
String paperId = baseService.createPaper(UserUtils.getUserId(), reqDTO.getExamId());
return super.success(new BaseIdRespDTO(paperId));
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "试卷详情")
@RequestMapping(value = "/paper-detail", method = { RequestMethod.POST})
public ApiRest<ExamDetailRespDTO> paperDetail(@RequestBody BaseIdReqDTO reqDTO) {
//根据ID删除
ExamDetailRespDTO respDTO = baseService.paperDetail(reqDTO.getId());
return super.success(respDTO);
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "试题详情")
@RequestMapping(value = "/qu-detail", method = { RequestMethod.POST})
public ApiRest<PaperQuDetailDTO> quDetail(@RequestBody PaperQuQueryDTO reqDTO) {
//根据ID删除
PaperQuDetailDTO respDTO = baseService.findQuDetail(reqDTO.getPaperId(), reqDTO.getQuId());
return super.success(respDTO);
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "填充答案")
@RequestMapping(value = "/fill-answer", method = { RequestMethod.POST})
public ApiRest<PaperQuDetailDTO> fillAnswer(@RequestBody PaperAnswerDTO reqDTO) {
//根据ID删除
baseService.fillAnswer(reqDTO);
return super.success();
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "交卷操作")
@RequestMapping(value = "/hand-exam", method = { RequestMethod.POST})
public ApiRest<PaperQuDetailDTO> handleExam(@RequestBody BaseIdReqDTO reqDTO) {
//根据ID删除
baseService.handExam(reqDTO.getId());
return super.success();
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "试卷详情")
@RequestMapping(value = "/paper-result", method = { RequestMethod.POST})
public ApiRest<ExamResultRespDTO> paperResult(@RequestBody BaseIdReqDTO reqDTO) {
//根据ID删除
ExamResultRespDTO respDTO = baseService.paperResult(reqDTO.getId());
return super.success(respDTO);
}
/**
*
* @return
*/
@ApiOperation(value = "检测进行中的考试")
@RequestMapping(value = "/check-process", method = { RequestMethod.POST})
public ApiRest<PaperDTO> checkProcess() {
//复制参数
PaperDTO dto = baseService.checkProcess(UserUtils.getUserId());
return super.success(dto);
}
}

@ -0,0 +1,79 @@
package com.yf.exam.modules.paper.dto;
import com.yf.exam.core.annon.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-05-25 17:31
*/
@Data
@ApiModel(value="试卷", description="试卷")
public class PaperDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "试卷ID", required=true)
private String id;
@Dict(dictTable = "sys_user", dicText = "real_name", dicCode = "id")
@ApiModelProperty(value = "用户ID", required=true)
private String userId;
@Dict(dictTable = "sys_depart", dicText = "dept_name", dicCode = "id")
@ApiModelProperty(value = "部门ID", required=true)
private String departId;
@ApiModelProperty(value = "规则ID", required=true)
private String examId;
@ApiModelProperty(value = "考试标题", required=true)
private String title;
@ApiModelProperty(value = "考试时长", required=true)
private Integer totalTime;
@ApiModelProperty(value = "用户时长", required=true)
private Integer userTime;
@ApiModelProperty(value = "试卷总分", required=true)
private Integer totalScore;
@ApiModelProperty(value = "及格分", required=true)
private Integer qualifyScore;
@ApiModelProperty(value = "客观分", required=true)
private Integer objScore;
@ApiModelProperty(value = "主观分", required=true)
private Integer subjScore;
@ApiModelProperty(value = "用户得分", required=true)
private Integer userScore;
@ApiModelProperty(value = "是否包含简答题", required=true)
private Boolean hasSaq;
@ApiModelProperty(value = "试卷状态", required=true)
private Integer state;
@ApiModelProperty(value = "创建时间", required=true)
private Date createTime;
@ApiModelProperty(value = "更新时间", required=true)
private Date updateTime;
@ApiModelProperty(value = "截止时间")
private Date limitTime;
}

@ -0,0 +1,48 @@
package com.yf.exam.modules.paper.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-05-25 17:31
*/
@Data
@ApiModel(value="试卷考题备选答案", description="试卷考题备选答案")
public class PaperQuAnswerDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "自增ID", required=true)
private String id;
@ApiModelProperty(value = "试卷ID", required=true)
private String paperId;
@ApiModelProperty(value = "回答项ID", required=true)
private String answerId;
@ApiModelProperty(value = "题目ID", required=true)
private String quId;
@ApiModelProperty(value = "是否正确项", required=true)
private Boolean isRight;
@ApiModelProperty(value = "是否选中", required=true)
private Boolean checked;
@ApiModelProperty(value = "排序", required=true)
private Integer sort;
@ApiModelProperty(value = "选项标签", required=true)
private String abc;
}

@ -0,0 +1,54 @@
package com.yf.exam.modules.paper.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-05-25 17:31
*/
@Data
@ApiModel(value="试卷考题", description="试卷考题")
public class PaperQuDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID", required=true)
private String id;
@ApiModelProperty(value = "试卷ID", required=true)
private String paperId;
@ApiModelProperty(value = "题目ID", required=true)
private String quId;
@ApiModelProperty(value = "题目类型", required=true)
private Integer quType;
@ApiModelProperty(value = "是否已答", required=true)
private Boolean answered;
@ApiModelProperty(value = "主观答案", required=true)
private String answer;
@ApiModelProperty(value = "问题排序", required=true)
private Integer sort;
@ApiModelProperty(value = "单题分分值", required=true)
private Integer score;
@ApiModelProperty(value = "实际得分(主观题)", required=true)
private Integer actualScore;
@ApiModelProperty(value = "是否答对", required=true)
private Boolean isRight;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save