Compare commits

..

17 Commits

2
.gitignore vendored

@ -21,3 +21,5 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
**/.idea

@ -1,18 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义XML文档的版本和编码 -->
<project version="4">
<!-- 定义项目版本 -->
<component name="CompilerConfiguration">
<!-- 定义编译器配置 -->
<annotationProcessing>
<!-- 定义注解处理 -->
<profile name="Maven default annotation processors profile" enabled="true">
<!-- 定义Maven默认注解处理器配置 -->
<sourceOutputDir name="target/generated-sources/annotations" />
<!-- 定义源代码输出目录 -->
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<!-- 定义测试代码输出目录 -->
<outputRelativeToContentRoot value="true" />
<!-- 定义输出目录相对于内容根目录 -->
<module name="exam" />
<!-- 定义模块名称 -->
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<!-- 定义Javac设置 -->
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<!-- 定义附加选项覆盖 -->
<module name="exam" options="-parameters" />
<!-- 定义模块名称和选项 -->
</option>
</component>
</project>

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义XML文档的版本和编码 -->
<project version="4">
<!-- 定义项目版本 -->
<component name="Encoding">
<!-- 定义组件名称 -->
<file url="file://$PROJECT_DIR$/backend/src/main/java" charset="UTF-8" />
<!-- 定义文件路径和字符编码 -->
</component>
</project>

@ -1,6 +1,68 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AliAccessStaticViaInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliArrayNamingShouldHaveBracket" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliControlFlowStatementWithoutBraces" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliDeprecation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliEqualsAvoidNull" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliLongLiteralsEndingWithLowercaseL" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliMissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliWrapperTypeEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAbstractClassShouldStartWithAbstractNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAbstractMethodOrInterfaceMethodMustUseJavadoc" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidApacheBeanUtilsCopy" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidCallStaticSimpleDateFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidCommentBehindStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidComplexCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidConcurrentCompetitionRandom" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidDoubleOrFloatEqualCompare" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidManuallyCreateThread" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidMissUseOfMathRandom" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidNegationOperator" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidNewDateGetTime" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidPatternCompileInMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidReturnInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidStartWithDollarAndUnderLineNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidUseTimer" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaBigDecimalAvoidDoubleConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaBooleanPropertyShouldNotStartWithIs" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassCastExceptionWithSubListToArrayList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassCastExceptionWithToArray" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassMustHaveAuthor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassNamingShouldBeCamel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaCollectionInitShouldAssignCapacity" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaCommentsMustBeJavadocFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaConcurrentExceptionWithModifyOriginSubList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaConstantFieldShouldBeUpperCase" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaCountDownShouldInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaDontModifyInForeachCircle" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaEnumConstantsMustHaveComment" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaExceptionClassShouldEndWithException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaIbatisMethodQueryForList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaLockShouldWithTryFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaLowerCamelCaseVariableNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaMethodReturnWrapperType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaMethodTooLong" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPackageNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPojoMustOverrideToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPojoMustUsePrimitiveField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPojoNoDefaultValue" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaRemoveCommentedCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaServiceOrDaoClassShouldEndWithImpl" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaSneakyThrowsWithoutExceptionType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaStringConcat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaSwitchExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaTestClassShouldEndWithTestNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaThreadLocalShouldRemove" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaThreadPoolCreation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaThreadShouldSetName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaTransactionMustHaveRollback" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUndefineMagicConstant" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUnsupportedExceptionWithModifyAsList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUseQuietReferenceNotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUseRightCaseForDateFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MapOrSetKeyShouldOverrideHashCodeEquals" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>

@ -1,19 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义项目版本 -->
<project version="4">
<!-- 定义远程仓库配置 -->
<component name="RemoteRepositoriesConfiguration">
<!-- 定义第一个远程仓库 -->
<remote-repository>
<!-- 定义远程仓库ID -->
<option name="id" value="central" />
<!-- 定义远程仓库名称 -->
<option name="name" value="Central Repository" />
<!-- 定义远程仓库URL -->
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<!-- 定义第二个远程仓库 -->
<remote-repository>
<!-- 定义远程仓库ID -->
<option name="id" value="central" />
<!-- 定义远程仓库名称 -->
<option name="name" value="Maven Central repository" />
<!-- 定义远程仓库URL -->
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<!-- 定义第三个远程仓库 -->
<remote-repository>
<!-- 定义远程仓库ID -->
<option name="id" value="jboss.community" />
<!-- 定义远程仓库名称 -->
<option name="name" value="JBoss Community repository" />
<!-- 定义远程仓库URL -->
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>

@ -1,14 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 项目版本 -->
<project version="4">
<!-- 外部存储配置管理器 -->
<component name="ExternalStorageConfigurationManager" enabled="true" />
<!-- Maven项目管理器 -->
<component name="MavenProjectsManager">
<!-- 原始文件列表 -->
<option name="originalFiles">
<list>
<!-- 后端项目的pom.xml文件 -->
<option value="$PROJECT_DIR$/backend/pom.xml" />
</list>
</option>
</component>
<!-- 项目根管理器 -->
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8 (2)" project-jdk-type="JavaSDK">
<!-- 输出目录 -->
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义XML文档的版本和编码 -->
<project version="4">
<!-- 定义项目的版本 -->
<component name="ProjectModuleManager">
<!-- 定义项目的模块管理器 -->
<modules>
<!-- 定义项目的模块 -->
<module fileurl="file://$PROJECT_DIR$/.idea/spring-boot-online-exam-master.iml" filepath="$PROJECT_DIR$/.idea/spring-boot-online-exam-master.iml" />
<!-- 定义项目的模块文件路径 -->
</modules>
</component>
</project>
</project>
<!-- 定义项目的根元素 -->

@ -1,8 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义XML文档的版本和编码 -->
<project version="4">
<!-- 定义一个项目版本为4 -->
<component name="DdlMappings">
<!-- 定义一个组件名称为DdlMappings -->
<mapping uuid="3f2269d9-40c7-4022-bc5b-913dfbbc6cdc" name="@localhost Mapping">
<!-- 定义一个映射uuid为3f2269d9-40c7-4022-bc5b-913dfbbc6cdc名称为@localhost Mapping -->
<data-sources db="52ce0e2c-1ca8-4087-a535-1632d84f6e81" ddl="7ee42c98-c0b3-46e7-9153-4802360edee6" />
<!-- 定义数据源数据库为52ce0e2c-1ca8-4087-a535-1632d84f6e81DDL为7ee42c98-c0b3-46e7-9153-4802360edee6 -->
</mapping>
</component>
</project>

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义一个项目版本号为4 -->
<project version="4">
<!-- 定义一个组件名称为SqlDialectMappings -->
<component name="SqlDialectMappings">
<!-- 定义一个文件URL为PROJECT方言为MySQL -->
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

@ -1,123 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<!-- 定义项目版本 -->
<component name="Palette2">
<!-- 定义调色板组件 -->
<group name="Swing">
<!-- 定义Swing组件组 -->
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义水平间距组件 -->
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
<!-- 定义默认约束 -->
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义垂直间距组件 -->
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义面板组件 -->
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<!-- 定义滚动面板组件 -->
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义按钮组件 -->
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<!-- 定义默认约束 -->
<initial-values>
<!-- 定义初始值 -->
<property name="text" value="Button" />
<!-- 定义按钮文本 -->
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义单选按钮组件 -->
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<!-- 定义默认约束 -->
<initial-values>
<!-- 定义初始值 -->
<property name="text" value="RadioButton" />
<!-- 定义单选按钮文本 -->
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义复选框组件 -->
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<!-- 定义默认约束 -->
<initial-values>
<!-- 定义初始值 -->
<property name="text" value="CheckBox" />
<!-- 定义复选框文本 -->
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义标签组件 -->
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<!-- 定义默认约束 -->
<initial-values>
<!-- 定义初始值 -->
<property name="text" value="Label" />
<!-- 定义标签文本 -->
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义文本框组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<!-- 定义默认约束 -->
<preferred-size width="150" height="-1" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义密码框组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<!-- 定义默认约束 -->
<preferred-size width="150" height="-1" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义格式化文本框组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<!-- 定义默认约束 -->
<preferred-size width="150" height="-1" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义文本区域组件 -->
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="150" height="50" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义文本面板组件 -->
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="150" height="50" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义编辑面板组件 -->
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="150" height="50" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义组合框组件 -->
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义表格组件 -->
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="150" height="50" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义列表组件 -->
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="150" height="50" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义树组件 -->
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="150" height="50" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义选项卡面板组件 -->
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="200" height="200" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义分割面板组件 -->
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<!-- 定义默认约束 -->
<preferred-size width="200" height="200" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<!-- 定义微调器组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义滑块组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义分隔符组件 -->
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义进度条组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义工具栏组件 -->
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<!-- 定义默认约束 -->
<preferred-size width="-1" height="20" />
<!-- 定义首选大小 -->
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<!-- 定义工具栏分隔符组件 -->
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
<!-- 定义默认约束 -->
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<!-- 定义滚动条组件 -->
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
<!-- 定义默认约束 -->
</item>
</group>
</component>

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -1,108 +1,18 @@
# spring-boot-online-exam
> 在线Demo预览http://129.211.88.191 账户分别是admin、teacher、student密码是admin123。视频讲解代码https://www.bilibili.com/video/BV1FP4y1L7xt/
> 好消息一个小伙伴做了Python实现欢迎大家starhttps://github.com/xingxingzaixian/django-drf-online-exam
## 1.快速体验
### 1.1 事先准备
> clone代码并进入代码路径
```shell
git clone git@github.com:lsgwr/spring-boot-online-exam.git
cd spring-boot-online-exam
```
下面按照Linux和windows说明快速搭建的方法
### 1.2 Linux
执行代码下的脚本start.sh即可
然后访问 http://ip:80 即可访问自己搭建的考试系统
### 1.3 windows
+ 1.安装JDK推荐JDK8
+ 2.从官方仓库下载发布的jar包建议选择最新版https://github.com/lsgwr/spring-boot-online-exam/releases
+ 3.安装MySQL创建数据库exam并初始化密码为aA111111导入doc/sql/exam.sql文件来创建数据库
+ 4.启动jar包`java -jar exam.jar`
+ 5.访问http://ip:9527 即可访问自己搭建的考试系统
## 2.介绍
基于springboot的在线考试系统
### 2.1 功能简介
+ 支持单选题、多选题、判断题
+ 支持学生(student)、教师(teacher)、管理员(admin)三种角色
+ 学生:参加考试和查看我的考试
+ 教师:学生的所有权限+创建/编辑题目+创建/编辑考试
+ 管理员:教师的所有权限+管理用户
### 2.3 软件架构
> 前后端分离,前段组件化,方便二次开发;后端
+ 后端采用SpringBoot+JPA++Swagger2+JWT校验,根据不同用户的权限返回给用户不同的数据
+ 后端采用Vue+AntDesign,组件化拆分,封装了很多年公共组件,方便维护和二次开发
### 2.3 使用教程
+ 1.下载代码
```shell
git clone https://github.com/19920625lsg/spring-boot-online-exam.git
```
+ 2.初始化数据库
> 安装mysql的步骤这里省略网上的教程很多。安装好mysql后新建exam数据库密码和`spring-boot-online-exam/backend/exam/src/main/resources/application.yml`的`password: xxxxxx`保持一致,然后导入`spring-boot-online-exam/doc/sql/exam.sql`
+ 3.启动后端
> 打开`spring-boot-online-exam/backend/exam`这个Maven项目可以在IDE里启动或者执行`mvn install`生成jar包启动
+ 4.启动前端
+ 进入到前端代码路径 `cd spring-boot-online-exam/frontend/exam/`
+ 安装依赖 `npm install`
+ 启动前端 `npm run serve`
+ 5.部署完毕,查看效果
> 打开 http://localhost:8000 或者 http://本机ip:8000 即可查看演示效果
## 3.功能图示
+ 1.管理题目
+ 1.1 题目列表
> ![题目查看](doc/images/question_list.png)
+ 1.2 题目创建
> ![题目创建](doc/images/question_create.png)
+ 1.3 题目更新
> ![题目更新](doc/images/question_update.png)
+ 2.考试管理
+ 2.1 考试列表
> ![考试查看](doc/images/exam_list.png)
+ 2.2 考试创建
> ![考试创建](doc/images/exam_create.png)
+ 2.3 考试更新(`还有点小bug开发中`)
> ![考试更新](doc/images/exam_update.png)
+ 3.我的考试
+ 3.1 参加考试
> 在"考试列表"模块点击自己想参加的考试卡片即可
> ![参加考试1](doc/images/exam_join.png)
> ![参加考试2](doc/images/exam_join2.png)
+ 3.2 考试记录查看
> ![考试记录查看](doc/images/exam_detail.png)
## 4.参与贡献
1. Fork 本仓库
2. 新建 exam_xxx 分支
3. 提交代码
4. 新建 Pull Request
## 5.Todo
+ `√`0.修复issue提地bug题目创建失败
+ `√`1.考试详情编辑
+ 2.支持题目和考试的删除`删除的话比较麻烦先不做了最好是弄个visible字段不实际删除要不后面有些关联数据找不到就不好了`
> 如果题目有关联的考试则必须先删除对应的考试,反过来删除考试则不用删除题目
+ 3.图片改成base64存到数据库中
+ 4.题干和选项支持富文本
+ 5.支持批量导入题目
+ 6.新增用户管理、学科管理功能
+ 7.老师能考到所有学生的成绩以及考试的统计信息
+ 8.更多的数据分析功能
+ 9.支持容器化一键式部署(编好Dockerfile)
+ 10.支持移动端最好用uniapp做
+ ......抓紧做吧,争取每周末做一点......
ResultVO<RecordDetailVo> getExamRecordDetail(@PathVariable String recordId) {
// 定义返回结果
ResultVO<RecordDetailVo> resultVO;
try {
// 调用examService获取考试记录详情
RecordDetailVo recordDetailVo = examService.getRecordDetail(recordId);
// 返回成功结果
resultVO = new ResultVO<>(0, "获取考试记录详情成功", recordDetailVo);
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
// 返回失败结果
resultVO = new ResultVO<>(-1, "获取考试记录详情失败", null);
}
// 返回结果
return resultVO;
}
}

@ -1,17 +1,15 @@
// 指定当前类所在的包路径这是Java中组织类的一种方式有助于类的管理和访问控制。
package lsgwr.exam;
// 导入Spring Boot的启动类和应用配置自动装配的注解类。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// @SpringBootApplication是一个方便的注解它包括了@Configuration@EnableAutoConfiguration和@ComponentScan注解。
// 它告诉Spring Boot基于当前类所在包及其子包下的组件来启动自动配置和组件扫描。
/**
* Spring BootSpring Boot
*/
@SpringBootApplication
public class ExamApplication {
// main方法是Java应用程序的入口点。当运行这个类时JVM会调用这个方法。
public static void main(String[] args)
{
// SpringApplication.run方法启动Spring应用传入ExamApplication.class当前启动类和main方法的参数args。
// 这个方法会执行一系列的操作包括启动Spring容器加载应用上下文自动配置等。
public static void main(String[] args) {
SpringApplication.run(ExamApplication.class, args);
}
}

@ -16,15 +16,22 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Slf4j
public class CORSConf {
// 创建一个 WebMvcConfigurer Bean
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
// 重写 addCorsMappings 方法
@Override
public void addCorsMappings(CorsRegistry registry) {
// 记录初始化 CORSConfiguration 配置
log.info("初始化 CORSConfiguration 配置");
// 添加映射
registry.addMapping("/**")
// 允许所有请求头
.allowedHeaders("*")
// 允许所有请求方法
.allowedMethods("*")
// 允许所有请求来源
.allowedOrigins("*");
}
};

@ -12,25 +12,18 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
*/
@Configuration
public class IntercepterConfig implements WebMvcConfigurer {
/**
* LoginInterceptor
*/
// 注入LoginInterceptor
@Autowired
private LoginInterceptor loginInterceptor;
/**
*
*/
// 添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截user下的api
registry.addInterceptor(loginInterceptor).addPathPatterns("/api/**");
}
}
}

@ -7,22 +7,16 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
/**
* Servlet
*/
@Configuration
public class ServletConfig {
/**
* WebServerFactoryCustomizer beanWeb
* @return WebServerFactoryCustomizer<ConfigurableWebServerFactory>
*/
// 创建一个WebServerFactoryCustomizer bean用于自定义WebServerFactory
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
// 返回一个lambda表达式用于自定义Web服务器的工厂
// 返回一个lambda表达式用于自定义WebServerFactory
return factory -> {
// 创建一个ErrorPage对象用于处理404错误
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/");
// 将ErrorPage对象添加到Web服务器的工厂
// 将ErrorPage对象添加到WebServerFactory
factory.addErrorPages(error404Page);
};
}

@ -23,48 +23,54 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
/**
* Docket bean
* @return Docket
*/
@Bean
public Docket api() {
// 创建一个参数构建器
ParameterBuilder ticketPar = new ParameterBuilder();
// 创建一个参数集合
List<Parameter> pars = new ArrayList<>();
// 设置参数名
ticketPar.name("Access-Token").description("Rest接口权限认证token,无需鉴权可为空")
// 设置参数类型
.modelRef(new ModelRef("string")).parameterType("header")
//header中的ticket参数非必填传空也可以
// header中的ticket参数非必填传空也可以
.required(false).build();
//根据每个方法名也知道当前方法在设置什么参数
// 根据每个方法名也知道当前方法在设置什么参数
pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2)
// 设置文档信息
.apiInfo(apiInfo())
// 选择要扫描的接口
.select()
// 自行修改为自己的包路径
.apis(RequestHandlerSelectors.basePackage("lsgwr"))
// 选择所有的路径
.paths(PathSelectors.any())
// 构建Docket
.build()
// 设置全局参数
.globalOperationParameters(pars);
}
/**
* ApiInfo
* @return ApiInfo
*/
private ApiInfo apiInfo() {
// 创建ApiInfo对象
return new ApiInfoBuilder()
// 设置API标题
.title("online exam by springboot")
// 设置API描述
.description("在线考试系统 by 梁山广 at 2021")
// 设置API服务条款URL
.termsOfServiceUrl("https://github.com/19920625lsg/spring-boot-online-exam")
// 设置API版本
.version("2.0")
// 设置API联系人信息
.contact(new Contact("liangshanguang", "https://github.com/lsgwr/spring-boot-online-exam", "liangshanguang2@gmail.com"))
// 构建ApiInfo对象
.build();
}
}
}

@ -1,6 +1,9 @@
/**
* @Description : REST,HTTP,,
*/
/***********************************************************
* @Description :
* @author : 广(Laing Shan Guang)
* @date : 2019-05-28 08:04
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.controller;
import lsgwr.exam.entity.Exam;
@ -26,7 +29,10 @@ public class ExamController {
private ExamService examService;
/**
* @Description: 使examService.getQuestionAll(),ResultVO,
* @Description: 使examService.getQuestionAll(),
*
* ResultVO,
*
* &#064;ApiOperationAPI,SwaggerUI
*/
@GetMapping("/question/all")
@ -70,10 +76,20 @@ ResultVO<List<QuestionVo>> getQuestionAll() {
/**
*
* @Description: QuestionCreateSimplifyVoQuestionCreateVoID使examService.questionCreate()
* @param questionCreateSimplifyVo QuestionCreateSimplifyVo
* @Description: QuestionCreateSimplifyVo
* QuestionCreateVoID
*
* 使examService.questionCreate()
*
* @param questionCreateSimplifyVo
*
* QuestionCreateSimplifyVo
*
* @param request HttpServletRequestID
*
* @return ResultVO<String>
*
*
*/
@PostMapping("/question/create")
@ApiOperation("创建问题")
@ -100,7 +116,10 @@ ResultVO<List<QuestionVo>> getQuestionAll() {
}
/**
* @Description: ,使examService.getSelections()ResultVO
* @Description: ,
*
* 使examService.getSelections()ResultVO
*
* @return ResultVO<QuestionAllVo>
*/
@GetMapping("/question/selection")
@ -118,12 +137,6 @@ ResultVO<QuestionSelectionVo> getSelections() {
return new ResultVO<>(-1, "获取问题分类选项失败", null);
}
}
/**
* @Description: 使examService.getQuestionDetail(id),ResultVO,
* @param id id
* @return ResultVO<QuestionDetailVo>
*/
@GetMapping("/question/detail/{id}")
@ApiOperation("根据问题的id获取问题的详细信息")
ResultVO<QuestionDetailVo> getQuestionDetail(@PathVariable String id) {
@ -138,17 +151,11 @@ ResultVO<QuestionSelectionVo> getSelections() {
} catch (Exception e) {
// 如果获取失败则打印异常信息并返回ResultVO对象状态码为-1提示信息为"获取问题详情失败"数据为null。
e.printStackTrace();
// 创建一个表示获取失败的ResultVO对象状态码设置为 -1表示获取操作失败消息设置为"获取问题详情失败"数据部分设置为null
resultVO = new ResultVO<>(-1, "获取问题详情失败", null);
}
// 返回ResultVO对象。
return resultVO;
}
/**
* @Description: 使examService.getExamAll(),ResultVO,
* @return ResultVO<QuestionDetailVo>
*/
@GetMapping("/all")
@ApiOperation("获取全部考试的列表")
ResultVO<List<ExamVo>> getExamAll() {
@ -167,11 +174,6 @@ ResultVO<QuestionSelectionVo> getSelections() {
}
return resultVO;
}
/**
* @Description: 使examService.getExamQuestionTypeList(),ResultVO,
* @return ResultVO<ExamQuestionTypeVo>
*/
@GetMapping("/question/type/list")
@ApiOperation("获取问题列表,按照单选、多选和判断题分类返回")
ResultVO<ExamQuestionTypeVo> getExamQuestionTypeList() {
@ -191,37 +193,27 @@ ResultVO<QuestionSelectionVo> getSelections() {
return resultVO;
}
/**
* @Description: 使examService.createExam(),ResultVO,
* @param examCreateVo
* @param request id
* @return ResultVO<Exam>
*/
@PostMapping("/create")
@ApiOperation("创建考试")
ResultVO<Exam> createExam(@RequestBody ExamCreateVo examCreateVo, HttpServletRequest request) {
// 定义一个ResultVO类型的变量用于存放最终要返回给客户端的结果对象其泛型参数指定为Exam表示包含新创建考试相关信息的结果封装
// 从前端传参数过来,在这里完成考试的入库。
ResultVO<Exam> resultVO;
// 从HttpServletRequest对象中获取用户ID信息通常这个用户ID是在请求处理的前置环节比如拦截器中设置到请求属性中的用于标识创建这个考试的用户是谁
// 获取当前用户的id。
String userId = (String) request.getAttribute("user_id");
try {
// 调用ExamService的create方法将创建考试的视图对象examCreateVo和获取到的用户ID传递进去由服务层实现将考试记录数据保存到数据库等具体的创建逻辑操作返回创建好的Exam对象代表新创建的考试信息
// 调用examService的create方法将examCreateVo和userId作为参数传入创建考试。
Exam exam = examService.create(examCreateVo, userId);
// 如果创建成功创建一个表示成功的ResultVO对象状态码设置为0表示创建操作成功消息设置为"创建考试成功"并将创建好的考试对象放入ResultVO对象中返回给客户端
// 创建一个ResultVO对象将创建成功的考试信息返回。
resultVO = new ResultVO<>(0, "创建考试成功", exam);
} catch (Exception e) {
// 捕获异常打印异常信息并创建一个ResultVO对象将创建失败的考试信息返回。
e.printStackTrace();
// 如果在创建过程中出现异常创建一个表示创建失败的ResultVO对象状态码设置为 -1表示创建操作失败消息设置为"创建考试失败"数据部分设置为null
resultVO = new ResultVO<>(-1, "创建考试失败", null);
}
// 返回ResultVO对象。
return resultVO;
}
/**
* @Description: 使examService.updateExam(),ResultVO,
* @param examVo
* @param request id
* @return
*/
@PostMapping("/update")
@ApiOperation("更新考试")
ResultVO<Exam> updateExam(@RequestBody ExamVo examVo, HttpServletRequest request) {
@ -242,9 +234,13 @@ ResultVO<QuestionSelectionVo> getSelections() {
}
return resultVO;
}
/**
* @Description:
* @return ResultVO<List<ExamCardVo>>
*
* @return ResultVO<List<ExamCardVo>>
*
*
*/
@GetMapping("/card/list")
@ApiOperation("获取考试列表,适配前端卡片列表")
@ -254,115 +250,127 @@ ResultVO<QuestionSelectionVo> getSelections() {
try {
// 调用examService的getExamCardList方法获取考试列表卡片。
List<ExamCardVo> examCardVoList = examService.getExamCardList();
// 如果获取数据成功创建一个表示成功的ResultVO对象状态码设置为0表示获取考试列表卡片成功消息设置为"获取考试列表卡片成功"并将获取到的考试卡片列表数据放入ResultVO对象中以便返回给客户端进行展示
// 如果获取成功,则返回成功的结果
resultVO = new ResultVO<>(0, "获取考试列表卡片成功", examCardVoList);
} catch (Exception e) {
// 如果获取失败,则打印异常信息,并返回失败的结果。
e.printStackTrace();
// 创建一个表示获取失败的ResultVO对象状态码设置为 -1表示获取考试列表卡片失败消息可根据业务实际情况设置为相应的提示语数据部分设置为null因为没有成功获取到有效的考试卡片列表数据。
resultVO = new ResultVO<>(-1, "获取考试列表卡片失败", null);
}
// 返回结果
return resultVO;
}
/**
* @Description: id
*
* @param id id
*
* @return ResultVO<ExamDetailVo>
*/
@GetMapping("/detail/{id}")
@ApiOperation("根据考试的id获取考试详情")
ResultVO<ExamDetailVo> getExamDetail(@PathVariable String id) {
// 根据id获取考试详情先定义一个用于存放最终要返回给客户端的ResultVO<ExamDetailVo>类型的结果对象,后续根据查询数据的情况进行赋值操作
// 根据id获取考试详情
ResultVO<ExamDetailVo> resultVO;
try {
// 调用ExamService的getExamDetail方法将接收到的考卷唯一标识符id传递进去由服务层实现从数据库或者其他数据源根据该ID查询对应考卷详细信息的逻辑返回一个ExamDetailVo对象代表该考卷的所有详细信息。
ExamDetailVo examDetail = examService.getExamDetail(id);
// 如果获取详细信息成功创建一个表示成功的ResultVO对象状态码设置为0表示获取考试详情成功消息设置为"获取考试详情成功"并将获取到的考卷详细信息对象放入ResultVO对象中以便返回给客户端进行展示。
resultVO = new ResultVO<>(0, "获取考试详情成功", examDetail);
} catch (Exception e) {
// 如果在获取考卷详细信息的过程中出现异常创建一个表示获取失败的ResultVO对象状态码设置为 -1表示获取考试详情失败消息可根据业务实际情况设置为相应的提示语数据部分设置为null因为没有成功获取到有效的考卷详细信息。
resultVO = new ResultVO<>(-1, "获取考试详情失败", null);
}
return resultVO;
}
/**
* @Description: 使examService.finishExam,ResultVO,
* @Description: 使examService.finishExam,
*
* ResultVO,
* @param examId id
*
* @param answersMap
*
* @param request id
* @return
*/
@PostMapping("/finish/{examId}")
@ApiOperation("根据用户提交的答案对指定id的考试判分")
// 完成考试
ResultVO<ExamRecord> finishExam(@PathVariable String examId, @RequestBody HashMap<String, List<String>> answersMap, HttpServletRequest request) {
// 定义一个用于存放结果的ResultVO<ExamRecord>类型的数据结构,后续根据评分操作的成功与否以及获取到的相关数据进行赋值,用于最终返回给客户端展示评分结果。
// 定义返回结果。
ResultVO<ExamRecord> resultVO;
try {
// 拦截器里设置上的用户id从HttpServletRequest对象中获取用户ID属性值该用户ID用于标识当前提交答案并进行评分的用户是后续业务逻辑处理如记录答题记录归属、判断是否有权限答题等的重要依据
// 拦截器里设置上的用户id
String userId = (String) request.getAttribute("user_id");
// 下面根据用户提交的信息进行判分返回用户的得分情况调用ExamService的judge方法传入获取到的用户ID、考试唯一标识符examId以及用户提交的答案集合answersMap由服务层实现具体的评分逻辑比如对比答案、计算得分等操作并返回一个ExamRecord对象包含了评分后的成绩记录等详细信息
// 下面根据用户提交的信息进行判分,返回用户的得分情况
ExamRecord examRecord = examService.judge(userId, examId, answersMap);
// 封装成绩记录到最终结果中创建一个表示评分成功的ResultVO对象状态码设置为0表示考卷提交评分成功消息设置为"考卷提交成功"并将包含成绩记录的ExamRecord对象放入ResultVO对象中以便返回给客户端展示评分结果。
resultVO = new ResultVO<>(0, "考卷提交成功", examRecord);// 封装成绩记录到最终结果中
// 返回结果。
resultVO = new ResultVO<>(0, "考卷提交成功", examRecord);
} catch (Exception e) {
// 如果在评分过程中出现异常,打印异常堆栈信息,方便开发人员排查问题,查看是答案解析出错还是其他业务逻辑环节出现的错误导致评分失败。
e.printStackTrace();
// 创建一个表示评分失败的ResultVO对象状态码设置为 -1表示考卷提交评分失败消息可根据业务实际情况设置为相应的提示语数据部分设置为null因为没有成功获取到有效的评分成绩记录
// 返回错误结果
resultVO = new ResultVO<>(-1, "考卷提交失败", null);
}
return resultVO;
}
/**
* @Description: 使examService.getExamRecordList,ResultVO,
* @Description: 使examService.getExamRecordList,
* ResultVO,
*
* @param request id
* @return ResultVO<List<ExamRecordVo>> ResultVOResultVO
*
* @return ResultVO<List<ExamRecordVo>>
*
* ResultVOResultVO
*/
@GetMapping("/record/list")
@ApiOperation("获取当前用户的考试记录")
// 获取考试记录列表。
ResultVO<List<ExamRecordVo>> getExamRecordList(HttpServletRequest request) {
// 定义一个用于存放结果的ResultVO<List<ExamRecordVo>>类型的数据结构,后续根据查询用户考试记录操作的成功与否以及获取到的相关数据进行赋值,用于最终返回给客户端展示查询结果。
ResultVO<List<ExamRecordVo>> resultVO;
try {
// 拦截器里设置上的用户id从HttpServletRequest对象中获取用户ID属性值该用户ID用于明确要查询其考试记录的目标用户是服务层准确获取对应数据的关键依据
// 拦截器里设置上的用户id
String userId = (String) request.getAttribute("user_id");
// 下面根据用户账号拿到他所有的考试信息注意要用VO封装下调用ExamService的getExamRecordList方法传入获取到的用户ID由服务层实现从数据库或者其他数据源获取该用户所有历史考试记录数据的逻辑并将其整理封装成List<ExamRecordVo>类型的列表返回每个ExamRecordVo对象包含了如时间、得分等详细信息
// 下面根据用户账号拿到他(她所有的考试信息)注意要用VO封装下
List<ExamRecordVo> examRecordVoList = examService.getExamRecordList(userId);
// 封装查询得到的信息到最终结果中创建一个表示获取成功的ResultVO对象状态码设置为0表示获取考试记录成功消息设置为"获取考试记录成功"并将获取到的用户考试记录列表数据放入ResultVO对象中以便返回给客户端展示历史考试记录情况。
resultVO = new ResultVO<>(0, "获取考试记录成功", examRecordVoList);//封装查询得到的信息到最终结果中;
resultVO = new ResultVO<>(0, "获取考试记录成功", examRecordVoList);
} catch (Exception e) {
// 如果在获取用户考试记录数据的过程中出现异常,打印异常堆栈信息,方便开发人员排查问题,查看是数据源查询出错还是数据封装等环节出现的错误导致获取失败。
e.printStackTrace();
// 创建一个表示获取失败的ResultVO对象状态码设置为 -1表示获取考试记录失败消息可根据业务实际情况设置为相应的提示语数据部分设置为null因为没有成功获取到有效的用户考试记录数据。
resultVO = new ResultVO<>(-1, "获取考试记录失败", null);
}
return resultVO;//返回封装好的 数据结构
return resultVO;
}
/**
* @Description: 使examService.getExamRecordDetail,ResultVO,id
* @Description: 使examService.getExamRecordDetail,
*
* ResultVO,id
*
* @param recordId id
* @return ResultVO<RecordDetailVo> ResultVOResultVO
*
* @return ResultVO<RecordDetailVo>
* ResultVO
*
* ResultVO
*/
@GetMapping("/record/detail/{recordId}")
@ApiOperation("根据考试记录id获取考试记录详情")
ResultVO<RecordDetailVo> getExamRecordDetail(@PathVariable String recordId) {
// 定义一个用于存放结果的ResultVO<RecordDetailVo>类型的数据结构,后续根据查询测验详细情况操作的成功与否以及获取到的相关数据进行赋值,用于最终返回给客户端展示查询结果。
// 定义返回结果。
ResultVO<RecordDetailVo> resultVO;
try {
// 调用ExamService的getRecordDetail方法将接收到的测验记录唯一标识符recordId传递进去由服务层实现从数据库或者其他数据源根据该ID查询对应测验详细信息的逻辑返回一个RecordDetailVo对象代表该次测验的所有详细信息
// 调用examService获取考试记录详情
RecordDetailVo recordDetailVo = examService.getRecordDetail(recordId);
// 封装查询得到的信息到最终结果中创建一个表示获取成功的ResultVO对象状态码设置为0表示获取考试记录详情成功消息设置为"获取考试记录详情成功"并将获取到的测验详细信息对象放入ResultVO对象中以便返回给客户端展示该次测验的详细情况
resultVO = new ResultVO<>(0, "获取考试记录详情成功", recordDetailVo);//封装查询得到的信息到最终结果中;
// 返回成功结果
resultVO = new ResultVO<>(0, "获取考试记录详情成功", recordDetailVo);
} catch (Exception e) {
// 如果在获取测验详细信息的过程中出现异常,打印异常堆栈信息,方便开发人员排查问题,查看是数据源查询出错还是数据解析等环节出现的错误导致获取失败
// 打印异常信息。
e.printStackTrace();
// 创建一个ResultVO对象表示操作失败
// 状态码设置为-1表示获取考试记录详情失败
// 消息设置为"获取考试记录详情失败"
// 由于查询失败详细信息对象设置为null
// 返回失败结果。
resultVO = new ResultVO<>(-1, "获取考试记录详情失败", null);
}
// 返回封装好的ResultVO对象给客户端
// 客户端可以根据状态码和消息判断操作是否成功,并根据详细信息对象获取测验的详细记录信息
// 返回结果。
return resultVO;
}
}

@ -15,13 +15,22 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/***********************************************************
*
* @note : ,SwaggerSwaggerUI
*
*
*
*
* @author : 广
* * AjaxResponseEntity
* *
* * 1. - `MultipartFile`
* * 2. - `MultipartFile []`
*
* * Ajax
*
* ResponseEntity
* *
*
* * 1.
* - `MultipartFile`
* * 2.
* - `MultipartFile []`
* * 3. - `@ModelAttribute`
* @version : V1.0 at 2018/7/16 20:43
***********************************************************/
@ -32,87 +41,63 @@ import java.io.IOException;
public class UploadDownloadController {
// @Autowired
// AITestConfig aiTestConfig;
//
// @PostMapping("/upload/single")
// @ApiOperation("单文件上传")
// public String uploadFile(@RequestParam("file") MultipartFile uploadfile) {
// return FileTransUtil.uploadFile(uploadfile, "/root/" + File.separator + uploadfile.getOriginalFilename());
// }
/**
*
*
* @param dir
* @param uploadfile
* @return String
*/
@ApiOperation("单文件上传,支持同时传入参数")
@PostMapping("/api/upload/singleAndparas")
public String uploadFileSingle(@RequestParam("dir") String dir, @RequestParam("file") MultipartFile uploadfile) {
// 单文件上传。
public String uploadFileSingle(@RequestParam("dir") String dir, @RequestParam("file") MultipartFile uploadfile) {
// 调用FileTransUtil工具类中的uploadFile方法将上传的文件和目录作为参数传入。
return FileTransUtil.uploadFile(uploadfile, dir);
}
/**
* Model
*
* @param model UploadModel2
* @return String
*/
// 单文件上传支持同时传入参数Model。
@ApiOperation("单文件上传,支持同时传入参数,Model")
@PostMapping("/upload/single/model")
public String singleUploadFileModel(@ModelAttribute("model") UploadModel2 model) {
// 调用FileTransUtil工具类中的uploadFile方法将上传的文件和目录作为参数传入。
return FileTransUtil.uploadFile(model.getFile(), model.getDir());
}
/**
*
*
* @param dir
* @param uploadfiles
* @return String
*/
// 多文件上传,支持同时传入参数。
@ApiOperation("多文件上传,支持同时传入参数")
@PostMapping("upload/multiAndparas")
public String uploadFileMulti(@RequestParam("dir") String dir, @RequestParam("files") MultipartFile[] uploadfiles) {
// 调用FileTransUtil工具类中的uploadFiles方法将上传的文件数组和目录作为参数传入。
return FileTransUtil.uploadFiles(uploadfiles, dir);
}
/**
*
*
* @param model UploadModel
* @return String
*/
// 多文件上传,支持同时传入参数。
@ApiOperation("多文件上传,支持同时传入参数")
@PostMapping(value = "/upload/multi/model")
public String multiUploadFileModel(@ModelAttribute(("model")) UploadModel model) {
// 调用FileTransUtil工具类中的uploadFiles方法将上传的文件数组和目录作为参数传入。
return FileTransUtil.uploadFiles(model.getFiles(), model.getDir());
}
/**
* Get
*
* @param filePath
* @return ResponseEntity<InputStreamResource>
* @throws IOException
*/
// Get下载文件
@ApiOperation("Get下载文件")
@GetMapping(value = "/download/get")
public ResponseEntity<InputStreamResource> downloadFileGet(@RequestParam String filePath) throws IOException {
// 调用FileTransUtil工具类中的downloadFile方法将文件路径作为参数传入。
return FileTransUtil.downloadFile(filePath);
}
/**
* Post
*
* @param downloadQo DownloadQo
* @return ResponseEntity<InputStreamResource>
* @throws IOException
*/
// Post下载文件
@ApiOperation("Post下载文件")
@PostMapping(value = "/download/post")
public ResponseEntity<InputStreamResource> downloadFilePost(@RequestBody DownloadQo downloadQo) throws IOException {
// 调用FileTransUtil工具类中的downloadFile方法将文件路径作为参数传入。
return FileTransUtil.downloadFile(downloadQo.getPath());
}
}

@ -29,12 +29,6 @@ public class UserController {
@Autowired
private UserService userService;
/**
*
*
* @param registerDTO RegisterDTO
* @return ResultVO<User>
*/
@PostMapping("/register")
@ApiOperation("注册")
ResultVO<User> register(@RequestBody RegisterDTO registerDTO) {
@ -50,12 +44,6 @@ public class UserController {
return resultVO;
}
/**
* ,token
*
* @param loginQo LoginQo
* @return ResultVO<String>
*/
@PostMapping("/login")
@ApiOperation("根据用户名或邮箱登录,登录成功返回token")
ResultVO<String> login(@RequestBody LoginQo loginQo) { // 这里不用手机号是因为手机号和用户名难以进行格式区分,而用户名和
@ -72,12 +60,6 @@ public class UserController {
return resultVO;
}
/**
*
*
* @param request HttpServletRequest
* @return ResultVO<UserVo>
*/
@GetMapping("/user-info")
@ApiOperation("获取用户信息")
// 根据请求获取用户信息
@ -90,12 +72,6 @@ public class UserController {
return new ResultVO<>(ResultEnum.GET_INFO_SUCCESS.getCode(), ResultEnum.GET_INFO_SUCCESS.getMessage(), userVo);
}
/**
*
*
* @param request HttpServletRequest
* @return ResultVO<UserInfoVo>
*/
@GetMapping("/info")
@ApiOperation("获取用户的详细信息,包括个人信息页面和操作权限")
// 获取用户信息的接口
@ -110,12 +86,6 @@ public class UserController {
return new ResultVO<>(ResultEnum.GET_INFO_SUCCESS.getCode(), ResultEnum.GET_INFO_SUCCESS.getMessage(), userInfoVo);
}
/**
*
*
* @param request HttpServletRequest
* @return String
*/
@GetMapping("/test")
@ApiOperation("测试接口")
String test(HttpServletRequest request) {
@ -126,4 +96,4 @@ public class UserController {
System.out.println("用户名:" + username);
return "用户id" + userId + "\n用户名" + username;
}
}
}

@ -8,9 +8,6 @@ package lsgwr.exam.dto;
import lombok.Data;
/**
*
*/
@Data
public class RegisterDTO {
/**

@ -11,29 +11,22 @@ import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
*
* ID
*/
@Data
@Entity
public class Action {
/**
* ID
*/
// 定义一个主键
@Id
// 自动生成主键
@GeneratedValue
private Integer actionId;
/**
*
*/
// 定义一个动作名称
private String actionName;
/**
*
*/
// 定义一个动作描述
private String actionDescription;
/**
*
*/
// 定义一个默认选中状态
private Boolean defaultCheck;
}

@ -4,109 +4,78 @@
* @date : 2019/5/14 07:42
* @email : liangshanguang2@gmail.com
***********************************************************/
// 定义包名,用于组织类文件,避免命名冲突
package lsgwr.exam.entity;
// 导入Jackson库的JsonFormat注解用于JSON序列化时自定义日期格式
import com.fasterxml.jackson.annotation.JsonFormat;
// 导入Lombok库的Data注解用于自动生成getter、setter、equals、hashCode和toString方法
import lombok.Data;
// 导入Hibernate的DynamicUpdate注解用于在实体更新时只更新发生变化的字段
import org.hibernate.annotations.DynamicUpdate;
// 导入JPA的Entity注解用于声明该类是一个JPA实体类
import javax.persistence.Entity;
// 导入JPA的Id注解用于声明该类中的某个字段作为主键
import javax.persistence.Id;
// 导入Java的Date类用于表示日期和时间
import java.util.Date;
@Entity
@Data
@DynamicUpdate
/**
*
*/
public class Exam {
// 使用JPA的@Id注解声明该字段为主键
/**
* ID
*/
// 考试ID
@Id
private String examId;
/**
*
*/
// 考试名称
private String examName;
/**
*
*/
// 考试头像
private String examAvatar;
/**
*
*/
// 考试描述
private String examDescription;
/**
* IDID
*/
// 考试问题ID
private String examQuestionIds;
/**
* ID
*/
// 考试单选题ID
private String examQuestionIdsRadio;
/**
* ID
*/
// 考试多选题ID
private String examQuestionIdsCheck;
/**
* ID
*/
// 考试判断题ID
private String examQuestionIdsJudge;
/**
*
*/
// 考试总分
private Integer examScore;
/**
*
*/
// 考试单选题总分
private Integer examScoreRadio;
/**
*
*/
// 考试多选题总分
private Integer examScoreCheck;
/**
*
*/
// 考试判断题总分
private Integer examScoreJudge;
/**
* ID
*/
// 考试创建者ID
private String examCreatorId;
/**
*
*/
// 考试时间限制
private Integer examTimeLimit;
/**
* 使Jackson@JsonFormat
*/
// 考试开始时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date examStartDate;
/**
*
*/
// 考试结束时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date examEndDate;
/**
* Java
* 使Jackson@JsonFormat便
/**
*
*
* , Java
*
*
*/
// 创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/**
* Java
* 使Hibernate@DynamicUpdate
* 使Jackson@JsonFormat便
* Java
*
*
* @DynamicUpdate
*
*
*/
// 更新时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

@ -7,52 +7,69 @@
package lsgwr.exam.entity;
import com.fasterxml.jackson.annotation.JsonFormat;// 引入Jackson库用于日期格式化的注解
import lombok.Data;// 引入Lombok库用于简化Java实体类的编写如自动生成getter和setter方法
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import javax.persistence.Entity;// 引入JPA注解用于标识这是一个实体类并映射到数据库中的一张表
// 引入JPA注解用于标识实体类的主键字段
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
// 使用@Data注解自动生成getter和setter方法以及toString、equals和hashCode方法
@Data
// 使用@Entity注解标识这是一个JPA实体类
@Entity
public class ExamRecord {
/**
*
*
*
*
*
*
*/
@Id
private String examRecordId;
/**
* ID
*
* id
*
*/
private String examId;
/**
* 线_线-
*
* (_
*
* -),
*/
private String answerOptionIds;
/**
* IDID
*
* userid
*
*/
private String examJoinerId;
/**
*
*
*
*
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date examJoinDate;
/**
*
*
* ()
*
*/
private Integer examTimeCost;
/**
*
*
*
*/
private Integer examJoinScore;
/**
*
*
*
*
*/
private Integer examResultLevel;
}
}

@ -7,31 +7,21 @@
package lsgwr.exam.entity;
import lombok.Data;// 引入Lombok库中的@Data注解用于自动生成getter、setter、toString、equals和hashCode方法
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Entity;// 引入JPAJava Persistence API中的@Entity注解标识这是一个实体类与数据库中的表相对应
import javax.persistence.GeneratedValue;// 引入JPA中的@GeneratedValue注解用于指定主键的生成策略这里未明确指定策略将使用默认策略
import javax.persistence.Id;// 引入JPA中的@Id注解标识实体类的主键字段
// 使用@Data注解自动生成getter、setter等方法
@Data
// 使用@Entity注解标识这是一个JPA实体类
@Entity
/**
*
*/
public class ExamRecordLevel {
/**
* ID@Id@GeneratedValue
*/
public class ExamRecordLevel {
// 考试记录等级ID
@Id
@GeneratedValue
private Integer examRecordLevelId;
/**
*
*/
// 考试记录等级名称
private String examRecordLevelName;
/**
*
*/
// 考试记录等级描述
private String examRecordLevelDescription;
}
}

@ -12,28 +12,20 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
*
*/
@Data
@Entity
public class Page {
/**
* ID@Id@GeneratedValue
*/
// 定义一个页面ID使用@Id注解表示该字段为主键使用@GeneratedValue注解表示该字段自动生成
@Id
@GeneratedValue
private Integer pageId;
/**
*
*/
// 定义一个页面名称
private String pageName;
/**
*
*/
// 定义一个页面描述
private String pageDescription;
/**
* ID
*/
// 定义一个动作ID用于存储页面上的动作
private String actionIds;
}

@ -14,59 +14,37 @@ import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
/**
*
* getter/setter
*/
@Data
@Entity
@DynamicUpdate
public class Question {
/**
* id
*/
// 问题ID
@Id
private String questionId;
/**
*
*/
// 问题名称
private String questionName;
/**
*
*/
// 问题分数
private Integer questionScore;
/**
* id
*/
// 问题创建者ID
private String questionCreatorId;
/**
* id
*/
// 问题等级ID
private Integer questionLevelId;
/**
* id
*/
// 问题类型ID
private Integer questionTypeId;
/**
* id
*/
// 问题分类ID
private Integer questionCategoryId;
/**
*
*/
// 问题描述
private String questionDescription;
/**
* idA,B,C,D
*/
// 问题选项ID
private String questionOptionIds;
/**
* idA
*/
// 问题答案选项ID
private String questionAnswerOptionIds;
/**
* , Java
*/
// 创建时间格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@ -74,6 +52,7 @@ public class Question {
* Java
* @DynamicUpdate
*/
// 更新时间格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

@ -13,27 +13,24 @@ import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
*
*/
@Data
@Entity
public class QuestionCategory {
/**
* id
*/
// 主键
@Id
// 自动生成主键
@GeneratedValue
// 将该字段映射到JSON中的id字段
@JsonProperty("id")
private Integer questionCategoryId;
/**
*
*/
// 将该字段映射到JSON中的name字段
@JsonProperty("name")
private String questionCategoryName;
/**
*
*/
// 将该字段映射到JSON中的description字段
@JsonProperty("description")
private String questionCategoryDescription;
}

@ -14,27 +14,19 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
*
*/
@Entity
@Data
public class QuestionLevel {
/**
* ID
*/
// 定义实体类,表示问题等级
@Id
@GeneratedValue
// 定义主键,自动生成
@JsonProperty("id")
private Integer questionLevelId;
/**
*
*/
@JsonProperty("name")
private String questionLevelName;
/**
*
*/
@JsonProperty("description")
private String questionLevelDescription;
}
}

@ -11,23 +11,15 @@ import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
*
*/
@Data
@Entity
public class QuestionOption {
/**
* ABCD
*/
// 定义问题选项的ID
@Id
private String questionOptionId;
/**
*
*/
// 定义问题选项的内容
private String questionOptionContent;
/**
*
*/
// 定义问题选项的描述
private String questionOptionDescription;
}

@ -14,28 +14,22 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* QuestionType
* ID
*/
@Data
@Entity
public class QuestionType {
/**
* ID
*/
// 主键
@Id
// 自动生成主键
@GeneratedValue
// 将该字段映射到JSON中的id字段
@JsonProperty("id")
private Integer questionTypeId;
/**
*
*/
// 将该字段映射到JSON中的name字段
@JsonProperty("name")
private String questionTypeName;
/**
*
*/
// 将该字段映射到JSON中的description字段
@JsonProperty("description")
private String questionTypeDescription;
}
}

@ -13,34 +13,25 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
*
* ID
*/
@Data
@Entity
public class Role {
/**
* ID
* @Id
*/
// 角色ID
@Id
@GeneratedValue
private Integer roleId;
/**
*
*/
// 角色名称
private String roleName;
/**
*
*/
// 角色描述
private String roleDescription;
/**
*
*/
// 角色详情
private String roleDetail;
/**
*
*
* 访(-)
*
*
*/
private String rolePageIds;
}

@ -14,61 +14,52 @@ import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
/**
*
*/
@Data
@Entity
@DynamicUpdate
public class User {
/**
* id
*/
// 用户ID
@Id
private String userId;
/**
*
*/
// 用户名
private String userUsername;
/**
*
*/
// 用户昵称
private String userNickname;
/**
*
*/
// 用户密码
private String userPassword;
/**
* id
*/
// 用户角色ID
private Integer userRoleId;
/**
*
*/
// 用户头像
private String userAvatar;
/**
*
*/
// 用户描述
private String userDescription;
/**
*
*/
// 用户邮箱
private String userEmail;
/**
*
*/
// 用户电话
private String userPhone;
/**
* , Java
*
*
* ,
*
* Java
*
*
*/
// 创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/**
* Java
* @DynamicUpdate
*
* Java
* @DynamicUpdate
*
*
*/
// 更新时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}
}

@ -3,18 +3,25 @@ package lsgwr.exam.enums;
import lombok.Getter;
/**
* @Description:
*
*
*
* @author liangshanguang
*
*
*/
@Getter
public enum LoginTypeEnum {
/**
* @Description:
* 12
*
* 12
*
*
*/
USERNAME(1, "用户名"),
EMAIL(2, "邮箱");
// 构造函数用于初始化code和description
LoginTypeEnum(Integer type, String name) {
this.type = type;
this.name = name;

@ -8,16 +8,17 @@ package lsgwr.exam.enums;
import lombok.Getter;
/**
* @Description :
*/
@Getter
public enum QuestionEnum {
/**
*
*/
RADIO(1, "单选题"),
CHECK(2, "多选题"),
JUDGE(3, "判断题");
// 构造函数用于初始化id和role
QuestionEnum(Integer id, String role) {
this.id = id;
this.role = role;

@ -2,9 +2,6 @@ package lsgwr.exam.enums;
import lombok.Getter;
/**
* @Description :
*/
@Getter
public enum ResultEnum {
// 下面是本项目用到的所有错误码
@ -20,16 +17,14 @@ public enum ResultEnum {
ORDER_UPDATE_ERR(15, "考试更新异常"),
ORDER_DETAIL_EMPTY(16, "用户详情为空");
/**
* @Description: codemessage
* @param code
* @param message
*/
// 构造方法,用于初始化错误码和错误信息
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
// 错误码
private Integer code;
// 错误信息
private String message;
}

@ -8,24 +8,21 @@ package lsgwr.exam.enums;
import lombok.Getter;
/**
* @Description:
*/
@Getter
public enum RoleEnum {
/**
*
*
* role
*
*
*/
ADMIN(1, "管理员"),
TEACHER(2, "教师"),
STUDENT(3, "学生");
/**
* @Description: codemessage
* @param id id
* @param role
*/
RoleEnum(Integer id, String role) {
this.id = id;
this.role = role;

@ -9,34 +9,24 @@ package lsgwr.exam.exception;
import lsgwr.exam.enums.ResultEnum;
import lombok.Getter;
/**
*
*/
@Getter
public class ExamException extends RuntimeException {
/**
*
*/
// 定义异常代码
private Integer code;
/**
*
*
* @param resultEnum ResultEnum
*/
// 构造函数传入ResultEnum枚举类型
public ExamException(ResultEnum resultEnum) {
// 调用父类构造函数传入ResultEnum枚举类型的消息
super(resultEnum.getMessage());
// 将ResultEnum枚举类型的代码赋值给异常代码
this.code = resultEnum.getCode();
}
/**
*
*
* @param code
* @param message
*/
// 构造函数,传入异常代码和消息
public ExamException( Integer code, String message) {
// 调用父类构造函数,传入消息
super(message);
// 将传入的异常代码赋值给异常代码
this.code = code;
}
}

@ -23,52 +23,71 @@ import java.io.PrintWriter;
*
* @author liangshanguang
*/
/**
* Token
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
*
* @Component使pplication.yml
*
*
*/
@Value("${interceptors.auth-ignore-uris}")
private String authIgnoreUris;
/**
*
*
* controller
*
*
* @param request
*
* @param response
*
* @param handler
*
* @return
*
* @throws Exception
*
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 打印进入拦截器
System.out.println("进入拦截器啦!");
// 获取请求的URI
String uri = request.getRequestURI();
// 打印URI
System.out.println(uri);
// 打印无需拦截的接口路径
System.out.println("无需拦截的接口路径:" + authIgnoreUris);
// 将无需拦截的接口路径分割成数组
String[] authIgnoreUriArr = authIgnoreUris.split(",");
// 登录和注册接口不需要进行token拦截和校验
for (String authIgnoreUri : authIgnoreUriArr) {
// 如果请求的URI在无需拦截的接口路径中则直接返回true
if (authIgnoreUri.equals(uri)) {
return true;
}
}
// 注意要和前端适配Access-Token属性前端会在登陆后的每个接口请求头加Access-Token属性
// 获取请求头中的token
String token = request.getHeader("Access-Token");
// 如果token不在header中则可能在参数中
if (token == null) {
// token不在header中时也可能在参数中(RequestParam)
// 从参数中获取token
token = request.getParameter("token");
}
// 如果token不为空
if (token != null) {
// 请求中是携带参数的
// 校验token
Claims claims = JwtUtils.checkJWT(token);
// 如果token校验失败
if (claims == null) {
// 返回null说明用户篡改了token导致校验失败
// 返回错误信息
sendJsonMessage(response, JsonData.buildError("token无效请重新登录"));
return false;
}
@ -77,10 +96,12 @@ public class LoginInterceptor implements HandlerInterceptor {
// 用户名
String username = (String) claims.get("username");
// 把这两个参数放到请求中从而可以在controller中获取到不需要在controller中在用Jwt解密了,request.getAttribute("属性名")即可获取
// 将用户id和用户名放到请求中
request.setAttribute("user_id", id);
request.setAttribute("username", username);
return true;
}
// 如果token为空则返回错误信息
sendJsonMessage(response, JsonData.buildError("token为null,请先登录!"));
return false;
}
@ -88,16 +109,23 @@ public class LoginInterceptor implements HandlerInterceptor {
/**
*
*
* @param response
* @param obj
* @throws Exception
* @param response
* @param obj
* @throws Exception
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj) throws Exception {
// 发送JSON消息
public static void sendJsonMessage(HttpServletResponse response, Object obj) throws Exception {
// 创建Gson对象
Gson g = new Gson();
// 设置响应内容类型为JSON
response.setContentType("application/json; charset=utf-8");
// 获取响应的PrintWriter对象
PrintWriter writer = response.getWriter();
// 将对象转换为JSON字符串并写入响应
writer.print(g.toJson(obj));
// 关闭PrintWriter对象
writer.close();
// 刷新响应缓冲区
response.flushBuffer();
}
}

@ -13,8 +13,6 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class DownloadQo {
/**
*
*/
// 下载路径
String path;
}
}

@ -10,23 +10,26 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginQo {
/**
*
* 12
*
*/
private Integer loginType;
/**
*
* /
*
*/
private String userInfo;
/**
*
*
*
*/
private String password;
}

@ -11,20 +11,25 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UploadModel {
/**
*
*
*
*
*
*/
private MultipartFile[] files;
/**
*
*
*
*
*
*/
private String dir;
}

@ -12,19 +12,24 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UploadModel2 {
/**
*
*
*
*
*
*/
private MultipartFile file;
/**
*
*
*
*
*
*/
private String dir;
}

@ -9,8 +9,5 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.ExamRecordLevel;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* ExamRecordLevelRepositoryJpaRepositoryExamRecordLevel
*/
public interface ExamRecordLevelRepository extends JpaRepository<ExamRecordLevel, Integer> {
}
}

@ -1,3 +1,9 @@
/***********************************************************
* @Description :
* @author : 广(Laing Shan Guang)
* @date : 2019-05-14 08:23
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.repository;
import lsgwr.exam.entity.ExamRecord;
@ -5,9 +11,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
*
*/
public interface ExamRecordRepository extends JpaRepository<ExamRecord, String> {
/**
*
@ -16,4 +19,4 @@ public interface ExamRecordRepository extends JpaRepository<ExamRecord, String>
* @return
*/
List<ExamRecord> findByExamJoinerIdOrderByExamJoinDateDesc(String userId);
}
}

@ -12,11 +12,7 @@ import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* ExamRepositoryJpaRepositoryExam
*/
public interface ExamRepository extends JpaRepository<Exam, String> {
// 使用JPQL查询语句查询Exam实体类按照updateTime降序排列
@Query("select e from Exam e order by e.updateTime desc")
List<Exam> findAll();
}

@ -8,8 +8,6 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.Page;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface PageRepository extends JpaRepository<Page, Integer> {
}

@ -8,8 +8,6 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.QuestionCategory;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface QuestionCategoryRepository extends JpaRepository<QuestionCategory, Integer> {
}

@ -8,8 +8,6 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.QuestionLevel;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface QuestionLevelRepository extends JpaRepository<QuestionLevel, Integer> {
}

@ -8,8 +8,6 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.QuestionOption;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface QuestionOptionRepository extends JpaRepository<QuestionOption, String> {
}

@ -11,22 +11,9 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
*
*/
public interface QuestionRepository extends JpaRepository<Question, String> {
/**
* ID
*
* @param id ID
* @return
*/
List<Question> findByQuestionTypeId(Integer id);
/**
*
*
* @return
*/
@Query("select q from Question q order by q.updateTime desc")
List<Question> findAll();
}

@ -8,8 +8,6 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.QuestionType;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface QuestionTypeRepository extends JpaRepository<QuestionType, Integer> {
}

@ -9,8 +9,5 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface RoleRepository extends JpaRepository<Role, Integer> {
}

@ -8,9 +8,7 @@ package lsgwr.exam.repository;
import lsgwr.exam.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
*/
public interface UserRepository extends JpaRepository<User, String> {
/**
*

@ -4,109 +4,189 @@
* @date : 2019-05-28 08:05
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.service;// 定义了接口所属的包名
package lsgwr.exam.service;
import lsgwr.exam.entity.Exam;// 导入了实体类Exam可能包含考试的基本信息
import lsgwr.exam.entity.ExamRecord;// 导入了实体类ExamRecord可能包含考试记录的详细信息
import lsgwr.exam.vo.*;// 导入了VO对象VO是值对象用于在不同层之间传递数据
import lsgwr.exam.entity.Exam;
import lsgwr.exam.entity.ExamRecord;
import lsgwr.exam.vo.*;
import java.util.HashMap;
import java.util.List;
import java.util.HashMap;// 导入了HashMap用于存储键值对
import java.util.List;// 导入了List用于存储一系列对象
// 定义了一个名为ExamService的接口
public interface ExamService {
/**
*
*
*
*
*/
List<QuestionVo> getQuestionAll();
/**
*
*
*
*
* @param questionVo
*
*
*
*/
QuestionVo updateQuestion(QuestionVo questionVo);
/**
*
*
*
*
*
* @param questionCreateVo
*
*
*/
void questionCreate(QuestionCreateVo questionCreateVo);
/**
*
*
*
*
* @return
*
*
*/
QuestionSelectionVo getSelections();
/**
*
*
*
*
* @param id id
*
*
* @return VO
*
*
*/
QuestionDetailVo getQuestionDetail(String id);
/**
*
*
*
*
*
*/
List<ExamVo> getExamAll();
/**
* 便
*
*
*
*
* @return
*
*
*
*/
ExamQuestionTypeVo getExamQuestionType();
/**
*
*
*
*
* @param examCreateVo
*
*
*
* @param userId id
*
*
*
* @return
*
*
*
*/
Exam create(ExamCreateVo examCreateVo, String userId);
/**
*
*
*
*
* @return
*
*
*/
List<ExamCardVo> getExamCardList();
/**
* id
*
*
*
*
* @param id exam
*
*
*
* @return VO
*
*
*/
ExamDetailVo getExamDetail(String id);
/**
*
*
*
*
* @param userId
*
*
* @param examId
*
*
* @param answersMap
*
*
* @return
*
*
*/
ExamRecord judge(String userId, String examId, HashMap<String, List<String>> answersMap);
/**
* id
*
*
*
* @param userId id
*
*
* @return
*
*
*/
List<ExamRecordVo> getExamRecordList(String userId);
/**
*
*
*
*
* @param recordId id
*
*
*
* @return
*
*
*
*/
RecordDetailVo getRecordDetail(String recordId);
@ -115,8 +195,17 @@ public interface ExamService {
*
*
* @param examVo
*
*
* @param userId
*
*
*
* @return
*
*
*
*
*/
Exam update(ExamVo examVo, String userId);
}

@ -16,30 +16,54 @@ public interface UserService {
/**
*
*
*
*
* @param registerDTO
*
*
*
* @return
*
*
*/
User register(RegisterDTO registerDTO);
/**
* token
*
*
*
* @param loginQo
*
*
* @return tokennull
*
*
*/
String login(LoginQo loginQo);
/**
* id
*
*
*
*
* @return
*
*
*/
UserVo getUserInfo(String userId);
/**
* ()
*
*
* @param userId id
*
*
* @return
*
*
*/
UserInfoVo getInfo(String userId);
}

@ -19,41 +19,37 @@ import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.*;
/**
*
*/
@Service
@Transactional
public class ExamServiceImpl implements ExamService {
// 试卷仓库
private final ExamRepository examRepository;
// 考试记录仓库
private final ExamRecordRepository examRecordRepository;
// 问题仓库
private final QuestionRepository questionRepository;
// 用户仓库
private final UserRepository userRepository;
// 问题等级仓库
private final QuestionLevelRepository questionLevelRepository;
// 问题类型仓库
private final QuestionTypeRepository questionTypeRepository;
// 问题类别仓库
private final QuestionCategoryRepository questionCategoryRepository;
private final QuestionOptionRepository questionOptionRepository;
/**
*
* @param questionRepository
* @param userRepository
* @param questionLevelRepository
* @param questionTypeRepository
* @param questionCategoryRepository
* @param questionOptionRepository
* @param examRepository
* @param examRecordRepository
*/
// 问题选项仓库
// 定义一个final类型的QuestionOptionRepository对象
private final QuestionOptionRepository questionOptionRepository;
// 构造函数传入多个Repository对象
public ExamServiceImpl(QuestionRepository questionRepository, UserRepository userRepository, QuestionLevelRepository questionLevelRepository, QuestionTypeRepository questionTypeRepository, QuestionCategoryRepository questionCategoryRepository, QuestionOptionRepository questionOptionRepository, ExamRepository examRepository, ExamRecordRepository examRecordRepository) {
// 将传入的Repository对象赋值给对应的成员变量
this.questionRepository = questionRepository;//
this.userRepository = userRepository;//
this.questionLevelRepository = questionLevelRepository;//
@ -63,23 +59,12 @@ public class ExamServiceImpl implements ExamService {
this.examRepository = examRepository;
this.examRecordRepository = examRecordRepository;
}
/**
* @decription :
* return List<QuestionVo>
*/
@Override
public List<QuestionVo> getQuestionAll() {
List<Question> questionList = questionRepository.findAll();
return getQuestionVos(questionList);
}
/**
* Vo
*
* @param questionList
* @return Vo
*/
private List<QuestionVo> getQuestionVos(List<Question> questionList) {
// 需要自定义的question列表
List<QuestionVo> questionVoList = new ArrayList<>();
@ -168,8 +153,10 @@ public class ExamServiceImpl implements ExamService {
for (int i = 0; i < questionOptionVoList.size(); i++) {
QuestionOptionVo questionOptionVo = questionOptionVoList.get(i);
QuestionOption questionOption = new QuestionOption();
// 2.把questionOptionVo中的属性值复制到questionOption中。
BeanUtils.copyProperties(questionOptionVo, questionOption);
questionOptionList.add(questionOption);
// 3.如果questionOptionVo中的answer属性为true则把questionOptionVo中的questionOptionId属性值添加到questionAnswerOptionIds中。
if (questionOptionVo.getAnswer()) {
if (i != size - 1) {
// 把更新后的答案的id加上去,记得用-连到一起。
@ -182,18 +169,25 @@ public class ExamServiceImpl implements ExamService {
}
// 1.更新问题。
// 根据问题id从数据库中查找问题
Question question = questionRepository.findById(questionVo.getQuestionId()).orElse(null);
// 断言问题不为空
assert question != null;
// 将questionVo中的属性值复制到question中
BeanUtils.copyProperties(questionVo, question);
// 将问题答案选项id转换为字符串并设置到question中
question.setQuestionAnswerOptionIds(questionAnswerOptionIds.toString());
// 保存更新后的question到数据库中
questionRepository.save(question);
// 2.更新所有的option。
// 将questionOptionList中的所有option保存到数据库中
questionOptionRepository.saveAll(questionOptionList);
// 返回更新后的问题,方便前端局部刷新。
// 将更新后的question转换为questionVo并返回
return getQuestionVo(question);
}
}
@Override
public void questionCreate(QuestionCreateVo questionCreateVo) {
@ -741,3 +735,4 @@ public class ExamServiceImpl implements ExamService {
return replaceLastSeparator(sb.toString());
}
}
//注释完毕

@ -47,12 +47,7 @@ public class UserServiceImpl implements UserService {
@Autowired
ActionRepository actionRepository;
/**
*
*
* @param registerDTO
* @return null
*/
@Override
public User register(RegisterDTO registerDTO) {
try {
@ -86,12 +81,6 @@ public class UserServiceImpl implements UserService {
}
}
/**
*
*
* @param loginQo
* @return tokennull
*/
@Override
public String login(LoginQo loginQo) {
User user;
@ -117,36 +106,38 @@ public class UserServiceImpl implements UserService {
}
return null;
}
/**
*
*
* @param userId id
* @return
*/
@Override
public UserVo getUserInfo(String userId) {
// 根据用户ID从数据库中查找用户信息
User user = userRepository.findById(userId).orElse(null);
// 创建一个UserVo对象
UserVo userVo = new UserVo();
// 断言user对象不为空
assert user != null;
// 将user对象的属性复制到userVo对象中
BeanUtils.copyProperties(user, userVo);
// 返回userVo对象
return userVo;
}
/**
*
*
* @param userId id
* @return
*/
@Override
public UserInfoVo getInfo(String userId) {
// 根据用户ID查询用户信息
User user = userRepository.findById(userId).orElse(null);
// 断言用户信息不为空
assert user != null;
// 创建用户信息视图对象
UserInfoVo userInfoVo = new UserInfoVo();
// 1.尽可能的拷贝属性
BeanUtils.copyProperties(user, userInfoVo);
// 获取用户角色ID
Integer roleId = user.getUserRoleId();
// 根据角色ID查询角色信息
Role role = roleRepository.findById(roleId).orElse(null);
// 断言角色信息不为空
assert role != null;
// 获取角色名称
String roleName = role.getRoleName();
// 2.设置角色名称
@ -170,17 +161,27 @@ public class UserServiceImpl implements UserService {
BeanUtils.copyProperties(page, pageVo);
// 4.2 向Page中添加action
List<ActionVo> actionVoList = new ArrayList<>();
String actionIdsStr = page.getActionIds();
String[] actionIdArr = actionIdsStr.split("-");
for (String actionIdStr : actionIdArr) {
Integer actionId = Integer.parseInt(actionIdStr);
Action action = actionRepository.findById(actionId).orElse(null);
ActionVo actionVo = new ActionVo();
assert action != null;
BeanUtils.copyProperties(action, actionVo);
actionVoList.add(actionVo);
}
// 创建一个ActionVo类型的列表
List<ActionVo> actionVoList = new ArrayList<>();
// 获取页面中的actionIds字符串
String actionIdsStr = page.getActionIds();
// 将actionIds字符串按照"-"进行分割,得到一个字符串数组
String[] actionIdArr = actionIdsStr.split("-");
// 遍历字符串数组
for (String actionIdStr : actionIdArr) {
// 将字符串转换为整数
Integer actionId = Integer.parseInt(actionIdStr);
// 根据actionId从数据库中查找对应的Action对象
Action action = actionRepository.findById(actionId).orElse(null);
// 创建一个ActionVo对象
ActionVo actionVo = new ActionVo();
// 断言action对象不为空
assert action != null;
// 将action对象的属性值复制到actionVo对象中
BeanUtils.copyProperties(action, actionVo);
// 将actionVo对象添加到列表中
actionVoList.add(actionVo);
}
// 设置actionVoList到pageVo中然后把pageVo加到pageVoList中
pageVo.setActionVoList(actionVoList);
// 设置pageVoList下面再设置到RoleVo中

@ -55,17 +55,24 @@ public class FileTransUtil {
* @return
*/
public static String uploadFiles(MultipartFile[] uploadfiles, String dir) {
// 打印调试信息
log.debug("Multiple file upload!");
// 将上传的文件名拼接成一个字符串
String uploadedFileName = Arrays.stream(uploadfiles).map(x -> x.getOriginalFilename()).filter(x -> !StringUtils.isEmpty(x)).collect(Collectors.joining(" , "));
// 如果文件名为空,则返回错误信息
if (StringUtils.isEmpty(uploadedFileName)) {
return "文件名不能为空";
}
try {
// 调用工具类保存上传的文件
FileTransUtil.saveUploadedFiles(Arrays.asList(uploadfiles), dir);
} catch (IOException e) {
// 如果保存文件时发生异常,则返回错误信息
return "后台服务异常";
}
// 打印上传成功的日志信息
log.info("file upload successfully! " + uploadedFileName);
// 返回上传成功的提示信息
return "文件上传成功";
}
@ -75,26 +82,37 @@ public class FileTransUtil {
* @param files
* @throws IOException
*/
// 保存上传的文件
public static void saveUploadedFiles(List<MultipartFile> files, String dir) throws IOException {
// 遍历文件列表
for (MultipartFile file : files) {
// 如果文件为空,则跳过
if (file.isEmpty()) {
continue;
}
// 如果文件夹不存在,则创建
if (!FileUtil.exist(dir)) {
// 文件夹不存在就创建
FileUtil.mkdir(dir);
}
// 获取文件的字节数组
byte[] bytes = file.getBytes();
// 获取文件的原始文件名,并替换反斜杠为正斜杠
String fileName = file.getOriginalFilename().replace("\\", "/");
// 如果文件名中包含斜杠,则说明是上传文件夹
if (fileName.lastIndexOf('/')>0){
// 上传文件夹的时候会有这种情况
// 获取文件夹路径
String fileDir = dir + "/" + fileName.substring(0, fileName.lastIndexOf('/'));
// 如果文件夹不存在,则创建
if (!FileUtil.exist(fileDir)) {
// 文件夹不存在就创建,创建文件夹的时候会用到
FileUtil.mkdir(fileDir);
}
}
// 获取文件的路径
Path path = Paths.get(dir + "/" + fileName);
// 将文件写入路径
Files.write(path, bytes);
}
}
@ -107,14 +125,23 @@ public class FileTransUtil {
* @throws IOException
*/
public static ResponseEntity<InputStreamResource> downloadFile(String filePath) throws IOException {
// 记录下载文件的信息
log.info("downloading file : " + filePath);
// 创建文件系统资源
FileSystemResource file = new FileSystemResource(filePath);
// 创建响应头
HttpHeaders headers = new HttpHeaders();
// 设置缓存控制
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
// 设置文件下载时的文件名
headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", new String(file.getFilename().getBytes("gbk"), "iso-8859-1")));
// 设置缓存控制
headers.add("Pragma", "no-cache");
// 设置缓存控制
headers.add("Expires", "0");
// 打印文件名
System.out.println(file.getFilename());
// 返回响应实体
return ResponseEntity
.ok()
.headers(headers)

@ -24,12 +24,18 @@ public class FileUtils {
* @param url
* @param suffix
*/
public static File createFileByUrl(String url, String suffix) {
// 根据URL创建文件
public static File createFileByUrl(String url, String suffix) {
// 从URL获取图片字节数组
byte[] byteFile = getImageFromNetByUrl(url);
// 如果字节数组不为空
if (byteFile != null) {
// 从字节数组获取文件
File file = getFileFromBytes(byteFile, suffix);
// 返回文件
return file;
} else {
// 否则返回null
return null;
}
}
@ -42,9 +48,13 @@ public class FileUtils {
*/
private static byte[] getImageFromNetByUrl(String strUrl) {
try {
// 创建URL对象
URL url = new URL(strUrl);
// 打开连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置请求方法
conn.setRequestMethod("GET");
// 设置连接超时时间
conn.setConnectTimeout(5 * 1000);
// 通过输入流获取图片数据
InputStream inStream = conn.getInputStream();
@ -65,41 +75,61 @@ public class FileUtils {
* @throws Exception
*/
private static byte[] readInputStream(InputStream inStream) throws Exception {
// 创建一个ByteArrayOutputStream对象用于存储读取的字节数据
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// 创建一个字节数组,用于存储每次读取的字节数据
byte[] buffer = new byte[1024];
// 定义一个变量,用于存储每次读取的字节数
int len = 0;
// 循环读取输入流中的数据,直到读取完毕
while ((len = inStream.read(buffer)) != -1) {
// 将读取的字节数据写入ByteArrayOutputStream对象中
outStream.write(buffer, 0, len);
}
// 关闭输入流
inStream.close();
// 将ByteArrayOutputStream对象中的字节数据转换为字节数组并返回
return outStream.toByteArray();
}
// 创建临时文件
private static File getFileFromBytes(byte[] b, String suffix) {
// 根据字节数组和后缀名创建临时文件
private static File getFileFromBytes(byte[] b, String suffix) {
// 定义输出流
BufferedOutputStream stream = null;
// 定义文件
File file = null;
try {
// 创建临时文件
file = File.createTempFile("pattern", "." + suffix);
// 输出临时文件位置
System.out.println("临时文件位置:" + file.getCanonicalPath());
// 创建文件输出流
FileOutputStream fstream = new FileOutputStream(file);
// 创建缓冲输出流
stream = new BufferedOutputStream(fstream);
// 将字节数组写入文件
stream.write(b);
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
} finally {
// 关闭输出流
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// 打印异常信息
e.printStackTrace();
}
}
}
// 返回文件
return file;
}
public static MultipartFile createImg(String url) {
// 创建一个MultipartFile对象根据给定的url
public static MultipartFile createImg(String url) {
try {
// File转换成MutipartFile
File file = FileUtils.createFileByUrl(url, "jpg");
@ -131,12 +161,15 @@ public class FileUtils {
}
public static boolean base64ToFile(String filePath, String base64Data) throws Exception {
// 定义变量dataPrix和data用于存储base64Data中的数据
String dataPrix = "";
String data = "";
// 判断base64Data是否为空
if (base64Data == null || "".equals(base64Data)) {
return false;
} else {
// 将base64Data按照"base64,"进行分割获取dataPrix和data
String[] d = base64Data.split("base64,");
if (d != null && d.length == 2) {
dataPrix = d[0];

@ -26,17 +26,21 @@ public class JwtUtils {
private static final String APP_SECRET = "liangshanguang";
public static String genJsonWebToken(User user) {
// 判断用户信息是否为空
if (user == null || user.getUserId() == null || user.getUserUsername() == null || user.getUserAvatar() == null) {
return null;
}
// 使用Jwts.builder()创建一个JWT对象
return Jwts.builder().setSubject(SUBJECT)
// 下面3行设置token中间字段携带用户的信息
.claim("id", user.getUserId())
.claim("username", user.getUserUsername())
.claim("avatar", user.getUserAvatar())
// 设置签发时间
.setIssuedAt(new Date())
// 设置过期时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
// 使用HS256算法进行签名
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
// 生成的结果字符串太长,这里压缩下
.compact();
@ -50,6 +54,7 @@ public class JwtUtils {
*/
public static Claims checkJWT(String token) {
try {
// 使用Jwts解析token并设置签名密钥为APP_SECRET
return Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token).getBody();
} catch (Exception e) {
// 篡改token会导致校验失败走到异常分支这里返回null

@ -7,32 +7,26 @@
package lsgwr.exam.utils;
import lsgwr.exam.vo.ResultVO;
/**
*
*/
public class ResultVOUtil {
/**
*
*/
// 返回一个包含code、msg和object的ResultVO对象
public static ResultVO success(Integer code, String msg, Object object) {
return new ResultVO(code, msg, object);
}
/**
*
*/
// 返回一个包含object的ResultVO对象默认code为0msg为"成功"
public static ResultVO success(Object object) {
return new ResultVO(0, "成功", object);
}
/**
*
*/
// 返回一个默认的ResultVO对象默认code为0msg为"成功"object为null
public static ResultVO success() {
return new ResultVO(0, "成功", null);
}
/**
*
*/
// 返回一个包含code和msg的ResultVO对象默认object为null
public static ResultVO error(Integer code, String msg) {
return new ResultVO(code, msg, null);
}

@ -8,24 +8,18 @@ package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* Action
*/
@Data
public class ActionVo {
/**
*
*/
// 使用@JsonProperty注解将actionName属性映射到JSON中的action字段
@JsonProperty("action")
private String actionName;
/**
*
*/
// 使用@JsonProperty注解将actionDescription属性映射到JSON中的describe字段
@JsonProperty("describe")
private String actionDescription;
/**
*
*/
// 使用@JsonProperty注解将defaultCheck属性映射到JSON中的defaultCheck字段
@JsonProperty("defaultCheck")
private Boolean defaultCheck;
}

@ -1,9 +1,3 @@
/***********************************************************
* @Description :
* @author : 广(Laing Shan Guang)
* @date : 2019-06-23 19:30
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
@ -16,31 +10,27 @@ import lombok.Data;
*/
@Data
public class ExamCardVo {
//考试ID
@JsonProperty("id")
private String examId;
/**
*
*/
//考试名称
@JsonProperty("title")
private String examName;
/**
*
*/
//考试头像
@JsonProperty("avatar")
private String examAvatar;
/**
*
*/
//考试描述
@JsonProperty("content")
private String examDescription;
/**
*
*/
//考试分数
@JsonProperty("score")
private Integer examScore;// 考试的分数
/**
*
*/
private Integer examScore;
//考试限制的时间,单位为分钟
@JsonProperty("elapse")
private Integer examTimeLimit;// 考试的时间限制,以分钟为单位
private Integer examTimeLimit;
}

@ -5,64 +5,54 @@
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;
// 引入Jackson库中的注解用于JSON序列化和反序列化时自定义属性名
import com.fasterxml.jackson.annotation.JsonProperty;
// 引入Lombok库中的@Data注解它会自动为你的类的字段生成getter和setter方法以及toString、equals和hashCode方法
import lombok.Data;
import java.util.List;
// 使用@Data注解来自动生成getter、setter等方法
@Data
public class ExamCreateVo {
// 考试名称,通过@JsonProperty注解指定JSON字段名为"name"
@JsonProperty("name")
private String examName;
// 考试头像或图标,通过@JsonProperty注解指定JSON字段名为"avatar"
@JsonProperty("avatar")
private String examAvatar;
// 考试描述,通过@JsonProperty注解指定JSON字段名为"desc"
@JsonProperty("desc")
private String examDescription;
/**
* @JsonPropertyJSON"elapse"
*
*/
//试时长,单位分钟
@JsonProperty("elapse")
private Integer examTimeLimit;
/**
* 使List<ExamQuestionSelectVo>
* ExamQuestionSelectVoVO
*/
//单选题
private List<ExamQuestionSelectVo> radios;
/**
* 使List<ExamQuestionSelectVo>
*/
//多选题
private List<ExamQuestionSelectVo> checks;
/**
* 使List<ExamQuestionSelectVo>
*/
//判断题
private List<ExamQuestionSelectVo> judges;
/**
* @JsonPropertyJSON"radioScore"
*/
//单选题的分数
@JsonProperty("radioScore")
private Integer examScoreRadio;
/**
* @JsonPropertyJSON"checkScore"
*/
//多选题的分数
@JsonProperty("checkScore")
private Integer examScoreCheck;
/**
* @JsonPropertyJSON"judgeScore"
*/
//判断题每题的分数
@JsonProperty("judgeScore")
private Integer examScoreJudge;
}

@ -5,43 +5,26 @@
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;
// 引入Exam实体类它可能包含了考试的详细信息如考试ID、名称、描述等
import lsgwr.exam.entity.Exam;
// 引入Lombok库的@Data注解用于自动生成getter、setter等方法
import lombok.Data;
// 使用@Data注解来自动生成这个类的getter、setter等方法
@Data
public class ExamDetailVo {
/**
*
* Exam
* ID
* ExamDetailVoExam
*/
//试的基本信息对象
private Exam exam;
/**
* id
* ID
*
* ID
*/
//单选题的id数组
private String[] radioIds;
/**
* id
* idID
*
*
*/
//多选题的id数组
private String[] checkIds;
/**
* id
* ID
*
* ID
*/
//判断题的id数组
private String[] judgeIds;
}

@ -4,41 +4,32 @@
* @date : 2019-06-22 17:00
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;// 定义包名,用于组织类文件,避免命名冲突
// 导入Jackson库的JsonProperty注解用于JSON序列化时自定义字段名
package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
// 导入Lombok库的Data注解用于自动生成getter、setter、equals、hashCode和toString方法
import lombok.Data;
// 导入Java的List接口用于存储ExamVo对象的集合
import java.util.List;
// 使用Lombok的@Data注解自动为该类生成getter、setter等方法
@Data
public class ExamPageVo {
/**
*
*/
//分页时每个分页的大小
private Integer pageSize;
/**
* 110
* 1
*/
//当前是在第几页注意要比前端传过来地小1
private Integer pageNo;
/**
*
*/
//一共有多少条符合条件的记录
private Long totalCount;
/**
*
*/
//一共有多少页
private Integer totalPage;
/**
* ExamVoExamVo
* @JsonProperty("data")JSONexamVoListJSON"data"
*/
//当前页的详细数据
@JsonProperty("data")
private List<ExamVo> examVoList;
}

@ -4,33 +4,25 @@
* @date : 2019-06-17 23:10
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;// 定义包名,用于组织类文件,避免命名冲突
// 导入Jackson库的JsonProperty注解用于JSON序列化时自定义字段名
package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
// 导入Lombok库的Data注解用于自动生成getter、setter、equals、hashCode和toString方法
import lombok.Data;
// 使用Lombok的@Data注解自动为该类生成getter、setter等方法
@Data
public class ExamQuestionSelectVo {
/**
* JSON"id"
*
*/
// 问题ID
@JsonProperty("id")
private String questionId;
/**
* JSON"name"
*
*/
// 问题名称
@JsonProperty("name")
private String questionName;
/**
* false
* true
*
* JSON"checked"
* .falsetrue
*
*/
@JsonProperty("checked")
private Boolean checked = false;
}
}

@ -4,35 +4,24 @@
* @date : 2019-06-23 11:00
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;// 定义包名,用于组织类文件,避免命名冲突
// 导入Jackson库的JsonProperty注解用于JSON序列化时自定义字段名
package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
// 导入Lombok库的Data注解用于自动生成getter、setter、equals、hashCode和toString方法
import lombok.Data;
// 导入Java的List接口用于存储对象的集合
import java.util.List;
// 使用Lombok的@Data注解自动为该类生成getter、setter等方法
@Data
public class ExamQuestionTypeVo {
/**
* ExamQuestionSelectVo
* JSON"radios"
* "radios"radio buttons
*/
// 单选题列表
@JsonProperty("radios")
private List<ExamQuestionSelectVo> examQuestionSelectVoRadioList;
/**
* ExamQuestionSelectVo
* JSON"checks"
* "checks"checkboxes
*/
// 多选题列表
@JsonProperty("checks")
private List<ExamQuestionSelectVo> examQuestionSelectVoCheckList;
/**
* ExamQuestionSelectVo
* JSON"judges"
* "judges"true/falseyes/no
*/
// 判断题列表
@JsonProperty("judges")
private List<ExamQuestionSelectVo> examQuestionSelectVoJudgeList;
}

@ -4,34 +4,23 @@
* @date : 2019/10/25 7:42
* @email : liangshanguang2@gmail.com
***********************************************************/
package lsgwr.exam.vo;// 定义包名,用于组织类文件,避免命名冲突
// 导入相关的实体类
import lsgwr.exam.entity.Exam;// 考试实体类
import lsgwr.exam.entity.ExamRecord; // 考试记录实体类
import lsgwr.exam.entity.User;// 用户实体类
// 导入Lombok库的Data注解用于自动生成getter、setter、equals、hashCode和toString方法
package lsgwr.exam.vo;
import lsgwr.exam.entity.Exam;
import lsgwr.exam.entity.ExamRecord;
import lsgwr.exam.entity.User;
import lombok.Data;
// 使用Lombok的@Data注解自动生成getter、setter等方法
@Data
public class ExamRecordVo {
/**
*
*
* ExamExam
*/
//当前考试记录对应的考试
private Exam exam;
/**
*
*
* ExamRecordExamRecord
*/
//当前考试对应的内容
private ExamRecord examRecord;
/**
*
*
* UserUser
*/
//参加考试的用户信息
private User user;
}

@ -4,83 +4,97 @@
* @date : 2019/5/14 07:42
* @email : liangshanguang2@gmail.com
***********************************************************/
// 定义包名,用于组织类文件并避免命名冲突
package lsgwr.exam.vo;
// 导入所需的库和注解
import com.fasterxml.jackson.annotation.JsonFormat;// 用于日期格式的序列化和反序列化
import com.fasterxml.jackson.annotation.JsonProperty;// 用于自定义JSON字段名
import lombok.Data;// 用于自动生成getter、setter等方法
import java.util.Date;// Java日期类
import java.util.List;// Java列表接口
// 使用Lombok的@Data注解自动生成getter、setter等方法
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class ExamVo {
// 使用@JsonProperty注解自定义JSON字段名为"id"
// 考试id
@JsonProperty("id")
private String examId;// 考试的唯一标识符
// 使用@JsonProperty注解自定义JSON字段名为"name"
private String examId;
// 考试名称
@JsonProperty("name")
private String examName;// 考试名称
// 使用@JsonProperty注解自定义JSON字段名为"avatar"
private String examName;
// 考试头像
@JsonProperty("avatar")
private String examAvatar;// 考试图标或头像
// 使用@JsonProperty注解自定义JSON字段名为"desc"
private String examAvatar;
// 考试描述
@JsonProperty("desc")
private String examDescription;// 考试描述
// 单选题列表JSON字段名为"radios"
private String examDescription;
// 单选题列表
@JsonProperty("radios")
private List<ExamQuestionSelectVo> examQuestionSelectVoRadioList;// 单选题选项列表
// 多选题列表JSON字段名为"checks"
private List<ExamQuestionSelectVo> examQuestionSelectVoRadioList;
// 多选题列表
@JsonProperty("checks")
private List<ExamQuestionSelectVo> examQuestionSelectVoCheckList;// 多选题选项列表
// 判断题列表JSON字段名为"judges"
private List<ExamQuestionSelectVo> examQuestionSelectVoCheckList;
// 判断题列表
@JsonProperty("judges")
private List<ExamQuestionSelectVo> examQuestionSelectVoJudgeList;// 判断题选项列表
// 考试总分JSON字段名为"score"
private List<ExamQuestionSelectVo> examQuestionSelectVoJudgeList;
// 考试总分数
@JsonProperty("score")
private Integer examScore;// 考试的总分数
// 单选题总分JSON字段名为"radioScore"
private Integer examScore;
// 单选题分数
@JsonProperty("radioScore")
private Integer examScoreRadio;// 单选题的总分数
// 多选题总分JSON字段名为"checkScore"
private Integer examScoreRadio;
// 多选题分数
@JsonProperty("checkScore")
private Integer examScoreCheck;// 多选题的总分数
// 判断题总分JSON字段名为"judgeScore"
private Integer examScoreCheck;
// 判断题分数
@JsonProperty("judgeScore")
private Integer examScoreJudge;// 判断题的总分数
private Integer examScoreJudge;
// 考试创建人的名称JSON字段名为"creator"
// 注意这里假设只存储了创建人的名称实际可能需要根据ID查询数据库获取
/**
* id
*/
@JsonProperty("creator")
private String examCreator;// 考试的创建者名称
private String examCreator;
// 考试的时间限制分钟JSON字段名为"elapse"
/**
*
*/
@JsonProperty("elapse")
private Integer examTimeLimit;// 考试的时间限制,单位为分钟
private Integer examTimeLimit;
// 考试开始时间JSON字段名为"startDate"
// 使用@JsonFormat注解指定日期格式和时区
/**
*
*/
@JsonProperty("startDate")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date examStartDate;// 考试的开始时间
private Date examStartDate;
// 考试结束时间JSON字段名为"endDate"
// 使用@JsonFormat注解指定日期格式和时区
/**
*
*/
@JsonProperty("endDate")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date examEndDate;// 考试的结束时间
private Date examEndDate;
// 创建时间JSON字段名为"createTime"
// 使用@JsonFormat注解指定日期格式和时区
/**
*
*/
@JsonProperty("createTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;// 记录的创建时间
private Date createTime;
// 更新时间JSON字段名为"updateTime"
// 使用@JsonFormat注解指定日期格式和时区
/**
*
*/
@JsonProperty("updateTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;// 记录的更新时间
}
private Date updateTime;
}

@ -16,48 +16,39 @@ import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
public class JsonData implements Serializable {
/**
* id
*/
private static final long serialVersionUID = 1L;
/**
* 0 1-1
*/
//状态码 0 表示成功1表示处理中-1表示失败
private Integer code;
/**
*
*/
//数据
private Object data;
/**
*
*/
//描述
private String msg;
/**
*
*/
//成功,传入数据
public static JsonData buildSuccess() {
return new JsonData(0, null, null);
}
/**
*
*/
//成功,传入数据
public static JsonData buildSuccess(Object data) {
return new JsonData(0, data, null);
}
/**
*
*/
//失败,传入描述信息
public static JsonData buildError(String msg) {
return new JsonData(-1, null, msg);
}
/**
* ,
*/
//失败,传入描述信息,状态码
public static JsonData buildError(String msg, Integer code) {
return new JsonData(code, null, msg);
}
@ -73,9 +64,8 @@ public class JsonData implements Serializable {
return new JsonData(0, data, msg);
}
/**
* ,
*/
//成功,传入数据,及状态码
public static JsonData buildSuccess(Object data, int code) {
return new JsonData(code, data, null);
}

@ -10,24 +10,18 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class PageVo {
/**
*
*/
// 使用@JsonProperty注解将actionEntitySet字段映射到JSON中的actionEntitySet字段
@JsonProperty("actionEntitySet")
private List<ActionVo> actionVoList;
/**
*
*/
// 使用@JsonProperty注解将permissionId字段映射到JSON中的permissionId字段
@JsonProperty("permissionId")
private String pageName;
/**
*
*/
// 使用@JsonProperty注解将permissionName字段映射到JSON中的permissionName字段
@JsonProperty("permissionName")
private String pageDescription;
}

@ -10,47 +10,38 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* ,
*/
@Data
public class QuestionCreateSimplifyVo {
/**
*
*/
//题目名称
@JsonProperty("name")
private String questionName;
@JsonProperty("desc")
private String questionDescription;
/**
* id
*/
//问题的难度的id
@JsonProperty("level")
private Integer questionLevelId;
/**
* ()
*/
//问题的类型(单选、多选、判断等)
@JsonProperty("type")
private Integer questionTypeId;
/**
*
*/
//题目的类别表,从内容角度划分,比如数学、语文、英语等
@JsonProperty("category")
private Integer questionCategoryId;
/**
*
*/
//创建选项 里添加的内容
@JsonProperty("option")
private String option;
/**
* truefalse
*/
//问题的选项列表带上了是否是答案的true和false
@JsonProperty("options")
private List<QuestionOptionCreateVo> questionOptionCreateVoList;
}

@ -10,53 +10,44 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class QuestionCreateVo {
/**
*
*/
//题目名称
@JsonProperty("name")
private String questionName;
@JsonProperty("desc")
private String questionDescription;
/**
* ,5
*/
//题目的分数,默认值是5
@JsonProperty("score")
private Integer questionScore = 5;
/**
* idtoken
*/
//题目的创建者的id从token中解析得到
@JsonProperty("creator")
private String questionCreatorId;
/**
* id
*/
//问题的难度的id
@JsonProperty("level")
private Integer questionLevelId;
/**
* ()
*/
//问题的类型(单选、多选、判断等)
@JsonProperty("type")
private Integer questionTypeId;
/**
*
*/
//题目的类别表,从内容角度划分,比如数学、语文、英语等
@JsonProperty("category")
private Integer questionCategoryId;
/**
* truefalse
*/
//问题的选项列表带上了是否是答案的true和false
@JsonProperty("options")
private List<QuestionOptionCreateVo> questionOptionCreateVoList;
}

@ -11,35 +11,26 @@ import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
@Data
public class QuestionDetailVo {
/**
* id
*/
//问题的id
private String id;
/**
*
*/
//考试题目
private String name;
/**
*
*/
//考试描述
private String description;
/**
*
*/
//问题的类型
private String type;
/**
*
*/
//问题的选项
private List<QuestionOption> options;
/**
* ,id
*/
//问题的答案,选项的id组成的数组
private List<String> answers = new ArrayList<>();
}

@ -8,21 +8,16 @@ package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
*
*/
@Data
public class QuestionOptionCreateVo {
/**
*
*/
//问题的内容
@JsonProperty("content")
private String questionOptionContent;
/**
*
*/
//当前的选项是否是问题大答案
@JsonProperty("answer")
private Boolean answer = false;

@ -8,29 +8,22 @@ package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
*
*/
@Data
public class QuestionOptionVo {
/**
* id
*/
// 题目选项ID
@JsonProperty("id")
private String questionOptionId;
/**
*
*/
// 题目选项内容
@JsonProperty("content")
private String questionOptionContent;
/**
*
*/
// 是否为答案
@JsonProperty("answer")
private Boolean answer = false;
/**
*
*/
// 题目选项描述
@JsonProperty("description")
private String questionOptionDescription;
}

@ -10,35 +10,28 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class QuestionPageVo {
/**
*
*/
//分页时每个分页的大小
private Integer pageSize;
/**
* 1
*/
//当前是在第几页注意要比前端传过来地小1
private Integer pageNo;
/**
*
*/
//一共有多少条符合条件的记录
private Long totalCount;
/**
*
*/
//一共有多少页
private Integer totalPage;
/**
*
*/
//当前页的详细数据
@JsonProperty("data")
private List<QuestionVo> questionVoList;
}

@ -13,24 +13,18 @@ import lsgwr.exam.entity.QuestionType;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class QuestionSelectionVo {
/**
*
*/
// 问题类型列表
@JsonProperty("types")
private List<QuestionType> questionTypeList;
/**
*
*/
// 问题类别列表
@JsonProperty("categories")
private List<QuestionCategory> questionCategoryList;
/**
*
*/
// 问题等级列表
@JsonProperty("levels")
private List<QuestionLevel> questionLevelList;
}

@ -12,66 +12,53 @@ import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* VO
*/
@Data
public class QuestionVo {
/**
* idquestion
*/
// 使用@JsonProperty注解将属性映射到JSON对象的属性
@JsonProperty("id")
private String questionId;
/**
* question
*/
// 使用@JsonProperty注解将属性映射到JSON对象的属性
@JsonProperty("name")
private String questionName;
/**
* question
*/
// 使用@JsonProperty注解将属性映射到JSON对象的属性
@JsonProperty("score")
private Integer questionScore;
/**
* questionCreatorId
*/
//根据questionCreatorId查询创建人
@JsonProperty("creator")
private String questionCreator;
/**
* questionLevelId
*/
//根据questionLevelId查询问题难度
@JsonProperty("level")
private String questionLevel;
/**
* question_levelid
*/
//问题难度级别在数据库表question_level中的id
@JsonProperty("levelId")
private int questionLevelId;
/**
* questionTypeId
*/
//问题类型根据questionTypeId获取
@JsonProperty("type")
private String questionType;
/**
* question_typeid
*/
//问题类型在数据库表question_type中的id
@JsonProperty("typeId")
private int questionTypeId;
/**
* questionCategoryId
*/
//问题分类根据questionCategoryId获得
@JsonProperty("category")
private String questionCategory;
/**
* question_categoryid
*/
//问题分类在数据库表question_category中的id
@JsonProperty("categoryId")
private int questionCategoryId;
@ -79,17 +66,15 @@ public class QuestionVo {
@JsonProperty("description")
private String questionDescription;
/**
* questionOptionIds,isAnswer
*/
//问题选项列表从questionOptionIds获得,需要自己额外给isAnswer赋值
@JsonProperty("options")
private List<QuestionOptionVo> questionOptionVoList;
/**
* Java
* @DynamicUpdate
*/
//更新时间设计表时设置了自动插入当前时间无需在Java代码中设置了。
//同时@DynamicUpdate注解可以时间当数据库数据变化时自动更新无需人工维护
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

@ -11,27 +11,21 @@ import lombok.Data;
import java.util.HashMap;
import java.util.List;
/**
* VO
*/
@Data
public class RecordDetailVo {
/**
*
*/
//含有考试记录原始信息的对象
private ExamRecord examRecord;
/**
* , idid
*/
//用户此次考试的作答信息, 键是题目的id值是选项id的列表
private HashMap<String, List<String>> answersMap;
/**
* MapidTrue or False
*/
//用户每题作答结果的Map键是问题的id值是用户这题是否回答正确True or False
private HashMap<String, String> resultsMap;
/**
* idid
*/
//正确答案键是题目的id值是正确答案的id组成的列表
private HashMap<String, List<String>> answersRightMap;
}

@ -13,33 +13,29 @@ import lombok.Data;
@JsonInclude(JsonInclude.Include.NON_NULL) // 避免返回NULL的字段
public class ResultVO<T> {
/**
*
* @param code
* @param msg
* @param data
*/
// 构造函数用于初始化ResultVO对象
public ResultVO(Integer code, String msg, T data) {
// 初始化code属性
this.code = code;
// 初始化msg属性
this.msg = msg;
// 初始化data属性
this.data = data;
}
public ResultVO() {
}
/**
*
*/
//错误码
private Integer code;
/**
*
*/
//提示信息
private String msg = "";
/**
*
*/
//具体内容
private T data;
}

@ -10,29 +10,22 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* VO
*/
@Data
public class RoleVo {
/**
*
*/
// 角色名称
@JsonProperty("id")
private String roleName;
/**
*
*/
// 角色描述
@JsonProperty("name")
private String roleDescription;
/**
*
*/
// 角色详情
@JsonProperty("describe")
private String roleDetail;
/**
*
*/
// 角色权限
@JsonProperty("permissions")
private List<PageVo> pageVoList;
}

@ -8,29 +8,23 @@ package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
*
*/
@Data
public class UserInfoVo {
/**
* ID
*/
// 用户ID
@JsonProperty("id")
private String userId;
/**
* URL
*/
// 用户头像
@JsonProperty("avatar")
private String userAvatar;
/**
*
*/
// 用户昵称
@JsonProperty("name")
private String userNickname;
/**
*
*/
// 用户名
@JsonProperty("username")
private String userUsername;
@ -39,18 +33,23 @@ public class UserInfoVo {
*/
private String password = "";
// 用户邮箱
@JsonProperty("email")
private String userEmail;
// 用户电话
@JsonProperty("telephone")
private String userPhone;
// 角色ID
@JsonProperty("roleId")
private String roleName;
// 用户描述
@JsonProperty("welcome")
private String userDescription;
// 角色信息
@JsonProperty("role")
private RoleVo roleVo;
}
}

@ -8,49 +8,38 @@ package lsgwr.exam.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
*
*/
@Data
public class UserVo {
/**
* ID
*/
// 用户ID
@JsonProperty("id")
private String userId;
/**
*
*/
// 用户名
@JsonProperty("username")
private String userUsername;
/**
*
*/
// 昵称
@JsonProperty("nickname")
private String userNickname;
/**
* ID
*/
// 角色ID
@JsonProperty("role")
private Integer userRoleId;
/**
*
*/
// 头像
@JsonProperty("avatar")
private String userAvatar;
/**
*
*/
// 描述
@JsonProperty("description")
private String userDescription;
/**
*
*/
// 邮箱
@JsonProperty("email")
private String userEmail;
/**
*
*/
// 电话
@JsonProperty("phone")
private String userPhone;
}

@ -5,7 +5,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: aA111111
url: jdbc:mysql://localhost:3306/exam?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&&allowPublicKeyRetrieval=true
url: jdbc:mysql://localhost:3306/exam?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
jpa:
# 调试的时候用用于打印完成SQL语句(但是不打印参数),联合下面的logging.level一同打印最完整的SQL信息(语句+参数)
show-sql: false

@ -1,44 +1,58 @@
events {
# 工作进程的最大连接数
worker_connections 1024;
}
http {
# 包含mime.types文件
include mime.types;
# 默认文件类型
default_type application/octet-stream;
# 开启sendfile功能
sendfile on;
# 保持连接超时时间
keepalive_timeout 65;
# 定义一个server
server {
# 监听80端口
listen 80;
# 服务器名称
server_name localhost;
# 404页面跳转
location / {
# 如果请求的文件不存在则尝试加载index.html
try_files $uri /index.html;
}
# 静态资源目录即vue打包后的dist里的静态资源
root /usr/share/nginx/html/;
# 默认首页
index index.html index.htm;
# 后端服务的配置
location /api/ {
proxy_redirect off;
# 关闭代理重定向
proxy_redirect off;
# 设置代理请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 后端服务地址
proxy_pass http://localhost:9527/api/;
}
# 错误页面
error_page 500 502 503 504 /50x.html;
# 错误页面路径
location = /50x.html {
root html;
}
}
}
}

File diff suppressed because one or more lines are too long

@ -1,7 +1,13 @@
# 定义语言为node_js
language: node_js
# 定义node_js版本为10.15.0
node_js:
- 10.15.0
# 缓存yarn
cache: yarn
# 定义脚本
script:
# 运行yarn
- yarn
- yarn run lint --no-fix && yarn run build
# 运行yarn run lint --no-fix && yarn run build
- yarn run lint --no-fix && yarn run build

@ -1,6 +1,9 @@
// 导出一个对象用于配置Babel
module.exports = {
// 使用@vue/app预设
presets: [
'@vue/app',
// 使用@babel/preset-env预设并设置useBuiltIns为entry
[
'@babel/preset-env',
{
@ -8,9 +11,10 @@ module.exports = {
}
]
]
// if your use import on Demand, Use this code
// 如果你的项目中使用了按需加载,可以使用以下代码
// ,
// plugins: [
// // 使用import插件配置ant-design-vue库的路径和样式
// [ 'import', {
// 'libraryName': 'ant-design-vue',
// 'libraryDirectory': 'es',

@ -1,23 +1,34 @@
// 导出配置文件
module.exports = {
// 模块文件扩展名
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
// 转换规则
transform: {
// 将.vue文件转换为jest可识别的格式
'^.+\\.vue$': 'vue-jest',
// 将.css、.styl、.less、.sass、.scss、.svg、.png、.jpg、.ttf、.woff、.woff2文件转换为stub
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
// 将.jsx、.js文件转换为babel可识别的格式
'^.+\\.jsx?$': 'babel-jest'
},
// 模块名称映射
moduleNameMapper: {
// 将../替换为<rootDir>/src/
'^../(.*)$': '<rootDir>/src/$1'
},
// 快照序列化器
snapshotSerializers: [
'jest-serializer-vue'
],
// 测试匹配规则
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
// 测试URL
testURL: 'http://localhost/'
}
}

@ -1,102 +1,35 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<!-- 设置字符编码为utf-8 -->
<meta charset="utf-8">
<!-- 设置IE浏览器兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置视口宽度为设备宽度初始缩放比例为1.0 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 设置网站图标 -->
<link rel="icon" href="<%= BASE_URL %>logo.png">
<!-- 设置网站标题 -->
<title>在线考试系统</title>
<!-- 定义加载中的样式 -->
<style>#loading-mask{
position:fixed;left:0;top:0;height:100%;
width:100%;background:#fff;user-select:none;
z-index:9999;overflow:hidden}
.loading-wrapper{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-100%)
}
.loading-dot{
animation:antRotate 1.2s infinite linear;
transform:rotate(45deg);
position:relative;
display:inline-block;
font-size:64px;
width:64px;
height:64px;
box-sizing:border-box}
.loading-dot i{
width:22px;
height:22px;
position:absolute;
display:block;
background-color:#1890ff;
border-radius:100%;
transform:scale(.75);
transform-origin:50% 50%;
opacity:.3;
animation:antSpinMove 1s infinite linear alternate
}
.loading-dot i:nth-child(1){
top:0;
left:0
}
.loading-dot i:nth-child(2){
top:0;
right:0;
-webkit-animation-delay:.4s;
animation-delay:.4s
}
.loading-dot i:nth-child(3){
right:0;
bottom:0;
-webkit-animation-delay:.8s;
animation-delay:.8s
}
.loading-dot i:nth-child(4){
bottom:0;
left:0;
-webkit-animation-delay:1.2s;
animation-delay:1.2s
}
@keyframes antRotate{
to{
-webkit-transform:rotate(405deg);
transform:rotate(405deg)
}
}
@-webkit-keyframes antRotate{to{
-webkit-transform:rotate(405deg);
transform:rotate(405deg)
}
}
@keyframes antSpinMove{
to{
opacity:1
}
}
@-webkit-keyframes antSpinMove{
to{
opacity:1
}
}
</style>
<!-- 设置加载动画样式 -->
<style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
</head>
<body>
<!-- 当JavaScript被禁用时显示提示信息 -->
<!-- 如果禁用了JavaScript则显示以下内容 -->
<noscript>
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- 应用的主容器 -->
<!-- 应用容器 -->
<div id="app">
<!-- 加载中的遮罩层 -->
<!-- 加载动画遮罩层 -->
<div id="loading-mask">
<!-- 加载动画容器 -->
<div class="loading-wrapper">
<!-- 加载动画 -->
<span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
</div>
</div>
<!-- built files will be auto injected -->
<!-- 自动注入构建文件 -->
</body>
</html>

@ -1,52 +1 @@
#preloadingAnimation{
position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;
z-index: 9999;overflow: hidden}
.lds-roller{
display:inline-block;
position:relative;
left:50%;
top:50%;
transform:translate(-50%,-50%);
width:64px;
height:64px;
}
.lds-roller div{
animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;
transform-origin:32px 32px;
}
.lds-roller div:after{
content:" ";
display:block;
position:absolute;
width:6px;
height:6px;
border-radius:50%;
background:#13c2c2;
margin:-3px 0 0 -3px;
}
.lds-roller div:nth-child(1){animation-delay:-0.036s;}
.lds-roller div:nth-child(1):after{top:50px;left:50px;}
.lds-roller div:nth-child(2){animation-delay:-0.072s;}
.lds-roller div:nth-child(2):after{top:54px;left:45px;}
.lds-roller div:nth-child(3){animation-delay:-0.108s;}
.lds-roller div:nth-child(3):after{top:57px;left:39px;}
.lds-roller div:nth-child(4){animation-delay:-0.144s;}
.lds-roller div:nth-child(4):after{top:58px;left:32px;}
.lds-roller div:nth-child(5){animation-delay:-0.18s;}
.lds-roller div:nth-child(5):after{top:57px;left:25px;}
.lds-roller div:nth-child(6){animation-delay:-0.216s;}
.lds-roller div:nth-child(6):after{top:54px;left:19px;}
.lds-roller div:nth-child(7){animation-delay:-0.252s;}
.lds-roller div:nth-child(7):after{top:50px;left:14px;}
.lds-roller div:nth-child(8){animation-delay:-0.288s;}
.lds-roller div:nth-child(8):after{top:45px;left:10px;}
#preloadingAnimation .load-tips{
color: #13c2c2;
font-size:2rem;
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);margin-top:80px;
text-align:center;width:400px;height:64px;}
@keyframes lds-roller{0%{transform:rotate(0deg);}
100%{transform:rotate(360deg);}}
#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}

@ -1,27 +1 @@
<div id="preloadingAnimation"><div class=lds-roller><div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
</div>
<div class=load-tips>Loading</div>
</div>
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>

@ -1,31 +1,5 @@
/**
* 预加载动画
*/
<div class="preloading-animate">
<!--预加载的包装容器 -->
<div class="preloading-wrapper">
<svg class="preloading-balls"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle cx="67.802" cy="59.907" r="6" fill="#51CACC">
<animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle>
<circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77">
<animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle>
<circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle>
</svg>
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
</div>
</div>

@ -1,20 +1 @@
.preloading-animate{
background:#ffffff;
width:100%;
height:100%;
position:fixed;
left:0;
top:0;
z-index:299;
}
.preloading-animate .preloading-wrapper{
position:absolute;
width:5rem;
height:5rem;
left:50%;
top:50%;
transform:translate(-50%,-50%);
}
.preloading-animate .preloading-wrapper .preloading-balls{
font-size:5rem;
}
.preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;}

@ -1,5 +1,7 @@
<template>
<!-- 使用a-locale-provider组件设置locale属性为locale -->
<a-locale-provider :locale="locale">
<!-- 在div中渲染router-view组件 -->
<div id="app">
<router-view />
</div>
@ -7,12 +9,16 @@
</template>
<script>
// zh_CN
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
// AppDeviceEnquire
import { AppDeviceEnquire } from './utils/mixin'
export default {
// 使AppDeviceEnquire
mixins: [AppDeviceEnquire],
data () {
// localezh_CN
return {
locale: zhCN
}
@ -23,34 +29,41 @@ export default {
}
</script>
<style>
/* 设置#app的高度为100% */
#app {
height: 100%;
}
/* 设置h1, h2, h3, h4, h5, h6的行高、字体大小和字体粗细为初始值 */
h1, h2, h3, h4, h5, h6 {
line-height: inherit !important;
font-size: initial !important;
font-weight: initial !important;
}
/* 设置.page-header .breadcrumb的背景颜色和内边距为初始值 */
.page-header .breadcrumb {
background-color: transparent !important;
padding: inherit !important;
}
/* 设置.row的左边距为初始值 */
.row {
margin-left: inherit !important;
}
/* 设置a标签的鼠标悬停效果为无下划线行高为初始值 */
a:hover {
text-decoration: none !important;
line-height: inherit !important;
}
/* 设置i标签下的svg的垂直对齐方式为初始值 */
i > svg {
vertical-align: initial !important;
}
/* 设置ul标签的下边距为初始值 */
ul {
margin-bottom: inherit !important;
}

@ -1,41 +1,32 @@
/**
* @description: 考试模块接口包括考试问题选项和评分等接口
*/
// 考试相关的接口,包括考试、问题、选项和评分等接口
import api from './index'
import { axios } from '../utils/request'
/**
* @description: 根据传入的参数获取问题列表
* @param {Object} parameter - 查询参数
* @returns {Promise} - 返回问题列表的Promise对象
*/
// 导出一个函数,用于获取问题列表
export function getQuestionList (parameter) {
// 使用axios发送get请求获取问题列表
return axios({
url: api.ExamQuestionList,
method: 'get',
params: parameter
url: api.ExamQuestionList, // 请求的URL
method: 'get', // 请求的方法
params: parameter // 请求的参数
})
}
/**
* @description: 获取所有问题
* @returns {Promise} - 返回所有问题的Promise对象
*/
// 导出一个函数,用于获取所有题目
export function getQuestionAll () {
// 使用axios发送get请求获取所有题目
return axios({
url: api.ExamQuestionAll,
method: 'get'
})
}
/**
* @description: 更新问题信息
* @param {Object} parameter - 问题更新参数
* @returns {Promise} - 返回更新操作的Promise对象
*/
// 导出一个名为questionUpdate的函数参数为parameter
export function questionUpdate (parameter) {
// 打印参数
console.log(parameter)
// 返回一个axios请求请求的url为api.ExamQuestionUpdate请求方法为post请求的数据为parameter
return axios({
url: api.ExamQuestionUpdate,
method: 'post',
@ -43,27 +34,26 @@ export function questionUpdate (parameter) {
})
}
/**
* @description: 获取问题的选项
* @returns {Promise} - 返回问题选项的Promise对象
*/
// 导出一个函数,用于获取题目选择
export function getQuestionSelection () {
// 使用axios发送get请求获取题目选择
return axios({
// 请求的URL
url: api.ExamQuestionSelection,
// 请求的方法
method: 'get',
// 请求的头部信息
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
/**
* @description: 创建新问题
* @param {Object} parameter - 问题创建参数
* @returns {Promise} - 返回创建操作的Promise对象
*/
// 导出一个名为questionCreate的函数参数为parameter
export function questionCreate (parameter) {
// 打印参数
console.log(parameter)
// 使用axios发送post请求url为api.ExamQuestionCreate参数为parameter
return axios({
url: api.ExamQuestionCreate,
method: 'post',
@ -71,12 +61,9 @@ export function questionCreate (parameter) {
})
}
/**
* @description: 根据传入的参数获取考试列表
* @param {Object} parameter - 查询参数
* @returns {Promise} - 返回考试列表的Promise对象
*/
// 导出一个函数,用于获取考试列表
export function getExamList (parameter) {
// 使用axios发送get请求获取考试列表
return axios({
url: api.ExamList,
method: 'get',
@ -84,22 +71,18 @@ export function getExamList (parameter) {
})
}
/**
* @description: 获取所有考试
* @returns {Promise} - 返回所有考试的Promise对象
*/
// 导出一个函数,用于获取所有考试
export function getExamAll () {
// 使用axios发送get请求获取所有考试
return axios({
url: api.ExamAll,
method: 'get'
})
}
/**
* @description: 获取所有问题并按单选多选和判断进行分类
* @returns {Promise} - 返回分类问题列表的Promise对象
*/
// 导出一个函数,用于获取所有问题,按照单选、多选和判断进行分类
export function getExamQuestionTypeList () {
// 使用axios发送get请求获取所有问题按照单选、多选和判断进行分类
return axios({
url: api.ExamQuestionTypeList,
method: 'get',
@ -109,11 +92,9 @@ export function getExamQuestionTypeList () {
})
}
/**
* @description: 获取考试卡片列表
* @returns {Promise} - 返回考试卡片列表的Promise对象
*/
// 导出一个函数,用于获取考试卡片列表
export function getExamCardList () {
// 使用axios发送get请求获取考试卡片列表
return axios({
url: api.ExamCardList,
method: 'get',
@ -123,13 +104,11 @@ export function getExamCardList () {
})
}
/**
* @description: 创建新考试
* @param {Object} parameter - 考试创建参数
* @returns {Promise} - 返回创建操作的Promise对象
*/
// 导出一个函数examCreate用于创建考试
export function examCreate (parameter) {
// 打印传入的参数
console.log(parameter)
// 返回一个axios请求请求的url为api.ExamCreate请求方法为post请求的数据为parameter
return axios({
url: api.ExamCreate,
method: 'post',
@ -137,56 +116,44 @@ export function examCreate (parameter) {
})
}
/**
* @description: 更新考试信息
* @param {Object} parameter - 考试更新参数
* @returns {Promise} - 返回更新操作的Promise对象
*/
// 导出一个函数examUpdate用于更新考试
export function examUpdate (parameter) {
// 打印传入的参数
console.log(parameter)
// 返回一个axios请求请求的url为api.ExamUpdate请求方法为post请求的数据为parameter
return axios({
url: api.ExamUpdate,
method: 'post',
data: parameter
})
}
/**
* @description: 根据考试ID获取考试详情
* @param {String} examId - 考试ID
* @returns {Promise} - 返回考试详情的Promise对象
*/
// 导出一个函数,用于获取考试详情
export function getExamDetail (examId) {
// 使用axios发送get请求获取考试详情
return axios({
url: api.ExamDetail + examId,
method: 'get',
url: api.ExamDetail + examId, // 请求的URL拼接了examId
method: 'get', // 请求方法为get
headers: {
'Content-Type': 'application/json;charset=UTF-8'
'Content-Type': 'application/json;charset=UTF-8' // 设置请求头指定请求体的类型为json
}
})
}
/**
* @description: 根据记录ID获取考试记录详情
* @param {String} recordId - 记录ID
* @returns {Promise} - 返回考试记录详情的Promise对象
*/
// 导出一个函数,用于获取考试记录详情
export function getExamRecordDetail (recordId) {
// 使用axios发送get请求获取考试记录详情
return axios({
url: api.recordDetail + recordId,
method: 'get',
url: api.recordDetail + recordId, // 请求的URL拼接了recordId
method: 'get', // 请求方法为get
headers: {
'Content-Type': 'application/json;charset=UTF-8'
'Content-Type': 'application/json;charset=UTF-8' // 设置请求头指定请求体的类型为json
}
})
}
/**
* @description: 根据问题ID获取问题详情
* @param {String} questionId - 问题ID
* @returns {Promise} - 返回问题详情的Promise对象
*/
// 导出一个函数,用于获取问题详情
export function getQuestionDetail (questionId) {
// 使用axios发送get请求获取问题详情
return axios({
url: api.QuestionDetail + questionId,
method: 'get',
@ -196,14 +163,11 @@ export function getQuestionDetail (questionId) {
})
}
/**
* @description: 提交考试答案完成考试
* @param {String} examId - 考试ID
* @param {Object} answersMap - 答案映射
* @returns {Promise} - 返回完成考试的Promise对象
*/
// 导出一个函数,用于完成考试
export function finishExam (examId, answersMap) {
// 打印答案
console.log(answersMap)
// 使用axios发送post请求完成考试
return axios({
url: api.FinishExam + examId,
method: 'post',
@ -214,11 +178,9 @@ export function finishExam (examId, answersMap) {
})
}
/**
* @description: 获取所有考试记录
* @returns {Promise} - 返回所有考试记录的Promise对象
*/
// 导出一个函数,用于获取考试记录列表
export function getExamRecordList () {
// 使用axios发送get请求获取考试记录列表
return axios({
url: api.ExamRecordList,
method: 'get',

@ -1,117 +1,61 @@
/**
* @description: api接口统一管理
*/
// 定义一个api对象包含各种接口的路径
const api = {
/**
* @description: 用户登录的接口
*/
// 登录接口
Login: '/auth/login',
/**
* @description: 用户注销的接口
*/
// 登出接口
Logout: '/auth/logout',
/**
* @description: 重置密码的接口
*/
// 密码找回接口
ForgePassword: '/auth/forge-password',
/**
* @description: 用户注册的接口
*/
// 注册接口
Register: '/auth/register',
/**
* @description: 两步验证码的接口
*/
// 二维码接口
twoStepCode: '/auth/2step-code',
/**
* @description: 发送短信的接口
*/
// 发送短信接口
SendSms: '/account/sms',
/**
* @description: 发送短信错误的接口
*/
// 发送短信错误接口
SendSmsErr: '/account/sms_err',
/**
* @description: 获取用户信息的接口
*/
// 获取用户信息接口
UserInfo: '/user/info',
// 下面是自己的用户认证的接口
/**
* @description: 用户注册自定义的接口
*/
// 用户注册接口
UserRegister: '/user/register',
/**
* @description: 用户登录自定义的接口
*/
// 用户登录接口
UserLogin: '/user/login',
// 考试的接口
/**
* @description: 获取考试问题列表的接口
*/
// 获取问题列表接口
ExamQuestionList: '/exam/question/list',
/**
* @description: 获取所有考试问题的接口
*/
// 获取所有问题接口
ExamQuestionAll: '/exam/question/all',
/**
* @description: 更新考试问题的接口
*/
// 更新问题接口
ExamQuestionUpdate: '/exam/question/update',
/**
* @description: 选择考试问题的接口
*/
// 选择问题接口
ExamQuestionSelection: '/exam/question/selection',
/**
* @description: 创建考试问题的接口
*/
// 创建问题接口
ExamQuestionCreate: '/exam/question/create',
/**
* @description: 获取考试列表的接口
*/
// 获取考试列表接口
ExamList: '/exam/list',
/**
* @description: 获取所有考试的接口
*/
// 获取所有考试接口
ExamAll: '/exam/all',
/**
* @description: 按照单选多选和判断分类获取问题列表的接口
*/
// 获取问题列表,按照单选、多选和判断进行分类接口
ExamQuestionTypeList: '/exam/question/type/list',
/**
* @description: 创建考试的接口
*/
// 创建考试接口
ExamCreate: '/exam/create',
/**
* @description: 更新考试的接口
*/
// 更新考试接口
ExamUpdate: '/exam/update',
/**
* @description: 获取考试卡片列表的接口
*/
// 获取考试卡片列表接口
ExamCardList: '/exam/card/list',
/**
* @description: 获取考试详情的接口
*/
// 获取考试详情接口
ExamDetail: '/exam/detail/',
/**
* @description: 获取问题详情的接口
*/
// 获取问题详情接口
QuestionDetail: '/exam/question/detail/',
/**
* @description: 交卷的接口
*/
// 交卷接口
FinishExam: '/exam/finish/',
/**
* @description: 获取考试记录列表的接口
*/
// 获取考试记录列表接口
ExamRecordList: '/exam/record/list',
/**
* @description: 获取考试记录详情的接口
*/
// 获取考试记录详情接口
recordDetail: '/exam/record/detail/'
}
/**
* @description: 默认导出api对象
*/
export default api
// 导出api对象
export default api

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

Loading…
Cancel
Save