From 7ce1989a4a34f4742ab9d32a70dd622dce366ef7 Mon Sep 17 00:00:00 2001 From: "MSI\\letre" Date: Fri, 27 Jun 2025 11:14:51 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E9=A1=B9=E7=9B=AE=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E3=80=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + mvnw | 259 +++ mvnw.cmd | 149 ++ pom.xml | 215 ++ .../com/example/exam/exam/StuApplication.java | 13 + .../example/exam/exam/config/CorsConfig.java | 33 + .../exam/exam/config/SecurityConfig.java | 36 + .../controller/ExamAppCheckController.java | 24 + .../exam/controller/auto/AutoController.java | 132 ++ .../exam/controller/auto/vo/StuInfoVo.java | 28 + .../example/exam/exam/dal/EducationPaper.java | 114 + .../exam/exam/dal/EducationPaperQu.java | 57 + .../exam/exam/dal/EducationPaperScheme.java | 211 ++ .../example/exam/exam/dal/ExamAppCheck.java | 20 + .../exam/exam/dal/ExamMysqlKeyword.java | 68 + .../example/exam/exam/dal/ExamQuestion.java | 423 ++++ .../exam/exam/dal/ExamQuestionAnswer.java | 163 ++ .../exam/exam/dal/ExamQuestionKeyword.java | 74 + .../exam/exam/dal/ExamQuestionScore.java | 168 ++ .../example/exam/exam/dal/SourceAndText.java | 20 + .../exam/exam/dal/StuPaperScoreDO.java | 70 + .../example/exam/exam/dal/SysFileUpload.java | 91 + .../example/exam/exam/dal/SystemTenant.java | 20 + .../exam/mapper/EducationPaperMapper.java | 18 + .../exam/mapper/EducationPaperQuMapper.java | 17 + .../mapper/EducationPaperSchemeMapper.java | 17 + .../exam/exam/mapper/ExamAppCheckMapper.java | 9 + .../exam/mapper/ExamMysqlKeywordMapper.java | 14 + .../exam/mapper/ExamQuestionAnswerMapper.java | 26 + .../mapper/ExamQuestionKeywordMapper.java | 20 + .../exam/exam/mapper/ExamQuestionMapper.java | 30 + .../exam/mapper/ExamQuestionScoreMapper.java | 19 + .../exam/exam/mapper/StuPaperScoreMapper.java | 24 + .../exam/exam/mapper/SysFileMapper.java | 20 + .../exam/exam/mapper/SystemTenantMapper.java | 10 + .../service/appcheck/ExamAppCheckService.java | 19 + .../appcheck/ExamAppCheckServiceImpl.java | 25 + .../autoforbrower/AutoForBrowerService.java | 15 + .../AutoForBrowerServiceImpl.java | 114 + .../service/autoforc/AutoForCService.java | 15 + .../service/autoforc/AutoForCServiceImpl.java | 111 + .../autoforchoice/AutoForChoiceService.java | 15 + .../AutoForChoiceServiceImpl.java | 316 +++ .../autoforfile/AutoForFileService.java | 15 + .../autoforfile/AutoForFileServiceImpl.java | 123 ++ .../autoformysql/AutoForMysqlService.java | 18 + .../autoformysql/AutoForMysqlServiceImpl.java | 114 + .../brower/JudgementBrowerService.java | 13 + .../brower/JudgementBrowerServiceImpl.java | 172 ++ .../exam/exam/service/c/JudgementService.java | 25 + .../exam/service/c/JudgementServiceImpl.java | 222 ++ .../service/mysql/IMysqlLocalService.java | 19 + .../service/mysql/IMysqlLocalServiceImpl.java | 1942 +++++++++++++++++ .../exam/service/mysql/vo/MysqlBooleanVo.java | 16 + .../exam/exam/service/mysql/vo/MysqlVo.java | 16 + .../question/ExamQuestionServiceImpl.java | 77 + .../question/IExamQuestionService.java | 25 + .../stupaperscore/StuPaperScoreService.java | 24 + .../StuPaperScoreServiceImpl.java | 51 + .../service/tenant/SystemTenantService.java | 10 + .../tenant/SystemTenantServiceImpl.java | 23 + .../service/winfile/FileServericeImpl.java | 140 ++ .../exam/service/winfile/IFileServerice.java | 18 + .../exam/service/wpsexcel/ChartTypeEntry.java | 14 + .../exam/service/wpsexcel/ExcelInfoReqVo.java | 28 + .../wpsexcel/JudgementWpsExcelService.java | 18 + .../JudgementWpsExcelServiceImpl.java | 73 + .../exam/service/wpsexcel/WpsExcelUtils.java | 1237 +++++++++++ .../exam/service/wpsexcel/XlsxCharVO.java | 13 + .../service/wpsexcel/XlsxDrawingSheetVo.java | 17 + .../exam/service/wpsexcel/XlsxInfoVo.java | 30 + .../excel/JudgementForExcelService.java | 20 + .../excel/JudgementForExcelServiceImpl.java | 117 + .../wpspptx/DeclareNamespaceForPPT.java | 40 + .../service/wpspptx/JudgementConvert.java | 161 ++ .../exam/service/wpspptx/JudgementReqVo.java | 39 + .../exam/service/wpspptx/JudgementWpsPPT.java | 300 +++ .../wpspptx/JudgementWpsPptxService.java | 14 + .../wpspptx/JudgementWpsPptxServiceImpl.java | 80 + .../service/wpspptx/WpsPptxJudgementDto.java | 14 + .../service/wpspptx/XmlRecursiveFinder.java | 39 + .../wpspptx/pptx/JudgementForPptxService.java | 20 + .../pptx/JudgementForPptxServiceImpl.java | 118 + .../exam/service/wpsword/ColorNameFinder.java | 65 + .../wpsword/JudgementWpsWordService.java | 26 + .../wpsword/JudgementWpsWordServiceImpl.java | 96 + .../service/wpsword/PageSizeDetector.java | 97 + .../service/wpsword/ShadowDirectionUtils.java | 77 + .../exam/service/wpsword/TwipConverter.java | 42 + .../service/wpsword/WordHeaderFooterVo.java | 11 + .../service/wpsword/WpsWordJudgementDto.java | 14 + .../service/wpsword/WpsWordNameSpaces.java | 30 + .../exam/service/wpsword/WpsWordReqDto.java | 36 + .../exam/service/wpsword/WpsWordUtils.java | 761 +++++++ .../wpsword/word/JudgementForWordService.java | 20 + .../word/JudgementForWordServiceImpl.java | 115 + .../example/exam/exam/utils/HtmlAppender.java | 53 + .../com/example/exam/exam/utils/Result.java | 54 + .../exam/utils/brower/BookmarkChecker.java | 77 + .../exam/utils/brower/BookmarkDeleter.java | 88 + .../exam/exam/utils/c/JudgementCUtils.java | 224 ++ .../exam/exam/utils/c/LogFileUtils.java | 73 + .../file/GetDifferencesBetweenFolders.java | 130 ++ .../example/exam/exam/utils/zip/ZipUtil.java | 117 + src/main/resources/application.properties | 17 + .../mapper/paper/EducationPaperQuMapper.xml | 21 + .../paper/EducationPaperSchemeMapper.xml | 29 + .../mapper/question/EducationPaperMapper.xml | 96 + .../question/ExamMysqlKeywordMapper.xml | 35 + .../question/ExamQuestionAnswerMapper.xml | 47 + .../question/ExamQuestionKeywordMapper.xml | 24 + .../mapper/question/ExamQuestionMapper.xml | 50 + .../question/ExamQuestionScoreMapper.xml | 35 + .../mapper/question/SysFileMapper.xml | 26 + .../stupaperscore/StuPaperScoreMapper.xml | 22 + .../exam/exam/StuApplicationTests.java | 49 + 116 files changed, 11359 insertions(+) create mode 100644 .gitignore create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/com/example/exam/exam/StuApplication.java create mode 100644 src/main/java/com/example/exam/exam/config/CorsConfig.java create mode 100644 src/main/java/com/example/exam/exam/config/SecurityConfig.java create mode 100644 src/main/java/com/example/exam/exam/controller/ExamAppCheckController.java create mode 100644 src/main/java/com/example/exam/exam/controller/auto/AutoController.java create mode 100644 src/main/java/com/example/exam/exam/controller/auto/vo/StuInfoVo.java create mode 100644 src/main/java/com/example/exam/exam/dal/EducationPaper.java create mode 100644 src/main/java/com/example/exam/exam/dal/EducationPaperQu.java create mode 100644 src/main/java/com/example/exam/exam/dal/EducationPaperScheme.java create mode 100644 src/main/java/com/example/exam/exam/dal/ExamAppCheck.java create mode 100644 src/main/java/com/example/exam/exam/dal/ExamMysqlKeyword.java create mode 100644 src/main/java/com/example/exam/exam/dal/ExamQuestion.java create mode 100644 src/main/java/com/example/exam/exam/dal/ExamQuestionAnswer.java create mode 100644 src/main/java/com/example/exam/exam/dal/ExamQuestionKeyword.java create mode 100644 src/main/java/com/example/exam/exam/dal/ExamQuestionScore.java create mode 100644 src/main/java/com/example/exam/exam/dal/SourceAndText.java create mode 100644 src/main/java/com/example/exam/exam/dal/StuPaperScoreDO.java create mode 100644 src/main/java/com/example/exam/exam/dal/SysFileUpload.java create mode 100644 src/main/java/com/example/exam/exam/dal/SystemTenant.java create mode 100644 src/main/java/com/example/exam/exam/mapper/EducationPaperMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/EducationPaperQuMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/EducationPaperSchemeMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/ExamAppCheckMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/ExamMysqlKeywordMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/ExamQuestionAnswerMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/ExamQuestionKeywordMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/ExamQuestionMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/ExamQuestionScoreMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/StuPaperScoreMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/SysFileMapper.java create mode 100644 src/main/java/com/example/exam/exam/mapper/SystemTenantMapper.java create mode 100644 src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckService.java create mode 100644 src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerService.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforc/AutoForCService.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforc/AutoForCServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceService.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileService.java create mode 100644 src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlService.java create mode 100644 src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/brower/JudgementBrowerService.java create mode 100644 src/main/java/com/example/exam/exam/service/brower/JudgementBrowerServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/c/JudgementService.java create mode 100644 src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalService.java create mode 100644 src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/mysql/vo/MysqlBooleanVo.java create mode 100644 src/main/java/com/example/exam/exam/service/mysql/vo/MysqlVo.java create mode 100644 src/main/java/com/example/exam/exam/service/question/ExamQuestionServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/question/IExamQuestionService.java create mode 100644 src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreService.java create mode 100644 src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/tenant/SystemTenantService.java create mode 100644 src/main/java/com/example/exam/exam/service/tenant/SystemTenantServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/winfile/FileServericeImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/winfile/IFileServerice.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/ChartTypeEntry.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/ExcelInfoReqVo.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelService.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/XlsxCharVO.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/XlsxDrawingSheetVo.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/XlsxInfoVo.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelService.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxService.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxService.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/ColorNameFinder.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordService.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/PageSizeDetector.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/ShadowDirectionUtils.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/TwipConverter.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/WordHeaderFooterVo.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/WpsWordJudgementDto.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/WpsWordNameSpaces.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/WpsWordReqDto.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/WpsWordUtils.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordService.java create mode 100644 src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordServiceImpl.java create mode 100644 src/main/java/com/example/exam/exam/utils/HtmlAppender.java create mode 100644 src/main/java/com/example/exam/exam/utils/Result.java create mode 100644 src/main/java/com/example/exam/exam/utils/brower/BookmarkChecker.java create mode 100644 src/main/java/com/example/exam/exam/utils/brower/BookmarkDeleter.java create mode 100644 src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java create mode 100644 src/main/java/com/example/exam/exam/utils/c/LogFileUtils.java create mode 100644 src/main/java/com/example/exam/exam/utils/file/GetDifferencesBetweenFolders.java create mode 100644 src/main/java/com/example/exam/exam/utils/zip/ZipUtil.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/mapper/paper/EducationPaperQuMapper.xml create mode 100644 src/main/resources/mapper/paper/EducationPaperSchemeMapper.xml create mode 100644 src/main/resources/mapper/question/EducationPaperMapper.xml create mode 100644 src/main/resources/mapper/question/ExamMysqlKeywordMapper.xml create mode 100644 src/main/resources/mapper/question/ExamQuestionAnswerMapper.xml create mode 100644 src/main/resources/mapper/question/ExamQuestionKeywordMapper.xml create mode 100644 src/main/resources/mapper/question/ExamQuestionMapper.xml create mode 100644 src/main/resources/mapper/question/ExamQuestionScoreMapper.xml create mode 100644 src/main/resources/mapper/question/SysFileMapper.xml create mode 100644 src/main/resources/mapper/stupaperscore/StuPaperScoreMapper.xml create mode 100644 src/test/java/com/example/exam/exam/StuApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b3124f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# 忽略 target 文件夹 +target/ + +# 忽略 .idea 文件夹中的所有文件 +.idea/ diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..19529dd --- /dev/null +++ b/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..249bdf3 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6a061f9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,215 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.5.3 + + + + com.example.exam + exam + 0.0.1-SNAPSHOT + stu + Student end grading + + + 17 + 1.18.30 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + com.h2database + h2 + runtime + + + com.mysql + mysql-connector-j + runtime + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 3.0.3 + + + com.baomidou + mybatis-plus-boot-starter + 3.5.5 + + + com.fasterxml.jackson.core + jackson-core + 2.18.2 + provided + + + org.springframework.boot + spring-boot-starter-security + + + + org.apache.xmlbeans + xmlbeans + 5.2.0 + + + net.sf.saxon + Saxon-HE + 12.5 + + + org.apache.commons + commons-compress + 1.26.0 + + + org.apache.commons + commons-compress + 1.24.0 + + + org.apache.commons + commons-lang3 + 3.13.0 + + + com.google.code.gson + gson + 2.10 + + + org.apache.poi + poi + 5.2.3 + + + + org.apache.poi + poi-ooxml + 5.2.3 + + + + org.apache.xmlbeans + xmlbeans + 5.2.0 + + + org.apache.poi + poi-ooxml-lite + 5.2.3 + + + + cn.afterturn + easypoi-base + 4.4.0 + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + + org.apache.poi + ooxml-schemas + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + 3.5.3 + + + + + com.example.exam.exam.StuApplication + + + + + repackage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/example/exam/exam/StuApplication.java b/src/main/java/com/example/exam/exam/StuApplication.java new file mode 100644 index 0000000..a902133 --- /dev/null +++ b/src/main/java/com/example/exam/exam/StuApplication.java @@ -0,0 +1,13 @@ +package com.example.exam.exam; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class StuApplication { + + public static void main(String[] args) { + SpringApplication.run(StuApplication.class, args); + } + +} diff --git a/src/main/java/com/example/exam/exam/config/CorsConfig.java b/src/main/java/com/example/exam/exam/config/CorsConfig.java new file mode 100644 index 0000000..feb03cc --- /dev/null +++ b/src/main/java/com/example/exam/exam/config/CorsConfig.java @@ -0,0 +1,33 @@ +package com.example.exam.exam.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; + +@Configuration +public class CorsConfig { + + // CORS 配置 + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("http://localhost:8080") // 前端地址 + .allowedOrigins("http://tauri.localhost/") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("Authorization", "Content-Type") // 允许的请求头 + .allowCredentials(true) // 允许携带 Cookie + .maxAge(3600); // 预检请求缓存时间 + } + }; + } +} + diff --git a/src/main/java/com/example/exam/exam/config/SecurityConfig.java b/src/main/java/com/example/exam/exam/config/SecurityConfig.java new file mode 100644 index 0000000..3781db8 --- /dev/null +++ b/src/main/java/com/example/exam/exam/config/SecurityConfig.java @@ -0,0 +1,36 @@ +package com.example.exam.exam.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +import static org.springframework.security.config.Customizer.withDefaults; + +@Configuration +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // 配置 HTTP 请求权限 + http + .authorizeHttpRequests(authz -> authz + .requestMatchers("/exam/auto/**").authenticated() // 配置要求认证的 URL + .anyRequest().permitAll() // 其他所有请求不需要认证 + ) + .formLogin(withDefaults()) // 使用表单登录(如果需要) + .csrf(csrf -> csrf.disable()) // 禁用 CSRF 防护(通常前后端分离时禁用) + .httpBasic(withDefaults()) // 启用 HTTP Basic Authentication(如果需要) + .cors(cors -> cors.configurationSource(request -> { + var corsConfig = new org.springframework.web.cors.CorsConfiguration(); + corsConfig.addAllowedOrigin("http://localhost:8080"); // 前端地址‘ + corsConfig.addAllowedOrigin("http://tauri.localhost/"); + corsConfig.addAllowedMethod("*"); // 允许的所有请求方法 + corsConfig.addAllowedHeader("*"); // 允许的所有请求头 + corsConfig.setAllowCredentials(true); // 允许携带 Cookie + corsConfig.setMaxAge(3600L); // 预检请求缓存时间 + return corsConfig; + })); // 配置 CORS + return http.build(); + } +} diff --git a/src/main/java/com/example/exam/exam/controller/ExamAppCheckController.java b/src/main/java/com/example/exam/exam/controller/ExamAppCheckController.java new file mode 100644 index 0000000..b1d8db5 --- /dev/null +++ b/src/main/java/com/example/exam/exam/controller/ExamAppCheckController.java @@ -0,0 +1,24 @@ +package com.example.exam.exam.controller; + +import com.example.exam.exam.dal.ExamAppCheck; +import com.example.exam.exam.service.appcheck.ExamAppCheckService; +import com.example.exam.exam.utils.Result; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/exam/app") +public class ExamAppCheckController { + @Resource + ExamAppCheckService examAppCheckService; + + @GetMapping("/getAppCheck") + public Result getAppCheckList(){ + // 使用传入的IP,进行ping,查看是否存在连接,并返回信号的强度 + return Result.success(examAppCheckService.getAppList()); + } +} diff --git a/src/main/java/com/example/exam/exam/controller/auto/AutoController.java b/src/main/java/com/example/exam/exam/controller/auto/AutoController.java new file mode 100644 index 0000000..4d88af6 --- /dev/null +++ b/src/main/java/com/example/exam/exam/controller/auto/AutoController.java @@ -0,0 +1,132 @@ +package com.example.exam.exam.controller.auto; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.service.autoforbrower.AutoForBrowerService; +import com.example.exam.exam.service.brower.JudgementBrowerService; +import com.example.exam.exam.service.autoforc.AutoForCService; +import com.example.exam.exam.service.autoforchoice.AutoForChoiceService; +import com.example.exam.exam.service.autoforfile.AutoForFileService; +import com.example.exam.exam.service.autoformysql.AutoForMysqlService; +import com.example.exam.exam.service.mysql.IMysqlLocalService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.wpsexcel.excel.JudgementForExcelService; +import com.example.exam.exam.service.wpspptx.pptx.JudgementForPptxService; +import com.example.exam.exam.service.wpsword.word.JudgementForWordService; +import com.example.exam.exam.utils.Result; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; + +/** + * @author REN + */ +@RestController +@RequestMapping("/exam/auto") +public class AutoController { + + @Resource + AutoForCService autoService; + @Resource + AutoForChoiceService autoForChoiceService; + @Resource + AutoForMysqlService autoForMysqlService; + @Resource + AutoForFileService autoForFileService; + @Resource + AutoForBrowerService autoForBrowerService; + @Resource + IMysqlLocalService mysqlLocalService; + @Resource + JudgementForWordService judgementForWordService; + @Resource + JudgementForPptxService judgementForPptxService; + @Resource + JudgementForExcelService judgementForExcelService; + @Resource + StuPaperScoreService stuPaperScoreService; + + // C语言 + + /** + * C语言判分 + * @param stuInfoVo 学生信息 + * @return 判分结果 + */ + @PostMapping("/judgementForC") + public Result judgementForC(@RequestBody StuInfoVo stuInfoVo){ + return Result.success(autoService.autoForC(stuInfoVo)); + } + // WPS-WORD + @PostMapping("/judgementForWord") + public Result judgementForWord(@RequestBody StuInfoVo stuInfoVo) throws Exception { + return Result.success(judgementForWordService.autoForWpsWord(stuInfoVo)); + } + + // WPS-EXCEL + @PostMapping("/judgementForExcel") + public Result judgementForExcel(@RequestBody StuInfoVo stuInfoVo) throws Exception { + return Result.success(judgementForExcelService.autoForWpsExcel(stuInfoVo)); + } + + // WPS-PPT + @PostMapping("/judgementForPptx") + public Result judgementForPptx(@RequestBody StuInfoVo stuInfoVo) throws Exception { + return Result.success(judgementForPptxService.autoForWpsPptx(stuInfoVo)); + } + + // Mysql + @PostMapping("/judgementForMysql") + public Result judgementForMysql(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException { + return Result.success(autoForMysqlService.autoForMysql(stuInfoVo)); + } + + /** + * 删除 本地学生的连接和库 + * @param tNames + * @throws Exception + */ + @PostMapping("/delMysqlConnect") + public void delete(@RequestBody List tNames) throws Exception { + mysqlLocalService.delMysqlConnect(tNames); + } + // 文件处理 + + @PostMapping("/judgementForFile") + public Result judgementForFile(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException { + return Result.success(autoForFileService.autoForFile(stuInfoVo)); + } + // 浏览器 + @PostMapping("/judgementForBrower") + public Result judgementForBrower(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException { + return Result.success(autoForBrowerService.autoForBrower(stuInfoVo)); + } + // 浏览器设置 + + // QQ邮箱 + + // win10虚拟界面 + + // 单项选择 + /** + * 将选择题写入到JSON文件中 + * @param stuInfoVo 学生选择题信息 + * @return String + */ + @PostMapping("/judgementForChoiceToJson") + public Result judgementForChoiceToJson(@RequestBody StuInfoVo stuInfoVo){ + return Result.success(autoForChoiceService.autoForChoiceToJson(stuInfoVo)); + } + + /** + * 选择题判分 + * @param stuInfoVo 学生选择题信息 + * @return String + */ + @PostMapping("/judgementForChoice") + public Result judgementForChoice(@RequestBody StuInfoVo stuInfoVo) throws IOException { + return Result.success(autoForChoiceService.autoForChoice(stuInfoVo)); + } +} diff --git a/src/main/java/com/example/exam/exam/controller/auto/vo/StuInfoVo.java b/src/main/java/com/example/exam/exam/controller/auto/vo/StuInfoVo.java new file mode 100644 index 0000000..0225c66 --- /dev/null +++ b/src/main/java/com/example/exam/exam/controller/auto/vo/StuInfoVo.java @@ -0,0 +1,28 @@ +package com.example.exam.exam.controller.auto.vo; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class StuInfoVo { + + // 学号 + private Long stuId; + + // 试卷编号 + private String paperId; + + // 文件路径 + private String filePath; + + // 试题编号 + private String questionId; + + // 选择题学生答案 + private String choiceAnswerId; + + // 学校名称 + private String schoolName; +} diff --git a/src/main/java/com/example/exam/exam/dal/EducationPaper.java b/src/main/java/com/example/exam/exam/dal/EducationPaper.java new file mode 100644 index 0000000..ce4f6c3 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/EducationPaper.java @@ -0,0 +1,114 @@ +package com.example.exam.exam.dal; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 试卷对象 education_paper + * + * @author pengchen + * @date 2025-04-14 + */ +@TableName(value = "education_paper", autoResultMap = true) +@Data +@Accessors(chain = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EducationPaper +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + @TableId + private String paperId; + + /** 试卷任务ID */ + // @Excel(name = "试卷任务ID") + private String taskId; + + private String num; + /** 使用次数 */ + // @Excel(name = "使用次数") + private String counts; + + /** 抽卷方式(0固定1AB2随机) */ + //@Excel(name = "抽卷方式(0固定1AB2随机)") + private String rollUp; + + /** 0A1B */ + //Excel(name = "0A1B") + private String isAb; + + /** 0启用1不启 */ + // @Excel(name = "0启用1不启") + private String status; + + private String paperScore; + + public String getPaperId() { + return paperId; + } + + public void setPaperId(String paperId) { + this.paperId = paperId; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getNum() { + return num; + } + + public void setNum(String num) { + this.num = num; + } + + public String getCounts() { + return counts; + } + + public void setCounts(String counts) { + this.counts = counts; + } + + public String getRollUp() { + return rollUp; + } + + public void setRollUp(String rollUp) { + this.rollUp = rollUp; + } + + public String getIsAb() { + return isAb; + } + + public void setIsAb(String isAb) { + this.isAb = isAb; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getPaperScore() { + return paperScore; + } + + public void setPaperScore(String paperScore) { + this.paperScore = paperScore; + } +} + diff --git a/src/main/java/com/example/exam/exam/dal/EducationPaperQu.java b/src/main/java/com/example/exam/exam/dal/EducationPaperQu.java new file mode 100644 index 0000000..d2de747 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/EducationPaperQu.java @@ -0,0 +1,57 @@ +package com.example.exam.exam.dal; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 试卷试题对象 education_paper_qu + * + * @author pengchen + * @date 2025-04-14 + */ +@TableName(value = "education_paper_qu", autoResultMap = true) +@Data +@Accessors(chain = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EducationPaperQu +{ + private static final long serialVersionUID = 1L; + + /** 试卷ID */ + // @Excel(name = "试卷ID") + private String paperId; + + /** 试题ID */ + // @Excel(name = "试题ID") + private String quId; + + /** 试题版本 */ + // @Excel(name = "试题版本") + private Integer sort; + + public String getPaperId() { + return paperId; + } + + public void setPaperId(String paperId) { + this.paperId = paperId; + } + + public String getQuId() { + return quId; + } + + public void setQuId(String quId) { + this.quId = quId; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/EducationPaperScheme.java b/src/main/java/com/example/exam/exam/dal/EducationPaperScheme.java new file mode 100644 index 0000000..86d6949 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/EducationPaperScheme.java @@ -0,0 +1,211 @@ +package com.example.exam.exam.dal; + + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 试卷方案对象 education_paper_scheme + * + * @author pengchen + * @date 2025-04-14 + */ +@TableName(value = "education_paper_scheme", autoResultMap = true) +@Data +@Accessors(chain = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EducationPaperScheme +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private String schemeId; + + /** 方案ID */ + // @Excel(name = "方案ID") + private String taskId; + + //题型标题别名 + private String quTitle; + + /** 题型 */ + // @Excel(name = "题型") + private String spName; + + /** 难度 */ + // @Excel(name = "难度") + private String quLevel; + + /** 关键字 */ + // @Excel(name = "关键字") + private String keywords; + + /** 知识点 */ + // @Excel(name = "知识点") + private String pointNames; + + /** 试题数量 */ + // @Excel(name = "试题数量") + private Integer quNumbers; + + /** 每题分数 */ + // @Excel(name = "每题分数") + private String quScores; + + private Integer sort; + + @TableField(exist = false) + private List keyword; + + @TableField(exist = false) + private List pointName; + + + /** 小计分数 */ + // @Excel(name = "小计分数") + private String subtotalScore; + + /** + * 试卷大写题号 + */ + @TableField(exist = false) + private String upperCase; + + public String getSchemeId() { + return schemeId; + } + + public void setSchemeId(String schemeId) { + this.schemeId = schemeId; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getQuTitle() { + return quTitle; + } + + public void setQuTitle(String quTitle) { + this.quTitle = quTitle; + } + + public String getSpName() { + return spName; + } + + public void setSpName(String spName) { + this.spName = spName; + } + + public String getQuLevel() { + return quLevel; + } + + public void setQuLevel(String quLevel) { + this.quLevel = quLevel; + } + + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + public String getPointNames() { + return pointNames; + } + + public void setPointNames(String pointNames) { + this.pointNames = pointNames; + } + + public Integer getQuNumbers() { + return quNumbers; + } + + public void setQuNumbers(Integer quNumbers) { + this.quNumbers = quNumbers; + } + + public String getQuScores() { + return quScores; + } + + public void setQuScores(String quScores) { + this.quScores = quScores; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } + + public List getKeyword() { + return keyword; + } + + public void setKeyword(List keyword) { + this.keyword = keyword; + } + + public List getPointName() { + return pointName; + } + + public void setPointName(List pointName) { + this.pointName = pointName; + } + + public String getSubtotalScore() { + return subtotalScore; + } + + public void setSubtotalScore(String subtotalScore) { + this.subtotalScore = subtotalScore; + } + + public String getUpperCase() { + return upperCase; + } + + public void setUpperCase(String upperCase) { + this.upperCase = upperCase; + } + + @Override + public String toString() { + return "EducationPaperScheme{" + + "schemeId='" + schemeId + '\'' + + ", taskId='" + taskId + '\'' + + ", quTitle='" + quTitle + '\'' + + ", spName='" + spName + '\'' + + ", quLevel='" + quLevel + '\'' + + ", keywords='" + keywords + '\'' + + ", pointNames='" + pointNames + '\'' + + ", quNumbers=" + quNumbers + + ", quScores='" + quScores + '\'' + + ", sort=" + sort + + ", keyword=" + keyword + + ", pointName=" + pointName + + ", subtotalScore='" + subtotalScore + '\'' + + ", upperCase='" + upperCase + '\'' + + '}'; + } +} + diff --git a/src/main/java/com/example/exam/exam/dal/ExamAppCheck.java b/src/main/java/com/example/exam/exam/dal/ExamAppCheck.java new file mode 100644 index 0000000..61688ca --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/ExamAppCheck.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.dal; + + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@TableName("exam_app_check") +@Data +public class ExamAppCheck { + /** + * Id + */ + @TableId + private Long id; + /** + * app名称 + */ + private String appName; +} diff --git a/src/main/java/com/example/exam/exam/dal/ExamMysqlKeyword.java b/src/main/java/com/example/exam/exam/dal/ExamMysqlKeyword.java new file mode 100644 index 0000000..a04d5f0 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/ExamMysqlKeyword.java @@ -0,0 +1,68 @@ +package com.example.exam.exam.dal; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@TableName(value = "exam_mysql_keyword", autoResultMap = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class ExamMysqlKeyword { + + /** + * 主键id + */ + @TableId(type = IdType.INPUT) + private String keywordId; + /** + * 答案id + */ + private String answerId; + /** + * 关键字 + */ + private String keyword; + /** + * 权值 + */ + private String scoreRate; + + + public String getKeywordId() { + return keywordId; + } + + public void setKeywordId(String keywordId) { + this.keywordId = keywordId; + } + + public String getAnswerId() { + return answerId; + } + + public void setAnswerId(String answerId) { + this.answerId = answerId; + } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } + + public String getScoreRate() { + return scoreRate; + } + + public void setScoreRate(String scoreRate) { + this.scoreRate = scoreRate; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/ExamQuestion.java b/src/main/java/com/example/exam/exam/dal/ExamQuestion.java new file mode 100644 index 0000000..0aa1f18 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/ExamQuestion.java @@ -0,0 +1,423 @@ +package com.example.exam.exam.dal; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 试题(hyc)对象 exam_question + * + * @author pengchen + * @date 2025-03-13 + */ + +@TableName(value = "exam_question", autoResultMap = true) +@Accessors(chain = true) +public class ExamQuestion { + + + /** 试题id */ + @TableId + private String quId; + + /** 题库id */ +// @Excel(name = "题库id") + private String quBankId; + + /** 题库名称 */ +// @Excel(name = "题库名称") + private String quBankName; + + private String quNum; + + /** 章节名称 */ +// @Excel(name = "章节名称") + private String chapteridDictText; + + @TableField(exist = false) + private String chapteridDictTextVo; + /** 题型名称 */ +// @Excel(name = "题型名称") + private String subjectName; + + /** 题型难度(0:简单,1:一般,2:困难) */ + // @Excel(name = "题型难度(0:简单,1:一般,2:困难)") + private Integer quLevel; + + /** 试题内容(带样式:

下列表格123

\n\n\n\n\) */ + // @Excel(name = "试题内容") + private String content; + + /** 试题内容(纯文本) */ + // @Excel(name = "试题内容(纯文本)") + private String contentText; + + /** 解析(带样式) */ + // @Excel(name = "解析(带样式)") + private String analysis; + + /**c语言参考答案*/ + private String answer; + + + /** 知识点 */ + // @Excel(name = "知识点") + private String pointNames; + + @TableField(exist = false) + private String pointNamesVo; + /** 关键字 */ + // @Excel(name = "关键字") + private String keywords; + + /** 是否人工(0否1是,简答题专用) */ + // @Excel(name = "是否人工,0=否1是,简答题专用") + private String manual; + + + /** 状态(0启用,1禁用) */ + // @Excel(name = "状态(0启用,1禁用)") + private String status; + + /** 审核 */ + // @Excel(name = "审核") + private String audit; + + /** 课程类别 */ + // @Excel(name = "课程类别") + private String courseName; + + /** 专业分类 */ + // @Excel(name = "专业分类") + private String specialtyName; + /** 数据库名 */ + private String tname; + + /** 试题答案 */ + // @Excel(name = "试题答案") + @TableField(exist = false) + private List answerList; + + /** 试题文件 */ + // @Excel(name = "试题文件") + @TableField(exist = false) + private List fileUploads; + + /** 试题判分 */ + // @Excel(name = "试题判分") + @TableField(exist = false) + private ExamQuestionScore questionScores; + + /** 试题关键字 */ + // @Excel(name = "试题关键字") + @TableField(exist = false) + private List questionKeywords; + + @TableField(exist = false) + private Long source; + + @TableField(exist = false) + private String createTeacher; + + @TableField(exist = false) + private String type; + @TableField(exist = false) + private String schoolName; + + /** 每题分数 */ + @TableField(exist = false) + private String quScores; + /** 排序 */ + @TableField(exist = false) + private int sort; + + + public String getQuId() { + return quId; + } + + public void setQuId(String quId) { + this.quId = quId; + } + + public String getQuBankId() { + return quBankId; + } + + public void setQuBankId(String quBankId) { + this.quBankId = quBankId; + } + + public String getQuBankName() { + return quBankName; + } + + public void setQuBankName(String quBankName) { + this.quBankName = quBankName; + } + + public String getQuNum() { + return quNum; + } + + public void setQuNum(String quNum) { + this.quNum = quNum; + } + + public String getChapteridDictText() { + return chapteridDictText; + } + + public void setChapteridDictText(String chapteridDictText) { + this.chapteridDictText = chapteridDictText; + } + + public String getChapteridDictTextVo() { + return chapteridDictTextVo; + } + + public void setChapteridDictTextVo(String chapteridDictTextVo) { + this.chapteridDictTextVo = chapteridDictTextVo; + } + + public String getSubjectName() { + return subjectName; + } + + public void setSubjectName(String subjectName) { + this.subjectName = subjectName; + } + + public Integer getQuLevel() { + return quLevel; + } + + public void setQuLevel(Integer quLevel) { + this.quLevel = quLevel; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContentText() { + return contentText; + } + + public void setContentText(String contentText) { + this.contentText = contentText; + } + + public String getAnalysis() { + return analysis; + } + + public void setAnalysis(String analysis) { + this.analysis = analysis; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } + + public String getPointNames() { + return pointNames; + } + + public void setPointNames(String pointNames) { + this.pointNames = pointNames; + } + + public String getPointNamesVo() { + return pointNamesVo; + } + + public void setPointNamesVo(String pointNamesVo) { + this.pointNamesVo = pointNamesVo; + } + + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + public String getManual() { + return manual; + } + + public void setManual(String manual) { + this.manual = manual; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getAudit() { + return audit; + } + + public void setAudit(String audit) { + this.audit = audit; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getSpecialtyName() { + return specialtyName; + } + + public void setSpecialtyName(String specialtyName) { + this.specialtyName = specialtyName; + } + + public String getTname() { + return tname; + } + + public void setTname(String tname) { + this.tname = tname; + } + + public List getAnswerList() { + return answerList; + } + + public void setAnswerList(List answerList) { + this.answerList = answerList; + } + + public List getFileUploads() { + return fileUploads; + } + + public void setFileUploads(List fileUploads) { + this.fileUploads = fileUploads; + } + + public ExamQuestionScore getQuestionScores() { + return questionScores; + } + + public void setQuestionScores(ExamQuestionScore questionScores) { + this.questionScores = questionScores; + } + + public List getQuestionKeywords() { + return questionKeywords; + } + + public void setQuestionKeywords(List questionKeywords) { + this.questionKeywords = questionKeywords; + } + + public Long getSource() { + return source; + } + + public void setSource(Long source) { + this.source = source; + } + + public String getCreateTeacher() { + return createTeacher; + } + + public void setCreateTeacher(String createTeacher) { + this.createTeacher = createTeacher; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSchoolName() { + return schoolName; + } + + public void setSchoolName(String schoolName) { + this.schoolName = schoolName; + } + + public String getQuScores() { + return quScores; + } + + public void setQuScores(String quScores) { + this.quScores = quScores; + } + + public int getSort() { + return sort; + } + + public void setSort(int sort) { + this.sort = sort; + } + + @Override + public String toString() { + return "ExamQuestion{" + + "quId='" + quId + '\'' + + ", quBankId='" + quBankId + '\'' + + ", quBankName='" + quBankName + '\'' + + ", quNum='" + quNum + '\'' + + ", chapteridDictText='" + chapteridDictText + '\'' + + ", chapteridDictTextVo='" + chapteridDictTextVo + '\'' + + ", subjectName='" + subjectName + '\'' + + ", quLevel=" + quLevel + + ", content='" + content + '\'' + + ", contentText='" + contentText + '\'' + + ", analysis='" + analysis + '\'' + + ", answer='" + answer + '\'' + + ", pointNames='" + pointNames + '\'' + + ", pointNamesVo='" + pointNamesVo + '\'' + + ", keywords='" + keywords + '\'' + + ", manual='" + manual + '\'' + + ", status='" + status + '\'' + + ", audit='" + audit + '\'' + + ", courseName='" + courseName + '\'' + + ", specialtyName='" + specialtyName + '\'' + + ", tname='" + tname + '\'' + + ", answerList=" + answerList + + ", fileUploads=" + fileUploads + + ", questionScores=" + questionScores + + ", questionKeywords=" + questionKeywords + + ", source=" + source + + ", createTeacher='" + createTeacher + '\'' + + ", type='" + type + '\'' + + ", schoolName='" + schoolName + '\'' + + ", quScores='" + quScores + '\'' + + ", sort=" + sort + + '}'; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/ExamQuestionAnswer.java b/src/main/java/com/example/exam/exam/dal/ExamQuestionAnswer.java new file mode 100644 index 0000000..6385a9d --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/ExamQuestionAnswer.java @@ -0,0 +1,163 @@ +package com.example.exam.exam.dal; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 试题答案选项(hyc)对象 exam_question_answer + * + * @author pengchen + * @date 2025-03-18 + */ +@TableName(value = "exam_question_answer", autoResultMap = true) +@Accessors(chain = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@AllArgsConstructor +@NoArgsConstructor +public class ExamQuestionAnswer +{ + private static final long serialVersionUID = 1L; + + /** 答案ID */ + @TableId + private String answerId; + + /** 问题ID */ +// @Excel(name = "问题ID") + private String quId; + + /** 是否正确(0是1否) */ +// @Excel(name = "是否正确(0是1否)") + private String isRight; + + /** 选项图片 */ +// @Excel(name = "选项图片") + private String image; + + /** 答案内容(输出) */ +// @Excel(name = "答案内容(输出)") + private String content; + + /** 答案内容(输入) */ +// @Excel(name = "答案内容(输入)") + private String contentIn; + + /** 权值 */ + // @Excel(name = "权值") + private String scoreRate; + + /** 排序 */ +// @Excel(name = "排序") + private Integer sort; + + private String attribute; + + @TableField(exist = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + private List examMysqlKeywordList; + + + public String getAnswerId() { + return answerId; + } + + public void setAnswerId(String answerId) { + this.answerId = answerId; + } + + public String getQuId() { + return quId; + } + + public void setQuId(String quId) { + this.quId = quId; + } + + public String getIsRight() { + return isRight; + } + + public void setIsRight(String isRight) { + this.isRight = isRight; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContentIn() { + return contentIn; + } + + public void setContentIn(String contentIn) { + this.contentIn = contentIn; + } + + public String getScoreRate() { + return scoreRate; + } + + public void setScoreRate(String scoreRate) { + this.scoreRate = scoreRate; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } + + public String getAttribute() { + return attribute; + } + + public void setAttribute(String attribute) { + this.attribute = attribute; + } + + public List getExamMysqlKeywordList() { + return examMysqlKeywordList; + } + + public void setExamMysqlKeywordList(List examMysqlKeywordList) { + this.examMysqlKeywordList = examMysqlKeywordList; + } + + @Override + public String toString() { + return "ExamQuestionAnswer{" + + "answerId='" + answerId + '\'' + + ", quId='" + quId + '\'' + + ", isRight='" + isRight + '\'' + + ", image='" + image + '\'' + + ", content='" + content + '\'' + + ", contentIn='" + contentIn + '\'' + + ", scoreRate='" + scoreRate + '\'' + + ", sort=" + sort + + ", attribute='" + attribute + '\'' + + ", examMysqlKeywordList=" + examMysqlKeywordList + + '}'; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/ExamQuestionKeyword.java b/src/main/java/com/example/exam/exam/dal/ExamQuestionKeyword.java new file mode 100644 index 0000000..f6cb73b --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/ExamQuestionKeyword.java @@ -0,0 +1,74 @@ +package com.example.exam.exam.dal; + + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 试题答案关键字对象 exam_question_keyword + * + * @author pengchen + * @date 2025-03-21 + */ + +@TableName(value = "exam_question_keyword", autoResultMap = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class ExamQuestionKeyword +{ + private static final long serialVersionUID = 1L; + + /** 答案ID */ + @TableId + private String keywordId; + + /** 问题ID */ +// @Excel(name = "问题ID") + private String quId; + + /** 关键字 */ + // @Excel(name = "关键字") + private String keyword; + + /** 权值 */ + // @Excel(name = "权值") + private String scoreRate; + + public String getKeywordId() { + return keywordId; + } + + public void setKeywordId(String keywordId) { + this.keywordId = keywordId; + } + + public String getQuId() { + return quId; + } + + public void setQuId(String quId) { + this.quId = quId; + } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } + + public String getScoreRate() { + return scoreRate; + } + + public void setScoreRate(String scoreRate) { + this.scoreRate = scoreRate; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/ExamQuestionScore.java b/src/main/java/com/example/exam/exam/dal/ExamQuestionScore.java new file mode 100644 index 0000000..7e12a80 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/ExamQuestionScore.java @@ -0,0 +1,168 @@ +package com.example.exam.exam.dal; + + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 试题判分标准对象 exam_question_score + * + * @author pengchen + * @date 2025-03-19 + */ + +@TableName(value = "exam_question_score", autoResultMap = true) +@Data +@Accessors(chain = true) +public class ExamQuestionScore +{ + private static final long serialVersionUID = 1L; + + /** id */ + @TableId + private String scoreId; + + /** 问题ID */ +// @Excel(name = "问题ID") + private String quId; + + /** 检查程序编译(0:是,1否) */ + // @Excel(name = "检查程序编译(0:是,1否)") + private String isPass; + + /** 检查程序结果(0:是,1否) */ +// @Excel(name = "检查程序结果(0:是,1否)") + private String isResult; + + /** 检查关键字(0:是,1否) */ + // @Excel(name = "检查关键字(0:是,1否)") + private String isKeyword; + + /** 使用测试用例(0:是,1否) */ +// @Excel(name = "使用测试用例(0:是,1否)") + private String isCompile; + + + /** 检查程序编译百分比 */ +// @Excel(name = "检查程序编译百分比") + private String isPassScore; + + /** 检查程序结果百分比 */ +// @Excel(name = "检查程序结果百分比") + private String isResultScore; + + /** 检查关键字百分比 */ + // @Excel(name = "检查关键字百分比") + private String isKeywordScore; + + /** 使用测试用例百分比 */ +// @Excel(name = "使用测试用例百分比") + private String isCompileScore; + + /** 关键字得分临界值 */ + // @Excel(name = "关键字得分临界值") + private String keywordCutoff; + + /** 测试用例得分临界值(<=测试用例个数) */ +// @Excel(name = "测试用例得分临界值(<=测试用例个数)") + private String compileCutoff; + + + public String getScoreId() { + return scoreId; + } + + public void setScoreId(String scoreId) { + this.scoreId = scoreId; + } + + public String getQuId() { + return quId; + } + + public void setQuId(String quId) { + this.quId = quId; + } + + public String getIsPass() { + return isPass; + } + + public void setIsPass(String isPass) { + this.isPass = isPass; + } + + public String getIsResult() { + return isResult; + } + + public void setIsResult(String isResult) { + this.isResult = isResult; + } + + public String getIsKeyword() { + return isKeyword; + } + + public void setIsKeyword(String isKeyword) { + this.isKeyword = isKeyword; + } + + public String getIsCompile() { + return isCompile; + } + + public void setIsCompile(String isCompile) { + this.isCompile = isCompile; + } + + public String getIsPassScore() { + return isPassScore; + } + + public void setIsPassScore(String isPassScore) { + this.isPassScore = isPassScore; + } + + public String getIsResultScore() { + return isResultScore; + } + + public void setIsResultScore(String isResultScore) { + this.isResultScore = isResultScore; + } + + public String getIsKeywordScore() { + return isKeywordScore; + } + + public void setIsKeywordScore(String isKeywordScore) { + this.isKeywordScore = isKeywordScore; + } + + public String getIsCompileScore() { + return isCompileScore; + } + + public void setIsCompileScore(String isCompileScore) { + this.isCompileScore = isCompileScore; + } + + public String getKeywordCutoff() { + return keywordCutoff; + } + + public void setKeywordCutoff(String keywordCutoff) { + this.keywordCutoff = keywordCutoff; + } + + public String getCompileCutoff() { + return compileCutoff; + } + + public void setCompileCutoff(String compileCutoff) { + this.compileCutoff = compileCutoff; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/SourceAndText.java b/src/main/java/com/example/exam/exam/dal/SourceAndText.java new file mode 100644 index 0000000..18a4f60 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/SourceAndText.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.dal; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class SourceAndText { + + /** + * 分数 + */ + private double score; + + /** + * 文本 + */ + private String text; +} diff --git a/src/main/java/com/example/exam/exam/dal/StuPaperScoreDO.java b/src/main/java/com/example/exam/exam/dal/StuPaperScoreDO.java new file mode 100644 index 0000000..9fe03f6 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/StuPaperScoreDO.java @@ -0,0 +1,70 @@ +package com.example.exam.exam.dal; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * 学生-试卷-分数 DO + * + * @author rwb + */ +@TableName("exam_stu_paper_score") +@Data +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class StuPaperScoreDO{ + + /** + * Id + */ + @TableId + private Long id; + /** + * 学生号 + */ + private Long stuId; + /** + * 试卷ID + */ + private String paperId; + /** + * 试题ID + */ + private String quId; + /** + * 得分 + */ + private BigDecimal score; + /** + * 分值 + */ + private BigDecimal trueScore; + /** + * 是否正确 + */ + private int isTrue; + /** + * 题型名称 + */ + private String subjectName; + /** + * 排序 + */ + private int sort; + /** + * 判分详情,富文本格式 + */ + private String content; + /** + * 试题编码 + */ + private String quNum; + + private Long tenantId; + +} \ No newline at end of file diff --git a/src/main/java/com/example/exam/exam/dal/SysFileUpload.java b/src/main/java/com/example/exam/exam/dal/SysFileUpload.java new file mode 100644 index 0000000..8b98cff --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/SysFileUpload.java @@ -0,0 +1,91 @@ +package com.example.exam.exam.dal; + + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 文件(hyc)对象 sys_file + * + * @author pengchen + * @date 2025-03-18 + */ +@TableName(value = "sys_file", autoResultMap = true) +@Data +@Accessors(chain = true) +public class SysFileUpload { + private static final long serialVersionUID = 1L; + + /** + * 文件id + */ + @TableId + private String fileId; + + /** + * 试题id + */ +// @Excel(name = "试题id") + private String quId; + + + + /** + * 文件名(完整) + */ +// @Excel(name = "文件名(完整)") + private String url; + + + /** + * 文件类型(0:素材,1:原始,2:结果) + */ +// @Excel(name = "文件类型(0:素材,1:原始,2:结果)") + private String fileType; + + private String fileName; + + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public String getQuId() { + return quId; + } + + public void setQuId(String quId) { + this.quId = quId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } +} diff --git a/src/main/java/com/example/exam/exam/dal/SystemTenant.java b/src/main/java/com/example/exam/exam/dal/SystemTenant.java new file mode 100644 index 0000000..0126359 --- /dev/null +++ b/src/main/java/com/example/exam/exam/dal/SystemTenant.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.dal; + + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@TableName("system_tenant") +@Data +public class SystemTenant { + /** + * Id + */ + @TableId + private Long id; + /** + * app名称 + */ + private String name; +} diff --git a/src/main/java/com/example/exam/exam/mapper/EducationPaperMapper.java b/src/main/java/com/example/exam/exam/mapper/EducationPaperMapper.java new file mode 100644 index 0000000..f5092e5 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/EducationPaperMapper.java @@ -0,0 +1,18 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.ExamQuestion; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +@Mapper +public interface EducationPaperMapper { + /** + * 根据试卷id返回 该试卷下的试题和试题答案 按顺序 + * @param paperId + * @return + */ + List selectPaperQuByPaperId(String paperId); + + + +} diff --git a/src/main/java/com/example/exam/exam/mapper/EducationPaperQuMapper.java b/src/main/java/com/example/exam/exam/mapper/EducationPaperQuMapper.java new file mode 100644 index 0000000..2b86626 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/EducationPaperQuMapper.java @@ -0,0 +1,17 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.EducationPaperQu; +import com.example.exam.exam.dal.ExamQuestion; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +@Mapper +public interface EducationPaperQuMapper { + + /** + * 根据试卷id返回试卷试题对象集合 + * @param paperId + * @return + */ + List selectPaperQuListByPaperId(String paperId); +} diff --git a/src/main/java/com/example/exam/exam/mapper/EducationPaperSchemeMapper.java b/src/main/java/com/example/exam/exam/mapper/EducationPaperSchemeMapper.java new file mode 100644 index 0000000..927ce97 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/EducationPaperSchemeMapper.java @@ -0,0 +1,17 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.EducationPaperScheme; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface EducationPaperSchemeMapper { + + /** + * 根据试卷id查询 试卷方案集合 + * @param paperId + * @return + */ + List selectEducationPaperTaskByPaperId(String paperId); +} diff --git a/src/main/java/com/example/exam/exam/mapper/ExamAppCheckMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamAppCheckMapper.java new file mode 100644 index 0000000..bf42cea --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/ExamAppCheckMapper.java @@ -0,0 +1,9 @@ +package com.example.exam.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.exam.exam.dal.ExamAppCheck; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ExamAppCheckMapper extends BaseMapper { +} diff --git a/src/main/java/com/example/exam/exam/mapper/ExamMysqlKeywordMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamMysqlKeywordMapper.java new file mode 100644 index 0000000..f1dc6ee --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/ExamMysqlKeywordMapper.java @@ -0,0 +1,14 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.ExamMysqlKeyword; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ExamMysqlKeywordMapper { + + List selectListByAnswerId(String answerId); + + String selectByAnswerIds(List answerIdList); +} diff --git a/src/main/java/com/example/exam/exam/mapper/ExamQuestionAnswerMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamQuestionAnswerMapper.java new file mode 100644 index 0000000..b71d7ca --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/ExamQuestionAnswerMapper.java @@ -0,0 +1,26 @@ +package com.example.exam.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 试题答案选项(hyc)Mapper接口 + * + * @author pengchen + * @date 2025-03-13 + */ +@Mapper +public interface ExamQuestionAnswerMapper extends BaseMapper +{ + + List selectExamQuestionAnswerByQuId(String quId); + + String selectAnswerFile(String quId); + + String selectCountPointByQuId(String quId); + + String selectExamQuestionAnswerScoreByAnswerId(String answerId); +} diff --git a/src/main/java/com/example/exam/exam/mapper/ExamQuestionKeywordMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamQuestionKeywordMapper.java new file mode 100644 index 0000000..cab7022 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/ExamQuestionKeywordMapper.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.ExamQuestionKeyword; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + + +/** + * 试题答案关键字Mapper接口 + * + * @author pengchen + * @date 2025-03-21 + */ +@Mapper +public interface ExamQuestionKeywordMapper +{ + + List selectExamQuestionKeywordByQuId(String quId); +} diff --git a/src/main/java/com/example/exam/exam/mapper/ExamQuestionMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamQuestionMapper.java new file mode 100644 index 0000000..fd22086 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/ExamQuestionMapper.java @@ -0,0 +1,30 @@ +package com.example.exam.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.exam.exam.dal.ExamQuestion; +import org.apache.ibatis.annotations.Mapper; + + +import java.util.List; + +/** + * 试题(hyc)Mapper接口 + * + * @author pengchen + * @date 2025-03-13 + */ +@Mapper +public interface ExamQuestionMapper extends BaseMapper +{ + /** + * 查询试题(hyc) + * + * @param quId 试题(hyc)主键 + * @return 试题(hyc) + */ + public ExamQuestion selectExamQuestionByQuId(String quId); + + + + +} diff --git a/src/main/java/com/example/exam/exam/mapper/ExamQuestionScoreMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamQuestionScoreMapper.java new file mode 100644 index 0000000..9fc1f9a --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/ExamQuestionScoreMapper.java @@ -0,0 +1,19 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.ExamQuestionScore; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 试题判分标准Mapper接口 + * + * @author pengchen + * @date 2025-03-19 + */ +@Mapper +public interface ExamQuestionScoreMapper +{ + + ExamQuestionScore selectExamQuestionScoreByQuId(String quId); +} diff --git a/src/main/java/com/example/exam/exam/mapper/StuPaperScoreMapper.java b/src/main/java/com/example/exam/exam/mapper/StuPaperScoreMapper.java new file mode 100644 index 0000000..61fe9d6 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/StuPaperScoreMapper.java @@ -0,0 +1,24 @@ +package com.example.exam.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.exam.exam.dal.StuPaperScoreDO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 学生-试卷-文件 Mapper + * + * @author rwb + */ +@Mapper +public interface StuPaperScoreMapper extends BaseMapper { + + List findByStuIdAndPaperId(@Param("stuId") Long stuId, @Param("paperId") String paperId); + + // 通过学生ID,试卷ID ,试题ID,查询数据 + StuPaperScoreDO findByStuIdAndPaperIdAndQuestionId(@Param("stuId") Long stuId, @Param("paperId") String paperId, @Param("quId") String questionId); + + void deleteByStuIdAndPaperId(@Param("stuId") Long stuId, @Param("paperId") String paperId); +} \ No newline at end of file diff --git a/src/main/java/com/example/exam/exam/mapper/SysFileMapper.java b/src/main/java/com/example/exam/exam/mapper/SysFileMapper.java new file mode 100644 index 0000000..5826bd7 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/SysFileMapper.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.mapper; + +import com.example.exam.exam.dal.SysFileUpload; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + + +/** + * 文件(hyc)Mapper接口 + * + * @author pengchen + * @date 2025-03-18 + */ +@Mapper +public interface SysFileMapper +{ + public List selectSysFileByQuid(String quId); + +} diff --git a/src/main/java/com/example/exam/exam/mapper/SystemTenantMapper.java b/src/main/java/com/example/exam/exam/mapper/SystemTenantMapper.java new file mode 100644 index 0000000..5689048 --- /dev/null +++ b/src/main/java/com/example/exam/exam/mapper/SystemTenantMapper.java @@ -0,0 +1,10 @@ +package com.example.exam.exam.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.exam.exam.dal.ExamAppCheck; +import com.example.exam.exam.dal.SystemTenant; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SystemTenantMapper extends BaseMapper { +} diff --git a/src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckService.java b/src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckService.java new file mode 100644 index 0000000..2415e67 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckService.java @@ -0,0 +1,19 @@ +package com.example.exam.exam.service.appcheck; + + +import com.example.exam.exam.dal.ExamAppCheck; + +/** + * 管理后台APP检测查询 Service 接口 + * APP检测查询 + * + * @author r w b + */ +public interface ExamAppCheckService { + + /** + * 获取哪些APP是需要查询得 + * @return App检测表 + */ + ExamAppCheck getAppList(); +} diff --git a/src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckServiceImpl.java b/src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckServiceImpl.java new file mode 100644 index 0000000..13ede89 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/appcheck/ExamAppCheckServiceImpl.java @@ -0,0 +1,25 @@ +package com.example.exam.exam.service.appcheck; + +import com.example.exam.exam.dal.ExamAppCheck; +import com.example.exam.exam.mapper.ExamAppCheckMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +/** + * 管理后台APP检测查询 Service 接口实现类 + * APP检测查询 + * + * @author r w b + */ +@Service +public class ExamAppCheckServiceImpl implements ExamAppCheckService { + + @Resource + ExamAppCheckMapper examAppCheckMapper; + + @Override + public ExamAppCheck getAppList() { + ExamAppCheck appList = examAppCheckMapper.selectById("1"); + return appList; + } +} diff --git a/src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerService.java b/src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerService.java new file mode 100644 index 0000000..3f60611 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerService.java @@ -0,0 +1,15 @@ +package com.example.exam.exam.service.autoforbrower; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +import java.io.IOException; + +/** + * @author REN + */ +public interface AutoForBrowerService { + + + Double autoForBrower(StuInfoVo stuInfoVo) throws IOException; + +} diff --git a/src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerServiceImpl.java b/src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerServiceImpl.java new file mode 100644 index 0000000..9220a10 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforbrower/AutoForBrowerServiceImpl.java @@ -0,0 +1,114 @@ +package com.example.exam.exam.service.autoforbrower; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.brower.JudgementBrowerService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import com.example.exam.exam.service.winfile.IFileServerice; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +@Service +public class AutoForBrowerServiceImpl implements AutoForBrowerService { + + @Resource + JudgementBrowerService judgementBrowerService; + + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + SystemTenantService systemTenantService; + /** + * 自动判题文件处理 + * @param stuInfoVo 学生考试信息 + * @return 是否通过 + */ + @Override + public Double autoForBrower(StuInfoVo stuInfoVo) throws IOException { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + for (File one_file : csFileList) { + String name = one_file.getName(); + if ("网络题".equals(name)) { + File mysql_file = new File(one_file.getPath()); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(name)).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText cpojo = judgementBrowerService.Judgement(Double.parseDouble(quScore), mysql_file, examQuestion, judgementStr); + score += cpojo.getScore(); + judgementStr = cpojo.getText(); + judgementStr += "

试题得分:" + cpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(cpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(cpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(name); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + System.out.println(judgementStr); + break; + + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/service/autoforc/AutoForCService.java b/src/main/java/com/example/exam/exam/service/autoforc/AutoForCService.java new file mode 100644 index 0000000..57b5cae --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforc/AutoForCService.java @@ -0,0 +1,15 @@ +package com.example.exam.exam.service.autoforc; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +/** + * @author REN + */ +public interface AutoForCService { + + /** + * 自动判题 C语言 + * @param stuInfoVo 学生信息 + */ + Double autoForC(StuInfoVo stuInfoVo); +} diff --git a/src/main/java/com/example/exam/exam/service/autoforc/AutoForCServiceImpl.java b/src/main/java/com/example/exam/exam/service/autoforc/AutoForCServiceImpl.java new file mode 100644 index 0000000..7631c60 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforc/AutoForCServiceImpl.java @@ -0,0 +1,111 @@ +package com.example.exam.exam.service.autoforc; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +@Service +public class AutoForCServiceImpl implements AutoForCService { + + @Resource + JudgementService judgementService; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + SystemTenantService systemTenantService; + /** + * 自动判题C语言 + * @param stuInfoVo 学生考试信息 + * @return 是否通过 + */ + @Override + public Double autoForC(StuInfoVo stuInfoVo) { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + String name = csFileList[0].getName(); + if ("编程题".equals(name)) { + File[] cs_file_list = csFiles.listFiles(); + String lastFilePath = cs_file_list[0].getPath(); + File[] lastFiles = cs_file_list[0].listFiles(); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(name)).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + for (File lastFile : lastFiles) { + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText cpojo = judgementService.ProgrammingC(Double.parseDouble(quScore), lastFilePath, lastFile.getName(), examQuestion, judgementStr); + judgementStr = cpojo.getText(); + judgementStr += "

试题得分:" + cpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + score += cpojo.getScore(); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(cpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(cpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(name); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + break; + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceService.java b/src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceService.java new file mode 100644 index 0000000..8936299 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceService.java @@ -0,0 +1,15 @@ +package com.example.exam.exam.service.autoforchoice; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +import java.io.IOException; + +/** + * @author REN + */ +public interface AutoForChoiceService { + + String autoForChoiceToJson(StuInfoVo stuInfoVo); + + Double autoForChoice(StuInfoVo stuInfoVo) throws IOException; +} diff --git a/src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceServiceImpl.java b/src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceServiceImpl.java new file mode 100644 index 0000000..788e4e7 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforchoice/AutoForChoiceServiceImpl.java @@ -0,0 +1,316 @@ +package com.example.exam.exam.service.autoforchoice; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperMapper; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import com.example.exam.exam.utils.c.LogFileUtils; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; + +@Service +public class AutoForChoiceServiceImpl implements AutoForChoiceService { + + @Resource + JudgementService judgementService; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + SystemTenantService systemTenantService; + @Autowired + private EducationPaperMapper educationPaperMapper; + /** + * 自动判题选择题 + * @param stuInfoVo 学生考试信息 + * @return 是否通过 + */ + @Override + public String autoForChoiceToJson(StuInfoVo stuInfoVo) { + // 试题ID + String quId = stuInfoVo.getQuestionId(); + // 学生答案ID + String choiceAnswerId = stuInfoVo.getChoiceAnswerId(); + // 保存ID路径 + String filePath = stuInfoVo.getFilePath(); + File file = new File(filePath + "/Choice.json"); + if (!file.exists()) { + LogFileUtils.createFile(filePath + "/Choice.json"); + } + Map map = new HashMap<>(); + map.put(quId, choiceAnswerId); + return writeMapToJson(map, filePath + "/Choice.json"); + } + + @Override + public Double autoForChoice(StuInfoVo stuInfoVo) throws IOException { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 文件路径 + String filePath = stuInfoVo.getFilePath() + "/Choice.json"; + // 读取JSON文件 + // 创建 ObjectMapper 实例 + File jsonFile = new File(filePath); + ObjectMapper objectMapper = new ObjectMapper(); + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals("选择题")).findFirst(); + String quScore = result.get().getQuScores(); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 获取Paper下的所有选择题ID + List examQuestions = educationPaperMapper.selectPaperQuByPaperId(stuInfoVo.getPaperId()); + List chioseList = new ArrayList<>(); + for (ExamQuestion examQuestion : examQuestions) { + if ("选择题".equals(examQuestion.getSubjectName())) { + chioseList.add(examQuestion); + } + } + if (jsonFile.exists()) { + try (FileInputStream fileInputStream = new FileInputStream(jsonFile)) { + // 读取文件并转换为 JsonNode + JsonNode rootNode = objectMapper.readTree(fileInputStream); + String jsonText = objectMapper.writeValueAsString(rootNode); + // 从 JSON 文件读取并转换为 Person 对象 + Map map = objectMapper.readValue(jsonText, Map.class); + // 获取全部的选择题 + for (ExamQuestion question : chioseList) { + String quId = question.getQuId(); + Optional results = map.entrySet().stream().filter(entry -> entry.getKey().equals(question.getQuId())).map(Map.Entry::getValue) + .findFirst(); + Optional resultss = educationPaperQus.stream().filter(entry -> entry.getQuId().equals(quId)) + .findFirst(); + EducationPaperQu educationPaperQu = resultss.get(); + if (results.isPresent()) { + // 获取学生作答的选择题ID + String stuAnswerId = results.get(); + // 根据ID查询试题 + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + String trueAnswerId = ""; + String trueAbswerSort = ""; + // 查询试题的正确答案及序号 + for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) { + if ("0".equals(examQuestionAnswer.getIsRight())) { + trueAnswerId = examQuestionAnswer.getAnswerId(); + trueAbswerSort = convertToLetter(examQuestionAnswer.getSort()); + break; + } + } + // 查询学生答案的序号 + List examQuestionAnswers = examQuestion.getAnswerList(); + Optional resultAnswer = examQuestionAnswers.stream().filter(quLists -> quLists.getAnswerId().equals(stuAnswerId)).findFirst(); + String stuAnswerSort = ""; + if (resultAnswer.isPresent()) { + stuAnswerSort = convertToLetter(resultAnswer.get().getSort()); + } + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称: 选择题

"; + judgementStr += "

学生答案:" + stuAnswerSort + "

"; + judgementStr += "

正确答案:" + trueAbswerSort + "

"; + boolean isRight = false; + // 开始判断学生的答案是否正确 + if (stuAnswerId.equals(trueAnswerId)) { + // 正确 + judgementStr += "

得分:" + Double.parseDouble(quScore) + "

"; + isRight = true; + score += Double.parseDouble(quScore); + } else { + // 不正确 + judgementStr += "

得分:0

"; + isRight = false; + } + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(quScore)); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName("选择题"); + stuPaperScoreDO.setIsTrue(isRight ? 0 : 1); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(0)); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName("选择题"); + insertInfo.setIsTrue(isRight ? 0 : 1); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + } else { + // 根据ID查询试题 + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + String trueAbswerSort = ""; + for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) { + if ("0".equals(examQuestionAnswer.getIsRight())) { + trueAbswerSort = convertToLetter(examQuestionAnswer.getSort()); + break; + } + } + // 没有找到作答情况直接该题判0分 + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称: 选择题

"; + judgementStr += "

学生答案: 未作答

"; + judgementStr += "

正确答案:" + trueAbswerSort + "

"; + judgementStr += "

得分:0

"; + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(0)); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName("选择题"); + stuPaperScoreDO.setIsTrue(1); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(0)); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName("选择题"); + insertInfo.setIsTrue(1); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + } + } + if (fileInputStream != null) { + try { + fileInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } else { + // 说明全部都没有做,直接全部給0分 + for (ExamQuestion question : chioseList) { + String quId = question.getQuId(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + String trueAbswerSort = ""; + // 查询试题的正确答案及序号 + for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) { + if ("0".equals(examQuestionAnswer.getIsRight())) { + trueAbswerSort = convertToLetter(examQuestionAnswer.getSort()); + break; + } + } + Optional resultss = educationPaperQus.stream().filter(entry -> entry.getQuId().equals(quId)) + .findFirst(); + EducationPaperQu educationPaperQu = resultss.get(); + // 没有找到作答情况直接该题判0分 + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称: 选择题

"; + judgementStr += "

学生答案: 未作答

"; + judgementStr += "

正确答案:" + trueAbswerSort + "

"; + judgementStr += "

得分:0

"; + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(0)); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName("选择题"); + stuPaperScoreDO.setIsTrue(1); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(0)); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName("选择题"); + insertInfo.setIsTrue(1); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + } + } + return score; + } + + // 将Map写入JSON文件 + // 将Map追加到JSON文件 + public static String writeMapToJson(Map map, String filePath) { + ObjectMapper objectMapper = new ObjectMapper(); + File file = new File(filePath); + + try { + // 如果文件存在且文件不为空,尝试读取现有内容 + Map existingMap = null; + if (file.exists() && file.length() > 0) { + existingMap = objectMapper.readValue(file, Map.class); + } + + // 如果文件为空或无法读取,初始化为空的Map + if (existingMap == null) { + existingMap = new HashMap<>(); + } + + // 合并新的Map数据 + existingMap.putAll(map); + + // 将合并后的Map写入到文件 + objectMapper.writeValue(file, existingMap); + } catch (IOException e) { + e.printStackTrace(); + } + return "学生答案已经写入到 " + filePath; + } + + + public static String convertToLetter(int number) { + // 判断是否在 1 到 26 的范围内 + if (number >= 1 && number <= 26) { + return String.valueOf((char) ('A' + number - 1)); // 转换为对应的字母 + } else { + return "Invalid"; // 如果不在 1-26 范围内,返回 "Invalid" + } + } +} diff --git a/src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileService.java b/src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileService.java new file mode 100644 index 0000000..7d79dd7 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileService.java @@ -0,0 +1,15 @@ +package com.example.exam.exam.service.autoforfile; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +import java.io.IOException; + +/** + * @author REN + */ +public interface AutoForFileService { + + + Double autoForFile(StuInfoVo stuInfoVo) throws IOException; + +} diff --git a/src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileServiceImpl.java b/src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileServiceImpl.java new file mode 100644 index 0000000..5877eaf --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoforfile/AutoForFileServiceImpl.java @@ -0,0 +1,123 @@ +package com.example.exam.exam.service.autoforfile; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.mysql.IMysqlLocalService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import com.example.exam.exam.service.winfile.IFileServerice; +import com.example.exam.exam.utils.c.LogFileUtils; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +public class AutoForFileServiceImpl implements AutoForFileService { + + + @Resource + IFileServerice fileServerice; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + SystemTenantService systemTenantService; + /** + * 自动判题文件处理 + * @param stuInfoVo 学生考试信息 + * @return 是否通过 + */ + @Override + public Double autoForFile(StuInfoVo stuInfoVo) throws IOException { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + for (File one_file : csFileList) { + String name = one_file.getName(); + if ("文件处理".equals(name)) { + File[] cs_file_list = csFiles.listFiles(); + String lastFilePath = one_file.getPath(); + File mysql_file = new File(one_file.getPath()); + File[] lastFiles = one_file.listFiles(); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(name)).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText cpojo = fileServerice.Judgement(Double.parseDouble(quScore), mysql_file, examQuestion, judgementStr); + score += cpojo.getScore(); + judgementStr = cpojo.getText(); + judgementStr += "

试题得分:" + cpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(cpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(cpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(name); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + System.out.println(judgementStr); + break; + + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlService.java b/src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlService.java new file mode 100644 index 0000000..e018169 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlService.java @@ -0,0 +1,18 @@ +package com.example.exam.exam.service.autoformysql; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +import java.io.IOException; +import java.sql.SQLException; + +/** + * @author REN + */ +public interface AutoForMysqlService { + + /** + * 自动判题 C语言 + * @param stuInfoVo 学生信息 + */ + Double autoForMysql(StuInfoVo stuInfoVo) throws SQLException, IOException; +} diff --git a/src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlServiceImpl.java b/src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlServiceImpl.java new file mode 100644 index 0000000..54f7873 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/autoformysql/AutoForMysqlServiceImpl.java @@ -0,0 +1,114 @@ +package com.example.exam.exam.service.autoformysql; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.mysql.IMysqlLocalService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +@Service +public class AutoForMysqlServiceImpl implements AutoForMysqlService { + + @Resource + IMysqlLocalService mysqlLocalService; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + SystemTenantService systemTenantService; + /** + * 自动判题C语言 + * @param stuInfoVo 学生考试信息 + * @return 是否通过 + */ + @Override + public Double autoForMysql(StuInfoVo stuInfoVo) throws SQLException, IOException { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + for (File one_file : csFileList) { + String name = one_file.getName(); + if ("程序设计".equals(name)) { + File mysql_file = new File(one_file.getPath()); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(one_file.getName())).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText cpojo = mysqlLocalService.Judgement(Double.parseDouble(quScore), mysql_file, examQuestion, judgementStr); + score += cpojo.getScore(); + judgementStr = cpojo.getText(); + judgementStr += "

试题得分:" + cpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(cpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(cpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(one_file.getName()); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(cpojo.getScore() == 0 ? 1 : cpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + System.out.println(judgementStr); + break; + + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/service/brower/JudgementBrowerService.java b/src/main/java/com/example/exam/exam/service/brower/JudgementBrowerService.java new file mode 100644 index 0000000..c53811d --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/brower/JudgementBrowerService.java @@ -0,0 +1,13 @@ +package com.example.exam.exam.service.brower; + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +import java.io.File; +import java.io.IOException; + +public interface JudgementBrowerService { + + SourceAndText Judgement(double score, File file, ExamQuestion question, String judgementStr) throws IOException; + +} diff --git a/src/main/java/com/example/exam/exam/service/brower/JudgementBrowerServiceImpl.java b/src/main/java/com/example/exam/exam/service/brower/JudgementBrowerServiceImpl.java new file mode 100644 index 0000000..dd3a6b1 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/brower/JudgementBrowerServiceImpl.java @@ -0,0 +1,172 @@ +package com.example.exam.exam.service.brower; + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.SourceAndText; +import com.example.exam.exam.mapper.ExamQuestionAnswerMapper; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.brower.BookmarkChecker; +import com.example.exam.exam.utils.brower.BookmarkDeleter; +import com.example.exam.exam.utils.file.GetDifferencesBetweenFolders; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; +@Service +public class JudgementBrowerServiceImpl implements JudgementBrowerService { + static String answerLogPath ; // 文件路径 + @Resource + private ExamQuestionAnswerMapper examQuestionAnswerMapper; + private static final DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + //EDGE浏览器 + String chromeBookmarkPath = System.getenv("LOCALAPPDATA") + "\\Microsoft\\Edge\\User Data\\Default\\Bookmarks"; + @Override + public SourceAndText Judgement(double score, File file, ExamQuestion question, String judgementStr) throws IOException { + //根据题目,查找考点 + + //得到该试题的得分点 + //Net得分点 文件类型 文件名 考点权值 + // 添加到文件夹 1 + // 添加到收藏夹 2 +// List answerList=new ArrayList<>(); +// answerList.add(new ExamQuestionAnswer("","","","","人民大学", "添加到文件夹 ", "1", "1")); +// answerList.add(new ExamQuestionAnswer("","","","","校情", "添加到文件夹", "1", "2")); +// answerList.add(new ExamQuestionAnswer("","","","","机构", "添加到文件夹", "1", "3")); + SourceAndText sourceAndText = new SourceAndText(); + List answerList = examQuestionAnswerMapper.selectExamQuestionAnswerByQuId(question.getQuId()); + + //判断如果类型为1,为添加到文件夹的html,后缀加 .html +// for (ExamQuestionAnswer answer : answerList) { +// if ("添加到文件夹".equals(answer.getContentIn())) { +// String fileName = answer.getContent(); +// if (!fileName.endsWith(".html")) { +// answer.setContent(fileName + ".html"); +// } +// } +// } + + + + //分为两点,1:文件夹-- 去考生文件夹去找 文件名 + // 2:收藏夹--去浏览器去找 根据值 文件名 查找 + // 找到后 —————— +权值 ,根据文件名删除 + + answerLogPath = file.getParent() + File.separator + "log.txt"; + + //根据路径,得到考生答题文件集合 + Map stuFiles = GetDifferencesBetweenFolders.listFilesAndFoldersWithAttributes(file.toPath()); + + + // 输出学生提交的内容 +// appendToFile(answerLogPath,"=== 学生提交内容(stu 目录) ==="); +// +// stuFiles.forEach((key, value) -> appendToFile(answerLogPath,key + " -> " + value)); + appendToFile(answerLogPath,"=== 学生提交内容得分点 ==="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"=== 学生提交内容得分点 ==="); + // 计算试题总分 + int totalScore = answerList.stream() + .mapToInt(a -> Integer.parseInt(a.getScoreRate())) + .sum(); + //这里指挥判断存在文件夹的得分点 + SourceAndText studentScorePojo = compareStuAndTestFiles(answerList, stuFiles,score,totalScore,judgementStr); + double studentScore = studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + //判断收藏夹得分点 + for (ExamQuestionAnswer examQuestionAnswer : answerList) { + int currentScore = Integer.parseInt(examQuestionAnswer.getScoreRate()); // 当前得分 + + if ( "添加到收藏夹".equals(examQuestionAnswer.getContentIn())){ + String bookmarkNameToDelete = examQuestionAnswer.getContent(); + //检查收藏夹是否有书签 + boolean isCorrect = BookmarkChecker.bookmarkExists(chromeBookmarkPath, bookmarkNameToDelete); + if (isCorrect) { + //如果有 +权值 + studentScore += currentScore; + + + // 计算该考点的权重得分并保留一位小数 + double weightScore = ((double) currentScore / totalScore) * score; + String formattedWeightScore = String.format("%.1f", weightScore); + + appendToFile(answerLogPath,"✅考点"+bookmarkNameToDelete + " -> 得分权值:" + currentScore+"-> 得分:"+formattedWeightScore); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"✅考点"+bookmarkNameToDelete + " -> 得分权值:" + currentScore+"-> 得分:"+formattedWeightScore); + //删除此书签 + BookmarkDeleter.deleteBookmarkByName(chromeBookmarkPath, bookmarkNameToDelete); + } + else { + appendToFile(answerLogPath,"❌考点"+bookmarkNameToDelete + " -> 得分权值:" + currentScore+"-> 得分:0"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"❌考点"+bookmarkNameToDelete + " -> 得分权值:" + currentScore+"-> 得分:0"); + + } + } + + } + + // 计算最终得分比例(保留两位小数) + double scoreRatio = totalScore == 0 ? 0 : (double) studentScore / totalScore; + + double roundedScoreRatio = Math.round(scoreRatio * 100.0) / 100.0; // 四舍五入到2位小数 + appendToFile(answerLogPath," 得分:"+roundedScoreRatio*score); + sourceAndText.setScore(roundedScoreRatio*score); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + + + // 对比学生提交内容与试题得分点 + static SourceAndText compareStuAndTestFiles(List answerList, Map stuFiles,double score,int total, String judgementStr) { + int totalScore = 0; // 记录总得分 + SourceAndText sourceAndText = new SourceAndText(); + for (ExamQuestionAnswer answer : answerList) { + if ("添加到文件夹".equals(answer.getContentIn())) { + String filePath = answer.getContent(); // 试题文件路径 + int currentScore = Integer.parseInt(answer.getScoreRate()); // 当前得分 + boolean isCorrect = false; + // 如果学生提交中存在该文件,则得分 + isCorrect = stuFiles.containsKey(filePath); + // 如果正确,则累加总分 + if (isCorrect) { + totalScore += currentScore; + // 计算该考点的权重得分并保留一位小数 + double weightScore = ((double) currentScore / total) * score; + String formattedWeightScore = String.format("%.1f", weightScore); + appendToFile(answerLogPath,"✅考点"+answer.getContent() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:"+formattedWeightScore); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"✅考点"+answer.getContent() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:"+formattedWeightScore); + }else { + appendToFile(answerLogPath,"❌考点"+answer.getContent() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:0"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"❌考点"+answer.getContent() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:0"); + } + } + } + //返回累加的得分点 + sourceAndText.setScore(totalScore); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + /** + * 将指定内容追加写入到指定文件中。 + * + * @param filePath 文件路径 + * @param content 要写入的内容 + */ + public static void appendToFile(String filePath, String content) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) { + String timestamp = LocalDateTime.now().format(formatter); + String logLine = String.format("[%s] %s", timestamp, content); + writer.write(logLine); + writer.newLine(); // 可选:添加换行符 + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/example/exam/exam/service/c/JudgementService.java b/src/main/java/com/example/exam/exam/service/c/JudgementService.java new file mode 100644 index 0000000..add3186 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/c/JudgementService.java @@ -0,0 +1,25 @@ +package com.example.exam.exam.service.c; + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +/** + * 判分逻辑集合 + * + * @author rwb + */ +public interface JudgementService { + + /** + * 程序设计判分 + * @param examQuestion 程序设计题内容 + * @param fileName 文件名称 + * @param pathC 文件路径 C语言文件 + * @param score 分数 + * @return 返回判分 + */ + public SourceAndText ProgrammingC(double score, String pathC, String fileName, ExamQuestion examQuestion, String judgementStr); + + +} diff --git a/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java b/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java new file mode 100644 index 0000000..d56a3a7 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java @@ -0,0 +1,222 @@ +package com.example.exam.exam.service.c; + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.ExamQuestionKeyword; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.c.JudgementCUtils; +import com.example.exam.exam.utils.c.LogFileUtils; +import org.springframework.stereotype.Service; +import com.example.exam.exam.dal.SourceAndText; + +import java.util.*; + +/** + * 判分逻辑集合 + * + * @author rwb + */ +@Service +public class JudgementServiceImpl implements JudgementService +{ + + /** + * 程序设计判分 + * @param examQuestion 程序设计题内容 + * @param fileName 文件名称 + * @param score 分数 + * @return 返回判分 + */ + @Override + public SourceAndText ProgrammingC(double score, String pathC, String fileName, ExamQuestion examQuestion, String judgementStr) { + SourceAndText sourceAndText = new SourceAndText(); + // 关键字比对,超过权重-测试用例/运行(测试用例全对,直接满分-不全对)-结果,不超过权重只给关键字几个的分 + // 获取该题有多少分 + // TODO 测试分数15,该程序设计题为15分 + // double score = 15; + // 总分 + double totalScore = 0; + // 测试用例结果分数 + double compile_score = 0; + // 关键字分数 + double key_score = 0; + // 关键字分数 + double result_score = 0; + + // 先获取题的组成部分 + // 是否需要程序编译 (0:true;1:false) + boolean is_pass = Objects.equals(examQuestion.getQuestionScores().getIsPass(), "0"); + // 是否需要程序结果 (0:true;1:false) + boolean is_result = Objects.equals(examQuestion.getQuestionScores().getIsResult(), "0"); + // 是否需要关键字 (0:true;1:false) + boolean is_keyword = Objects.equals(examQuestion.getQuestionScores().getIsKeyword(), "0"); + // 是否需要测试用例 (0:true;1:false) + boolean is_compile = Objects.equals(examQuestion.getQuestionScores().getIsCompile(), "0"); + + // 占比百分比 + double is_pass_score = Integer.parseInt(examQuestion.getQuestionScores().getIsPassScore()) / 100.0; + double is_result_score = Integer.parseInt(examQuestion.getQuestionScores().getIsResultScore()) / 100.0; + double is_keyword_score = Integer.parseInt(examQuestion.getQuestionScores().getIsKeywordScore()) / 100.0; + double is_compile_score = Integer.parseInt(examQuestion.getQuestionScores().getIsCompileScore()) / 100.0; + + // 关键字分数占比 + double keyword_score = score * is_keyword_score; + // 编译分数分数占比 + double pass_score = score * is_pass_score; + + // 创建log文件txt,用于记录 + LogFileUtils.createFile(pathC + "/log.txt"); + String code = JudgementCUtils.readFile(pathC, fileName); + LogFileUtils.writeLine("✅ 系统开始读取文件:" + code); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 系统开始读取文件:" + code); + if (code == "") { + // 如果没有读到源码 + LogFileUtils.writeLine("❌ 系统没有读取到文件。"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 系统没有读取到文件。"); + + LogFileUtils.close(); + // 该题不得分,直接算成0分 + sourceAndText.setScore(0); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + int true_number = 0; + // 关键字分数 + if (is_keyword) { + // 总权重值 + int weight = 0; + List> key_list = new ArrayList<>(); + // 进行关键字权重比对进行判断 + + for (ExamQuestionKeyword examQuestionKeyword : examQuestion.getQuestionKeywords()) { + boolean keyword_run = code.contains(examQuestionKeyword.getKeyword()); + // 计算权值 + Map item = new HashMap<>(); + item.put("success", keyword_run); + item.put("score_rate", examQuestionKeyword.getScoreRate()); + LogFileUtils.writeLine("✅ 关键字比对:" + examQuestionKeyword.getKeyword() + "--" + keyword_run); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 关键字比对:" + examQuestionKeyword.getKeyword() + "--" + keyword_run); + weight += Integer.parseInt(examQuestionKeyword.getScoreRate()); + key_list.add(item); + } + // 所有的关键字比对完成之后进行盘端关键字的分数 + // 先获取关键字应该分数占比 + double one_keyword_score = keyword_score / weight; + // 根据权重进行给分 + for (Map item : key_list) { + // 判断首先等于true 的情况下 + if ((boolean)item.get("success")) { + // 每个选项分值 = 总分 / 总权重 + true_number += 1; + key_score += one_keyword_score * Integer.parseInt((String) item.get("score_rate")); + LogFileUtils.writeLine("✅ 关键字得分:" + key_score); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 关键字得分:" + key_score); + + } + } + } + // 程序需要在运行测试用例,及满足关键字的情况下进行给出运行得分 + // 关键字临界得分值 + int todo_key_percentage = Integer.parseInt(examQuestion.getQuestionScores().getKeywordCutoff()); + if (key_score > keyword_score * ((double) todo_key_percentage / 100) ) { + // 编译代码运行 + if (is_pass) { + // 如果使用程序编译,进行程序编译 + LogFileUtils.writeLine("✅ 正在使用-std=c99进行编译..."); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 正在使用-std=c99进行编译..."); + + // 使用C99 运行并得出结果 + String code_return = JudgementCUtils.run_code(pathC,code,null,"-std=c99", "编译通过运行"); + if (!code_return.contains("error")) { + // 编译没有报错,加上编译分数 + totalScore += pass_score; + LogFileUtils.writeLine("✅ 编译通过得分:" + pass_score); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 编译通过得分:" + pass_score); + + } else { + LogFileUtils.writeLine("❌ 编译未通过。"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 编译未通过。"); + } + } + // 进行判断测试用例 + // 测试用例 + // 判断是否要程序结果 ,需要程序结果的,就需要测试用例 + if (is_compile) { + // 先运行程序,再将测试用例进行比对 + // 运行完成后在判断是否需要进行关键字比对 + boolean run_code = false; + List runList = new ArrayList<>(); + LogFileUtils.writeLine("✅ 使用测试用例进行判分..."); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 使用测试用例进行判分..."); + for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) { + // 使用C99 运行并得出结果 + String code_return = JudgementCUtils.run_code(pathC,code, examQuestionAnswer.getContentIn(),"-std=c99",null); + String actual = code_return.trim(); + String expected = examQuestionAnswer.getContent().trim(); + if (actual.equals(expected)) { + // 判断测试用例结果是否正确 + runList.add(true); + // 获取测试用例临界值,并进行判断 +// if (runList.size() >= Integer.parseInt(examQuestion.getQuestionScores().getCompileCutoff()) && !runList.contains(false)) { +// // 测试用例得分 +// compile_score += (double) (score * is_compile_score); +// // 结果得分 +// result_score += (double) (score * is_result_score); +// LogFileUtils.writeLine("✅ 测试用例得分:" + compile_score); +// break; +// } + } + } + // 记录存在多少个测试用例,并且同时记录正确测试用例个数 + int test_case_number = examQuestion.getAnswerList().size(); + int true_test_case_number = runList.size(); + + // 判断正确关系 + // 1、如果完全相等,说明完全正确,直接给满分 + if (test_case_number == true_test_case_number) { + // 满分,该题多少分就是多少分 + LogFileUtils.writeLine("✅ 测试用例全部正确:"+ score); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 测试用例全部正确:"+ score); + LogFileUtils.close(); + sourceAndText.setScore(score); + sourceAndText.setText(judgementStr); + return sourceAndText; + } else if (test_case_number > true_test_case_number) { + // 2、测试用例没有完全正确,对多少个就是多少分 + // 公式:测试用例总分数 / 测试用例数量 * 正确测试用例数量 + compile_score += (double) ((score * is_compile_score) / test_case_number) * true_test_case_number; + LogFileUtils.writeLine("✅ 测试用例数量:"+ test_case_number + ",正确数量:" + true_test_case_number + ",得分:" + compile_score); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 测试用例数量:"+ test_case_number + ",正确数量:" + true_test_case_number + ",得分:" + compile_score); + + } + } + + // 总分 = 总分 + 测试用例得分 + totalScore += compile_score; + if (compile_score > 0) { + // 如果测试用例正确有得分的 + // 结果 + if (is_result) { + // 总分 = 总分 + 结果得分 + totalScore += result_score; + LogFileUtils.writeLine("✅ 结果得分:" + result_score); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 结果得分:" + result_score); + } + } + totalScore += key_score; + LogFileUtils.close(); + sourceAndText.setScore(totalScore); + sourceAndText.setText(judgementStr); + return sourceAndText; + } else { + // 关键字对几个给几分,没有达到临界值的情况下 + totalScore += key_score; + LogFileUtils.writeLine("❌ 关键字没有达到临界值,正确数量:"+ true_number); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 关键字没有达到临界值,正确数量:"+ true_number); + LogFileUtils.close(); + sourceAndText.setScore(totalScore); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + } +} diff --git a/src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalService.java b/src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalService.java new file mode 100644 index 0000000..adfe516 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalService.java @@ -0,0 +1,19 @@ +package com.example.exam.exam.service.mysql; + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; + + +public interface IMysqlLocalService { + + + void delMysqlConnect(List tNames); + SourceAndText Judgement(double sorce, File file, ExamQuestion examQuestion, String judgementStr) throws IOException, SQLException; + +} diff --git a/src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalServiceImpl.java b/src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalServiceImpl.java new file mode 100644 index 0000000..a27db89 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/mysql/IMysqlLocalServiceImpl.java @@ -0,0 +1,1942 @@ +package com.example.exam.exam.service.mysql; + +import com.example.exam.exam.dal.ExamMysqlKeyword; +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.SourceAndText; +import com.example.exam.exam.mapper.ExamMysqlKeywordMapper; +import com.example.exam.exam.mapper.ExamQuestionAnswerMapper; +import com.example.exam.exam.service.mysql.IMysqlLocalService; +import com.example.exam.exam.service.mysql.vo.MysqlBooleanVo; +import com.example.exam.exam.service.mysql.vo.MysqlVo; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.zip.ZipUtil; +import io.micrometer.common.util.StringUtils; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringEscapeUtils; +import org.springframework.stereotype.Service; + + +import java.io.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +@Service +public class IMysqlLocalServiceImpl implements IMysqlLocalService { + + static String databaseName; + static String databaseNameStu; + static String answerLogPath; // 文件路径 + private static final DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + @Resource + private ExamMysqlKeywordMapper examMysqlKeywordMapper; + @Resource + private ExamQuestionAnswerMapper examQuestionAnswerMapper; + + @Override + public SourceAndText Judgement(double score, File filepath, ExamQuestion examQuestion, String judgementStr) throws IOException, SQLException { + String stuDataName=examQuestion.getTname(); + SourceAndText sourceAndText = new SourceAndText(); + double scoreTotal =0.0; + String fileUrl= examQuestionAnswerMapper.selectAnswerFile(examQuestion.getQuId()); + String path = ZipUtil.downloadStudentFile(fileUrl, "data"); + // 4、获取到得是zip文件,需要解压 + String stuFilePath = ZipUtil.unzipToNamedFolder(path); + File folderzip = new File(path); + File folder = new File(stuFilePath); + // 5、解压之后得文件获取文件夹和文件 + String stu_files = null; + + // 5.2、查询试题ID + + List examQuestionAnswers = examQuestionAnswerMapper.selectExamQuestionAnswerByQuId(examQuestion.getQuId()); + String totalKeyScore ="0"; + //得出 这个题总共的权值点 + totalKeyScore=examQuestionAnswerMapper.selectCountPointByQuId(examQuestion.getQuId()); + + answerLogPath = filepath.getParent() + File.separator + "log.txt"; + + AtomicInteger total = new AtomicInteger(); + // 文件路径 + +// String file = "D:\\Desktop\\202504211120_mysql\\71\\MYS_010_122\\结果素材\\结果素材\\Teacher"; +// String file = new File(filepath, "结果").getAbsolutePath(); +// String filePath = "D:\\Desktop\\202504211120_mysql\\71\\MYS_010_122\\结果素材\\结果素材\\Teacher\\answer.txt"; // 答案文件路径 + String filePath = new File(stuFilePath).getAbsolutePath(); // 结果/answer.txt + +// String answerPath = "D:\\Desktop\\202504211120_mysql\\71\\MYS_010_122\\结果素材\\结果素材\\Teacher\\db\\db_escape.myd"; // 原始 SQL 文件路径 + File dbDir = new File(stuFilePath, "db"); + String answerPath = null; + if (dbDir.exists() && dbDir.isDirectory()) { + File[] mydFiles = dbDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".myd")); + if (mydFiles != null && mydFiles.length > 0) { + answerPath = mydFiles[0].getAbsolutePath(); // 默认取第一个 .myd 文件 + } + } + + +// String fileStu = "D:\\Desktop\\202504211120_mysql\\71\\MYS_010_122\\考试素材\\考试素材\\Student"; + String fileStu = filepath.getAbsolutePath(); + +// String stuAnswerPath = "D:\\Desktop\\202504211120_mysql\\71\\MYS_010_122\\考试素材\\考试素材\\Student\\db\\db_escape.myd"; + File stuDbDir = new File(fileStu, "db"); + String stuAnswerPath = null; + if (stuDbDir.exists() && stuDbDir.isDirectory()) { + File[] stuMydFiles = stuDbDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".myd")); + if (stuMydFiles != null && stuMydFiles.length > 0) { + stuAnswerPath = stuMydFiles[0].getAbsolutePath(); // 默认取第一个 .myd 文件 + } + } + + // 生成随机数据库名,临时使用 + databaseName = "db_" + UUID.randomUUID().toString().replace("-", "").substring(0, 8); + +// databaseName = "db_6f80867f"; +// databaseNameStu= "db_6f80867e"; + // 连接到 MySQL 默认数据库 + String url = "jdbc:mysql://localhost:6033/"+stuDataName+"?useSSL=false&serverTimezone=Asia/Shanghai"; + String user = "root"; + String password = ""; + try { + // **连接到 MySQL 默认数据库,创建新的数据库** + try (Connection conn = DriverManager.getConnection(url, user, password); + Statement stmt = conn.createStatement()) { + String createDbSql = "CREATE DATABASE IF NOT EXISTS " + databaseName + + " CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"; +// String createDbSql = "CREATE DATABASE " + databaseName; + stmt.executeUpdate(createDbSql); + System.out.println("已创建数据库:" + databaseName); + + + + } + + + } catch (SQLException e) { + e.printStackTrace(); + } + +// // **建立连接到新创建的数据库** + String newDbUrl = "jdbc:mysql://localhost:6033/" + databaseName + "?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8"; + String stuDbUrl = "jdbc:mysql://localhost:6033/" + stuDataName + "?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8"; + // **通过命令行执行 SQL 文件** 建立 完整正确数据库 + boolean sqlFileExecuted = executeSqlFileUsingCommandLine(answerPath, databaseName, user, password); + //建立考生答题数据库 +// boolean sqlFileExecutedstu = executeSqlFileUsingCommandLine(stuAnswerPath, databaseNameStu, user, password); + + if (sqlFileExecuted ) { + + + Map result = readFilesAsMap(stuFilePath); + Map resultStu = readFilesAsMap(fileStu); + + try (Connection conn = DriverManager.getConnection(newDbUrl, user, password); + Statement stmt = conn.createStatement()) { + for (ExamQuestionAnswer examQuestionAnswer : examQuestionAnswers) { + String sql=examQuestionAnswer.getContent(); + if(sql.trim().toUpperCase().startsWith("CREATE TABLE")) { + appendToFile(answerLogPath, "==================建表语句=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================建表语句=================="); + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList= examMysqlKeywordMapper.selectListByAnswerId(answerId); + + // 正则表达式匹配表名 + Pattern pattern = Pattern.compile("CREATE TABLE\\s+`?(\\w+)`?\\s*\\("); + Matcher matcher = pattern.matcher(sql); + + if (matcher.find()) { + String tableName = matcher.group(1).replace("`", ""); // 获取表名 + + + // 查询建表语句 + String showCreateTableSql = "SHOW CREATE TABLE " + tableName; + + String sql1 = "SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA " + + "FROM INFORMATION_SCHEMA.COLUMNS " + + "WHERE TABLE_NAME = '" + tableName + "' AND TABLE_SCHEMA = '" + databaseName + "'"; + String sql2 = "SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA " + + "FROM INFORMATION_SCHEMA.COLUMNS " + + "WHERE TABLE_NAME = '" + tableName + "' AND TABLE_SCHEMA = '" + stuDataName + "'"; + + // 获取主库字段信息 + Set> table1Columns = new HashSet<>(); + try (ResultSet rs = stmt.executeQuery(sql1)) { + while (rs.next()) { + Map column = new HashMap<>(); + column.put("COLUMN_NAME", rs.getString("COLUMN_NAME")); + column.put("COLUMN_TYPE", rs.getString("COLUMN_TYPE")); + column.put("IS_NULLABLE", rs.getString("IS_NULLABLE")); + column.put("COLUMN_KEY", rs.getString("COLUMN_KEY")); + column.put("EXTRA", rs.getString("EXTRA")); + table1Columns.add(column); + + } +// appendToFile(answerLogPath, "标准答案建表键值对:"+table1Columns); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "标准答案建表键值对:"+table1Columns); + + + } + // 获取学生库字段信息 + Set> table2Columns = new HashSet<>(); + boolean tableExists = false; + //学生语句 + String stuSQL=null; + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + try (ResultSet rsstu = stmtstu.executeQuery(sql2)) { + // 检查是否有数据返回 + tableExists = rsstu.next(); // 第一次调用next()判断是否有数据 + if (tableExists) { + try (ResultSet rs = stmt.executeQuery(showCreateTableSql)) { + if (rs.next()) { + String createTableSqlExport = rs.getString("Create Table"); + // 替换 result 中的值 + stuSQL=createTableSqlExport; + } + } + + // 如果有数据,处理第一行(因为上面已经调用了next()) + do { + Map column = new HashMap<>(); + column.put("COLUMN_NAME", rsstu.getString("COLUMN_NAME")); + column.put("COLUMN_TYPE", rsstu.getString("COLUMN_TYPE")); + column.put("IS_NULLABLE", rsstu.getString("IS_NULLABLE")); + column.put("COLUMN_KEY", rsstu.getString("COLUMN_KEY")); + column.put("EXTRA", rsstu.getString("EXTRA")); + table2Columns.add(column); + } while (rsstu.next()); + + } else { + // 表不存在时的处理逻辑 + System.out.println("表 " + tableName + " 在学生数据库中不存在"); + + + + } + + + } + } + + String yuju= compareTables(table1Columns, table2Columns, stuDataName,tableName, tableName,judgementStr); + judgementStr=yuju; + + + + if (table1Columns.equals(table2Columns)) { + // + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,stuSQL,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } else { + SourceAndText studentScorePojo= calculateTotalScoreRate(stuSQL, examMysqlKeywordList,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } + } + + } + + if (sql.trim().toUpperCase().startsWith("INSERT")) { + appendToFile(answerLogPath, "==================插入语句=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================插入语句=================="); + // 正则表达式匹配表名 + Pattern pattern = Pattern.compile("INSERT INTO\\s+`?(\\w+)`?\\s*\\("); + Matcher matcher = pattern.matcher(sql); + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + if (matcher.find()) { + String tableName = matcher.group(1).replace("`", ""); // 获取表名 + // 匹配成功 + String answerId= examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList = examMysqlKeywordMapper.selectListByAnswerId(answerId); + + + //把select语句 转化成验证语句sql + String yanzheng = convertInsertToSelect(sql); + + String stuSql=null; + List> answerList= new ArrayList<>(); + List> answerListStu = new ArrayList<>(); + try (Connection connanswer = DriverManager.getConnection(newDbUrl, user, password); + Statement stmtan = connanswer.createStatement()) { + try (ResultSet answer = stmtan.executeQuery(yanzheng)) { + answerList = getAnswerList(answer); +// appendToFile(answerLogPath, "查找语句标准答案"); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "查找语句标准答案"); +// printResult(answerList,judgementStr); + }catch (SQLException e) { + appendToFile(answerLogPath, "答案表执行验证语句"+yanzheng+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案表执行验证语句"+yanzheng+"时发生错误: " + e.getMessage()); + e.printStackTrace(); + } + } + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + try (ResultSet answer = stmtstu.executeQuery(yanzheng)) { + answerListStu = getAnswerList(answer); + //这里根据answerListStu ,还原学生的sql语句 + List columnNames = getColumnNames(answer); // ← 获取字段名 + + String tName = extractTableNameFromInsert(sql); + + //构建还原 SQL(你可以自定义表名,比如传 "student_table") + stuSql = generateInsertSQL(tName, columnNames, answerListStu); +// appendToFile(answerLogPath, "学生对"+tName+"插入的SQL:\n" + stuSql); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "学生对"+tName+"+插入的SQL:" ); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, stuSql); +// appendToFile(answerLogPath, "学生语句答案"); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "学生语句答案"); +// printResult(answerListStu,judgementStr); + } catch (SQLException e) { + appendToFile(answerLogPath, "学生表执行验证语句"+yanzheng+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "学生表执行验证语句"+yanzheng+"时发生错误: " + e.getMessage()); + } + } + boolean isEquivalent =false; + + if (answerListStu!=null&&answerListStu.size()>0){ + isEquivalent = compareResultsSelect(answerList, answerListStu); + } + + if (isEquivalent) { + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,stuSql,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } else { + SourceAndText studentScorePojo= calculateTotalScoreRate(stuSql, examMysqlKeywordList,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } + } + + } + if (sql.trim().toUpperCase().startsWith("DELETE")) { + appendToFile(answerLogPath, "==================删除语句=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================删除语句=================="); + + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + sql = sql.trim().replaceAll(";+\\s*$", ""); + // 正则提取表名和 WHERE 条件 + Pattern pattern = Pattern.compile("DELETE\\s+FROM\\s+(\\w+)\\s+WHERE\\s+(.+)", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(sql); + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList = examMysqlKeywordMapper.selectListByAnswerId(answerId); + + if (matcher.find()) { + String tableName = matcher.group(1).trim(); + String whereClause = matcher.group(2).trim(); + DeleteInfo deleteInfo = new DeleteInfo(tableName, whereClause); +// appendToFile(answerLogPath, "提取出的表名: " + deleteInfo.tableName); +// appendToFile(answerLogPath, "提取出的条件: " + deleteInfo.whereClause); + // 构造验证 SQL + String verifySql = "SELECT COUNT(*) FROM " + deleteInfo.tableName + " WHERE " + deleteInfo.whereClause; +// appendToFile(answerLogPath, "验证 SQL: " + verifySql); + //查找正确答案的 +// try (Connection conn = DriverManager.getConnection(newDbUrl, user, password); +// Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery(verifySql)) { + if (rs.next()) { + int count = rs.getInt(1); + if (count == 0) { + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + try (ResultSet rsstu = stmtstu.executeQuery(verifySql)) { + if (rsstu.next()) { + int countstu = rsstu.getInt(1); + if (countstu == 0) { + //累加删除语句的所有权值 examQuestionKeywords累加scorerate + appendToFile(answerLogPath, "验证通过:符合 DELETE 条件的记录已删除。"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "验证通过:符合 DELETE 条件的记录已删除。"); + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,sql,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } else { + SourceAndText studentScorePojo= calculateTotalScoreRate(null, examMysqlKeywordList,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } + } + }catch (SQLException e) { + appendToFile(answerLogPath, "验证学生库失败,"+"得分:0 ❌"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "验证学生库失败,"+"得分:0 ❌"); + } + + } + + + } else { + appendToFile(answerLogPath, "验证答案库失败:还有 " + count + " 条记录符合 DELETE 条件,未被删除。"+"得分:0 ❌"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "验证答案库失败:还有 " + count + "条记录符合 DELETE 条件,未被删除。得分:0 ❌"); + } + } + + } catch (SQLException e) { + appendToFile(answerLogPath, "执行验证 SQL 出错!"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行验证 SQL 出错!"); + e.printStackTrace(); + } + + } + + } + + if (sql.trim().toUpperCase().startsWith("UPDATE")) { + appendToFile(answerLogPath, "==================更新语句=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================更新语句=================="); + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList= examMysqlKeywordMapper.selectListByAnswerId(answerId); + //转换成select语句 + String selectSql = convertUpdateToSelectWhere(sql); + + List> answerList= new ArrayList<>(); + List> answerListStu = new ArrayList<>(); + try (Connection connanswer = DriverManager.getConnection(newDbUrl, user, password); + Statement stmtan = connanswer.createStatement()) { + try (ResultSet answer = stmtan.executeQuery(selectSql)) { + answerList = getAnswerList(answer); +// appendToFile(answerLogPath, "查找语句标准答案"); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "查找语句标准答案"); +// printResult(answerList,judgementStr); + }catch (SQLException e) { + appendToFile(answerLogPath, "答案表执行验证语句"+selectSql+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案表执行验证语句"+selectSql+"时发生错误: " + e.getMessage()); + e.printStackTrace(); + } + } + String stuSql =null; + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + try (ResultSet answer = stmtstu.executeQuery(selectSql)) { + answerListStu = getAnswerList(answer); + //还原 sql + ResultSetMetaData meta = answer.getMetaData(); + List columns = new ArrayList<>(); + for (int i = 1; i <= meta.getColumnCount(); i++) { + columns.add(meta.getColumnLabel(i)); + } + + stuSql = restoreUpdateFromResult(sql, columns, answerListStu); +// appendToFile(answerLogPath, "还原学生 UPDATE 语句:" + stuSql); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "还原学生 UPDATE 语句:" + stuSql); +// appendToFile(answerLogPath, "学生语句答案:"); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "学生语句答案:"); +// printResult(answerListStu,judgementStr); + } catch (SQLException e) { + appendToFile(answerLogPath, "学生表执行验证语句"+selectSql+"时发生错误:" + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "学生表执行验证语句"+selectSql+"时发生错误: " + e.getMessage()); + } + + + } + boolean isEquivalent =false; + + if (answerListStu!=null&&answerListStu.size()>0){ + isEquivalent = compareResultsSelect(answerList, answerListStu); + } + // + if (isEquivalent) { + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,stuSql,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } else { + SourceAndText studentScorePojo= calculateTotalScoreRate(stuSql, examMysqlKeywordList,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } + + } + if (sql.trim().toUpperCase().startsWith("SELECT")) { + appendToFile(answerLogPath, "==================查找语句=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================查找语句=================="); + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList= examMysqlKeywordMapper.selectListByAnswerId(answerId); + + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + + AtomicReference stuAnswer = new AtomicReference<>(); + + // 根据sql 的内容查询结果文件夹语句的文件名,再根据文件名 查找 考生 + try { + String finalSql = sql; + Files.walk(Paths.get(filePath)) + .filter(Files::isRegularFile) + .forEach(filePaths -> { + try { + String fileContent = Files.readString(filePaths, StandardCharsets.UTF_8); + String normalizedFileContent = fileContent.trim().replaceAll("\\s+", "").toLowerCase(); + String normalizedFinalSql = finalSql.trim().replaceAll("\\s+", "").toLowerCase(); + if (normalizedFinalSql.equals(normalizedFileContent)) { + + String stuPath=fileStu+"\\"+filePaths.getFileName(); + stuAnswer.set(readSQLFromFile(stuPath)); + System.out.println("考生语句"+stuAnswer); + } + } catch (Exception e) { + appendToFile(answerLogPath,"读取文件失败:" + filePaths); + e.printStackTrace(); + } + } + ); + } catch (IOException e) { + appendToFile(answerLogPath,"遍历目录出错!"); + e.printStackTrace(); + } + + List> answerList= new ArrayList<>(); + List> answerListStu = new ArrayList<>(); + try (Connection connanswer = DriverManager.getConnection(newDbUrl, user, password); + Statement stmtan = connanswer.createStatement()) { + try (ResultSet answer = stmtan.executeQuery(sql)) { + answerList = getAnswerList(answer); + appendToFile(answerLogPath, "运行标准答案语句的查询结果:"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "运行标准答案语句的查询结果:"); + judgementStr = printResult(answerList,judgementStr); + }catch (SQLException e) { + appendToFile(answerLogPath, "执行验证语句"+sql+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行验证语句"+sql+"时发生错误: " + e.getMessage()); + e.printStackTrace(); + } + } + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + if (StringUtils.isBlank(stuAnswer.get())) { + SourceAndText sourceAndTextError = new SourceAndText(); + appendToFile(answerLogPath, "❌考生SQL文件丢失或未作答,无法评分,得分:0.0"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌考生SQL文件丢失或未作答,无法评分,得分:0.0"); + sourceAndTextError.setText(judgementStr); + sourceAndTextError.setScore(0.0); + continue; + } + try (ResultSet answer = stmtstu.executeQuery(String.valueOf(stuAnswer))) { + answerListStu = getAnswerList(answer); + appendToFile(answerLogPath, "运行学生语句的查询结果:"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "运行学生语句的查询结果:"); + judgementStr= printResult(answerListStu,judgementStr); + } catch (SQLException e) { + appendToFile(answerLogPath, "执行学生库语句"+String.valueOf(stuAnswer)+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行学生库语句"+String.valueOf(stuAnswer)+"时发生错误: " + e.getMessage()); + } + + } + boolean isEquivalent =false; + + if (answerListStu!=null&&answerListStu.size()>0){ + isEquivalent = compareResultsSelect(answerList, answerListStu); + } + + if (isEquivalent) { + //todo 得分 + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,String.valueOf(stuAnswer),totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } else { + SourceAndText studentScorePojo= calculateTotalScoreRate(String.valueOf(stuAnswer), examMysqlKeywordList,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } + + } + + + if (sql.trim().replaceAll("\\s+", " ").matches("(?i)^CREATE( OR REPLACE)?( ALGORITHM\\s*=\\s*\\w+)?( DEFINER\\s*=\\s*[`\"'\\w@%\\.]+)?( SQL SECURITY \\w+)? VIEW\\b.*")) { + appendToFile(answerLogPath, "==================视图语句=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================视图语句=================="); + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList= examMysqlKeywordMapper.selectListByAnswerId(answerId); + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + + // 正则表达式,用于匹配 "VIEW" 后面的视图名称 + String regex = "(?<=VIEW\\s)([\\w`_]+)"; + String sqlviewAnswer = removeComments(sql); + String viewNam1 = extractViewName(sqlviewAnswer, regex); + + String showCreateViewSql = "SHOW CREATE VIEW " + viewNam1; + + // 执行查询,获取答案 视图的结果 + appendToFile(answerLogPath, "执行正确答案视图语句的查询结果:"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行正确答案视图语句的查询结果:"); + String sql3 = "SELECT * FROM " + viewNam1; + List> result1 = executeQuery(stmt, sql3); + judgementStr= printResult(result1,judgementStr); + + List> result2 =new ArrayList<>(); + String stuSQL =null; + boolean hasError = false; // 添加标志变量 + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + + try (ResultSet rs = stmtstu.executeQuery(showCreateViewSql)) { + if (rs.next()) { + String createViewSqlExport = rs.getString("Create View"); + // 替换 result 中的值 + stuSQL=createViewSqlExport; + } + }catch (SQLException e) { + appendToFile(answerLogPath, "执行学生库语句"+showCreateViewSql+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行学生库语句"+showCreateViewSql+"时发生错误: " + e.getMessage()); + hasError = true; // 发生异常,设为 true + } + + // 执行查询,获取考生 视图的结果 + appendToFile(answerLogPath, "执行考生视图语句的查询结果:"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行考生视图语句的查询结果:"); + + try { + result2 = executeQuery(stmtstu, sql3); + + } catch (SQLException e) { + appendToFile(answerLogPath, "执行学生库语句"+sql3+"时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行学生库语句"+sql3+"时发生错误: " + e.getMessage()); + hasError = true; // 发生异常,设为 true + } + + + judgementStr= printResult(result2,judgementStr); + } + + // 比较两个视图的结果 + boolean isEquivalent =!hasError &&compareResults(result1, result2); + appendToFile(answerLogPath, "\n是否结果等价: " + isEquivalent); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "\n是否结果等价: " + isEquivalent); + + + if (isEquivalent) { + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,stuSQL,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } else { + SourceAndText studentScorePojo= calculateTotalScoreRate(stuSQL, examMysqlKeywordList,totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } +// } + + } + + //存储过程 + if (sql.trim().toUpperCase().toUpperCase().contains("PROCEDURE")) { + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList= examMysqlKeywordMapper.selectListByAnswerId(answerId); + + appendToFile(answerLogPath, "==================储存过程=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================储存过程=================="); + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + + + AtomicReference stuAnswer = new AtomicReference<>(); + + // 根据sql 的内容查询结果文件夹语句的文件名,再根据文件名 查找 考生 + try { + String finalSql = sql; + Files.walk(Paths.get(filePath)) + .filter(Files::isRegularFile) + .forEach(filePaths -> { + try { + String fileContent = Files.readString(filePaths, StandardCharsets.UTF_8); + String normalizedFileContent = fileContent.trim().replaceAll("\\s+", "").toLowerCase(); + String normalizedFinalSql = finalSql.trim().replaceAll("\\s+", "").toLowerCase(); + if (normalizedFinalSql.equals(normalizedFileContent)) { + + String stuPath=fileStu+"\\"+filePaths.getFileName(); + stuAnswer.set(readSQLFromFile(stuPath)); + System.out.println("考生语句"+stuAnswer); + } + } catch (Exception e) { + appendToFile(answerLogPath,"读取文件失败:" + filePaths); + e.printStackTrace(); + } + } + + + ); + } catch (IOException e) { + appendToFile(answerLogPath,"遍历目录出错!"); + e.printStackTrace(); + } + + //提取call语句 + List extractCallStatements = extractCallStatements(sql); + //标准答案 集合 + List> anwerResults = new ArrayList<>(); + //考生答案 集合 + List> stuResults = new ArrayList<>(); + if (extractCallStatements != null && extractCallStatements.size() > 0) { + boolean hasError = false; // 添加标志变量 + for (String extractCallStatement : extractCallStatements) { + appendToFile(answerLogPath, "测试 call 语句:" + extractCallStatement); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "测试 call 语句:" + extractCallStatement); + + try { + + ResultSet newResult = stmt.executeQuery(extractCallStatement); + anwerResults.addAll(extractResults(newResult)); + } catch (SQLException e) { + appendToFile(answerLogPath, "执行标准库 SQL CALL 语句时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行标准库 SQL CALL 语句时发生错误: " + e.getMessage()); + } + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + if (StringUtils.isBlank(stuAnswer.get())) { + SourceAndText sourceAndTextError = new SourceAndText(); + appendToFile(answerLogPath, "❌考生SQL文件丢失或未作答,无法评分,得分:0.0"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌考生SQL文件丢失或未作答,无法评分,得分:0.0"); + sourceAndTextError.setText(judgementStr); + sourceAndTextError.setScore(0.0); + continue; + } + + + try { + ResultSet oldResult = stmtstu.executeQuery(extractCallStatement); + stuResults.addAll(extractResults(oldResult)); + } catch (SQLException e) { + appendToFile(answerLogPath, "执行学生库 SQL CALL 语句时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行学生库 SQL CALL 语句时发生错误: " + e.getMessage()); + hasError = true; // 发生异常,设为 true + } + } + + } + // 比较结果(如果发生异常,可以认为比较失败) + MysqlBooleanVo mysqlBooleanVo = compareExtractResults(anwerResults, stuResults,judgementStr); + judgementStr=mysqlBooleanVo.getText(); + boolean flag= !hasError &&mysqlBooleanVo.isFlag(); + + if (flag) { + //todo 得分 + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList,total,answerLogPath,stuAnswer.get(),totalKeyScore,score,answerId,scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } else { + //得分 + SourceAndText studentScorePojo = calculateTotalScoreRate(stuAnswer.get(), examMysqlKeywordList, totalKeyScore, score, answerId, scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } + } else { + appendToFile(answerLogPath, "此存储过程无 CALL 语句"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "此存储过程无 CALL 语句"); + } + } + if (sql.trim().toUpperCase().toUpperCase().contains("TRIGGER")) { + appendToFile(answerLogPath, "==================触发器=================="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==================触发器=================="); + // 匹配成功 + String answerId = examQuestionAnswer.getAnswerId(); + List examMysqlKeywordList= examMysqlKeywordMapper.selectListByAnswerId(answerId); + + appendToFile(answerLogPath, "答案语句: " + sql); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "答案语句: "); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sql); + + + AtomicReference stuAnswer = new AtomicReference<>(); + + // 根据sql 的内容查询结果文件夹语句的文件名,再根据文件名 查找 考生 + try { + String finalSql = sql; + Files.walk(Paths.get(filePath)) + .filter(Files::isRegularFile) + .forEach(filePaths -> { + try { + String fileContent = Files.readString(filePaths, StandardCharsets.UTF_8); + String normalizedFileContent = fileContent.trim().replaceAll("\\s+", "").toLowerCase(); + String normalizedFinalSql = finalSql.trim().replaceAll("\\s+", "").toLowerCase(); + if (normalizedFinalSql.equals(normalizedFileContent)) { + + String stuPath=fileStu+"\\"+filePaths.getFileName(); + stuAnswer.set(readSQLFromFile(stuPath)); + System.out.println("考生语句"+stuAnswer); + } + } catch (Exception e) { + appendToFile(answerLogPath,"读取文件失败:" + filePaths); + e.printStackTrace(); + } + } + + + ); + } catch (IOException e) { + appendToFile(answerLogPath,"遍历目录出错!"); + e.printStackTrace(); + } + + + String regex = "(?i)(?<=CREATE\\s+TRIGGER\\s)([`\\w_]+)"; + String cleanSql = removeComments(sql); + String triggerName = extractViewName(cleanSql, regex); + String stuSQL =null; + String answerSQL =null; + boolean hasError = false; // 添加标志变量 + if (triggerName != null && !triggerName.isEmpty()) { + // 去除反引号 + triggerName = triggerName.replace("`", ""); + String showCreateTriggerSql = "SHOW CREATE TRIGGER " + triggerName + ";"; + + + try (ResultSet rs = stmt.executeQuery(showCreateTriggerSql)) { + if (rs.next()) { + String createTriggerSqlExport = rs.getString("SQL Original Statement"); + answerSQL = createTriggerSqlExport; + } + } catch (SQLException e) { + appendToFile(answerLogPath, "执行答案语句" + showCreateTriggerSql + "时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行答案语句" + showCreateTriggerSql + "时发生错误: " + e.getMessage()); + } + + + + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + if (StringUtils.isBlank(stuAnswer.get())) { + SourceAndText sourceAndTextError = new SourceAndText(); + appendToFile(answerLogPath, "❌考生SQL文件丢失或未作答,无法评分,得分:0.0"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌考生SQL文件丢失或未作答,无法评分,得分:0.0"); + sourceAndTextError.setText(judgementStr); + sourceAndTextError.setScore(0.0); + continue; + } + try (ResultSet rs = stmtstu.executeQuery(showCreateTriggerSql)) { + if (rs.next()) { + String createTriggerSqlExport = rs.getString("SQL Original Statement"); + stuSQL = createTriggerSqlExport; + } + } catch (SQLException e) { + appendToFile(answerLogPath, "执行学生库语句" + showCreateTriggerSql + "时发生错误: " + e.getMessage()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "执行学生库语句" + showCreateTriggerSql + "时发生错误: " + e.getMessage()); + hasError = true; // 发生异常,设为 true + } + } + + + } else { + appendToFile(answerLogPath, "⚠ 无法提取触发器名"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "⚠ 无法提取触发器名"); + } + + + + + if (!hasError){ + + // 清洗触发器语句(去除注释等) + String triggerStatementCleanSql1 = cleanProcedureSQL(answerSQL); + + // 标准化触发器内容(去空格、换行、小写) + String normalizedTriggerSql1 = triggerStatementCleanSql1.trim().replaceAll("\\s+", "").toLowerCase(); + + + // 清洗触发器语句(去除注释等) + String triggerStatementCleanSql2 = cleanProcedureSQL(stuSQL); + + // 标准化触发器内容(去空格、换行、小写) + String normalizedTriggerSql2 = triggerStatementCleanSql2.trim().replaceAll("\\s+", "").toLowerCase(); + + boolean equals = normalizedTriggerSql1.equals(normalizedTriggerSql2); + if (equals) { + //todo 得分 + SourceAndText studentScorePojo = accumulateScoreAndLog(examMysqlKeywordList, total, answerLogPath, stuAnswer.get(), totalKeyScore, score, answerId, scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + + } + else { + SourceAndText studentScorePojo = calculateTotalScoreRate(stuAnswer.get(), examMysqlKeywordList, totalKeyScore, score, answerId, scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } + } + + else { + SourceAndText studentScorePojo = calculateTotalScoreRate(stuAnswer.get(), examMysqlKeywordList, totalKeyScore, score, answerId, scoreTotal,judgementStr); + scoreTotal += studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + } + } + } + + //删除临时创建的数据库databaseName + String dropDbSql = "DROP DATABASE " + databaseName; + stmt.executeUpdate(dropDbSql); + + //todo 删除学生答题的数据库 单独写一个接口 +// String dropDbSql2 = "DROP DATABASE " + stuDataName; +// stmt.executeUpdate(dropDbSql2); + + + } + } + appendToFile(answerLogPath, "共得分:" + String.format("%.2f", scoreTotal)); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "共得分:" + String.format("%.2f", scoreTotal)); + folderzip.delete(); + deleteFolder(folder); + //todo 删除学生答题的数据库连接 单独写一个接口 +// deleteRegistryKey(); + sourceAndText.setScore(scoreTotal); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + + @Override + public void delMysqlConnect(List tNames) { + String stuDbUrl = "jdbc:mysql://localhost:6033/mysql?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8"; + String user = "root"; // 建议从配置中获取 + String password = ""; // 建议从配置中获取 + + try (Connection connstu = DriverManager.getConnection(stuDbUrl, user, password); + Statement stmtstu = connstu.createStatement()) { + + for (String tName : tNames) { + // 校验数据库名格式,防止 SQL 注入 + if (!tName.matches("^[a-zA-Z0-9_]+$")) { + System.err.println("非法数据库名,跳过: " + tName); + continue; + } + + // 检查数据库是否存在 + String checkSql = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '" + tName + "'"; + try (ResultSet rs = stmtstu.executeQuery(checkSql)) { + if (rs.next()) { + String dropDbSql = "DROP DATABASE `" + tName + "`"; + stmtstu.executeUpdate(dropDbSql); + System.out.println("已删除数据库:" + tName); + } else { + System.out.println("数据库不存在,跳过:" + tName); + } + } + } + + } catch (SQLException e) { + e.printStackTrace(); + throw new RuntimeException("数据库操作失败", e); + } finally { + try { + deleteRegistryKey(); + } catch (Exception ex) { + System.err.println("删除注册表失败:" + ex.getMessage()); + } + } + } + + + private static String compareTables(Set> standardSet, Set> studentSet, String dbTable, String tableName,String tableNameStu,String judgementStr) { + int index = 1; + // 判断表名是否一致 + String tableNameCheck = tableName.equalsIgnoreCase(tableNameStu) ? "✔" : "x"; + // 输出 + System.out.printf("%02d.【数据表】【%s】【名称】【%s】【%s】\n", index, dbTable, tableName, tableNameCheck); + appendToFile(answerLogPath, "%02d.【数据表】【%s】【名称】【%s】【%s】\n", index, dbTable, tableName, tableNameCheck); + judgementStr = HtmlAppender.appendHtmlLineMysql(judgementStr, "%02d.【数据表】【%s】【名称】【%s】【%s】\n", index, dbTable, tableName, tableNameCheck); + // 把Set转成Map,方便通过字段名快速取值 + Map> standardMap = convertSetToMap(standardSet); + Map> studentMap = convertSetToMap(studentSet); + + for (String columnName : standardMap.keySet()) { + Map stdCol = standardMap.get(columnName); + Map stuCol = studentMap.get(columnName); + + String fullName = dbTable + "." + columnName; + + String nameCheck = stuCol != null ? "✔" : "x"; + System.out.printf("%02d.【字段】【%s】【名称】【%s】【%s】\n", ++index, fullName, columnName, nameCheck); + judgementStr = HtmlAppender.appendHtmlLineMysql(judgementStr, "%02d.【字段】【%s】【名称】【%s】【%s】\n", index, fullName, columnName, nameCheck); + appendToFile(answerLogPath, "%02d.【字段】【%s】【名称】【%s】【%s】\n", index, fullName, columnName, nameCheck); + + if (stuCol != null) { + MysqlVo mysqlVo1 = compareField(index, fullName, "类型", stdCol.get("COLUMN_TYPE"), stuCol.get("COLUMN_TYPE"),judgementStr); + index=mysqlVo1.getIndex(); + judgementStr=mysqlVo1.getText(); + MysqlVo mysqlVo2 = compareField(index, fullName, "允许为空", stdCol.get("IS_NULLABLE"), stuCol.get("IS_NULLABLE"),judgementStr); + index=mysqlVo2.getIndex(); + judgementStr=mysqlVo2.getText(); + MysqlVo mysqlVo3 = compareField(index, fullName, "扩展", stdCol.get("EXTRA"), stuCol.get("EXTRA"),judgementStr); + index=mysqlVo3.getIndex(); + judgementStr=mysqlVo3.getText(); + MysqlVo mysqlVo4 = compareField(index, fullName, "键类型", stdCol.get("COLUMN_KEY"), stuCol.get("COLUMN_KEY"),judgementStr); + index=mysqlVo4.getIndex(); + judgementStr=mysqlVo4.getText(); + } else { + // 缺失字段,直接输出所有属性错误 + MysqlVo mysqlVo1 = printMissing(index, fullName, "类型", stdCol.get("COLUMN_TYPE"),judgementStr); + index=mysqlVo1.getIndex(); + judgementStr=mysqlVo1.getText(); + MysqlVo mysqlVo2 = printMissing(index, fullName, "允许为空", stdCol.get("IS_NULLABLE"),judgementStr); + index=mysqlVo2.getIndex(); + judgementStr=mysqlVo2.getText(); + MysqlVo mysqlVo3 = printMissing(index, fullName, "扩展", stdCol.get("EXTRA"),judgementStr); + index=mysqlVo3.getIndex(); + judgementStr=mysqlVo3.getText(); + MysqlVo mysqlVo4 = printMissing(index, fullName, "键类型", stdCol.get("COLUMN_KEY"),judgementStr); + index=mysqlVo4.getIndex(); + judgementStr=mysqlVo4.getText(); + } + } + return judgementStr; + } + + private static MysqlVo compareField(int index, String fullName, String property, String stdValue, String stuValue, String judgementStr) { + MysqlVo mysqlVo=new MysqlVo(); + String mark = stdValue.equalsIgnoreCase(stuValue) ? "✔" : "x"; + System.out.printf("%02d.【字段】【%s】【%s】【%s】【%s】\n", index + 1, fullName, property, stuValue, mark); + appendToFile(answerLogPath, "%02d.【字段】【%s】【%s】【%s】【%s】\n", index + 1, fullName, property, stuValue, mark); + judgementStr = HtmlAppender.appendHtmlLineMysql(judgementStr, "%02d.【字段】【%s】【%s】【%s】【%s】\n", index + 1, fullName, property, stuValue, mark); + mysqlVo.setText(judgementStr); + mysqlVo.setIndex(index + 1); + return mysqlVo; + } + + private static MysqlVo printMissing(int index, String fullName, String property, String stdValue,String judgementStr) { + MysqlVo mysqlVo=new MysqlVo(); + System.out.printf("%02d.【字段】【%s】【%s】【%s】【x】\n", index + 1, fullName, property, stdValue); + appendToFile(answerLogPath, "%02d.【字段】【%s】【%s】【%s】【x】\n", index + 1, fullName, property, stdValue); + judgementStr = HtmlAppender.appendHtmlLineMysql(judgementStr, "%02d.【字段】【%s】【%s】【%s】【x】\n", index + 1, fullName, property, stdValue); + mysqlVo.setText(judgementStr); + mysqlVo.setIndex(index + 1); + return mysqlVo; + } + + private static Map> convertSetToMap(Set> set) { + Map> map = new LinkedHashMap<>(); + for (Map column : set) { + map.put(column.get("COLUMN_NAME"), column); + } + return map; + } + public static void deleteFolder(File folder) { + if (folder.isDirectory()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + deleteFolder(file); // 递归删除所有子文件/文件夹 + } + } + } + folder.delete(); // 删除空文件夹或文件 + } + //如果这个小题对了,直接累加对应的权值分 + private SourceAndText accumulateScoreAndLog(List examMysqlKeywordList, AtomicInteger total, String answerLogPath, String sql2, String totalKeyScore, double score,String answerId,double scoreTotal,String judgementStr) { + SourceAndText sourceAndText = new SourceAndText(); + if(StringUtils.isBlank(answerId)){ + appendToFile(answerLogPath, "❌该语句找不到试题id,请检查出题内容!" ); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌该语句找不到试题id,请检查出题内容!"); + sourceAndText.setText(judgementStr); + sourceAndText.setScore(0.0); + //返回累加的总分 + return sourceAndText; + } + //用answerid查对应答案的权值 。除以总权值 + String scoreRateStr= examQuestionAnswerMapper.selectExamQuestionAnswerScoreByAnswerId(answerId); + + // 解析权值 + double scoreRate = 0.0; + double totalKey = 0.0; + double singleScore = 0.0; + try { + scoreRate = Double.parseDouble(scoreRateStr); + totalKey = Double.parseDouble(totalKeyScore); + // 计算该答案对应的得分 + + if (totalKey > 0) { + singleScore = (scoreRate / totalKey) * score; + singleScore = Math.round(singleScore * 100.0) / 100.0; + } + + String[] lines = sql2.split("\\r?\\n"); + + // 开头:✅学生语句: + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅学生语句:"); + // 第一行直接加在后面 + if (lines.length > 0) { + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, StringEscapeUtils.escapeHtml4(lines[0])); + } + + // 后续行添加换行和缩进 + for (int i = 1; i < lines.length; i++) { + judgementStr = HtmlAppender.appendHtmlLine(judgementStr," "+ StringEscapeUtils.escapeHtml4(lines[i].trim())); + } + // 最终加入 judgementStr + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,",正确,语句得分权值:"+scoreRateStr + ",得分" + singleScore); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅学生语句" + sql2 + "正确,语句得分权值:" + scoreRateStr + ",得分" + singleScore); + appendToFile(answerLogPath, "✅学生语句" + sql2 + "正确,语句得分权值:" + scoreRateStr + ",得分" + singleScore); + } catch (NumberFormatException e) { + System.err.println("无效的totalKeyScore值:" + totalKeyScore); + } + sourceAndText.setText(judgementStr); + sourceAndText.setScore(singleScore); + //返回累加的总分 + return sourceAndText; + + } + + + + + public static String convertUpdateToSelectWhere(String updateSQL) { + // 清理语句 + updateSQL = updateSQL.trim().replaceAll(";\\s*$", ""); + + // 正则提取表名和 WHERE 条件 + Pattern pattern = Pattern.compile( + "UPDATE\\s+(\\w+)\\s+SET\\s+.+?\\s+WHERE\\s+(.+)", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL + ); + Matcher matcher = pattern.matcher(updateSQL); + if (matcher.find()) { + String tableName = matcher.group(1).trim(); + String whereClause = matcher.group(2).trim(); + return "SELECT * FROM " + tableName + " WHERE " + whereClause + ";"; + } else { + return "-- 无法识别的 UPDATE 语句"; + } + } + + public static String extractTableNameFromInsert(String sql) { + Pattern pattern = Pattern.compile("INSERT\\s+INTO\\s+(\\w+)\\s*\\(", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(sql); + if (matcher.find()) { + return matcher.group(1); + } else { + return "unknown_table"; + } + } + + + private List getColumnNames(ResultSet rs) throws SQLException { + List columnNames = new ArrayList<>(); + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount(); + for (int i = 1; i <= columnCount; i++) { + columnNames.add(metaData.getColumnName(i)); + } + return columnNames; + } + + public static String generateInsertSQL(String tableName, List columns, List> rows) { + StringBuilder sb = new StringBuilder(); + sb.append("INSERT INTO ").append(tableName).append(" ("); + sb.append(String.join(", ", columns)); + sb.append(") VALUES\n"); + + List valueLines = new ArrayList<>(); + for (List row : rows) { + List formattedValues = new ArrayList<>(); + for (String value : row) { + if (value == null) { + formattedValues.add("NULL"); + } else if (value.matches("-?\\d+(\\.\\d+)?")) { + formattedValues.add(value); // 数字不加引号 + } else { + formattedValues.add("'" + value.replace("'", "''") + "'"); // 字符串加引号并转义 + } + } + valueLines.add("(" + String.join(", ", formattedValues) + ")"); + } + + sb.append(String.join(",\n", valueLines)).append(";"); + return sb.toString(); + } + + public SourceAndText calculateTotalScoreRate(String sql, List examQuestionKeywords, String totalKeyScore, double score,String answerId,double scoreTotal,String judgementStr) { + SourceAndText sourceAndText = new SourceAndText(); + if(StringUtils.isBlank(answerId) ||StringUtils.isBlank(sql)){ + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌该语句未作答,得分:0.0"); + appendToFile(answerLogPath, "❌该语句未作答,得分:0.0"); + sourceAndText.setText(judgementStr); + sourceAndText.setScore(0.0); + //返回累加的总分 + return sourceAndText; + } + //用answerid查对应答案的权值 。除以总权值 + String scoreRateStr= examQuestionAnswerMapper.selectExamQuestionAnswerScoreByAnswerId(answerId); + + + + // 解析权值 + double scoreRate = 0.0; + double totalKey = 0.0; + // 计算该答案对应的得分 + double singleScore = 0.0; + try { + scoreRate = Double.parseDouble(scoreRateStr); + totalKey = Double.parseDouble(totalKeyScore); + + if (totalKey > 0) { + singleScore = (scoreRate / totalKey) * score; + singleScore = Math.round(singleScore * 100.0) / 100.0; + } + + + + } catch (NumberFormatException e) { + System.err.println("无效的totalKeyScore值:" + totalKeyScore); + } + + + int totalScoreRate = 0; + Set matchedKeywords = new HashSet<>(); + for (ExamMysqlKeyword keyword : examQuestionKeywords) { + String keywordValue = keyword.getKeyword(); + if (keywordValue != null && !keywordValue.isEmpty() && !matchedKeywords.contains(keywordValue)) { + // 使用正则,确保是完整单词(字段)匹配 + String regex = keywordValue; + if (sql.contains(regex)) { + try { + totalScoreRate += Integer.parseInt(keyword.getScoreRate()); + + matchedKeywords.add(keywordValue); + } catch (NumberFormatException e) { + System.err.println("Invalid scoreRate format for keyword: " + keywordValue); + } + } + } + } + //累加答案关键字的所有权值 + int totalKeyScoreInt = examQuestionKeywords.stream() + .map(ExamMysqlKeyword::getScoreRate) + .filter(s -> s != null && !s.isEmpty()) + .mapToInt(Integer::parseInt) + .sum(); + + //乘以 对了多少个 关键字 权值 + double finalRoundedScore; + if (totalKeyScoreInt == 0) { + // 如果总键值为0,可以设置为0或其他默认值 + finalRoundedScore = 0.0; + } else { + finalRoundedScore = new BigDecimal( + ((double) totalScoreRate / totalKeyScoreInt) * singleScore + ).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + String[] lines = sql.split("\\r?\\n"); + + // 开头:✅学生语句: + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌学生语句:"); + // 第一行直接加在后面 + if (lines.length > 0) { + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, StringEscapeUtils.escapeHtml4(lines[0])); + } + + // 后续行添加换行和缩进 + for (int i = 1; i < lines.length; i++) { + judgementStr = HtmlAppender.appendHtmlLine(judgementStr," "+StringEscapeUtils.escapeHtml4(lines[i].trim())); + } + // 最终加入 judgementStr + judgementStr = HtmlAppender.appendHtmlLine(judgementStr,",不正确,语句权值:"+scoreRateStr+",关键权值:"+totalKeyScoreInt+",答对得分点为:"+matchedKeywords+",答对关键得分权值"+ totalScoreRate+ ",得分" + finalRoundedScore); +// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌语句"+sql+"不正确,语句权值:"+scoreRateStr+",关键权值:"+totalKeyScoreInt+",答对得分点为:"+matchedKeywords+",答对关键得分权值"+ totalScoreRate+ ",得分" + finalRoundedScore); + + appendToFile(answerLogPath,"❌语句"+sql+"不正确,语句权值:"+scoreRateStr+",关键权值:"+totalKeyScoreInt+",答对得分点为:"+matchedKeywords+",答对关键得分权值"+ totalScoreRate+ ",得分" + finalRoundedScore); + sourceAndText.setText(judgementStr); + sourceAndText.setScore(finalRoundedScore); + //返回累加的总分 + return sourceAndText; + } + + // 预处理函数:去除空格和换行并转为大写 + private static String normalize(String str) { + return str.replaceAll("\\s+", "").toUpperCase(); + } + + + // 删除 数据库连接 + public static void deleteRegistryKey() { + try { + String sid = getCurrentUserSid(); + System.out.println("检测到 SID: " + sid); + + String regKey = "HKEY_USERS\\" + sid + "\\Software\\PremiumSoft\\Navicat\\Servers\\答题专用"; + System.out.println("将要删除注册表路径:" + regKey); + + String[] cmd = {"reg", "delete", regKey, "/f"}; + Process process = Runtime.getRuntime().exec(cmd); + + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "GBK")); + String errorLine; + while ((errorLine = errorReader.readLine()) != null) { + System.err.println("删除错误信息: " + errorLine); + } + + int exitCode = process.waitFor(); + if (exitCode == 0) { + System.out.println("✅ 注册表项删除成功"); + } else { + System.out.println("❌ 注册表项删除失败,退出码:" + exitCode); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public static String getCurrentUserSid() throws Exception { + Process sidProcess = Runtime.getRuntime().exec(new String[]{"whoami", "/user"}); + BufferedReader reader = new BufferedReader(new InputStreamReader(sidProcess.getInputStream(), "GBK")); + + String sid = null; + String line; + boolean dataSectionStarted = false; + + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) { + continue; // 跳过空行 + } + // 过滤掉标题行,找到数据开始行 + if (!dataSectionStarted) { + // 找到分割线(如 ====== 这类)之后数据才开始 + if (line.matches("=+\\s*=+")) { + dataSectionStarted = true; + } + continue; + } + // 一旦进入数据区,第一行就是用户信息 + // 该行格式:用户名(含反斜杠) + 空格 + SID + // 取最后一个空格后面的字符串作为SID + String[] parts = line.split("\\s+"); + if (parts.length < 2) { + throw new RuntimeException("解析SID失败,行格式异常:" + line); + } + sid = parts[parts.length - 1]; + break; + } + reader.close(); + + int exitCode = sidProcess.waitFor(); + if (exitCode != 0) { + throw new RuntimeException("执行 whoami /user 命令失败,退出码:" + exitCode); + } + if (sid == null) { + throw new RuntimeException("未能获取到当前用户 SID"); + } + return sid; + } + + + + public static String cleanProcedureSQL(String sql) { + // 删除 DELIMITER 和 $$ 标记 + sql = sql.replaceAll("(?i)DELIMITER\\s+\\$\\$", ""); // 删除 DELIMITER $$ + sql = sql.replaceAll("(?i)DELIMITER\\s+;", ""); // 删除 DELIMITER ; + sql = sql.replaceAll("\\$\\$", ""); // 删除结尾 $$ + + // 删除 CALL 语句(例如 CALL pro_xxx(...);) + sql = sql.replaceAll("(?i)CALL\\s+[^;]+;", ""); // 删除 CALL 语句(不区分大小写) + + + // 删除 -- 行内注释(从 -- 到行尾) + sql = sql.replaceAll("(?m)--.*?$", ""); // (?m) 多行模式,$ 表示行尾 + + // 删除 /**/ 多行注释 + sql = sql.replaceAll("(?s)/\\*.*?\\*/", ""); // (?s) 单行模式,.*? 非贪婪匹配 + + // 去掉多余空白字符 + return sql.trim(); + } + public static class DeleteInfo { + public String tableName; + public String whereClause; + + public DeleteInfo(String tableName, String whereClause) { + this.tableName = tableName; + this.whereClause = whereClause; + } + } + + + + + // 比较两个结果集 + private static MysqlBooleanVo compareExtractResults(List> newResults, List> oldResults, String judgementStr) { + MysqlBooleanVo mysqlBooleanVo=new MysqlBooleanVo(); + appendToFile(answerLogPath,"==== 标准答案 ===="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==== 标准答案 ===="); + for (Map row : newResults) { + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, row.toString()); + appendToFile(answerLogPath,row.toString()); + } + appendToFile(answerLogPath,"==== 考生答案 ===="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "==== 考生答案 ===="); + for (Map row : oldResults) { + appendToFile(answerLogPath,row.toString()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, row.toString()); + } + + if (newResults.size() != oldResults.size()) { + appendToFile(answerLogPath,"❌考生答案与标准答案个数不对"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌考生答案与标准答案个数不对"); + mysqlBooleanVo.setText(judgementStr); + mysqlBooleanVo.setFlag(false); + return mysqlBooleanVo; + } + + // 不比较顺序,直接判断两个列表内容是否一样(将其转换为 Set) + Set> newSet = new HashSet<>(newResults); + Set> oldSet = new HashSet<>(oldResults); + + if (!newSet.equals(oldSet)) { + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 内容相同但顺序不同,或存在差异:"); + appendToFile(answerLogPath,"❌ 内容相同但顺序不同,或存在差异:"); + Set> onlyInNew = new HashSet<>(newSet); + onlyInNew.removeAll(oldSet); + + Set> onlyInOld = new HashSet<>(oldSet); + onlyInOld.removeAll(newSet); + + for (Map row : onlyInNew) { + appendToFile(answerLogPath,"⚠️ 标准答案中有但考生答案中没有: " + row); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "⚠️ 标准答案中有但考生答案中没有: " + row); + } + + for (Map row : onlyInOld) { + appendToFile(answerLogPath,"⚠️ 考生答案中有但标准答案中没有: " + row); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "⚠️ 考生答案中有但标准答案中没有: " + row); + } + mysqlBooleanVo.setText(judgementStr); + mysqlBooleanVo.setFlag(false); + return mysqlBooleanVo; + } + appendToFile(answerLogPath,"两个结果集内容一致(不考虑顺序)!"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "两个结果集内容一致(不考虑顺序)!"); + mysqlBooleanVo.setText(judgementStr); + mysqlBooleanVo.setFlag(true); + return mysqlBooleanVo; + } + + + // 提取ResultSet中的结果到一个列表中 + private static List> extractResults(ResultSet resultSet) throws SQLException { + List> results = new ArrayList<>(); + + // 获取列数 + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + + while (resultSet.next()) { + Map row = new HashMap<>(); + for (int i = 1; i <= columnCount; i++) { + String columnName = metaData.getColumnLabel(i); + Object value = resultSet.getObject(i); + row.put(columnName, value); + } + results.add(row); + } + + return results; + } + + + public static String restoreUpdateFromResult( + String originalUpdateSql, + List columns, + List> answerListStu + ) { + + System.out.println("========================================================= " ); + System.out.println("originalUpdateSql = " + originalUpdateSql); + System.out.println("columns = " + columns); + System.out.println("answerListStu = " + answerListStu); + System.out.println("========================================================= " ); + + + Pattern p = Pattern.compile("UPDATE\\s+(\\w+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+);?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + Matcher m = p.matcher(originalUpdateSql); + if (!m.find()) { + throw new IllegalArgumentException("无法解析原始 UPDATE 语句"); + } + String tableName = m.group(1); + String wherePart = m.group(3); + + if (answerListStu.isEmpty()) { + throw new IllegalArgumentException("查询结果为空,无法还原"); + } + + List firstRow = answerListStu.get(0); + List answerRow = answerListStu.get(1); + List setClauses = new ArrayList<>(); + + for (int i = 0; i < columns.size(); i++) { + String col = columns.get(i); + String val = answerRow.get(i); + + String valStr; + if (val == null || val.equalsIgnoreCase("null")) { + valStr = "NULL"; + } else if (val.matches("-?\\d+(\\.\\d+)?")) { + valStr = val; + } else { + valStr = "'" + val.replace("'", "''") + "'"; + } + setClauses.add(col + " = " + valStr); + } + + String setPart = String.join(", ", setClauses); + return "UPDATE " + tableName + " SET " + setPart + " WHERE " + wherePart ; + } + + public static List extractCallStatements(String sql) { + List callStatements = new ArrayList<>(); + Pattern callPattern = Pattern.compile("CALL\\s+\\w+\\s*\\([^;]*?\\);", Pattern.CASE_INSENSITIVE); + Matcher matcher = callPattern.matcher(sql); + + while (matcher.find()) { + callStatements.add(matcher.group()); + } + + return callStatements; + } + public static String convertInsertToSelect(String insertSql) { + // 匹配表名和插入值 + Pattern pattern = Pattern.compile("INSERT INTO\\s+`?(\\w+)`?\\s*\\(.*?\\)\\s*VALUES\\s*(.+);?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + Matcher matcher = pattern.matcher(insertSql); + + if (!matcher.find()) { + throw new IllegalArgumentException("无法解析 INSERT 语句"); + } + + String tableName = matcher.group(1); + String valuesPart = matcher.group(2); + + // 匹配所有 ('xxx', ...) 或者 ('xxx') + Pattern valPattern = Pattern.compile("\\((.*?)\\)"); + Matcher valMatcher = valPattern.matcher(valuesPart); + + Set values = new LinkedHashSet<>(); + while (valMatcher.find()) { + String valueStr = valMatcher.group(1).trim(); + // 如果是单字段插入,可能只有一个值 + if (!valueStr.contains(",")) { + values.add(valueStr.replaceAll("^'|'$", "").replace("'", "''")); + } + } + + if (values.isEmpty()) { + throw new IllegalArgumentException("未提取到插入值"); + } + + // 拼接验证 SQL + StringBuilder selectBuilder = new StringBuilder("SELECT * FROM "); + selectBuilder.append(tableName).append(" WHERE teamName IN ("); + int i = 0; + for (String val : values) { + if (i++ > 0) selectBuilder.append(", "); + selectBuilder.append("'").append(val).append("'"); + } + selectBuilder.append(")"); + + return selectBuilder.toString(); + } + + + /** + * 比较两个查询的结果 + */ + private static boolean compareResults(List> result1, List> result2) { + // 获取列名并比较(顺序无关) + List columnNames1 = result1.get(0); + List columnNames2 = result2.get(0); + + // 检查列名是否一致(顺序无关) + Set columnSet1 = new HashSet<>(columnNames1); + Set columnSet2 = new HashSet<>(columnNames2); + if (!columnSet1.equals(columnSet2)) { + return false; + } + + // 获取数据行(去除列名) + List> rows1 = result1.subList(1, result1.size()); + List> rows2 = result2.subList(1, result2.size()); + + // 使用 Set 存储每行数据并比较 + Set> rowSet1 = new HashSet<>(); + for (List row : rows1) { + rowSet1.add(new HashSet<>(row)); + } + + Set> rowSet2 = new HashSet<>(); + for (List row : rows2) { + rowSet2.add(new HashSet<>(row)); + } + + // 比较行数据(无顺序) + return rowSet1.equals(rowSet2); + } + /** + * 执行 SQL 查询并返回结果 + */ + private static List> executeQuery(Statement stmt, String sql) throws SQLException { + List> result = new ArrayList<>(); + + + try { + + ResultSet rs = stmt.executeQuery(sql); + // 获取列数和列名 + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount(); + + // 获取列名 + List columnNames = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + columnNames.add(metaData.getColumnLabel(i)); + } + result.add(columnNames); // 将列名添加为结果的第一行 + + // 遍历结果集 + while (rs.next()) { + List row = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + row.add(rs.getString(i)); + } + result.add(row); + } + } catch (SQLException e) { + appendToFile(answerLogPath, "执行学生库 SQL CALL 语句时发生错误: " + e.getMessage()); + } + + + return result; + } + /** + * 从文件读取 SQL 语句 + */ + private static String readSQLFromFile(String filePath) throws IOException { + File file = new File(filePath); + if (!file.exists()) { + throw new FileNotFoundException("❌ 找不到SQL文件:" + filePath); + } + + StringBuilder sql = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + sql.append(line).append(" "); + } + } + return sql.toString().trim(); + } + + // 去除 SQL 中的注释部分 + private static String removeComments(String sql) { + // 正则表达式匹配 SQL 中以 -- 开头的注释 + return sql.replaceAll("(?m)^[\\s]*--.*$", ""); + } + /** + * 比较两个查询的结果 + */ + private static boolean compareResultsSelect(List> result1, List> result2) { + // 获取列名并比较(顺序无关) + List columnNames1 = result1.get(0); + List columnNames2 = result2.get(0); + + // 检查列名是否一致(顺序无关) + Set columnSet1 = new HashSet<>(columnNames1); + Set columnSet2 = new HashSet<>(columnNames2); + if (!columnSet1.equals(columnSet2)) { + return false; + } + + // 获取数据行(去除列名) + List> rows1 = result1.subList(1, result1.size()); + List> rows2 = result2.subList(1, result2.size()); + + // 使用 Set 存储每行数据并比较 + Set> rowSet1 = new HashSet<>(); + for (List row : rows1) { + rowSet1.add(new HashSet<>(row)); + } + + Set> rowSet2 = new HashSet<>(); + for (List row : rows2) { + rowSet2.add(new HashSet<>(row)); + } + + // 比较行数据(无顺序) + return rowSet1.equals(rowSet2); + } + /** + * 打印查询结果 + */ + private static String printResult(List> result,String judgementStr) { + if (result.isEmpty()) { + appendToFile(answerLogPath, "查询结果为空"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "查询结果为空"); + return judgementStr; + } else { + for (int i = 0; i < result.size(); i++) { + List row = result.get(i); + StringBuilder sb = new StringBuilder(); + for (String value : row) { + sb.append(value).append("\t"); + } + + // 输出整行 + appendToFile(answerLogPath, sb.toString()); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, sb.toString()); + } + } + return judgementStr; + } + + /** + * 使用提供的正则表达式模式从给定的 SQL 语句中提取视图名称。 + */ + private static String extractViewName(String sql, String regex) { + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(sql); + + // 如果找到匹配项,返回匹配到的视图名称 + if (matcher.find()) { + return matcher.group(1).replace("`", ""); // 如果有反引号(`),则移除 + } else { + return "未找到视图名称"; + } + } + + private static List> getAnswerList(ResultSet answer) throws SQLException { + List> result = new ArrayList<>(); + // 获取列数和列名 + ResultSetMetaData metaData = answer.getMetaData(); + int columnCount = metaData.getColumnCount(); + + // 获取列名 + List columnNames = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + columnNames.add(metaData.getColumnLabel(i)); + } + result.add(columnNames); // 将列名添加为结果的第一行 + + // 遍历结果集 + while (answer.next()) { + List row = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + row.add(answer.getString(i)); + } + result.add(row); + } + + return result; + + } + + + + public static void generateInsertStatements(String tableName, Map.Entry entry, String newDbUrl, String user, String password) { + // 确保 entry 里包含的是 INSERT 语句 + if (entry.getValue().trim().toUpperCase().startsWith("INSERT")) { + System.out.println("==================插入语句=================="); + // 创建一个列表来存储所有的 INSERT INTO 语句 + List insertQueries = new ArrayList<>(); + try (Connection conn = DriverManager.getConnection(newDbUrl, user, password); + Statement stmt = conn.createStatement()) { + + // 查询表的所有列 + String columnsQuery = "DESCRIBE " + tableName; + + try (ResultSet columnsRs = stmt.executeQuery(columnsQuery)) { + List columnNames = new ArrayList<>(); + while (columnsRs.next()) { + String columnName = columnsRs.getString("Field"); + columnNames.add(columnName); + } + + // 获取表的数据并生成 INSERT INTO 语句 + String selectQuery = "SELECT * FROM " + tableName; + try (ResultSet dataRs = stmt.executeQuery(selectQuery)) { + + while (dataRs.next()) { + StringBuilder insertQuery = new StringBuilder("INSERT INTO `" + tableName + "` ("); + + // 构建列名部分 + for (int i = 0; i < columnNames.size(); i++) { + insertQuery.append("`").append(columnNames.get(i)).append("`"); + if (i < columnNames.size() - 1) { + insertQuery.append(", "); + } + } + insertQuery.append(") VALUES ("); + + // 构建值部分 + for (int i = 0; i < columnNames.size(); i++) { + Object value = dataRs.getObject(columnNames.get(i)); + if (value == null) { + insertQuery.append("NULL"); + } else if (value instanceof String) { + insertQuery.append("'").append(value.toString().replace("'", "''")).append("'"); + } else { + insertQuery.append(value); + } + + if (i < columnNames.size() - 1) { + insertQuery.append(", "); + } + } + insertQuery.append(");"); + // 将生成的 INSERT INTO 语句添加到列表中 + insertQueries.add(insertQuery.toString()); + // 输出生成的 INSERT INTO 语句 + System.out.println(insertQuery.toString()); + } + + } + catch (SQLException e) { + System.err.println("生成插入语句失败,表名:" + tableName); + e.printStackTrace(); + } + } + } catch (SQLException e) { + System.err.println("生成插入语句失败,表名:" + tableName); + e.printStackTrace(); + } + // 将列表赋值给 entry 的值 + entry.setValue(insertQueries.toString()); + System.out.println(entry.getValue()); + } + } + + /** + * 读取指定目录下所有文件,返回一个文件名到文件内容的键值对Map + * @param directoryPath 文件夹路径 + * @return Map<文件名, 文件内容> + * @throws IOException 读取文件异常 + */ + public static Map readFilesAsMap(String directoryPath) throws IOException { + Map fileContentMap = new HashMap<>(); + + Path dirPath = Paths.get(directoryPath); + if (!Files.isDirectory(dirPath)) { + throw new IllegalArgumentException("路径不是一个有效的目录: " + directoryPath); + } + + try (DirectoryStream stream = Files.newDirectoryStream(dirPath)) { + for (Path path : stream) { + if (Files.isRegularFile(path)) { + String fileName = path.getFileName().toString(); + String content = new String(Files.readAllBytes(path)); + fileContentMap.put(fileName, content); + } + } + } + + return fileContentMap; + } + + + /** + * 通过命令行执行 SQL 文件 + */ + public static boolean executeSqlFileUsingCommandLine(String sqlFilePath, String dbName, String user, String password) { + try { + // 拼接命令 + String[] command = { + "mysql", + "-u" + user, + "-P", "6033", + dbName, + "-e", "source " + sqlFilePath + }; + + // 启动进程 + Process process = Runtime.getRuntime().exec(command); + int exitCode = process.waitFor(); + + return exitCode == 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } +// private static boolean executeSqlFileUsingCommandLine(String filePath, String databaseName, String user, String password) throws IOException { +// // 构建 MySQL 命令 +// String command = String.format("mysql -u %s -p%s %s < %s", user, password, databaseName, filePath); +// +// // 使用 ProcessBuilder 执行命令 +// ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", command); +// processBuilder.inheritIO(); // 将输入输出重定向到当前控制台 +// Process process = processBuilder.start(); +// +// try { +// // 等待命令执行完成 +// int exitCode = process.waitFor(); +// if (exitCode == 0) { +// System.out.println("SQL 文件执行成功:" + filePath); +// return true; // 返回 true 表示执行成功 +// } else { +// System.err.println("SQL 文件执行失败:" + filePath); +// return false; // 返回 false 表示执行失败 +// } +// } catch (InterruptedException e) { +// e.printStackTrace(); +// return false; +// } +// } + /** + * 将指定内容追加写入到指定文件中。 + * + * @param filePath 文件路径 + * @param content 要写入的内容 + */ + public static void appendToFile(String filePath, String content) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) { + String timestamp = LocalDateTime.now().format(formatter); + String logLine = String.format("[%s] %s", timestamp, content); + writer.write(logLine); + writer.newLine(); // 可选:添加换行符 + } catch (IOException e) { + e.printStackTrace(); + } + } + public static void appendToFile(String filePath, String format, Object... args) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) { + String timestamp = LocalDateTime.now().format(formatter); + String content = String.format(format, args); + String logLine = String.format("[%s] %s", timestamp, content); + writer.write(logLine); + writer.newLine(); // 可选:添加换行符 + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/example/exam/exam/service/mysql/vo/MysqlBooleanVo.java b/src/main/java/com/example/exam/exam/service/mysql/vo/MysqlBooleanVo.java new file mode 100644 index 0000000..f5ff834 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/mysql/vo/MysqlBooleanVo.java @@ -0,0 +1,16 @@ +package com.example.exam.exam.service.mysql.vo; + +import lombok.Data; + +@Data +public class MysqlBooleanVo { + /** + * 是否正确 + */ + private boolean flag; + + /** + * 文本 + */ + private String text; +} diff --git a/src/main/java/com/example/exam/exam/service/mysql/vo/MysqlVo.java b/src/main/java/com/example/exam/exam/service/mysql/vo/MysqlVo.java new file mode 100644 index 0000000..a6b42e7 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/mysql/vo/MysqlVo.java @@ -0,0 +1,16 @@ +package com.example.exam.exam.service.mysql.vo; + +import lombok.Data; + +@Data +public class MysqlVo { + /** + * 序列 + */ + private int index; + + /** + * 文本 + */ + private String text; +} diff --git a/src/main/java/com/example/exam/exam/service/question/ExamQuestionServiceImpl.java b/src/main/java/com/example/exam/exam/service/question/ExamQuestionServiceImpl.java new file mode 100644 index 0000000..8419bd0 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/question/ExamQuestionServiceImpl.java @@ -0,0 +1,77 @@ +package com.example.exam.exam.service.question; + + +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 试题(hyc)Service业务层处理 + * + * @author pengchen + * @date 2025-03-13 + */ +@Service +public class ExamQuestionServiceImpl implements IExamQuestionService +{ + + @Autowired + private ExamQuestionMapper examQuestionMapper; + @Autowired + private ExamQuestionAnswerMapper examQuestionAnswerMapper; + @Autowired + private SysFileMapper sysFileMapper; + @Autowired + private ExamQuestionScoreMapper examQuestionScoreMapper; + @Autowired + private ExamQuestionKeywordMapper examQuestionKeywordMapper; + + /** + * 查询试题(hyc) + * + * @param quId 试题(hyc)主键 + * @return 试题(hyc) + */ + @Override + public ExamQuestion selectExamQuestionByQuId(String quId) + { + //查找试题答案 + List examQuestionAnswers = examQuestionAnswerMapper.selectExamQuestionAnswerByQuId(quId); + //查找试题文件 + List sysFileUploads =sysFileMapper.selectSysFileByQuid(quId); + //查找试题判分 + ExamQuestionScore examQuestionScore =examQuestionScoreMapper.selectExamQuestionScoreByQuId(quId); + //获取试题关键字 + List examQuestionKeywords = examQuestionKeywordMapper.selectExamQuestionKeywordByQuId(quId); + + ExamQuestion examQuestion = examQuestionMapper.selectExamQuestionByQuId(quId); + if (examQuestion!=null){ + if (examQuestionAnswers!=null&&examQuestionAnswers.size()>0){ + examQuestion.setAnswerList(examQuestionAnswers); + } + if (sysFileUploads!=null&&sysFileUploads.size()>0){ + examQuestion.setFileUploads(sysFileUploads); + } + if (examQuestionScore!=null){ + examQuestion.setQuestionScores(examQuestionScore); + } + if (examQuestionKeywords!=null&&examQuestionKeywords.size()>0){ + examQuestion.setQuestionKeywords(examQuestionKeywords); + } + } + + String chapteridDictText = examQuestion.getChapteridDictText(); + String pointNames = examQuestion.getPointNames(); +// String point= knowledgePointsMapper.selectKnowledgeNameBySpId(pointNames); +// String chapterText= knowledgePointsMapper.selectKnowledgeNameBySpId(chapteridDictText); +// examQuestion.setPointNamesVo(point); +// examQuestion.setChapteridDictTextVo(chapterText); + return examQuestion; + } + + + +} diff --git a/src/main/java/com/example/exam/exam/service/question/IExamQuestionService.java b/src/main/java/com/example/exam/exam/service/question/IExamQuestionService.java new file mode 100644 index 0000000..ef28981 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/question/IExamQuestionService.java @@ -0,0 +1,25 @@ +package com.example.exam.exam.service.question; + + + +import com.example.exam.exam.dal.ExamQuestion; + +import java.util.List; + +/** + * 试题(hyc)Service接口 + * + * @author pengchen + * @date 2025-03-13 + */ +public interface IExamQuestionService +{ + /** + * 查询试题(hyc) + * + * @param quId 试题(hyc)主键 + * @return 试题(hyc) + */ + public ExamQuestion selectExamQuestionByQuId(String quId); + +} diff --git a/src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreService.java b/src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreService.java new file mode 100644 index 0000000..97ea1f7 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreService.java @@ -0,0 +1,24 @@ +package com.example.exam.exam.service.stupaperscore; + + +import com.example.exam.exam.dal.StuPaperScoreDO; + +import java.util.List; + +/** + * 学生-试卷-分数 Service 接口 + * + * @author rwb + */ +public interface StuPaperScoreService { + + List findByStuIDAndPaperId(Long stuId, String paperId); + + void insertStuPaperScore(StuPaperScoreDO stuPaperScoreDO); + + void updateStuPaperScore(StuPaperScoreDO stuPaperScoreDO); + + StuPaperScoreDO getStuScoreByPaperIdAndQuid(Long stuId, String paperId, String quId); + + void deleteStuPaperScore(Long stuId, String paperId); +} \ No newline at end of file diff --git a/src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreServiceImpl.java b/src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreServiceImpl.java new file mode 100644 index 0000000..8ed7a73 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/stupaperscore/StuPaperScoreServiceImpl.java @@ -0,0 +1,51 @@ +package com.example.exam.exam.service.stupaperscore; + +import com.example.exam.exam.dal.StuPaperScoreDO; +import com.example.exam.exam.mapper.EducationPaperMapper; +import com.example.exam.exam.mapper.StuPaperScoreMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 学生-试卷-文件 Service 实现类 + * + * @author rwb + */ +@Service +@Validated +public class StuPaperScoreServiceImpl implements StuPaperScoreService { + + @Resource + private StuPaperScoreMapper stuPaperScoreMapper; + @Resource + private EducationPaperMapper educationPaperMapper; + + @Override + public List findByStuIDAndPaperId(Long stuId, String paperId) { + return stuPaperScoreMapper.findByStuIdAndPaperId(stuId, paperId); + } + + @Override + public void insertStuPaperScore(StuPaperScoreDO stuPaperScoreDO) { + stuPaperScoreMapper.insert(stuPaperScoreDO); + } + + @Override + public void updateStuPaperScore(StuPaperScoreDO stuPaperScoreDO) { + stuPaperScoreMapper.updateById(stuPaperScoreDO); + } + + @Override + public StuPaperScoreDO getStuScoreByPaperIdAndQuid(Long stuId, String paperId, String quId) { + return stuPaperScoreMapper.findByStuIdAndPaperIdAndQuestionId(stuId, paperId, quId); + } + + @Override + public void deleteStuPaperScore(Long stuId, String paperId) { + stuPaperScoreMapper.deleteByStuIdAndPaperId(stuId, paperId); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/exam/exam/service/tenant/SystemTenantService.java b/src/main/java/com/example/exam/exam/service/tenant/SystemTenantService.java new file mode 100644 index 0000000..9b5c3b5 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/tenant/SystemTenantService.java @@ -0,0 +1,10 @@ +package com.example.exam.exam.service.tenant; + + +import com.example.exam.exam.dal.ExamAppCheck; +import com.example.exam.exam.dal.SystemTenant; + +public interface SystemTenantService { + + SystemTenant getId(String name); +} diff --git a/src/main/java/com/example/exam/exam/service/tenant/SystemTenantServiceImpl.java b/src/main/java/com/example/exam/exam/service/tenant/SystemTenantServiceImpl.java new file mode 100644 index 0000000..27a0c21 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/tenant/SystemTenantServiceImpl.java @@ -0,0 +1,23 @@ +package com.example.exam.exam.service.tenant; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.exam.exam.dal.ExamAppCheck; +import com.example.exam.exam.dal.SystemTenant; +import com.example.exam.exam.mapper.ExamAppCheckMapper; +import com.example.exam.exam.mapper.SystemTenantMapper; +import com.example.exam.exam.service.appcheck.ExamAppCheckService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +@Service +public class SystemTenantServiceImpl implements SystemTenantService { + + @Resource + SystemTenantMapper tenantMapper; + + @Override + public SystemTenant getId(String name) { + SystemTenant systemTenant = tenantMapper.selectOne(new QueryWrapper().eq("name", name)); + return systemTenant; + } +} diff --git a/src/main/java/com/example/exam/exam/service/winfile/FileServericeImpl.java b/src/main/java/com/example/exam/exam/service/winfile/FileServericeImpl.java new file mode 100644 index 0000000..24550d8 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/winfile/FileServericeImpl.java @@ -0,0 +1,140 @@ +package com.example.exam.exam.service.winfile; + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.SourceAndText; +import com.example.exam.exam.mapper.ExamQuestionAnswerMapper; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.file.GetDifferencesBetweenFolders; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + + +@Service +public class FileServericeImpl implements IFileServerice { + static String answerLogPath ; // 文件路径 + @Resource + private ExamQuestionAnswerMapper examQuestionAnswerMapper; + private static final DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + @Override + public SourceAndText Judgement(double score, File file, ExamQuestion question, String judgementStr) throws IOException { + SourceAndText sourceAndText = new SourceAndText(); + List answerList = examQuestionAnswerMapper.selectExamQuestionAnswerByQuId(question.getQuId()); + + //todo 得到学生的考题答案 +// List answerList=new ArrayList<>(); +// answerList.add(new ExamQuestionAnswer("","","","","HGACYL\\RLQM.MEM", "考察删除", "1", "1")); +// answerList.add(new ExamQuestionAnswer("","","","","TING\\XYU\\AUTOE.BAT", "考察删除", "1", "2")); +// answerList.add(new ExamQuestionAnswer("","","","","AHEWL\\KMENS", "考察名称", "1", "3")); +// answerList.add(new ExamQuestionAnswer("","","","","EDZK\\RONGHE.COM", "考察名称", "1", "4")); +// answerList.add(new ExamQuestionAnswer("","","","","HGACYL\\PLAY.MEM", "考察名称", "1", "5")); +// answerList.add(new ExamQuestionAnswer("","","","","WUE\\PB6.txt", "考察名称", "1", "6")); + + + File stuPath = file; + + // 设置日志文件路径为file所在目录下的answerLogFile.txt + answerLogPath = file.getParent() + File.separator + "log.txt"; + System.out.println(answerLogPath); + // 获取 stu 文件夹的内容 + Map stuFiles = GetDifferencesBetweenFolders.listFilesAndFoldersWithAttributes(stuPath.toPath()); + + // 输出学生提交的内容 +// appendToFile(answerLogPath,"=== 学生提交内容(stu 目录) ==="); +// +// stuFiles.forEach((key, value) -> appendToFile(answerLogPath,key + " -> " + value)); + +// 计算试题总分 + int totalScore = answerList.stream() + .mapToInt(a -> Integer.parseInt(a.getScoreRate())) + .sum(); + appendToFile(answerLogPath,"=== 学生提交内容得分点 ==="); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "=== 学生提交内容得分点 ==="); + // 对比学生提交内容与试题得分点 + SourceAndText studentScorePojo = compareStuAndTestFiles(answerList, stuFiles,score,totalScore,judgementStr); + double studentScore = studentScorePojo.getScore(); + judgementStr = studentScorePojo.getText(); + //获取answerList里的所有sorcerate,和integer相除得到一个小于等于1的数 + + // 计算最终得分比例(保留两位小数) + double scoreRatio = totalScore == 0 ? 0 : (double) studentScore / totalScore; + double roundedScoreRatio = Math.round(scoreRatio * 100.0) / 100.0; // 四舍五入到2位小数 + appendToFile(answerLogPath,"得分:"+roundedScoreRatio*score); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "得分:"+roundedScoreRatio*score); + sourceAndText.setScore(roundedScoreRatio*score); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + + + // 对比学生提交内容与试题得分点 + static SourceAndText compareStuAndTestFiles(List answerList, Map stuFiles,double score,int total, String judgementStr) { + SourceAndText sourceAndText = new SourceAndText(); + int totalScore = 0; // 记录总得分 + for (ExamQuestionAnswer answer : answerList) { + String filePath = answer.getContent(); // 试题文件路径 + String checkType = answer.getContentIn(); // 考察类型(考察删除 / 考察名称 / 考察属性) + int currentScore = Integer.parseInt(answer.getScoreRate()); // 当前得分 + boolean isCorrect = false; + + if ("考察删除".equals(checkType)) { + // 如果学生提交中不存在该文件,则得分 + isCorrect = !stuFiles.containsKey(filePath); + } else if ("考察名称".equals(checkType)) { + // 如果学生提交中存在该文件,则得分 + isCorrect = stuFiles.containsKey(filePath); + } else if ("考察属性".equals(checkType)) { + // 如果学生提交中存在该文件,且属性匹配,则得分 + if (stuFiles.containsKey(filePath)) { + String studentAttrs = stuFiles.get(filePath); // 学生提交的属性 + String expectedAttrs = answer.getAttribute(); // 试题中期望的属性 + isCorrect = studentAttrs.equals(expectedAttrs); + } + } + // 如果正确,则累加总分 + if (isCorrect) { + totalScore += currentScore; + // 计算该考点的权重得分并保留一位小数 + double weightScore = ((double) currentScore / total) * score; + String formattedWeightScore = String.format("%.1f", weightScore); + appendToFile(answerLogPath,"✅"+answer.getContent() + " -> " + answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:"+formattedWeightScore); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ " + answer.getContent() + " -> " + answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:"+formattedWeightScore); + }else { + appendToFile(answerLogPath,"❌"+answer.getContent() + " -> " + answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:0"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ " + answer.getContent() + " -> " + answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 得分:0"); + } + } + sourceAndText.setText(judgementStr); + sourceAndText.setScore(totalScore); + //返回累加的总分 + return sourceAndText; + } + + /** + * 将指定内容追加写入到指定文件中。 + * + * @param filePath 文件路径 + * @param content 要写入的内容 + */ + public static void appendToFile(String filePath, String content) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) { + String timestamp = LocalDateTime.now().format(formatter); + String logLine = String.format("[%s] %s", timestamp, content); + writer.write(logLine); + writer.newLine(); // 可选:添加换行符 + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/example/exam/exam/service/winfile/IFileServerice.java b/src/main/java/com/example/exam/exam/service/winfile/IFileServerice.java new file mode 100644 index 0000000..d567e70 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/winfile/IFileServerice.java @@ -0,0 +1,18 @@ +package com.example.exam.exam.service.winfile; + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +import java.io.File; +import java.io.IOException; + +public interface IFileServerice { + + + + + SourceAndText Judgement(double score, File file, ExamQuestion question, String judgementStr) throws IOException; + + +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/ChartTypeEntry.java b/src/main/java/com/example/exam/exam/service/wpsexcel/ChartTypeEntry.java new file mode 100644 index 0000000..a5e202a --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/ChartTypeEntry.java @@ -0,0 +1,14 @@ +package com.example.exam.exam.service.wpsexcel; + +import lombok.Data; + +@Data +public class ChartTypeEntry { + private String tag; + private String displayName; + + public ChartTypeEntry(String tag, String displayName) { + this.tag = tag; + this.displayName = displayName; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/ExcelInfoReqVo.java b/src/main/java/com/example/exam/exam/service/wpsexcel/ExcelInfoReqVo.java new file mode 100644 index 0000000..a469454 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/ExcelInfoReqVo.java @@ -0,0 +1,28 @@ + package com.example.exam.exam.service.wpsexcel; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author REN + */ +@Data +public class ExcelInfoReqVo { + + private String name; + + private String englishName; + + private String filePath; + + private String parentId; + + private String selectName; + + private String id; + + private List children = new ArrayList<>(); + +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelService.java b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelService.java new file mode 100644 index 0000000..5ac8729 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelService.java @@ -0,0 +1,18 @@ +package com.example.exam.exam.service.wpsexcel; + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +import java.util.List; + +/** + * 判分逻辑集合(wps excel) + * + * @author rwb + */ +public interface JudgementWpsExcelService { + + // 根据给定的分数、路径、考试题目和判断题字符串,返回一个SourceAndText对象 + SourceAndText judgementWpsXlsx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java new file mode 100644 index 0000000..855a78a --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java @@ -0,0 +1,73 @@ +package com.example.exam.exam.service.wpsexcel; + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.SourceAndText; +import com.example.exam.exam.service.wpspptx.WpsPptxJudgementDto; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.c.LogFileUtils; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.util.List; + +@Service +public class JudgementWpsExcelServiceImpl implements JudgementWpsExcelService { + + + + @Override + public SourceAndText judgementWpsXlsx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception { + SourceAndText sourceAndText = new SourceAndText(); + // 1创建log文件txt,用于记录 + File pathCDir = new File(pathC); + File parentDir = pathCDir.getParentFile(); + + // 拼接同级目录下的目标文件路径 + String targetFilePath = new File(parentDir, "WPS_Xlsx判分过程.txt").getPath(); + LogFileUtils.createFile(targetFilePath); + LogFileUtils.writeLine("✅ 开始WPS_Xlsx判分"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Xlsx判分"); + double wpsXlsxScore = 0; + List margins = WpsExcelUtils.wpsExcel(path, "0"); + // 3、获取答案得组成 + List answerList = examQuestion.getAnswerList(); + // 考点 sheetNumber@type@secondType@englishName@value + // 中文 sheetName@typeName@secondTypeName@chineseName@value + // 4、进行关联判断 + for (ExamQuestionAnswer examQuestionAnswer : answerList) { + boolean flag = false; + double one_sorce = 0; + for (WpsPptxJudgementDto xlsxInfoVo : margins) { + // 原始考点 + if (xlsxInfoVo.getContent().equals(examQuestionAnswer.getContent())) { + flag = true; + // 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重 + if (examQuestionAnswer.getScoreRate().equals("1")) { + // 说明权重相等,直接平分分数 + one_sorce = sorce / answerList.size(); + } else { + one_sorce = sorce * Double.parseDouble(examQuestionAnswer.getScoreRate()); + } + break; + } + } + wpsXlsxScore += one_sorce; + if (flag) { + LogFileUtils.writeLine("✅ " + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ " + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce); + } else { + LogFileUtils.writeLine("❌ " + examQuestionAnswer.getContentIn() + " 得分失败"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ " + examQuestionAnswer.getContentIn() + " 得分失败"); + } + } + LogFileUtils.writeLine("✅ 结束WPS_Xlsx判分,试题得分:" + wpsXlsxScore); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 结束WPS_Xlsx判分,试题得分:" + wpsXlsxScore); + sourceAndText.setText(judgementStr); + sourceAndText.setScore(wpsXlsxScore); + // 关闭已经打开得文件 + LogFileUtils.close(); + return sourceAndText; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java b/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java new file mode 100644 index 0000000..3abb770 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java @@ -0,0 +1,1237 @@ +package com.example.exam.exam.service.wpsexcel; + +import com.example.exam.exam.service.wpspptx.WpsPptxJudgementDto; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlObject; + +import javax.xml.namespace.QName; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * @author REN + */ +public class WpsExcelUtils { + + public static final String namespace = "declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' "; + + public static final List> drawingSheetVOS = new ArrayList<>(); + + public static int dxfId = 0; + + + public static String getStringRandom() { + String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + + // 生成指定长度的随机字符字符串 + for (int i = 0; i < 10; i++) { + int randomIndex = random.nextInt(characters.length()); + // 随机字符 + sb.append(characters.charAt(randomIndex)); + } + return sb.toString(); + } + + public static List wpsExcelInfo(String filePath) throws Exception { + List excelInfoReqVoList = new ArrayList<>(); + try (FileInputStream fis = new FileInputStream(filePath); + OPCPackage pkg = OPCPackage.open(fis); + XSSFWorkbook workbook = new XSSFWorkbook(pkg)) { + // 获取有多少个工作表 + int sheetNumber = workbook.getNumberOfSheets(); + String firstIdSheet = getStringRandom(); + for (int i = 0; i < sheetNumber; i++) { + String secondIdSheet = getStringRandom(); + // 获取工作表内容 + XSSFSheet sheetXss = workbook.getSheetAt(i); + // 获取工作表的XML对象 + XmlObject worksheetXml = sheetXss.getCTWorksheet(); + setWordInfo("Sheet" + sheetNumber + 1, "Sheet" + sheetNumber + 1, "sheet" + sheetNumber + 1 + ".xml", filePath, secondIdSheet, firstIdSheet, excelInfoReqVoList); + + // 开始查找指定得 单元格、范围、行、列、数据排序、数据透视表、表格、图标、页面、试图、属性 + String thirdIdC = getStringRandom(); + setWordInfo("单元格", "c", "c", filePath, thirdIdC, secondIdSheet, excelInfoReqVoList); + String thirdIdCC = getStringRandom(); + setWordInfo("范围", "cc", "cc", filePath, thirdIdCC, secondIdSheet, excelInfoReqVoList); + String thirdIdRow = getStringRandom(); + setWordInfo("行", "row", "row", filePath, thirdIdRow, secondIdSheet, excelInfoReqVoList); + String thirdIdCol = getStringRandom(); + setWordInfo("列", "cols", "cols", filePath, thirdIdCol, secondIdSheet, excelInfoReqVoList); +// String thirdIdCol = getStringRandom(); + // 判断是否存在图表 + setWordInfo("列", "cols", "cols", filePath, thirdIdCol, secondIdSheet, excelInfoReqVoList); + + System.out.println(worksheetXml.xmlText()); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (InvalidFormatException e) { + throw new RuntimeException(e); + } + + + return excelInfoReqVoList; + } + public static void setWordInfo(String chineseName, String englishName, String selectName, String filePath, String id, String parentId, List excelInfoReqVos) throws Exception { + ExcelInfoReqVo excelInfos = new ExcelInfoReqVo(); + excelInfos.setName(chineseName); + excelInfos.setEnglishName(englishName); + excelInfos.setFilePath(filePath); + excelInfos.setId(id); + excelInfos.setSelectName(selectName); + excelInfos.setParentId(parentId); + excelInfoReqVos.add(excelInfos); + } + + + public static List wpsExcel(String filePath, String index) throws Exception { + // 获取共享字符串 + String[] sharedStrings = extractSharedStrings(filePath); + // 读取样式xml + XmlCursor styleXml = extractStyleXml(filePath); + // 作簿中的数据连接(Data Connections),即 Excel 与外部数据源(如数据库、Web 服务、其他文件等)的链接信息 + XmlCursor connectionsXml = extractConnectionsXml(filePath); + // 获取工作簿中的图表数据 +// List chartListXmls = extractChartXml(filePath); + getDrawingInfos(filePath); + // 单元格数据 + List xlsxInfoVos = new ArrayList<>(); + try (FileInputStream fis = new FileInputStream(filePath); + OPCPackage pkg = OPCPackage.open(fis); + XSSFWorkbook workbook = new XSSFWorkbook(pkg)) { + // 获取有多少个工作表 + int sheetNumber = workbook.getNumberOfSheets(); + for (int i = 0; i < sheetNumber; i++) { +// System.out.println("第 " + (i + 1) + " 个工作表"); + // 获取工作表内容 + XSSFSheet sheetXss = workbook.getSheetAt(i); + // 获取工作表的XML对象 + XmlObject worksheetXml = sheetXss.getCTWorksheet(); + // 读取条件格式,并转化条件 + XmlCursor conditionalFormatting = worksheetXml.newCursor(); + conditionalFormatting.selectPath(namespace + "//ns:conditionalFormatting"); + String sqref = ""; + String formula = ""; + boolean conditionalFormattingFlag = false; + if (conditionalFormatting.toNextSelection()) { + conditionalFormattingFlag = true; + // 使用了条件格式,作用范围 + XmlCursor sqrefXml = conditionalFormatting.getObject().newCursor(); + sqrefXml.selectPath(namespace + "./@sqref"); + if (sqrefXml.toNextSelection()) { + sqref = sqrefXml.getTextValue(); + } + // 查询条件 + XmlCursor cfRuleXml = conditionalFormatting.getObject().newCursor(); + cfRuleXml.selectPath(namespace + "./ns:cfRule"); + if (cfRuleXml.toNextSelection()){ + dxfId = Integer.parseInt(cfRuleXml.getAttributeText(new QName("dxfId"))); + XmlCursor formulaXml = cfRuleXml.getObject().newCursor(); + formulaXml.selectPath(namespace + "./ns:formula"); + if (formulaXml.toNextSelection()) { + formula = cfRuleXml.getTextValue(); + } + } + } + try (XmlCursor cursor = worksheetXml.newCursor()) { +// drawingXmlInfo(cursor, String.valueOf(i+1)); + // 查询所有 row 元素 + cursor.selectPath(namespace + "//ns:worksheet/ns:sheetData/ns:row"); + int rowIndex = 0; + while (cursor.toNextSelection()) { + // 行的参数 + XmlCursor rowXml = cursor.getObject().newCursor(); + addXlsxInfo(String.valueOf(i+1), "行高", "ns:row/@ht", rowXml.getAttributeText(new QName("ht")), "row", "行" + rowXml.getAttributeText(new QName("r")), "style", "样式", "r"+rowXml.getAttributeText(new QName("r")), xlsxInfoVos); + addXlsxInfo(String.valueOf(i+1), "行高", "ns:row/@ht", rowXml.getAttributeText(new QName("ht")), "row", "行" + rowXml.getAttributeText(new QName("r")),"style", "样式", "r"+rowXml.getAttributeText(new QName("r")), xlsxInfoVos); + + + try (XmlCursor rowCursor = rowXml.newCursor()) { + rowCursor.selectPath(namespace + "./ns:c"); + // 接着获取单元格 + int colIndex = 0; + while (rowCursor.toNextSelection()) { + // 判断是否使用了条件样式进行判断值 + boolean isConditionalFormatting = false; + if (conditionalFormattingFlag) { + // 进行拆分单元格 + // 1. 提取列字母(正则匹配 $K2 或 K2) + // 字符串替换 + String formulas = formula.replaceAll("\\$", "").split("=")[0]; + Pattern pattern = Pattern.compile("^[A-Za-z]+"); + Matcher matcher = pattern.matcher(formulas); + String colLetters = ""; + while (matcher.find()) { + colLetters = matcher.group(0); + } + int colIndexMatcher = columnLetterToNumber(colLetters); + // 读取指定单元格 + Row row = sheetXss.getRow(rowIndex); + if (row == null) { + break; + } + Cell cell = row.getCell(colIndexMatcher-1); + String value = getCellValue(cell); + // 获取对比值 + String compareValue = formula.split("=")[1].replace("\"", "");; + if (Objects.equals(value, compareValue)) { + isConditionalFormatting = true; + } + } + Row row = sheetXss.getRow(rowIndex); + if (row == null) { + break; + } + // 获取行属性 + Cell cell = row.getCell(colIndex); + if (row != null && cell != null) { + // 获取单元格 + String rowName = rowCursor.getAttributeText(new QName("r")); + // 获取style编号 + String styleId = rowCursor.getAttributeText(new QName("s")); + // 获取共享字符串 + String tName = rowCursor.getAttributeText(new QName("t")); + // 获取具体的值 + try (XmlCursor valueXml = rowCursor.getObject().newCursor()) { + String value = ""; + valueXml.selectPath(namespace + "./ns:v"); + if (valueXml.toNextSelection()) { + value = valueXml.getTextValue(); + } + // 如果是共享字符串 + if (tName != null && tName.equals("s")) { + value = sharedStrings[Integer.parseInt(value)]; + addXlsxInfo(String.valueOf(i+1), "单元格文本", "ns:celltext", value, "cell", "单元格", "text", "文本", rowName, xlsxInfoVos); + } else { + addXlsxInfo(String.valueOf(i+1), "单元格文本", "ns:celltext", value, "cell", "单元格", "text", "文本", rowName, xlsxInfoVos); + } + + valueXml.dispose(); + } + // 获取公式 + try (XmlCursor formulaXml = rowCursor.getObject().newCursor()) { + String value = ""; + formulaXml.selectPath(namespace + "./ns:f"); + if (formulaXml.toNextSelection()) { + value = formulaXml.getTextValue(); + addXlsxInfo(String.valueOf(i+1), "单元格公式", "ns:formula", value, "cell", "单元格", "text", "文本", rowName, xlsxInfoVos); + } + formulaXml.dispose(); + } + // 获取单元格的格式 + CellStyle cellStyle = cell.getCellStyle(); +// System.out.println(rowIndex + "-" +colIndex); + short dataFormat = cellStyle.getDataFormat(); + String formatString = cellStyle.getDataFormatString(); +// System.out.println(formatString); + boolean isMerge = !formatString.equals("General"); +// System.out.println(rowName); + if (styleId != null) { + extractStyle(xlsxInfoVos, Integer.parseInt(styleId), styleXml, true, rowName, String.valueOf(i + 1), isConditionalFormatting); + } + XmlCursor cellXml = rowCursor.getObject().newCursor(); + cellXml.dispose(); + } + colIndex ++; + } + rowCursor.dispose(); + rowIndex ++; + } + rowXml.dispose(); + } + cursor.dispose(); + } catch (Exception e) { + e.printStackTrace(); + } + + List chartTypeEntries = getChartTypes(); + + + // 开始获取图表等信息(包含数据透视表) + try (XmlCursor drawingXmlcursor = worksheetXml.newCursor()) { + drawingXmlcursor.selectPath(namespace + "./ns:drawing"); + while (drawingXmlcursor.toNextSelection()) { + try (XmlCursor drawingXml = drawingXmlcursor.getObject().newCursor()) { + // 上指定的drawingList下进行查找 + List xlsxDrawingSheetVos = drawingSheetVOS.get(i); + // 1、已经获取到了对应sheet下的图表信息等 + // 开始进行查询 + for (XlsxDrawingSheetVo xlsxDrawingSheetVo : xlsxDrawingSheetVos) { + // TODO 校验文件名称有问题 + if (xlsxDrawingSheetVo.getTypeName().contains("chart")) { + // 添加考点 + addXlsxInfo(String.valueOf(i+1), "图表是否存在", "isChart", String.valueOf(0), "chart", "图表", "isChart", "是否存在", "", xlsxInfoVos); + // 说明是图表 + try (XmlCursor chartXml = xlsxDrawingSheetVo.getXmlCursor().newCursor()) { +// System.out.println(chartXml.xmlText()); + String chartNameSpace = getNamespace(chartXml.xmlText()); + // 开始获取标题 + XlsxInfoVo xlsxInfoVo = new XlsxInfoVo(); + XmlCursor titleXml = chartXml.newCursor(); +// System.out.println(titleXml.xmlText()); + titleXml.selectPath(chartNameSpace + ".//c:chart/c:title/c:tx/c:rich"); + while (titleXml.toNextSelection()) { + addXlsxInfo(String.valueOf(i+1), "图表标题是否存在", "isTitle", String.valueOf(0), "chart", "图表", "title", "标题", "", xlsxInfoVos); + + XmlCursor titleAnchorXml = titleXml.newCursor(); + titleAnchorXml.selectPath(chartNameSpace + ".//a:bodyPr/@anchor"); + if (titleAnchorXml.toNextSelection()) { + addXlsxInfo(String.valueOf(i+1), "图表标题位置", "a:bodyPr/@anchor", titleAnchorXml.getTextValue(), "chart", "图表", "title", "标题", "", xlsxInfoVos); + } + XmlCursor titleNameXml = titleXml.newCursor(); + titleNameXml.selectPath(chartNameSpace + ".//a:p/a:r/a:t"); + if (titleNameXml.toNextSelection()) { + addXlsxInfo(String.valueOf(i+1), "图表标题文本", "a:p/a:r/a:t", titleNameXml.getTextValue(), "chart", "图表", "title", "标题", "", xlsxInfoVos); + } + } +// titleXml.dispose(); + XmlCursor charTypeXml = chartXml.newCursor(); + charTypeXml.selectPath(chartNameSpace + ".//c:chart/c:plotArea"); + while (charTypeXml.toNextSelection()) { + addXlsxInfo(String.valueOf(i+1), "绘图是否存在", "c:chart/c:plotArea", String.valueOf(0), "chart", "图表", "plotArea", "绘图", "", xlsxInfoVos); + // 获取图表类型 + for (ChartTypeEntry chartTypeEntry : chartTypeEntries) { + XmlCursor charTypeXmlInfo = charTypeXml.newCursor(); + // 判断是否存在参数 +// System.out.println(charTypeXmlInfo.xmlText()); +// System.out.println(".//c:plotArea/" + chartTypeEntry.getTag()); + charTypeXmlInfo.selectPath(chartNameSpace + "./" + chartTypeEntry.getTag()); + if (charTypeXmlInfo.toNextSelection()) { + if (chartTypeEntry.getTag().equals("c:barChart")) { + // 说明要在继续判断值 + charTypeXmlInfo.selectPath(chartNameSpace + ".//c:barDir/@val"); + if (charTypeXmlInfo.toNextSelection()) { + String value = charTypeXmlInfo.getTextValue(); + if (value.equals("col")) { + addXlsxInfo(String.valueOf(i+1), "图表类型", "c:barDir/@val", "簇型柱状图", "chart", "图表", "plotArea","绘图", "", xlsxInfoVos); + } + } else { + addXlsxInfo(String.valueOf(i+1), "图表类型", chartTypeEntry.getTag(), chartTypeEntry.getDisplayName(), "chart", "图表", "plotArea","绘图", "", xlsxInfoVos); + } + } else { + addXlsxInfo(String.valueOf(i+1), "图表类型", chartTypeEntry.getTag(), chartTypeEntry.getDisplayName(), "chart", "图表", "plotArea","绘图", "", xlsxInfoVos); + } + + } + } + + // 获取X,Y轴信息 + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } catch (InvalidFormatException e) { + throw new RuntimeException(e); + } + List xlsxInfos = new ArrayList<>(); + for (XlsxInfoVo xlsxInfoVo : xlsxInfoVos) { + WpsPptxJudgementDto wpsPptxJudgementDto = new WpsPptxJudgementDto(); + wpsPptxJudgementDto.setContentIn(xlsxInfoVo.getSheetName() + "@" + xlsxInfoVo.getTypeName() + "@" + xlsxInfoVo.getCell() + "@" + xlsxInfoVo.getSecondTypeName() + "@" + xlsxInfoVo.getChineseName() + "@"+ xlsxInfoVo.getValue()); + wpsPptxJudgementDto.setContent(xlsxInfoVo.getSheetName() + "@" + xlsxInfoVo.getType() + "@" + xlsxInfoVo.getCell() + "@" + xlsxInfoVo.getSecondType() + "@" + xlsxInfoVo.getEnglishName() + "@"+ xlsxInfoVo.getValue()); + wpsPptxJudgementDto.setScoreRate("1"); + xlsxInfos.add(wpsPptxJudgementDto); + } + // TODO + if (index == "1") { + List randomItems = getRandomItems(xlsxInfos, 50); + return randomItems; + } else { + return xlsxInfos; + } + } + + // 扁平化多维列表 + public static List getRandomItems(List list, int count) { + List copy = new ArrayList<>(list); // 创建副本,避免修改原列表 + Collections.shuffle(copy); // 打乱顺序 + return copy.subList(0, Math.min(count, copy.size())); // 抽取前count个 + } + + // 支持所有类型的单元格 + private static String getCellValue(Cell cell) { + switch (cell.getCellType()) { + case STRING: + return cell.getStringCellValue(); + + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue().toString(); + } + return String.valueOf(cell.getNumericCellValue()); + + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + + case FORMULA: + return cell.getCellFormula(); // 或者用 cell.getNumericCellValue() 等 + + case BLANK: + return ""; + + default: + return "[UNKNOWN TYPE]"; + } + } + + public static int columnLetterToNumber(String column) { + int result = 0; + for (int i = 0; i < column.length(); i++) { + result = result * 26 + (column.charAt(i) - 'A' + 1); + } + return result; + } + + + public static boolean isCellInRange(String cellRef, String rangeRef) { + int[] cellPos = parseCellRef(cellRef); // [col, row] + int[] startPos = parseCellRef(rangeRef.split(":")[0]); // A2 + int[] endPos = parseCellRef(rangeRef.split(":")[1]); // O18 + + return cellPos[0] >= startPos[0] && cellPos[0] <= endPos[0] && + cellPos[1] >= startPos[1] && cellPos[1] <= endPos[1]; + } + + public static int[] parseCellRef(String ref) { + String colLetters = ref.replaceAll("\\d", ""); // 取字母部分,如 A + String rowDigits = ref.replaceAll("\\D", ""); // 取数字部分,如 7 + + int colNum = columnNameToIndex(colLetters); + int rowNum = Integer.parseInt(rowDigits); + return new int[]{colNum, rowNum}; + } + + public static int columnNameToIndex(String column) { + int result = 0; + for (int i = 0; i < column.length(); i++) { + result = result * 26 + (column.charAt(i) - 'A' + 1); + } + return result; + } + + + public static List getChartTypes() { + // 创建一个图表 + List chartTypes = new ArrayList<>(); + chartTypes.add(new ChartTypeEntry("c:barChart", "横向柱状图")); + chartTypes.add(new ChartTypeEntry("c:lineChart", "折线图")); + chartTypes.add(new ChartTypeEntry("c:pieChart", "饼图")); + chartTypes.add(new ChartTypeEntry("c:areaChart", "面积图")); + chartTypes.add(new ChartTypeEntry("c:scatterChart", "散点图")); + chartTypes.add(new ChartTypeEntry("c:bubbleChart", "气泡图")); + chartTypes.add(new ChartTypeEntry("c:radarChart", "雷达图")); + chartTypes.add(new ChartTypeEntry("c:stockChart", "股票图")); + chartTypes.add(new ChartTypeEntry("c:surfaceChart", "曲面图")); + chartTypes.add(new ChartTypeEntry("c:ofPieChart", "分类饼图(饼中饼/条中饼)")); + chartTypes.add(new ChartTypeEntry("c:doughnutChart", "圆环图")); + chartTypes.add(new ChartTypeEntry("c:bar3DChart", "3D柱状图")); + chartTypes.add(new ChartTypeEntry("c:line3DChart", "3D折线图")); + chartTypes.add(new ChartTypeEntry("c:area3DChart", "3D面积图")); + chartTypes.add(new ChartTypeEntry("c:surface3DChart", "3D曲面图")); + return chartTypes; + } + + public static void addXlsxInfo(String sheetName, String chineseName, String englishName, String value, String type, String typeName, String secondType, String secondTypeName, String cellName, List xlsxInfoVos) { + XlsxInfoVo xlsxInfoVo = new XlsxInfoVo(); + xlsxInfoVo.setSheetNumber(sheetName); + // 拼接数据 + sheetName = "【sheet" + sheetName + "】"; + xlsxInfoVo.setSheetName(sheetName); + xlsxInfoVo.setChineseName(chineseName); + xlsxInfoVo.setEnglishName(englishName); + xlsxInfoVo.setValue(value); + xlsxInfoVo.setType(type); + xlsxInfoVo.setTypeName(typeName); + xlsxInfoVo.setCell(cellName); + xlsxInfoVo.setSecondType(secondType); + xlsxInfoVo.setSecondTypeName(secondTypeName); + xlsxInfoVos.add(xlsxInfoVo); + } + + public static String drawingXmlInfo(XmlCursor cursor, String sheetName) throws Exception { + // 判断是否存在表格数据 标签数据 + try (XmlCursor drawingXml = cursor.newCursor()) { + drawingXml.selectPath(namespace + "./ns:drawing"); + int drawingIndex = 0; + while (drawingXml.toNextSelection()) { + try (XmlCursor drawingXmlCursor = drawingXml.getObject().newCursor()) { +// System.out.println(drawingXmlCursor.xmlText()); + drawingXmlCursor.selectPath("declare namespace r='http://schemas.openxmlformats.org/officeDocument/2006/relationships' ./@r:id"); + if (drawingXmlCursor.toNextSelection()) { + String rId = drawingXmlCursor.getTextValue(); + + } + } + drawingIndex ++; + } + } + return ""; + } + + /** + * 从 Excel 文件中提取共享字符串 + * @param excelFilePath Excel 文件路径 + * @return 共享字符串数组 + * @throws Exception 如果文件不存在或无法读取,则抛出异常 + */ + public static String[] extractSharedStrings(String excelFilePath) throws Exception { + // 解压 Excel 文件并读取 sharedStrings.xml + ZipFile zipFile = new ZipFile(excelFilePath); + // 找到 sharedStrings.xml 文件的路径 + ZipEntry sharedStringsEntry = zipFile.getEntry("xl/sharedStrings.xml"); + + // 如果找不到 sharedStrings.xml,则返回空数组 + if (sharedStringsEntry == null) { + zipFile.close(); + return new String[0]; + } + + // 读取 sharedStrings.xml 文件 + InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); + byte[] content = inputStream.readAllBytes(); + String xmlContent = new String(content); + + // 使用 XmlObject 解析 sharedStrings.xml + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + // 查询所有 sharedString 元素 + cursor.selectPath(namespace + "//ns:sst/ns:si"); + + List sharedStringList = new ArrayList<>(); + + // 遍历每个 sharedString 元素并提取文本值 + while (cursor.toNextSelection()) { + // 获取单元格内容 + String sharedString = cursor.getTextValue(); + sharedStringList.add(sharedString); + } + + cursor.dispose(); // 关闭游标 + zipFile.close(); // 关闭 zip 文件 + + // 将 List 转换为数组并返回 + return sharedStringList.toArray(new String[0]); + } + + public static XmlCursor extractStyle(List xlsxInfoVos, int serialNumber, XmlCursor cursor, boolean isTrue, String colName, String sheetName, boolean isCellStyle) throws Exception { + XmlCursor xmlCursor = cursor.newCursor(); + if (isTrue) { + // 自定义格式 + xmlCursor.selectPath(namespace + "//ns:cellXfs/ns:xf"); + } else { +// xlsxStyleCellStyleXfxVo.setCol(colName); + xmlCursor.selectPath(namespace + "//ns:cellStyleXfs/ns:xf"); + } + int index = 0; + String numFmtId = null; + String fontId = null; + String fillId = null; + String borderId = null; + String applyFont = null; + String applyFill = null; + String applyProtection = null; + String wrapText = null; + String textRotation = null; + String shrinkToFit = null; + String applyBorder = null; + String applyNumberFormat = null; + String applyAlignment = null; + while(xmlCursor.toNextSelection()) { + // 通过序号查询指定数据 + if (index == serialNumber) { + XmlCursor designateXml = xmlCursor.getObject().newCursor(); + // 数字格式的ID + numFmtId = designateXml.getAttributeText(new QName("numFmtId")); + // 字体的ID + fontId = designateXml.getAttributeText(new QName("fontId")); + // 填充(背景颜色)的ID + fillId = designateXml.getAttributeText(new QName("fillId")); + // 边框样式的ID + borderId = designateXml.getAttributeText(new QName("borderId")); + // 字体设置 + applyFont = designateXml.getAttributeText(new QName("applyFont")); + // 背景填充 + applyFill = designateXml.getAttributeText(new QName("applyFill")); + // 表示不应用保护设置(即不锁定单元格) + applyProtection = designateXml.getAttributeText(new QName("applyProtection")); + // 文本旋转45度 + wrapText = designateXml.getAttributeText(new QName("wrapText")); + // 文本缩进一级。 + textRotation = designateXml.getAttributeText(new QName("textRotation")); + // 启用文本自动缩小以适应单元格。 + shrinkToFit = designateXml.getAttributeText(new QName("shrinkToFit")); + applyBorder = designateXml.getAttributeText(new QName("applyBorder")); + applyNumberFormat = designateXml.getAttributeText(new QName("applyNumberFormat")); + applyAlignment = designateXml.getAttributeText(new QName("applyAlignment")); + designateXml.selectPath(namespace + "./ns:alignment"); + if (designateXml.toNextSelection()) { + // 获取 水平对齐 属性 + String horizontal = designateXml.getAttributeText(new QName("horizontal")); + // 获取 垂直对齐 属性 + String vertical = designateXml.getAttributeText(new QName("vertical")); + if (isTrue) { + addXlsxInfo(sheetName, "水平对齐", "vertical", horizontal, "cell", "单元格", "alignment", "对齐", colName, xlsxInfoVos); + addXlsxInfo(sheetName, "垂直对齐", "horizontal", vertical, "cell", "单元格", "alignment", "对齐", colName, xlsxInfoVos); + } else { +// xlsxStyleCellStyleXfxVo.setVertical(vertical); +// xlsxStyleCellStyleXfxVo.setHorizontal(horizontal); + } + } + break; + } + index ++; + } + // 接着进行查找 + if (numFmtId != null) { + try (XmlCursor cursorNumFmts = cursor.newCursor()) { + cursorNumFmts.selectPath(namespace + "//ns:numFmts/ns:numFmt"); + while (cursorNumFmts.toNextSelection()) { + // 获取 numFmtId 属性 + String numFmtIdInfo = cursorNumFmts.getAttributeText(new QName("numFmtId")); + if (numFmtIdInfo.equals(numFmtId)) { + // 获取 formatCode 属性 + String formatCode = cursorNumFmts.getAttributeText(new QName("formatCode")); + if (isTrue) { + addXlsxInfo(sheetName, "formatCode", "formatCode", formatCode, "cell", "单元格", "formatCode", "自定义格式", colName, xlsxInfoVos); + } else { +// xlsxStyleCellStyleXfxVo.setFormatCode(formatCode); + } + break; + } + } + } + } + xmlCursor.dispose(); + // 应用了字体设置 + if (fontId != null) { + try (XmlCursor cursorFonts = cursor.newCursor()) { + cursorFonts.selectPath(namespace + "//ns:fonts/ns:font"); + int fontIndex = 0; + while (cursorFonts.toNextSelection()) { + if (fontIndex == Integer.parseInt(fontId)) { + // 判断是否加粗(是否存在 标签) + try (XmlCursor cursorsFontBold = cursorFonts.getObject().newCursor()){ + cursorsFontBold.selectPath(namespace + "./ns:b"); + boolean isBold = cursorsFontBold.toNextSelection(); + addXlsxInfo(sheetName, "文本加粗", "ns:b", String.valueOf(isBold), "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + cursorsFontBold.dispose(); + } + // 获取 sz 的 val 属性(字体大小) + try (XmlCursor cursorsFontSz = cursorFonts.getObject().newCursor()){ + cursorsFontSz.selectPath(namespace + "./ns:sz"); + if (cursorsFontSz.toNextSelection()) { + String fontSize = cursorsFontSz.getAttributeText(new QName("val")); + addXlsxInfo(sheetName, "文本大小", "ns:sz", fontSize, "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + } + cursorsFontSz.dispose(); + } + // 获取 color 的 rgb 属性(字体颜色) + try (XmlCursor cursorsFontColor = cursorFonts.getObject().newCursor()){ + cursorsFontColor.selectPath(namespace + "./ns:color"); + if (cursorsFontColor.toNextSelection()) { + String fontColor = cursorsFontColor.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "文本颜色", "ns:color", fontColor, "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + } + cursorsFontColor.dispose(); + } + // 获取 name 的 val 属性(字体名称) + try (XmlCursor cursorsFontName = cursorFonts.getObject().newCursor()){ + cursorsFontName.selectPath(namespace + "./ns:name"); + if (cursorsFontName.toNextSelection()) { + String fontName = cursorsFontName.getAttributeText(new QName("val")); + addXlsxInfo(sheetName, "字体名称", "ns:name", fontName, "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + + } + cursorsFontName.dispose(); + } + // 获取 charset 的 val 属性(字符集) + try (XmlCursor cursorsFontCharset = cursorFonts.getObject().newCursor()){ + cursorsFontCharset.selectPath(namespace + "./ns:charset"); + if (cursorsFontCharset.toNextSelection()) { + String fontCharset = cursorsFontCharset.getAttributeText(new QName("val")); + addXlsxInfo(sheetName, "字体字符集", "ns:charset", fontCharset, "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + + } + cursorsFontCharset.dispose(); + } + // 获取 scheme 的 val 属性(字体方案) + try (XmlCursor cursorsFontScheme = cursorFonts.getObject().newCursor()){ + cursorsFontScheme.selectPath(namespace + "./ns:scheme"); + if (cursorsFontScheme.toNextSelection()) { + String fontScheme = cursorsFontScheme.getAttributeText(new QName("val")); + addXlsxInfo(sheetName, "字体方案", "ns:scheme", fontScheme, "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + + } + cursorsFontScheme.dispose(); + } + break; + } + fontIndex ++; + } + cursorFonts.dispose(); + } + } + // 背景颜色填充 + if (fillId != null) { + try (XmlCursor cursorFills = cursor.newCursor()) { + if (!isCellStyle) { + cursorFills.selectPath(namespace + "//ns:fills/ns:fill"); + int fillIndex = 0; + while (cursorFills.toNextSelection()) { + if (fillIndex == Integer.parseInt(fillId)) { + try (XmlCursor cursorsFill = cursorFills.getObject().newCursor()) { + cursorsFill.selectPath(namespace + "./ns:patternFill"); + if (cursorsFill.toNextSelection()) { + // 获取 patternType 属性(填充样式) + String patternType = cursorsFill.getAttributeText(new QName("patternType")); + addXlsxInfo(sheetName, "填充样式", "ns:scheme/@theme", patternType, "cell", "单元格", "fill", "背景", colName, xlsxInfoVos); + // 获取 fgColor 的 theme 和 tint 属性(填充颜色) + try (XmlCursor cursorsFgColor = cursorsFill.getObject().newCursor()) { + cursorsFgColor.selectPath(namespace + "./ns:fgColor"); + if (cursorsFgColor.toNextSelection()) { + String theme = cursorsFgColor.getAttributeText(new QName("theme")); + String rgb = cursorsFgColor.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "填充theme", "ns:scheme/@theme", theme, "cell", "单元格", "fill", "背景", colName, xlsxInfoVos); + addXlsxInfo(sheetName, "填充颜色", "ns:fgColor/@rgb", rgb, "cell", "单元格", "fill", "背景", colName, xlsxInfoVos); + } + cursorsFgColor.dispose(); + } + // 获取 bgColor 的 indexed 属性(背景色) + try (XmlCursor cursorsBgColor = cursorFills.getObject().newCursor()) { + cursorsBgColor.selectPath(namespace + "./ns:bgColor"); + if (cursorsBgColor.toNextSelection()) { + String rgb = cursorsBgColor.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "背景色", "ns:bgColor/@rgb", rgb, "cell", "单元格", "fill", "背景", colName, xlsxInfoVos); + } + cursorsBgColor.dispose(); + } + } + } + break; + } + fillIndex ++; + } + cursorFills.dispose(); + } else { + XmlCursor cursorDxf = cursorFills.newCursor(); + cursorDxf.selectPath(namespace + "//ns:dxfs/ns:dxf"); + int dxfIndex = 0; + while (cursorDxf.toNextSelection()) { + if (dxfIndex == dxfId) { + XmlCursor fontXmlCursor = cursorDxf.getObject().newCursor(); + fontXmlCursor.selectPath(namespace + "./ns:font/color/@rgb"); + if (fontXmlCursor.toNextSelection()) { + addXlsxInfo(sheetName, "文本颜色", "ns:color", fontXmlCursor.getTextValue(), "cell", "单元格", "font", "字体", colName, xlsxInfoVos); + } + fontXmlCursor.dispose(); + XmlCursor fillXmlCursor = cursorDxf.getObject().newCursor(); + fillXmlCursor.selectPath(namespace + "./ns:fill/ns:patternFill/ns:bgColor/@rgb"); + if (fillXmlCursor.toNextSelection()) { + addXlsxInfo(sheetName, "背景色", "ns:bgColor/@rgb", fillXmlCursor.getTextValue(), "cell", "单元格", "fill", "背景", colName, xlsxInfoVos); + } + fillXmlCursor.dispose(); + } + } + } + } + } + // 是否启用了边框设置 + if (borderId != null) { + try (XmlCursor cursorBorder = cursor.newCursor()) { + cursorBorder.selectPath(namespace + "//ns:borders/ns:border"); + int borderIndex = 0; + while (cursorBorder.toNextSelection()) { + if (borderIndex == Integer.parseInt(borderId)) { + // 定义对角线边框。常用于带有斜线的单元格。如果 标签存在,表示对角线有边框,否则没有。 + try (XmlCursor cursorsDiagonal = cursorBorder.getObject().newCursor()){ + cursorsDiagonal.selectPath(namespace + "./ns:diagonal"); + boolean isDiagonal = cursorsDiagonal.toNextSelection(); + addXlsxInfo(sheetName, "斜线的单元格", "ns:diagonal", String.valueOf(isDiagonal), "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + cursorsDiagonal.dispose(); + } + // 获取上边框 + try (XmlCursor cursorsTop = cursorBorder.getObject().newCursor()){ + XmlCursor cursorsTops = cursorBorder.getObject().newCursor(); + cursorsTops.selectPath(namespace + "./ns:top"); + if (cursorsTops.toNextSelection()) { + // 样式 + String topStyle = cursorsTops.getAttributeText(new QName("style")); + addXlsxInfo(sheetName, "上边框", "ns:top/@style", topStyle, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + + } + cursorsTops.selectPath(namespace + "./ns:color"); + if (cursorsTops.toNextSelection()) { + String topColor = cursorsTops.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "上边框颜色", "ns:top/@rgb", topColor, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + + } + cursorsTops.dispose(); + cursorsTop.dispose(); + } + // 获取下边框 + try (XmlCursor cursorsBottom = cursorBorder.getObject().newCursor()){ + XmlCursor cursorsBottoms = cursorBorder.getObject().newCursor(); + cursorsBottoms.selectPath(namespace + "./ns:bottom"); + if (cursorsBottoms.toNextSelection()) { + // 样式 + String bottomStyle = cursorsBottoms.getAttributeText(new QName("style")); + addXlsxInfo(sheetName, "下边框", "ns:top/@rgb", bottomStyle, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + + } + // 颜色 + cursorsBottoms.selectPath(namespace + "./ns:color"); + if (cursorsBottoms.toNextSelection()) { + String bottomColor = cursorsBottoms.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "下边框颜色", "ns:top/@rgb", bottomColor, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + + } + cursorsBottoms.dispose(); + cursorsBottom.dispose(); + } + // 获取左边框 + try (XmlCursor cursorsLeft = cursorBorder.getObject().newCursor()){ + XmlCursor cursorsLefts = cursorBorder.getObject().newCursor(); + cursorsLefts.selectPath(namespace + "./ns:left"); + if (cursorsLefts.toNextSelection()) { + // 样式 + String leftStyle = cursorsLefts.getAttributeText(new QName("style")); + addXlsxInfo(sheetName, "左边框", "ns:top/@rgb", leftStyle, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + } + // 颜色 + cursorsLefts.selectPath(namespace + "./ns:color"); + if (cursorsLefts.toNextSelection()) { + String leftColor = cursorsLefts.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "左边框颜色", "ns:top/@rgb", leftColor, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + + } + cursorsLefts.dispose(); + cursorsLeft.dispose(); + } + // 获取右边框 + try (XmlCursor cursorsRight = cursorBorder.getObject().newCursor()){ + XmlCursor cursorsRights = cursorBorder.getObject().newCursor(); + cursorsRights.selectPath(namespace + "./ns:right"); + if (cursorsRights.toNextSelection()) { + // 样式 + String rightStyle = cursorsRights.getAttributeText(new QName("style")); + addXlsxInfo(sheetName, "右边框", "ns:top/@rgb", rightStyle, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + + } + // 颜色 + cursorsRights.selectPath(namespace + "./ns:color"); + if (cursorsRights.toNextSelection()) { + String rightColor = cursorsRights.getAttributeText(new QName("rgb")); + addXlsxInfo(sheetName, "右边框颜色", "ns:top/@rgb", rightColor, "cell", "单元格", "border", "边框", colName, xlsxInfoVos); + } + cursorsRights.dispose(); + cursorsRight.dispose(); + } + break; + } + borderIndex ++; + } + cursorBorder.dispose(); + } + } + return cursor; + } + + public static XmlCursor extractStyleXml(String excelFilePath) throws Exception { + // 解压 Excel 文件并读取 sharedStrings.xml + ZipFile zipFile = new ZipFile(excelFilePath); + // 找到 sharedStrings.xml 文件的路径 + ZipEntry sharedStringsEntry = zipFile.getEntry("xl/styles.xml"); + + // 如果找不到 sharedStrings.xml,则返回空数组 + if (sharedStringsEntry == null) { + zipFile.close(); + return null; + } + + // 读取 sharedStrings.xml 文件 + InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); + byte[] content = inputStream.readAllBytes(); + String xmlContent = new String(content); + // 使用 XmlObject 解析 sharedStrings.xml + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + zipFile.close(); // 关闭 zip 文件 + return cursor; + } + + public static XmlCursor extractConnectionsXml(String excelFilePath) throws Exception { + // 解压 Excel 文件并读取 sharedStrings.xml + ZipFile zipFile = new ZipFile(excelFilePath); + // 找到 sharedStrings.xml 文件的路径 + ZipEntry sharedStringsEntry = zipFile.getEntry("xl/connections.xml"); + + // 如果找不到 sharedStrings.xml,则返回空数组 + if (sharedStringsEntry == null) { + zipFile.close(); + return null; + } + + // 读取 sharedStrings.xml 文件 + InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); + byte[] content = inputStream.readAllBytes(); + String xmlContent = new String(content); + // 使用 XmlObject 解析 sharedStrings.xml + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + zipFile.close(); // 关闭 zip 文件 + return cursor; + } + +// public static String getDrawingInfo(String excelFilePath) throws Exception { +// // 解压 Excel 文件并读取 sharedStrings.xml +// ZipFile zipFile = new ZipFile(excelFilePath); +// Enumeration entries = zipFile.entries(); +// while (entries.hasMoreElements()) { +// ZipEntry entry = entries.nextElement(); +// String entryName = entry.getName(); +// // 1、获取sheet*.xml.rels下面的文件 +// if (entryName.startsWith("xl/worksheets/_rels") && entryName.endsWith(".xml.rels")) { +// XlsxDrawingSheetVo xlsxDrawingSheetVo = new XlsxDrawingSheetVo(); +// // 读取数据 +// ZipEntry sharedStringsEntry = zipFile.getEntry(entryName); +// InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); +// byte[] content = inputStream.readAllBytes(); +// String xmlContent = new String(content); +// // 使用 XmlObject 解析 sharedStrings.xml +// XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); +// XmlCursor sheetCursor = xmlObject.newCursor(); +// // 正则匹配:提取连续的数字 +// StringBuilder numbers = new StringBuilder(); +// for (char c : entryName.toCharArray()) { +// // 检查是否是数字 +// if (Character.isDigit(c)) { +// numbers.append(c); +// } +// } +// // 第几个sheet对应的索引 +// int number = Integer.parseInt(numbers.toString()); +// xlsxDrawingSheetVo.setSheetNumber(String.valueOf(number)); +// sheetCursor.selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship"); +// while (sheetCursor.toNextSelection()) { +// // 2、开始获取内容 +// // 2-1、获取ID +// String id = sheetCursor.getAttributeText(new QName("Id")); +// // 2-2、获取文件路径 +// String target = sheetCursor.getAttributeText(new QName("Target")); +// // 3、在往下获取对应的文件,及文件路径 +// // 进行路径字符串替换 .. -> xl +// String newTarget = target.replace("..", "xl"); +// System.out.println("newTarget = " + newTarget); +// ZipEntry sharedStringsEntry2 = zipFile.getEntry(newTarget); +// InputStream inputStream2 = zipFile.getInputStream(sharedStringsEntry2); +// byte[] content2 = inputStream2.readAllBytes(); +// String xmlContent2 = new String(content2); +// // 使用 XmlObject 解析 sharedStrings.xml +// XmlObject xmlObject2 = XmlObject.Factory.parse(xmlContent2); +// XmlCursor sheetCursor2 = xmlObject2.newCursor(); +// // 4、判断是否存在对应文件的_rels * 文件 +// // 路径自定义 xl/drawings/drawing1.xml +// // 分割字符串 +// String[] split = newTarget.split("/"); +// String newRelsFilePath = split[0]+"/"+split[1]+"/_rels/"+split[2]+".rels"; +// // 在进行获取数据,获取文件路径 +// ZipEntry sharedStringsEntry3 = zipFile.getEntry(newRelsFilePath); +// if (sharedStringsEntry3 != null) { +// InputStream inputStream3 = zipFile.getInputStream(sharedStringsEntry3); +// byte[] content3 = inputStream3.readAllBytes(); +// String xmlContent3 = new String(content3); +// // 使用 XmlObject 解析 sharedStrings.xml +// XmlObject xmlObject3 = XmlObject.Factory.parse(xmlContent3); +// XmlCursor sheetCursor3 = xmlObject3.newCursor(); +// sheetCursor3.selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship"); +// while (sheetCursor3.toNextSelection()) { +// // 2、开始获取内容 +// // 2-1、获取ID +// String id2 = sheetCursor3.getAttributeText(new QName("Id")); +// // 2-2、获取文件路径 +// String target2 = sheetCursor3.getAttributeText(new QName("Target")); +// // 3、在往下获取对应的文件,及文件路径 +// System.out.println("target2 = " + target2); +// // 进行路径字符串替换 .. -> xl +// String newTarget2 = target2.replace("..", "xl"); +// } +// } +// +// } +// } +// } +// zipFile.close(); // 关闭 zip 文件 +// return ""; +// } + + public static String getDrawingInfos(String excelFilePath) throws Exception { + try (ZipFile zipFile = new ZipFile(excelFilePath)) { + Enumeration entries = zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String entryName = entry.getName(); + + if (entryName.startsWith("xl/worksheets/_rels") && entryName.endsWith(".xml.rels")) { + String xmlContent = readZipEntry(zipFile, entryName); + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + + // 正则匹配:提取连续的数字 + StringBuilder numbers = new StringBuilder(); + for (char c : entryName.toCharArray()) { + // 检查是否是数字 + if (Character.isDigit(c)) { + numbers.append(c); + } + } + // 第几个sheet对应的索引 + int number = Integer.parseInt(numbers.toString()); + List drawingVOS = new ArrayList<>(); + cursor.selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship"); + while (cursor.toNextSelection()) { + // 2、开始获取内容 + XlsxDrawingSheetVo xlsxDrawingSheetVo = new XlsxDrawingSheetVo(); + String rId = cursor.getAttributeText(new QName("Id")); + String target = cursor.getAttributeText(new QName("Target")); + String nextPath = target.replace("..", "xl"); + xlsxDrawingSheetVo.setSheetNumber(String.valueOf(number)); + xlsxDrawingSheetVo.setRId(rId); + traverseRels(zipFile, nextPath, xlsxDrawingSheetVo, String.valueOf(number), drawingVOS); + } + drawingSheetVOS.add(drawingVOS); + } + } + } + return ""; + } + // 递归遍历 _rels 下级文件 + private static void traverseRels(ZipFile zipFile, String path, XlsxDrawingSheetVo xlsxDrawingSheetVo, String sheetName, List drawingVOS) throws Exception { + if (path == null || path.isEmpty()) { + return; + } + // 1、获取对应文件内容 + String content = readZipEntry(zipFile, path); + // 文件不存在,终止链 + if (content == null) { + return; + } + XmlObject xmlObject = XmlObject.Factory.parse(content); + XmlCursor cursor = xmlObject.newCursor(); + xlsxDrawingSheetVo.setXmlCursor(cursor); +// System.out.println(cursor.xmlText()); + // 生成对应的 .rels 路径 + String[] parts = path.split("/"); + String relsPath = parts[0] + "/" + parts[1] + "/_rels/" + parts[2] + ".rels"; + xlsxDrawingSheetVo.setTypeName(path); + drawingVOS.add(xlsxDrawingSheetVo); + String contents = readZipEntry(zipFile, relsPath); + // 文件不存在,终止链 + if (contents == null) { + return; + } + XmlObject xmlObjects = XmlObject.Factory.parse(contents); + XmlCursor cursors = xmlObjects.newCursor(); + cursors.selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship"); + // 2、开始获取内容 + while (cursors.toNextSelection()) { +// System.out.println(cursors.xmlText()); + XlsxDrawingSheetVo xlsxDrawingSheetVos = new XlsxDrawingSheetVo(); + String target = cursors.getAttributeText(new QName("Target")); + String rId = cursors.getAttributeText(new QName("Id")); + xlsxDrawingSheetVos.setRId(rId); + xlsxDrawingSheetVos.setSheetNumber(sheetName); + xlsxDrawingSheetVos.setRId(rId); + if (target == null) { + continue; + } + String newPath = ""; + if (!target.contains("./")) { + // 如果没有包含路径,可能是在原本的路径下面 + newPath = parts[0] + "/" + parts[1] + "/" + target; + } else { + newPath = target.replace("..", "xl"); + } + +// System.out.println("Found path: " + newPath); + // 递归继续查找下一级 + if (zipFile.getEntry(relsPath) != null) { + traverseRels(zipFile, newPath, xlsxDrawingSheetVos, sheetName, drawingVOS); + } + } + } + // 工具方法:从 zip 文件中读取条目内容 + private static String readZipEntry(ZipFile zipFile, String entryName) throws IOException { + ZipEntry entry = zipFile.getEntry(entryName); + if (entry == null) { + return null; + } + try (InputStream is = zipFile.getInputStream(entry)) { + return new String(is.readAllBytes()); + } + } + + + public static List extractChartXml(String excelFilePath) throws Exception { + List xlsxCharVos = new ArrayList<>(); + // 解压 Excel 文件并读取 sharedStrings.xml + ZipFile zipFile = new ZipFile(excelFilePath); + + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String entryName = entry.getName(); + // 检查是否属于 xl/charts/ 目录下的图表文件 + if (entryName.startsWith("xl/charts/chart") && entryName.endsWith(".xml")) { + // 读取数据 + ZipEntry sharedStringsEntry = zipFile.getEntry(entryName); + InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); + byte[] content = inputStream.readAllBytes(); + String xmlContent = new String(content); + // 使用 XmlObject 解析 sharedStrings.xml + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + // 正则匹配:提取连续的数字 + StringBuilder numbers = new StringBuilder(); + for (char c : entryName.toCharArray()) { + // 检查是否是数字 + if (Character.isDigit(c)) { + numbers.append(c); + } + } + int number = Integer.parseInt(numbers.toString()); + // 如果列表为空,则创建新的xlsxCharVO对象 + // 如果列表不为空,则检查最后一个元素的count是否等于当前数字 + Optional result = xlsxCharVos.stream().filter(xlsxCharVO -> number == xlsxCharVO.getCount()).findFirst(); + if (result.isEmpty()) { + // 创建新的xlsxCharVO对象 + XlsxCharVO xlsxCharVO = new XlsxCharVO(); + xlsxCharVO.setCount(number); + xlsxCharVO.setChars(cursor); + xlsxCharVos.add(xlsxCharVO); + } else { + XlsxCharVO lastXlsxCharVO = result.get(); + lastXlsxCharVO.setChars(cursor); + xlsxCharVos.removeIf(xlsxCharVO -> number == xlsxCharVO.getCount()); + xlsxCharVos.add(lastXlsxCharVO); + } + } + if (entryName.startsWith("xl/charts/style") && entryName.endsWith(".xml")) { + // 读取数据 + ZipEntry sharedStringsEntry = zipFile.getEntry(entryName); + InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); + byte[] content = inputStream.readAllBytes(); + String xmlContent = new String(content); + // 使用 XmlObject 解析 sharedStrings.xml + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + // 正则匹配:提取连续的数字 + StringBuilder numbers = new StringBuilder(); + for (char c : entryName.toCharArray()) { + // 检查是否是数字 + if (Character.isDigit(c)) { + numbers.append(c); + } + } + int number = Integer.parseInt(numbers.toString()); + // 如果列表为空,则创建新的xlsxCharVO对象 + // 如果列表不为空,则检查最后一个元素的count是否等于当前数字 + Optional result = xlsxCharVos.stream().filter(xlsxCharVO -> number == xlsxCharVO.getCount()).findFirst(); + if (result.isEmpty()) { + // 创建新的xlsxCharVO对象 + XlsxCharVO xlsxCharVO = new XlsxCharVO(); + xlsxCharVO.setCount(number); + xlsxCharVO.setStyle(cursor); + xlsxCharVos.add(xlsxCharVO); + } else { + XlsxCharVO lastXlsxCharVO = result.get(); + lastXlsxCharVO.setStyle(cursor); + xlsxCharVos.removeIf(xlsxCharVO -> number == xlsxCharVO.getCount()); + xlsxCharVos.add(lastXlsxCharVO); + } + } + if (entryName.startsWith("xl/charts/color") && entryName.endsWith(".xml")) { + // 读取数据 + ZipEntry sharedStringsEntry = zipFile.getEntry(entryName); + InputStream inputStream = zipFile.getInputStream(sharedStringsEntry); + byte[] content = inputStream.readAllBytes(); + String xmlContent = new String(content); + // 使用 XmlObject 解析 sharedStrings.xml + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + // 正则匹配:提取连续的数字 + StringBuilder numbers = new StringBuilder(); + for (char c : entryName.toCharArray()) { + // 检查是否是数字 + if (Character.isDigit(c)) { + numbers.append(c); + } + } + int number = Integer.parseInt(numbers.toString()); + // 如果列表为空,则创建新的xlsxCharVO对象 + // 如果列表不为空,则检查最后一个元素的count是否等于当前数字 + Optional result = xlsxCharVos.stream().filter(xlsxCharVO -> number == xlsxCharVO.getCount()).findFirst(); + if (result.isEmpty()) { + // 创建新的xlsxCharVO对象 + XlsxCharVO xlsxCharVO = new XlsxCharVO(); + xlsxCharVO.setCount(number); + xlsxCharVO.setColors(cursor); + xlsxCharVos.add(xlsxCharVO); + } else { + XlsxCharVO lastXlsxCharVO = result.get(); + lastXlsxCharVO.setColors(cursor); + xlsxCharVos.removeIf(xlsxCharVO -> number == xlsxCharVO.getCount()); + xlsxCharVos.add(lastXlsxCharVO); + } + } + } + zipFile.close(); // 关闭 zip 文件 + return xlsxCharVos; + } + + + public static String getNamespace(String xmlText) { + // 1-1、创建最全的命名空间 + Pattern pattern = Pattern.compile("xmlns:(\\w+)=\"([^\"]+)\""); + Matcher matcher = pattern.matcher(xmlText); + Map namespaces = new HashMap<>(); + while (matcher.find()) { + String prefix = matcher.group(1); + String uri = matcher.group(2); + namespaces.put(prefix, uri); + } + StringBuilder xpathBuilder = new StringBuilder(); + namespaces.forEach((prefix, uri) -> + xpathBuilder.append("declare namespace ") + .append(prefix) + .append("='") + .append(uri) + .append("' ") + ); + // 2-1、获取出来最全的命名空间 + String allPathx = xpathBuilder.toString(); + return allPathx; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxCharVO.java b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxCharVO.java new file mode 100644 index 0000000..a8cc479 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxCharVO.java @@ -0,0 +1,13 @@ +package com.example.exam.exam.service.wpsexcel; + +import lombok.Data; +import org.apache.xmlbeans.XmlCursor; + +@Data +public class XlsxCharVO { + + private Integer count; + private XmlCursor chars; + private XmlCursor colors; + private XmlCursor style; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxDrawingSheetVo.java b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxDrawingSheetVo.java new file mode 100644 index 0000000..fdce0b3 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxDrawingSheetVo.java @@ -0,0 +1,17 @@ +package com.example.exam.exam.service.wpsexcel; + +import lombok.Data; +import org.apache.xmlbeans.XmlCursor; + +/** + * @author REN + */ +@Data +public class XlsxDrawingSheetVo { + + private String sheetNumber; + private String rId; + private String typeName; + private XmlCursor xmlCursor; + +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxInfoVo.java b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxInfoVo.java new file mode 100644 index 0000000..66e8729 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxInfoVo.java @@ -0,0 +1,30 @@ +package com.example.exam.exam.service.wpsexcel; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class XlsxInfoVo { + + private String sheetNumber; + + private String sheetName; + + private String cell; + + private String chineseName; + + private String englishName; + + private String value; + + private String type; + + private String typeName; + + private String secondType; + + private String secondTypeName; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelService.java b/src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelService.java new file mode 100644 index 0000000..9d00b0c --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelService.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.service.wpsexcel.excel; + + + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +/** + * 判分逻辑集合(wps word) + * + * @author rwb + */ +public interface JudgementForExcelService { + + /** + * word 判分 + * @param stuInfoVo 学生考试信息 + * @return 结果 + */ + Double autoForWpsExcel(StuInfoVo stuInfoVo) throws Exception; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelServiceImpl.java new file mode 100644 index 0000000..b9d1052 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/excel/JudgementForExcelServiceImpl.java @@ -0,0 +1,117 @@ +package com.example.exam.exam.service.wpsexcel.excel; + + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import com.example.exam.exam.service.wpsexcel.JudgementWpsExcelService; +import com.example.exam.exam.service.wpspptx.JudgementWpsPptxService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + + +/** + * @author REN + */ +@Service +public class JudgementForExcelServiceImpl implements JudgementForExcelService { + @Resource + JudgementService judgementService; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + JudgementWpsExcelService judgementWpsExcelService; + @Resource + SystemTenantService systemTenantService; + + @Override + public Double autoForWpsExcel(StuInfoVo stuInfoVo) throws Exception { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + String name = csFileList[0].getName(); + if ("表格".equals(name)) { + File[] cs_file_list = csFiles.listFiles(); + String lastFilePath = cs_file_list[0].getPath(); + File[] lastFiles = cs_file_list[0].listFiles(); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(name)).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + for (File lastFile : lastFiles) { + if (lastFile.getName().contains("文档")) { + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText excelpojo = judgementWpsExcelService.judgementWpsXlsx(Double.parseDouble(quScore), lastFilePath, lastFile.getPath(), examQuestion, judgementStr); + score += excelpojo.getScore(); + judgementStr = excelpojo.getText(); + judgementStr += "

试题得分:" + excelpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(excelpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(excelpojo.getScore() == 0 ? 1 : excelpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(excelpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(name); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(excelpojo.getScore() == 0 ? 1 : excelpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + break; + } + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java b/src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java new file mode 100644 index 0000000..44e4ec2 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java @@ -0,0 +1,40 @@ +package com.example.exam.exam.service.wpspptx; + +/** + * @author REN + */ +public class DeclareNamespaceForPPT { + public static String getNameSpace(String titleName) { + if ("p".equals(titleName)) { + return "http://schemas.openxmlformats.org/presentationml/2006/main"; + } + if ("a".equals(titleName)) { + return "http://schemas.openxmlformats.org/drawingml/2006/main"; + } + if ("pic".equals(titleName)) { + return "http://schemas.openxmlformats.org/drawingml/2006/picture"; + } + if ("c".equals(titleName)) { + return "http://schemas.openxmlformats.org/drawingml/2006/chart"; + } + if ("dgm".equals(titleName)) { + return "http://schemas.openxmlformats.org/drawingml/2006/diagram"; + } + if ("lc".equals(titleName)) { + return "http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas"; + } + if ("v".equals(titleName)) { + return "urn:schemas-microsoft-com:vml"; + } + if ("m".equals(titleName)) { + return "http://schemas.openxmlformats.org/officeDocument/2006/math"; + } + if ("mc".equals(titleName)) { + return "http://schemas.openxmlformats.org/markup-compatibility/2006"; + } + if ("a14".equals(titleName)) { + return "http://schemas.microsoft.com/office/drawing/2010/main"; + } + return ""; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java new file mode 100644 index 0000000..4c51eef --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java @@ -0,0 +1,161 @@ +package com.example.exam.exam.service.wpspptx; + + +import java.util.List; + +/** + * @author REN + */ +public class JudgementConvert { + public static String getConvert(List valueList, String formula){ + String value = ""; + if ("NULL".equals(formula)) { + value = valueList.get(0); + } else if (formula.contains("PageSize")) { + value = pageSize(valueList.get(0), valueList.get(1)); + } else if (formula.contains("timing")) { + // 查询自定义动画效果 + value = getTiming(valueList.get(0), valueList.get(1)); + } else if (formula.contains("Timing_filter")) { + // 动画方向查询 + value = getTimingFilter(valueList.get(0)); + } else if (formula.contains("Timing_Evt")) { + // 动画触发方式 + value = getTimingEvt(valueList.get(0)); + } + // 继续转换 + return value; + } + + private static String getTimingEvt(String evt) { + String evtValue = ""; + if (evt.contains("onBegin")) { + if (evt.contains("downRight")) { + evtValue += "自动开始"; + } + if (evt.contains("onClick")) { + evtValue += "鼠标点击时开始"; + } + if (evt.contains("afterPrevious")) { + evtValue += "在上一个动画之后开始"; + } + if (evt.contains("withPrevious")) { + evtValue += "与上一个动画同时开始"; + } + if (evt.contains("onEnd")) { + evtValue += "在另一个动画结束时开始"; + } + if (evt.contains("onNext")) { + evtValue += "在下一动画前触发"; + } + if (evt.contains("onPrev")) { + evtValue += "在上一动画前触发"; + } + } + return evtValue; + } + + private static String getTimingFilter(String filter) { + String filterValue = ""; + if (filter.contains("strips")) { + if (filter.contains("downRight")) { + filterValue += "从左上往右下飞入"; + } + if (filter.contains("upLeft")) { + filterValue += "从右下往左上飞入"; + } + if (filter.contains("upRight")) { + filterValue += "从左下往右上飞入"; + } + if (filter.contains("downLeft")) { + filterValue += "从右上往左下飞入"; + } + } + return filterValue; + } + private static String getTiming(String transition, String filter){ + String transitionValue = ""; + String filterValue = ""; + if ("in".equals(transition)) { + transitionValue = "进入动画"; + if ("fade".equals(filter)) { + filterValue = "淡入淡出"; + } else if ("fly(fromLeft)".equals(filter)) { + filterValue = "飞入(从左)"; + } else if ("fly(fromRight)".equals(filter)) { + filterValue = "飞入(从右)"; + } else if ("fly(fromTop)".equals(filter)) { + filterValue = "飞入(从上)"; + } else if ("fly(fromBottom)".equals(filter)) { + filterValue = "飞入(从下)"; + } else if ("strips(downRight)".equals(filter)) { + filterValue = "条纹(右下)"; + } else if ("strips(upLeft)".equals(filter)) { + filterValue = "条纹(左上)"; + } else if ("checkerboard(across)".equals(filter)) { + filterValue = "棋盘格(横向)"; + } else if ("wipe(right)".equals(filter)) { + filterValue = "擦除(从左向右)"; + } else if ("wipe(left)".equals(filter)) { + filterValue = "擦除(从右向左)"; + } else if ("randomBars(vertical)".equals(filter)) { + filterValue = "随机条形(垂直)"; + } else if ("zoom".equals(filter)) { + filterValue = "缩放"; + } else if ("circle(out)".equals(filter)) { + filterValue = "圆形展开"; + } else if ("plus(out)".equals(filter)) { + filterValue = "十字形展开"; + } else if ("diamond(out)".equals(filter)) { + filterValue = "菱形展开"; + } else if ("shape(circle)".equals(filter)) { + filterValue = "使用形状(圆形)"; + } + } else if ("out".equals(transition)) { + transitionValue = "退出动画"; + if ("fade".equals(filter)) { + filterValue = "淡出"; + } else if ("fly(toLeft)".equals(filter)) { + filterValue = "飞出(向左)"; + } else if ("fly(toRight)".equals(filter)) { + filterValue = "飞出(向右)"; + } else if ("strips(upLeft)".equals(filter)) { + filterValue = "条纹(左上)"; + } else if ("wipe(left)".equals(filter)) { + filterValue = "擦除(向左)"; + } else if ("zoom".equals(filter)) { + filterValue = "缩小"; + } else if ("shape(diamond)".equals(filter)) { + filterValue = "菱形消失"; + } + } else if ("emph".equals(transition)) { + transitionValue = "强调动画"; + } else if ("path".equals(transition)) { + transitionValue = "动作路径动画"; + } + return transitionValue + filterValue; + } + private static String pageSize(String widthTwip, String heightTwip){ + // 转换为 cm:1 twip = 1/1440 英寸 = 2.54 / 1440 cm + double widthCm = Double.parseDouble(widthTwip) / 360000; + double heightCm = Double.parseDouble(heightTwip) / 360000; + + // 四舍五入保留一位小数 + widthCm = Math.round(widthCm * 10) / 10.0; + heightCm = Math.round(heightCm * 10) / 10.0; + + // 判断标准纸型(以 cm 为单位进行匹配) + String paperType = "自定义"; + if (approx(widthCm, 21.0) && approx(heightCm, 29.7)) { + paperType = "A4"; + } else if (approx(widthCm, 29.7) && approx(heightCm, 42.0)) { + paperType = "A3"; + } else if (approx(widthCm, 14.8) && approx(heightCm, 21.0)) { + paperType = "A5"; + } + return paperType; + } + private static boolean approx(double a, double b) { + return Math.abs(a - b) < 0.5; // 误差范围 0.5cm 内 + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java new file mode 100644 index 0000000..d19a27f --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java @@ -0,0 +1,39 @@ +package com.example.exam.exam.service.wpspptx; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class JudgementReqVo { + + private String path; + // 中文考点 + private String chineseName; + + // 查询段落所在文件名称 + private String fileNama; + + // 查询的段落 + private String paragraph; + + // 需要查询的标签 + private String title; + + // 需要查询的参数 当isText == 1 为null + private String valueList; + + // 值转换类型 String + private String type; + + // 需要查找的是文本还是参数(0:参数;1:文本) + private String isText; + + // 对结果的判断(0:否 返回值,1:是 返回 是否) + private String isTrue; + + // 0:内参数;1:外参数;2;内外参数(方法使用#继续分割) + private String isParameter; + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java new file mode 100644 index 0000000..960b43f --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java @@ -0,0 +1,300 @@ +package com.example.exam.exam.service.wpspptx; + +import org.apache.commons.io.IOUtils; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * @author REN + */ +public class JudgementWpsPPT { + public static List getValues(List judgementReq) throws InvalidFormatException, IOException, ParserConfigurationException, SAXException { + // 1、文件路径或者文件的属性 + // 2、查找文件的属性其中字段为:name firstFunction SecondFunction ThirdFunction FourthFunction isboo betong 转换 + // 3、名称 标签 标签段落 值标签 关联关系 关联文件名称 ) + // 4、逻辑关系 : 通过name查询文档数据,根据标签段落查询对应的段落值,再根据标签地址查询对应,在查询对应的值标签(外部文件暂定) +// String fileNama = "presentation"; +// String functions = "(//p:presentation)[1]"; +// String slide = "p:sldSz"; +// String values = "cx#cy"; + // 创建返回值 + List judgementList = new ArrayList<>(); + + for (JudgementReqVo reqVo : judgementReq) { + String path = reqVo.getPath(); + String chineseName = reqVo.getChineseName(); + String fileNama = reqVo.getFileNama(); + String paragraph = reqVo.getParagraph(); + String title = reqVo.getTitle(); + String values = reqVo.getValueList(); + String type = reqVo.getType(); + String isText = reqVo.getIsText(); + String isTrue = reqVo.getIsTrue(); + String isParameter = reqVo.getIsParameter(); + + String firstName = paragraph.split(":")[0].split("/")[paragraph.split(":")[0].split("/").length -1]; + // 组合之后可能用到的考点信息 + String englishName = fileNama + "@!" + paragraph + "@!" + title + "@!" + values + "@!" + type + "@!" + isText + "@!" + isTrue + "@!" + isParameter; + XmlCursor cursor = getXmlCursor(path, fileNama, null); + System.out.println(cursor.xmlText()); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + XmlCursor slideCursor = cursor; + if (title.contains(">")) { + // 说明想要 + title = title.replace(">", ""); + judgementList = setData(judgementList, chineseName + "正在开发中", englishName + "正在开发中"); + + } else + // 当标签存在特定值得时候,不需要继续第一段得查询,直接用所有得进行查找 + if (title.contains("?")) { + title = title.replace("?", ""); + // 条件成立 + // 获取 XML 字符串 + String xmlText = slideCursor.getObject().xmlText(); + InputStream is = new ByteArrayInputStream(xmlText.getBytes(StandardCharsets.UTF_8)); + // 然后再用 builder.parse + Document doc = builder.parse(is); + System.out.println(xmlText); + // 通过递归调用 查询标签位置,可能出现得层级关系,避免出现一层不对得情况查询多个标签地址 + Element element = XmlRecursiveFinder.findElement(doc, title); + List valuesList = new ArrayList<>(); + if ("1".equals(isTrue)) { + String value = element == null ? "否" : "是" ; + judgementList = setData(judgementList, chineseName + value, englishName + value); + } else { + String[] valuesArr = values.split("#"); + String oneValue = ""; + for (String yivalue : valuesArr) { + if ("0".equals(isText)) { + oneValue = element.getAttribute(yivalue); + } else { + oneValue = element.getTextContent(); + } + valuesList.add(oneValue); + } + // 开始进行考点等的判断 + String pointValue = JudgementConvert.getConvert(valuesList, type); + if ("1".equals(isTrue)) { + pointValue = pointValue != null ? "是" : "否"; + } + judgementList = setData(judgementList, chineseName + pointValue, englishName + pointValue); + } + } else { + if (!paragraph.isEmpty()) { + slideCursor.selectPath("declare namespace " + firstName + "='" + DeclareNamespaceForPPT.getNameSpace(firstName) + "' " + paragraph); + } + // 当查询到或者说不需要查询的时候进入 + if (slideCursor.toNextSelection() || paragraph.isEmpty()) { + // 获取 XML 字符串 + String xmlText = slideCursor.getObject().xmlText(); + InputStream is = new ByteArrayInputStream(xmlText.getBytes(StandardCharsets.UTF_8)); + // 然后再用 builder.parse + Document doc = builder.parse(is); + if ("Filling_Method".equals(type)) { + // 说明判断填充方式 + String value = getFillingMethod(doc, slideCursor, title); + judgementList = setData(judgementList, chineseName + value, englishName + value); + } else if ("Filling_Bg".equals(type)) { + // 说明判断背景填充方式 + String value = getFillingBg(doc, slideCursor, title); + judgementList = setData(judgementList, chineseName + value, englishName + value); + } else if ("Line_Spacing".equals(type)) { + // 行距类型 + String value = getLineSpacing(doc, slideCursor, title); + judgementList = setData(judgementList, chineseName + value, englishName + value); + } + else { + // 通过递归调用 查询标签位置,可能出现得层级关系,避免出现一层不对得情况查询多个标签地址 + Element element = XmlRecursiveFinder.findElement(doc, title); + List valuesList = new ArrayList<>(); + if ("1".equals(isTrue)) { + String value = element == null ? "否" : "是" ; + judgementList = setData(judgementList, chineseName + value, englishName + value); + } else { + String[] valuesArr = values.split("#"); + String oneValue = ""; + for (String yivalue : valuesArr) { + if ("0".equals(isText)) { + oneValue = element.getAttribute(yivalue); + } else { + oneValue = element.getTextContent(); + } + valuesList.add(oneValue); + } + // 开始进行考点等的判断 + String pointValue = JudgementConvert.getConvert(valuesList, type); + if ("1".equals(isTrue)) { + pointValue = pointValue != null ? "是" : "否"; + } + judgementList = setData(judgementList, chineseName + pointValue, englishName + pointValue); + } + } + } else { + // 如果存在备选方案,使用备选的参数 + // 先过去RID的对应的值所有方法中带有了#区分第一段是获取值,第二段是获取外文件的参数 + + } + } + + } + return judgementList; + } + + + public static String getFillingMethod(Document doc, XmlCursor cursor, String titleName) { + String value = ""; + for (String title : titleName.split("#")) { + // 获取填充方法 + NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]); + if (nodeList.getLength() > 0) { + if (title.equals("a:solidFill")) { + value = "纯色填充"; + } + } + } + return value; + } + public static String getLineSpacing(Document doc, XmlCursor cursor, String titleName) { + String value = ""; + for (String title : titleName.split("#")) { + // 获取填充方法 + NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]); + if (nodeList.getLength() > 0) { + if (title.equals("a:spcPts")) { + value = "固定行距"; + } + if (title.equals("a:spcPct")) { + value = "多倍行距"; + } + } + } + return value; + } + public static String getProjectSymbols(Document doc, XmlCursor cursor, String titleName) { + String value = ""; + for (String title : titleName.split("#")) { + // 获取填充方法 + NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]); + if (nodeList.getLength() > 0) { + if (title.equals("a:buChar")) { + value = "普通圆点"; + } + if (title.equals("a:buBlip")) { + value = "图片项目符号"; + } + if (title.equals("a:buAutoNum")) { + value = "自动编号"; + } + } + } + return value; + } + public static String getFillingBg(Document doc, XmlCursor cursor, String titleName) { + String value = ""; + for (String title : titleName.split("#")) { + // 获取填充方法 + NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]); + if (nodeList.getLength() > 0) { + if (title.equals("a:solidFill")) { + value = "纯色填充"; + } + if (title.equals("a:gradFill")) { + value = "渐变填充"; + } + if (title.equals("a:blipFill")) { + value = "图片填充"; + } + if (title.equals("a:pattFill")) { + value = "图案填充"; + } + if (title.equals("a:grpFill")) { + value = "分组填充"; + } + } + } + return value; + } + + + public static XmlCursor getXmlCursor(String path, String titleName, String value) throws IOException, InvalidFormatException { + XmlCursor cursor = null; + try (OPCPackage pkg = OPCPackage.open(path)) { + for (PackagePart part : pkg.getParts()) { + String entryName = part.getPartName().getName(); + if (value == null) { + // 如果说明是空的话,直接查询文件名称 + if (entryName.contains(titleName) && entryName.contains(".xml") && !entryName.contains(".rels")) { + try (InputStream is = part.getInputStream()) { + String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8); + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + cursor = xmlObject.newCursor(); + break; + } catch (XmlException e) { + throw new RuntimeException(e); + } + } + } else { + // 想查询外置文件 + if (entryName.contains(titleName) && entryName.contains(".xml") && entryName.contains(".rels")) { + try (InputStream is = part.getInputStream()) { + String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8); + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + xmlObject.newCursor().selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship"); + if (xmlObject.newCursor().toNextSelection()) { + String id = xmlObject.newCursor().getAttributeText(new QName("Id")); + if (id.equals(value)) { + String target = xmlObject.newCursor().getAttributeText(new QName("Target")); + getXmlCursor(path, target.split("/")[target.split("/").length-1], null); + } + } + } catch (XmlException e) { + throw new RuntimeException(e); + } + + } + } + } + } + return cursor; + } + + /** + * 向考点对象存放数据 + * @param wpsPptxJudgementDtoList 考点对象数组 + * @param chineseName 给考点对象添加的中文描述 + * @param englishName 给考点对象添加的英文描述 + * @return 考点对象数组 + */ + private static List setData(List wpsPptxJudgementDtoList, + String chineseName, + String englishName) { + WpsPptxJudgementDto judgementDto = new WpsPptxJudgementDto(); + judgementDto.setContentIn(chineseName); + judgementDto.setContent(englishName); + judgementDto.setScoreRate("1"); + wpsPptxJudgementDtoList.add(judgementDto); + return wpsPptxJudgementDtoList; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxService.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxService.java new file mode 100644 index 0000000..0e2810f --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxService.java @@ -0,0 +1,14 @@ +package com.example.exam.exam.service.wpspptx; + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +import java.util.List; + +/** + * @author REN + */ +public interface JudgementWpsPptxService { + SourceAndText judgementWpsPptx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception; +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java new file mode 100644 index 0000000..f693d25 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java @@ -0,0 +1,80 @@ +package com.example.exam.exam.service.wpspptx; + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.SourceAndText; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.c.LogFileUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author REN + */ +@Service +public class JudgementWpsPptxServiceImpl implements JudgementWpsPptxService { + + @Override + public SourceAndText judgementWpsPptx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception { + SourceAndText sourceAndText = new SourceAndText(); + // 创建log文件txt,用于记录 + LogFileUtils.createFile(pathC + "/WPS_Word判分过程.txt"); + LogFileUtils.writeLine("✅ 开始WPS_Pptx判分"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Pptx判分"); + double wpsPptScore = 0; + List judgementReq = new ArrayList<>(); + // 3、获取答案得组成 + List answerList = examQuestion.getAnswerList(); + for (ExamQuestionAnswer examQuestionAnswer : answerList) { + JudgementReqVo judgementReqVo = new JudgementReqVo(); + // 拆分数据、 + String[] pptxInfos = examQuestionAnswer.getContent().split("@!"); + judgementReqVo.setFileNama(pptxInfos[0]); + judgementReqVo.setParagraph(pptxInfos[1]); + judgementReqVo.setTitle(pptxInfos[2]); + judgementReqVo.setValueList(pptxInfos[3]); + judgementReqVo.setType(pptxInfos[4]); + judgementReqVo.setIsText(pptxInfos[5]); + judgementReqVo.setIsTrue(pptxInfos[6]); + judgementReqVo.setIsParameter(pptxInfos[7]); + judgementReqVo.setPath(path); + judgementReq.add(judgementReqVo); + } + List judgementDtos = JudgementWpsPPT.getValues(judgementReq); + // 4、进行关联判断 + for (ExamQuestionAnswer examQuestionAnswer : answerList) { + boolean flag = false; + double one_sorce = 0; + for (WpsPptxJudgementDto pptxJudgementDto : judgementDtos) { + if (pptxJudgementDto.getContent().equals(examQuestionAnswer.getContent())) { + flag = true; + // 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重 + if (examQuestionAnswer.getScoreRate().equals("1")) { + // 说明权重相等,直接平分分数 + one_sorce = sorce / answerList.size(); + } else { + one_sorce = sorce * Double.parseDouble(examQuestionAnswer.getScoreRate()); + } + break; + } + } + wpsPptScore += one_sorce; + if (flag) { + LogFileUtils.writeLine("✅" + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ " + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce); + } else { + LogFileUtils.writeLine("❌ " + examQuestionAnswer.getContentIn() + " 得分失败"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ " + examQuestionAnswer.getContentIn() + " 得分失败"); + } + } + LogFileUtils.writeLine("✅ 结束WPS_Pptx判分,试题得分:" + wpsPptScore); + // 关闭已经打开得文件 + LogFileUtils.close(); + sourceAndText.setScore(wpsPptScore); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java b/src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java new file mode 100644 index 0000000..051f9df --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java @@ -0,0 +1,14 @@ +package com.example.exam.exam.service.wpspptx; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class WpsPptxJudgementDto { + private String content; + private String contentIn; + private String image; + private String scoreRate; +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java b/src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java new file mode 100644 index 0000000..de6835d --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java @@ -0,0 +1,39 @@ +package com.example.exam.exam.service.wpspptx; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class XmlRecursiveFinder { + // 递归入口 + public static Element findElementRecursive(Node startNode, String[] tags, int index) { + if (index >= tags.length || startNode == null) return null; + + String tag = tags[index].split(":")[1]; + String namespace = tags[index].split(":")[0]; + NodeList children; + + if (startNode instanceof Document) { + children = ((Document) startNode).getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(namespace), tag); + } else { + children = ((Element) startNode).getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(namespace), tag); + } + + if (children.getLength() == 0) return null; + + Element child = (Element) children.item(0); + if (index == tags.length - 1) { + // 最后一层,返回当前节点 + return child; + } + + return findElementRecursive(child, tags, index + 1); + } + + // 封装外部调用 + public static Element findElement(Document doc, String titleName) { + String[] title = titleName.split("@"); + return findElementRecursive(doc, title, 0); + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxService.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxService.java new file mode 100644 index 0000000..d28c45f --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxService.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.service.wpspptx.pptx; + + + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +/** + * 判分逻辑集合(wps word) + * + * @author rwb + */ +public interface JudgementForPptxService { + + /** + * word 判分 + * @param stuInfoVo 学生考试信息 + * @return 结果 + */ + Double autoForWpsPptx(StuInfoVo stuInfoVo) throws Exception; +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxServiceImpl.java new file mode 100644 index 0000000..595b702 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx/JudgementForPptxServiceImpl.java @@ -0,0 +1,118 @@ +package com.example.exam.exam.service.wpspptx.pptx; + + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import com.example.exam.exam.service.wpspptx.JudgementWpsPptxService; +import com.example.exam.exam.service.wpsword.JudgementWpsWordService; +import com.example.exam.exam.service.wpsword.word.JudgementForWordService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + + +/** + * @author REN + */ +@Service +public class JudgementForPptxServiceImpl implements JudgementForPptxService { + @Resource + JudgementService judgementService; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + JudgementWpsPptxService judgementWpsPptxService; + @Resource + SystemTenantService systemTenantService; + + @Override + public Double autoForWpsPptx(StuInfoVo stuInfoVo) throws Exception { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + String name = csFileList[0].getName(); + if ("演示".equals(name)) { + File[] cs_file_list = csFiles.listFiles(); + String lastFilePath = cs_file_list[0].getPath(); + File[] lastFiles = cs_file_list[0].listFiles(); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(name)).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + for (File lastFile : lastFiles) { + if (lastFile.getName().contains("文档")) { + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText pptxpojo = judgementWpsPptxService.judgementWpsPptx(Double.parseDouble(quScore), lastFilePath, lastFile.getPath(), examQuestion, judgementStr); + score += pptxpojo.getScore(); + judgementStr = pptxpojo.getText(); + judgementStr += "

试题得分:" + pptxpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(pptxpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(pptxpojo.getScore() == 0 ? 1 : pptxpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(pptxpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(name); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(pptxpojo.getScore() == 0 ? 1 : pptxpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + break; + } + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/ColorNameFinder.java b/src/main/java/com/example/exam/exam/service/wpsword/ColorNameFinder.java new file mode 100644 index 0000000..0882a63 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/ColorNameFinder.java @@ -0,0 +1,65 @@ +package com.example.exam.exam.service.wpsword; + +import java.util.HashMap; +import java.util.Map; + +public class ColorNameFinder { + // 颜色代码到名称的映射 + private static final Map COLOR_MAP = new HashMap<>(); + + static { + COLOR_MAP.put("FF0000", "红色"); + COLOR_MAP.put("00FF00", "绿色"); + COLOR_MAP.put("0000FF", "蓝色"); + COLOR_MAP.put("FFFF00", "黄色"); + COLOR_MAP.put("FFA500", "橙色"); + COLOR_MAP.put("800080", "紫色"); + COLOR_MAP.put("000000", "黑色"); + COLOR_MAP.put("FFFFFF", "白色"); + COLOR_MAP.put("BB6433", "棕橙色"); + COLOR_MAP.put("FFC000", "淡黄色"); + COLOR_MAP.put("0070C0", "蓝色"); + + // 你可以继续扩展 + } + + public static String getColorName(String input) { + String hex = null; + + input = input.trim(); + + if (input.startsWith("#")) { + hex = input.substring(1).toUpperCase(); + } else if (input.matches("[0-9a-fA-F]{6}")) { + hex = input.toUpperCase(); + } else if (input.toLowerCase().startsWith("rgb")) { + // 解析 rgb(255,0,0) 格式 + hex = rgbToHex(input); + } + + if (hex != null) { + return COLOR_MAP.getOrDefault(hex, "未知颜色"); + } + + return "格式错误"; + } + + private static String rgbToHex(String rgb) { + // rgb(255,0,0) -> "FF0000" + try { + String inner = rgb.substring(rgb.indexOf('(') + 1, rgb.indexOf(')')); + String[] parts = inner.split(","); + if (parts.length != 3) return null; + + int r = Integer.parseInt(parts[0].trim()); + int g = Integer.parseInt(parts[1].trim()); + int b = Integer.parseInt(parts[2].trim()); + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) return null; + + return String.format("%02X%02X%02X", r, g, b); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordService.java b/src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordService.java new file mode 100644 index 0000000..04736d1 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordService.java @@ -0,0 +1,26 @@ +package com.example.exam.exam.service.wpsword; + + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.SourceAndText; + +import java.util.List; + +/** + * 判分逻辑集合(wps word) + * + * @author rwb + */ +public interface JudgementWpsWordService { + + /** + * 读取考生文件,与题型中要求进行判断 + * @param path 文件路径 + * @param examQuestion 试题参数 + * @param sorce 试题分数 + * @return 得分 + * @throws Exception 异常 + */ + SourceAndText judgementWpsWord(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordServiceImpl.java new file mode 100644 index 0000000..39208b2 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/JudgementWpsWordServiceImpl.java @@ -0,0 +1,96 @@ +package com.example.exam.exam.service.wpsword; + + +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.dal.ExamQuestionAnswer; +import com.example.exam.exam.dal.SourceAndText; +import com.example.exam.exam.utils.HtmlAppender; +import com.example.exam.exam.utils.c.LogFileUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author REN + */ +@Service +public class JudgementWpsWordServiceImpl implements JudgementWpsWordService { + + @Override + public SourceAndText judgementWpsWord(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception { + SourceAndText sourceAndText = new SourceAndText(); + // 创建log文件txt,用于记录 + LogFileUtils.createFile(pathC + "/WPS_Word判分过程.txt"); + LogFileUtils.writeLine("✅ 开始WPS_Word判分"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Word判分"); + double wps_word_sorce = 0; + // 2、docx文件读取并返回考点及说明信息 +// List margins = WpsWordUtils.wps_word(path); + // 3、获取答案得组成 + List answerList = examQuestion.getAnswerList(); + // 4、进行关联判断 + for (ExamQuestionAnswer examQuestionAnswer : answerList) { + // 拆分数据、 + String[] wordInfos = examQuestionAnswer.getContent().split("-/"); + String[] chineseName = examQuestionAnswer.getContentIn().split("-"); + String[] typeList = examQuestionAnswer.getImage().split("-"); + // 创建拼接数据 + // 只取三层结构 + List wordReqDto = new ArrayList<>(); + WpsWordReqDto wpsWordReqDto = new WpsWordReqDto(); + wpsWordReqDto.setName(examQuestionAnswer.getContentIn()); + wpsWordReqDto.setEnglishName(wordInfos[0].split("]")[0] + "]"); + wpsWordReqDto.setFilePath(path); + // 存放类型 + wpsWordReqDto.setType(typeList[0]); + wpsWordReqDto.setBelongTo(typeList[1]); + wpsWordReqDto.setIsboo(typeList[2]); + wpsWordReqDto.setUnit(typeList[3]); + wpsWordReqDto.setFunction(wordInfos[0]); + wpsWordReqDto.setIsExam("1"); + wordReqDto.add(wpsWordReqDto); + System.out.println(examQuestionAnswer.getContentIn()); + System.out.println(examQuestionAnswer.getContent()); + List judgementDtos = WpsWordUtils.getWordInfo(wordReqDto); + boolean flag = false; + double one_sorce = 0; + for (WpsWordJudgementDto wordJudgementDto : judgementDtos) { + if (wordJudgementDto.getContent() != null) { +// for (String str : wordJudgementDto.getFunction()) { + int index = 0; + if (wordJudgementDto.getContent().equals(examQuestionAnswer.getContent())) { + flag = true; + // 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重 + if (examQuestionAnswer.getScoreRate().equals("1")) { + // 说明权重相等,直接平分分数 + one_sorce = sorce / answerList.size(); + } else { + one_sorce = sorce * Double.parseDouble(examQuestionAnswer.getScoreRate()); + } + break; + } +// } + } + } + wps_word_sorce += one_sorce; + if (flag) { + LogFileUtils.writeLine("✅ " + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ " + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce); + + } else { + LogFileUtils.writeLine("❌ " + examQuestionAnswer.getContentIn() + " 得分失败"); + judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ " + examQuestionAnswer.getContentIn() + " 得分失败"); + + } + } + LogFileUtils.writeLine("✅ 结束WPS_Word判分,试题得分:" + wps_word_sorce); + // 关闭已经打开得文件 + LogFileUtils.close(); + sourceAndText.setScore(wps_word_sorce); + sourceAndText.setText(judgementStr); + return sourceAndText; + } + +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/PageSizeDetector.java b/src/main/java/com/example/exam/exam/service/wpsword/PageSizeDetector.java new file mode 100644 index 0000000..7ffefc4 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/PageSizeDetector.java @@ -0,0 +1,97 @@ +package com.example.exam.exam.service.wpsword; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PageSizeDetector { + public static String detectPaperSize(String pgSzXml) { + Pattern pattern = Pattern.compile("w:w=\"(\\d+)\".*?"); + Matcher matcher = pattern.matcher(pgSzXml); + int widthTwip = 0; + if (matcher.find()) { + widthTwip = Integer.parseInt(matcher.group(1)); + } + Pattern patterns = Pattern.compile("w:h=\"(\\d+)\".*?"); + Matcher matchers = patterns.matcher(pgSzXml); + int heightTwip = 0; + if (matchers.find()) { + heightTwip = Integer.parseInt(matchers.group(1)); + } + Pattern patternss = Pattern.compile("w:orient\\s*=\\s*\"(.*?)\""); + Matcher matcherss = patternss.matcher(pgSzXml); + String orient = ""; + if (matcherss.find()) { + orient = matcherss.group(1); + } + // 如果方向是 landscape,宽高调换(视觉上) + if ("landscape".equalsIgnoreCase(orient)) { + int temp = widthTwip; + widthTwip = heightTwip; + heightTwip = temp; + } + // 转换为 cm:1 twip = 1/1440 英寸 = 2.54 / 1440 cm + double widthCm = widthTwip * 2.54 / 1440; + double heightCm = heightTwip * 2.54 / 1440; + + // 四舍五入保留一位小数 + widthCm = Math.round(widthCm * 10) / 10.0; + heightCm = Math.round(heightCm * 10) / 10.0; + + // 判断标准纸型(以 cm 为单位进行匹配) + String paperType = "Unknown"; + if (approx(widthCm, 21.0) && approx(heightCm, 29.7)) { + paperType = "A4"; + } else if (approx(widthCm, 29.7) && approx(heightCm, 42.0)) { + paperType = "A3"; + } else if (approx(widthCm, 14.8) && approx(heightCm, 21.0)) { + paperType = "A5"; + } + + return String.format("纸张类型:%s(%.1fcm × %.1fcm)", paperType, widthCm, heightCm); + } + + public static String detectPaperPPTSize(String pgSzXml) { + Pattern pattern = Pattern.compile("cx=\"(\\d+)\".*?"); + Matcher matcher = pattern.matcher(pgSzXml); + int widthTwip = 0; + if (matcher.find()) { + widthTwip = Integer.parseInt(matcher.group(1)); + } + Pattern patterns = Pattern.compile("cy=\"(\\d+)\".*?"); + Matcher matchers = patterns.matcher(pgSzXml); + int heightTwip = 0; + if (matchers.find()) { + heightTwip = Integer.parseInt(matchers.group(1)); + } + // 转换为 cm:1 twip = 1/1440 英寸 = 2.54 / 1440 cm + double widthCm = widthTwip / 360000; + double heightCm = heightTwip / 360000; + + // 四舍五入保留一位小数 + widthCm = Math.round(widthCm * 10) / 10.0; + heightCm = Math.round(heightCm * 10) / 10.0; + + // 判断标准纸型(以 cm 为单位进行匹配) + String paperType = "自定义"; + if (approx(widthCm, 21.0) && approx(heightCm, 29.7)) { + paperType = "A4"; + } else if (approx(widthCm, 29.7) && approx(heightCm, 42.0)) { + paperType = "A3"; + } else if (approx(widthCm, 14.8) && approx(heightCm, 21.0)) { + paperType = "A5"; + } + + return String.format("纸张类型:%s(%.1fcm × %.1fcm)", paperType, widthCm, heightCm); + } + + // 判断两个 double 值是否接近 + private static boolean approx(double a, double b) { + return Math.abs(a - b) < 0.5; // 误差范围 0.5cm 内 + } + + // 示例用法 + public static void main(String[] args) { + String xml = ""; + System.out.println(detectPaperSize(xml)); + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/ShadowDirectionUtils.java b/src/main/java/com/example/exam/exam/service/wpsword/ShadowDirectionUtils.java new file mode 100644 index 0000000..2ff5784 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/ShadowDirectionUtils.java @@ -0,0 +1,77 @@ +package com.example.exam.exam.service.wpsword; + +public class ShadowDirectionUtils { + + // 中文阴影方向枚举 + public enum ShadowDirection { + 上("上"), + 右上("右上"), + 右("右"), + 右下("右下"), + 下("下"), + 左下("左下"), + 左("左"), + 左上("左上"), + 中心("中心"), + 未知("未知"); + + private final String label; + + ShadowDirection(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } + } + + /** + * 根据 dir 值判断阴影方向类型(返回中文方向) + * @param dir 阴影方向(单位为 1/60000 度) + * @return 中文方向名 + */ + public static ShadowDirection getShadowDirection(long dir) { + // 转换为角度 + double angle = dir / 60000.0; + angle = ((angle % 360) + 360) % 360; // 归一化到 0~360° + + if (angle >= 337.5 || angle < 22.5) { + return ShadowDirection.右; + } else if (angle >= 22.5 && angle < 67.5) { + return ShadowDirection.右下; + } else if (angle >= 67.5 && angle < 112.5) { + return ShadowDirection.下; + } else if (angle >= 112.5 && angle < 157.5) { + return ShadowDirection.左下; + } else if (angle >= 157.5 && angle < 202.5) { + return ShadowDirection.左; + } else if (angle >= 202.5 && angle < 247.5) { + return ShadowDirection.左上; + } else if (angle >= 247.5 && angle < 292.5) { + return ShadowDirection.上; + } else if (angle >= 292.5 && angle < 337.5) { + return ShadowDirection.右上; + } else { + return ShadowDirection.未知; + } + } + + // 示例用法 + public static void main(String[] args) { + long[] testDirs = { + 0, // 右 + 1350000, // 右下 + 5400000, // 下 + 13500000, // 左 + 18900000, // 右上 + 16200000, // 上 + 21600000 // 右 + }; + + for (long dir : testDirs) { + ShadowDirection direction = getShadowDirection(dir); + System.out.printf("dir=%d (%.1f°) => 阴影方向:%s%n", dir, dir / 60000.0, direction.getLabel()); + } + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/TwipConverter.java b/src/main/java/com/example/exam/exam/service/wpsword/TwipConverter.java new file mode 100644 index 0000000..bd00907 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/TwipConverter.java @@ -0,0 +1,42 @@ +package com.example.exam.exam.service.wpsword; + +public class TwipConverter { + + // 转换常量 + private static final double TWIPS_PER_INCH = 1440.0; + private static final double TWIPS_PER_CM = 567.0; + private static final double TWIPS_PER_POINT = 20.0; + private static final double EMUS_PER_POINT = 12700.0; + + // Twip 转厘米 + public static double toCentimeters(int twip) { + return twip * 2.54 / TWIPS_PER_INCH; + } + + // Twip 转英寸 + public static double toInches(int twip) { + return twip / TWIPS_PER_INCH; + } + + // Twip 转磅(point) + public static double toPoints(int twip) { + return twip / TWIPS_PER_POINT; + } + public static double toEmus(int twip) { + return twip / EMUS_PER_POINT; + } + // 保留两位小数格式化(通用) + public static String formatDouble(double value) { + return String.format("%.2f", value); + } + + // 示例主方法(可删除) + public static void main(String[] args) { + int twipValue = 1134; + + System.out.println("Twip: " + twipValue); + System.out.println("厘米: " + formatDouble(toCentimeters(twipValue)) + " cm"); + System.out.println("英寸: " + formatDouble(toInches(twipValue)) + " in"); + System.out.println("磅数: " + formatDouble(toPoints(twipValue)) + " pt"); + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/WordHeaderFooterVo.java b/src/main/java/com/example/exam/exam/service/wpsword/WordHeaderFooterVo.java new file mode 100644 index 0000000..ecfbf76 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/WordHeaderFooterVo.java @@ -0,0 +1,11 @@ +package com.example.exam.exam.service.wpsword; + +import lombok.Data; + +@Data +public class WordHeaderFooterVo { + + private String rid; + private String type; + private String xmlType; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/WpsWordJudgementDto.java b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordJudgementDto.java new file mode 100644 index 0000000..06a1215 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordJudgementDto.java @@ -0,0 +1,14 @@ +package com.example.exam.exam.service.wpsword; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class WpsWordJudgementDto { + private String content; + private String contentIn; + private String image; + private String scoreRate; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/WpsWordNameSpaces.java b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordNameSpaces.java new file mode 100644 index 0000000..a0b7bbf --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordNameSpaces.java @@ -0,0 +1,30 @@ +package com.example.exam.exam.service.wpsword; + +@SuppressWarnings("all") +public class WpsWordNameSpaces { + + public static String getNameSpace(String xmlString) { + // 2、创建最全的命名空间 +// Pattern pattern = Pattern.compile("xmlns:(\\w+)='([^']+)'"); +// Matcher matcher = pattern.matcher(xmlString); +// Map namespaces = new HashMap<>(); +// while (matcher.find()) { +// // 如 w, wp, a +// String prefix = matcher.group(1); +// // 如 http://schemas.openxmlformats.org/... +// String uri = matcher.group(2); +// namespaces.put(prefix, uri); +// } +// StringBuilder xpathBuilder = new StringBuilder(); +// namespaces.forEach((prefix, uri) -> +// xpathBuilder.append("declare namespace ") +// .append(prefix) +// .append("='") +// .append(uri) +// .append("' ") +// ); +// // 2-1、获取出来最全的命名空间 +// String allPathx = xpathBuilder.toString(); + return "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' declare namespace r='http://schemas.openxmlformats.org/officeDocument/2006/relationships' declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' declare namespace pic='http://schemas.openxmlformats.org/drawingml/2006/picture' declare namespace wp='http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing' declare namespace wp14='http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing' declare namespace w14='http://schemas.microsoft.com/office/word/2010/wordml' declare namespace w15='http://schemas.microsoft.com/office/word/2012/wordml' declare namespace w16='http://schemas.microsoft.com/office/word/2018/wordml' declare namespace w16cid='http://schemas.microsoft.com/office/word/2016/wordml/cid' declare namespace v='urn:schemas-microsoft-com:vml' declare namespace o='urn:schemas-microsoft-com:office:office' declare namespace m='http://schemas.openxmlformats.org/officeDocument/2006/math' declare namespace mc='http://schemas.openxmlformats.org/markup-compatibility/2006' declare namespace wpg='http://schemas.microsoft.com/office/word/2010/wordprocessingGroup' declare namespace wpi='http://schemas.microsoft.com/office/word/2010/wordprocessingInk' declare namespace wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape' declare namespace c='http://schemas.openxmlformats.org/drawingml/2006/chart' declare namespace cx='http://schemas.microsoft.com/office/drawing/2014/chart' declare namespace dgm='http://schemas.openxmlformats.org/drawingml/2006/diagram' declare namespace lc='http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas' declare namespace svg='http://schemas.microsoft.com/office/drawing/2016/SVG/main' declare namespace aink='http://schemas.microsoft.com/office/drawing/2016/ink' declare namespace am3d='http://schemas.microsoft.com/office/drawing/2017/model3D' declare namespace wne='http://schemas.microsoft.com/office/word/2006/wordml' "; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/WpsWordReqDto.java b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordReqDto.java new file mode 100644 index 0000000..1d4fcd6 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordReqDto.java @@ -0,0 +1,36 @@ +package com.example.exam.exam.service.wpsword; + +import lombok.Data; + +/** + * @author REN + */ +@Data +public class WpsWordReqDto { + +// @Schema(description = "主中文名称") + private String name; + +// @Schema(description = "主英文名称") + private String englishName; + +// @Schema(description = "文件路径") + private String filePath; + +// @Schema(description = "内外参数(0:内参数;1:外参数)") + private String type; + +// @Schema(description = "归属(0:段落;1:页眉页脚;2:图形)") + private String belongTo; + +// @Schema(description = "参数类型(0:值;1:判断)") + private String isboo; + +// @Schema(description = "子标签") + private String function; + + private String unit; + + private String isExam; + +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/WpsWordUtils.java b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordUtils.java new file mode 100644 index 0000000..2145cf2 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/WpsWordUtils.java @@ -0,0 +1,761 @@ +package com.example.exam.exam.service.wpsword; +import org.apache.commons.io.IOUtils; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFFooter; +import org.apache.poi.xwpf.usermodel.XWPFHeader; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlObject; + +import javax.xml.namespace.QName; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author REN + */ +public class WpsWordUtils { + public static String getStringRandom() { + String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + + // 生成指定长度的随机字符字符串 + for (int i = 0; i < 10; i++) { + int randomIndex = random.nextInt(characters.length()); + // 随机字符 + sb.append(characters.charAt(randomIndex)); + } + return sb.toString(); + } + + public static List getWordInfo(List wpsWordReqDtos) throws Exception { + String filePath = wpsWordReqDtos.get(0).getFilePath(); + // 创建返回数组 + List judgementList = new ArrayList<>(); + // 创建文件路径数组 + List> filePathList = new ArrayList<>(); + for (WpsWordReqDto wpsWordReqDto : wpsWordReqDtos) { + try (OPCPackage pkg = OPCPackage.open(wpsWordReqDto.getFilePath()); + XWPFDocument document = new XWPFDocument(pkg)) { + for (PackagePart part : pkg.getParts()) { + String entryName = part.getPartName().getName(); + if (entryName.contains("word/_rels") && entryName.contains(".xml.rels")) { + try (InputStream is = part.getInputStream()) { + String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8); + // 解析 xmlContent + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursor = xmlObject.newCursor(); + cursor.selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship"); + // 存放数据 + while (cursor.toNextSelection()) { + Map map = new HashMap<>(); + String rId = cursor.getAttributeText(new QName("Id")); + String target = cursor.getAttributeText(new QName("Target")); + map.put(rId, target); + filePathList.add(map); + } + } + } + } + XmlObject docXml = document.getDocument(); + XmlCursor cursor = docXml.newCursor(); + String nameSpace = WpsWordNameSpaces.getNameSpace(cursor.xmlText()); + // 需要联合查询数据,查询位置xml来获取准确的文件 + // 开始查询标签 + // 1、段落 + if ("0".equals(wpsWordReqDto.getBelongTo())) { + if (wpsWordReqDto.getFunction().contains("w:cols")) { + XmlCursor wColsCursor = cursor.newCursor(); + XmlCursor wColsCursors = null; + // 如果是分栏需要单独进行查询 + // 需要查找查询第几个段落 + String number = wpsWordReqDto.getFunction().split("]")[0]; + number = number.split("\\[")[1]; + boolean flag = true; + int numbers = Integer.parseInt(number); + while (flag) { + numbers += 1; + // 向下查询是否存在样式段落 + String nameSpaces = nameSpace + "(//w:p)["+numbers+"]"; + wColsCursor.selectPath(nameSpaces); + if (wColsCursor.toNextSelection()) { + if (wColsCursor.xmlText().contains("w:cols")) { + System.out.println(wColsCursor.xmlText()); + wColsCursors = wColsCursor.newCursor(); + flag = false; + } + } + if (numbers > 20) { + flag = false; + } + } + if (wColsCursors != null) { + String nameSpaceCols = nameSpace + "." + wpsWordReqDto.getFunction().split("]")[1]; + System.out.println(wColsCursors.xmlText()); + wColsCursors.selectPath(nameSpaceCols); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + if (wColsCursors.toNextSelection()) { + String value = wColsCursors.getTextValue(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setScoreRate("1"); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + // 查询指定值,返回固定的文本 + judgementList.add(judgement); + } + } + } else { + String function = wpsWordReqDto.getFunction(); + String chineseName = wpsWordReqDto.getName(); + + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + // 1-1、创建新的数据组 + XmlCursor wpCursor = cursor.newCursor(); + wpCursor.selectPath(nameSpace + function); + if (wpCursor.toNextSelection()) { + if ("1".equals(wpsWordReqDto.getIsboo())) { + judgement.setContentIn(chineseName + "是"); + judgement.setContent(function + "-/true"); + } else { + String value = wpCursor.getTextValue(); + if (wpsWordReqDto.getName().contains("倾斜")) { + value = wpCursor.getAttributeText(new QName("w:val")); + value = value == null ? "是" : value == "0" ? "否" :value == "1" ? "是" : "否"; + } + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(chineseName + value); + judgement.setContent(function + "-/" + value); + } + judgement.setScoreRate("1"); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + // 查询指定值,返回固定的文本 + judgementList.add(judgement); + } else { + // 获取是否在对应的styles.xml里面 + wpCursor.selectPath(nameSpace + wpsWordReqDto.getEnglishName() + "/w:pPr/w:pStyle/@w:val"); + if (wpCursor.toNextSelection()) { + String value = wpCursor.getTextValue(); + for (PackagePart part : pkg.getParts()) { + String entryName = part.getPartName().getName(); + if (entryName.contains("word/styles") && entryName.contains(".xml")) { + try (InputStream is = part.getInputStream()) { + String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8); + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursorStyle = xmlObject.newCursor(); + String nameSpaceStyle = WpsWordNameSpaces.getNameSpace(cursorStyle.xmlText()); + cursorStyle.selectPath(nameSpaceStyle + ".//w:style[@w:styleId='"+value+"']"); + if (cursorStyle.toNextSelection()) { + String functions = wpsWordReqDto.getFunction().replace("/w:r/", ""); + if (functions.charAt(0) == '/') { + functions = ".//" + functions.split("]")[1]; + } else { + functions = "./" + functions.split("]")[1]; + } + + cursorStyle.selectPath(nameSpaceStyle + functions); + if (cursorStyle.toNextSelection()) { + + if ("1".equals(wpsWordReqDto.getIsboo())) { + judgement.setContentIn(chineseName + "是"); + judgement.setContent(function + "-/true"); + } else { + String valueStyle = cursorStyle.getTextValue(); + valueStyle = getValueType(wpsWordReqDto, valueStyle); + judgement.setContentIn(chineseName + valueStyle); + judgement.setContent(function + "-/" + valueStyle); + } + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + } + } + } else if ("1".equals(wpsWordReqDto.getIsboo())) { + judgement.setContentIn(chineseName + "否"); + judgement.setContent(function + "-/false"); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + + } + } + } + List headers = document.getHeaderList(); + List footers = document.getFooterList(); + // 2、节 + if ("1".equals(wpsWordReqDto.getBelongTo())) { + // 先要获取ID + XmlCursor sectPrCursor = docXml.newCursor(); + // 内外参数(0:内参数;1:外参数) + if ("1".equals(wpsWordReqDto.getType())) { + sectPrCursor.selectPath(nameSpace + wpsWordReqDto.getFunction().split("#")[0]); + String rId = ""; + String type = ""; + List headerFooterVos = new ArrayList<>(); + while (sectPrCursor.toNextSelection()) { + rId = sectPrCursor.getAttributeText(new QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id")); + type = sectPrCursor.getAttributeText(new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "type")); + WordHeaderFooterVo headerFooterVo = new WordHeaderFooterVo(); + headerFooterVo.setRid(rId); + headerFooterVo.setType(type); + headerFooterVo.setXmlType(wpsWordReqDto.getFunction()); + headerFooterVos.add(headerFooterVo); + } + String fileName = ""; + for (Map str : filePathList) { + // 页眉页脚 + for (WordHeaderFooterVo headerVo : headerFooterVos) { + if (str.containsKey(headerVo.getRid())) { + fileName = str.get(headerVo.getRid()); + if (headerVo.getXmlType().contains("headerReference")) { + for (XWPFHeader header : headers) { + if (header.getPackagePart().getPartName().getName().contains(fileName)) { + // 2-1、针对不同的进行排查 + String chineseType = Objects.equals(headerVo.getType(), "default") ? "奇数页" : Objects.equals(headerVo.getType(), "even") ? "偶数页" : ""; + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + List headerpar = header.getParagraphs(); + XmlCursor headerXml = headerpar.get(0).getCTP().newCursor(); + if (wpsWordReqDto.getFunction().contains("w:page")) { + if (headerXml.xmlText().contains("PAGE")) { + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + " 是"); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + "false" + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } else { + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + " 否"); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + "false" + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } else if (wpsWordReqDto.getName().contains("水印")) { + if (wpsWordReqDto.getName().contains("水印类型")) { + if (headerXml.xmlText().contains("v:shape") && headerXml.xmlText().contains("v:textpath")) { + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + " 文字水印"); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + "文字水印" + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + if (wpsWordReqDto.getName().contains("文本")) { + headerXml.selectPath(nameSpace + "//v:shape/v:textpath/@string"); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + if (wpsWordReqDto.getName().contains("字体")) { + headerXml.selectPath(nameSpace + "//v:shape/v:textpath/@style"); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + Pattern pattern = Pattern.compile("font-family:([^;]+)"); + Matcher matcher = pattern.matcher(value); + if (matcher.find()) { + String fontFamily = matcher.group(1).trim(); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" +fontFamily); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + fontFamily + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + if (wpsWordReqDto.getName().contains("字号")) { + headerXml.selectPath(nameSpace + "//v:shape/v:textpath/@style"); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + Pattern pattern = Pattern.compile("font-size:([^;]+)"); + Matcher matcher = pattern.matcher(value); + if (matcher.find()) { + String fontFamily = matcher.group(1).trim(); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" +fontFamily); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + fontFamily + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + if (wpsWordReqDto.getName().contains("颜色")) { + headerXml.selectPath(nameSpace + "//v:shape/@fillcolor"); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" +value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + if (wpsWordReqDto.getName().contains("透明度")) { + headerXml.selectPath(nameSpace + "//v:shape/v:fill/@opacity"); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + while (value.length() < 8) { + value = "0" + value; + } + long bits = Long.parseLong(value, 16); + float opacityFloat = Float.intBitsToFloat((int) bits); + // 转为百分比(例如 0.3 -> 30%) + float opacityPercent = opacityFloat * 100; + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" +opacityPercent); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + opacityPercent + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + if (wpsWordReqDto.getName().contains("版式")) { + headerXml.selectPath(nameSpace + "//v:shape/@style"); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + value = detectWatermarkLayout(value); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" +value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } else { + headerXml.selectPath(nameSpace + "./" + wpsWordReqDto.getFunction().split("#")[1]); + if (headerXml.toNextSelection()) { + String value = headerXml.getTextValue(); + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-" +value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + + } + + } + } else if (headerVo.getXmlType().contains("footerReference")) { + for (XWPFFooter footer : footers) { + if (footer.getPackagePart().getPartName().getName().contains(fileName)) { + // 2-1、针对不同的进行排查 + String chineseType = Objects.equals(headerVo.getType(), "default") ? "奇数页" : Objects.equals(headerVo.getType(), "even") ? "偶数页" : ""; + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + List footerpar = footer.getParagraphs(); + XmlCursor footerXml = footerpar.get(0).getCTP().newCursor(); + if (wpsWordReqDto.getFunction().contains("w:page")) { + if (footerXml.xmlText().contains("PAGE")) { + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + " 是"); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + "true" + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } else { + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + " 否"); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + "false" + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } else { + footerXml.selectPath(nameSpace + "./" + wpsWordReqDto.getFunction().split("#")[1]); + if (footerXml.toNextSelection()) { + String value = footerXml.getTextValue(); + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(wpsWordReqDto.getName() + chineseType + "-"+value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value + "-"+ headerVo.getType()); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + + } + } + } + } + } + } + } else { + // 需要查询主页面的方法 + XmlCursor sectPrXsml = docXml.newCursor(); + if (wpsWordReqDto.getUnit().contains("twipstolines")) { + String[] docGrid = wpsWordReqDto.getFunction().split("#"); + XmlCursor docGridXsml = sectPrXsml; + docGridXsml.selectPath(nameSpace + docGrid[0]); + String docGridValues = ""; + String topValues = ""; + String bottomValues = ""; + String allValues = ""; + if (docGridXsml.toNextSelection()) { + docGridValues = docGridXsml.getTextValue(); + } + XmlCursor pgMgXsml = sectPrXsml; + pgMgXsml.selectPath(nameSpace + wpsWordReqDto.getEnglishName() + docGrid[1]); + if (pgMgXsml.toNextSelection()) { + topValues = pgMgXsml.getAttributeText(new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "top")); + bottomValues = pgMgXsml.getAttributeText(new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "bottom")); + } + XmlCursor allXsml = sectPrXsml; + pgMgXsml.selectPath(nameSpace + wpsWordReqDto.getEnglishName() + docGrid[2]); + if (allXsml.toNextSelection()) { + allValues = allXsml.getAttributeText(new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "h")); + } + Integer values = Integer.parseInt(allValues) - Integer.parseInt(bottomValues) + - Integer.parseInt(topValues); + Integer docGridValuesInt = values / Integer.parseInt(docGridValues); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + docGridValuesInt); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + docGridValuesInt); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } else if (wpsWordReqDto.getUnit().contains("paper_shape")) { + sectPrXsml.selectPath(nameSpace + wpsWordReqDto.getFunction()); + // 纸型 + if (sectPrXsml.toNextSelection()) { + String text = sectPrXsml.xmlText(); + String value = PageSizeDetector.detectPaperSize(text); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } else{ + sectPrXsml.selectPath(nameSpace + wpsWordReqDto.getFunction()); + if (sectPrXsml.toNextSelection()) { + String value = sectPrXsml.getTextValue(); + value = getValueType(wpsWordReqDto, value); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + } + // 3、图形 + if ("2".equals(wpsWordReqDto.getBelongTo())) { + if (wpsWordReqDto.getName().contains("环绕方式")) { + XmlCursor wpCursor = cursor.newCursor(); + wpCursor.selectPath(nameSpace + wpsWordReqDto.getFunction()); { + if (wpCursor.toNextSelection()) { + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + String text = wpCursor.xmlText(); + String value = getDrawingType(text); + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(wpsWordReqDto.getName() + value.split("#")[1]); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value.split("#")[0]); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + // 查询指定值,返回固定的文本 + judgementList.add(judgement); + } + } + } else if (wpsWordReqDto.getName().contains("形状填充") && wpsWordReqDto.getName().contains("填充方式")) { + XmlCursor wpCursor = cursor.newCursor(); + wpCursor.selectPath(nameSpace + wpsWordReqDto.getFunction()); + if (wpCursor.toNextSelection()) { + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + String text = wpCursor.xmlText(); + String value = getValueType(wpsWordReqDto, text); + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } else { + String function = ""; + if (wpsWordReqDto.getIsExam().equals("0")) { + String[] functionList = wpsWordReqDto.getFunction().split("#"); + if (wpsWordReqDto.getName().contains("图片")) { + function = functionList[0]; + } else { + function = wpsWordReqDto.getEnglishName() + functionList[1]; + } + } else { + function = wpsWordReqDto.getFunction(); + } + String chineseName = wpsWordReqDto.getName(); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + // 1-1、创建新的数据组 + XmlCursor wpCursor = cursor.newCursor(); + System.out.println(nameSpace + function); + wpCursor.selectPath(nameSpace + function); + if (wpCursor.toNextSelection()) { + if ("1".equals(wpsWordReqDto.getIsboo())) { + judgement.setContentIn(wpsWordReqDto.getName() + "是"); + judgement.setContent(function + "-/" + "true"); + + } else { + String value = wpCursor.getTextValue(); + value = getValueType(wpsWordReqDto, value); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(function + "-/" + value); + } + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + // 查询指定值,返回固定的文本 + judgementList.add(judgement); + } else { + // 参数类型(0:值;1:判断) + if ("1".equals(wpsWordReqDto.getIsboo())) { + judgement.setContentIn(wpsWordReqDto.getName() + "false"); + judgement.setContent(function + "-/" + "false"); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + + } + // 4、尾注 + if ("3".equals(wpsWordReqDto.getBelongTo())) { + if ("0".equals(wpsWordReqDto.getType())) { + if (wpsWordReqDto.getName().contains("插入尾注")) { + // 判断是否存在 + XmlCursor wpCursor = cursor.newCursor(); + wpCursor.selectPath(nameSpace + "//w:endnoteReference/@w:id"); + if (wpCursor.toNextSelection()) { + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + " 是"); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + "true"); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } else { + for (PackagePart part : pkg.getParts()) { + String entryName = part.getPartName().getName(); + if (entryName.contains("word/endnotes") && entryName.contains(".xml")) { + try (InputStream is = part.getInputStream()) { + String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8); + XmlObject xmlObject = XmlObject.Factory.parse(xmlContent); + XmlCursor cursorEnd = xmlObject.newCursor(); + String namespace = WpsWordNameSpaces.getNameSpace(cursorEnd.xmlText()); + if (wpsWordReqDto.getName().contains("内容")) { + System.out.println(cursorEnd.xmlText()); + String value = cursorEnd.getTextValue(); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + } + } + } + // 表格属性 + if ("4".equals(wpsWordReqDto.getBelongTo())) { + XmlCursor tblCursor = cursor.newCursor(); + tblCursor.selectPath(nameSpace + wpsWordReqDto.getFunction()); + if (tblCursor.toNextSelection()) { + String value = tblCursor.getTextValue(); + value = getValueType(wpsWordReqDto, value); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + // 5、域 + if ("5".equals(wpsWordReqDto.getBelongTo())) { + XmlCursor yuCursor = cursor.newCursor(); + yuCursor.selectPath(nameSpace + wpsWordReqDto.getFunction().replace("/chao","")); + if (yuCursor.toNextSelection()) { + if (wpsWordReqDto.getName().contains("域代码")) { + String value = yuCursor.getTextValue(); + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + if (wpsWordReqDto.getName().contains("域类型")) { + String value = yuCursor.getTextValue(); + if (value.contains("HYPERLINK")) { + value = "连接和引用->打开并跳到指定文件"; + } + WpsWordJudgementDto judgement = new WpsWordJudgementDto(); + judgement.setContentIn(wpsWordReqDto.getName() + value); + judgement.setContent(wpsWordReqDto.getFunction() + "-/" + value); + judgement.setImage(wpsWordReqDto.getType()+"-"+wpsWordReqDto.getBelongTo()+"-"+wpsWordReqDto.getIsboo()+"-"+wpsWordReqDto.getUnit()); + judgement.setScoreRate("1"); + judgementList.add(judgement); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return judgementList; + } + public static String getValueType(WpsWordReqDto wpsWordReqDto, String value) { + String values = value; + if (wpsWordReqDto.getUnit().contains("Filling_method")) { + if (value.contains("solidFill")) { + values = "纯色填充"; + } else if (value.contains("gradFill")) { + values = "渐变填充"; + } else if (value.contains("pattFill")) { + values = "图案填充"; + } else if (value.contains("blipFill")) { + values = "图片填充"; + } + } + if (wpsWordReqDto.getUnit().contains("twiptopt")) { + values = TwipConverter.formatDouble(TwipConverter.toPoints(Integer.parseInt(value))) + "磅"; + } + if (wpsWordReqDto.getUnit().contains("emustopt")) { + values = TwipConverter.formatDouble(TwipConverter.toEmus(Integer.parseInt(value))) + "磅"; + } + if (wpsWordReqDto.getUnit().contains("color")) { + values = ColorNameFinder.getColorName(value); + } + if (wpsWordReqDto.getUnit().contains("underline")) { + if ("double".equals(value)) { + values = "双下划线"; + } else if ("single".equals(value)) { + values = "单下划线"; + } else if ("none".equals(value)) { + values = "无下划线"; + } else if ("dotted".equals(value)) { + values = "点划线"; + } else if ("dottedHeavy".equals(value)) { + values = "粗点划线"; + } else if ("dash".equals(value)) { + values = "虚线"; + } else if ("dashedHeavy".equals(value)) { + values = "粗虚线"; + } else if ("dashLong".equals(value)) { + values = "长虚线"; + } + } + if (wpsWordReqDto.getUnit().contains("direction")) { + // 方向英文转换 + if ("landscape".equals(value)) { + values = "横向"; + } else if ("portrait".equals(value)) { + values = "纵向"; + } else if ("up".equals(value)) { + values = "上对齐"; + } else if ("down".equals(value)) { + values = "下对齐"; + } + } + if (value.contains("center")) { + values = "居中对齐"; + } + if (wpsWordReqDto.getUnit().contains("textdirection")) { + // 方向英文转换 + if ("left".equals(value)) { + values = "左对齐"; + } else if ("center".equals(value)) { + values = "居中对齐"; + } else if ("right".equals(value)) { + values = "右对齐"; + } else if ("both".equals(value)) { + values = "两端对齐"; + } + } + if (wpsWordReqDto.getUnit().contains("halfpt")) { + values = String.valueOf (Integer.parseInt(value) / 2); + } + if (wpsWordReqDto.getUnit().contains("shadowdirection")) { + ShadowDirectionUtils.ShadowDirection direction = ShadowDirectionUtils.getShadowDirection(Long.valueOf(value)); + values = direction.getLabel(); + } + if (wpsWordReqDto.getUnit().contains("ershifenzhiyi")) { + values = String.valueOf (Integer.parseInt(value) / 20); + } + if (wpsWordReqDto.getUnit().contains("duanluojianju")) { + if (Integer.parseInt(value) == 240) { + values = "1.5 倍行距"; + } else if (Integer.parseInt(value) == 360) { + values = "2 倍行距"; + } else if (Integer.parseInt(value) == 480) { + values = "3 倍行距"; + } + } + + return values; + } + + public static String detectWatermarkLayout(String style) { + if (style == null || !style.contains("rotation:")) { + return "水平"; + } + + try { + // 提取 rotation 的值 + String[] parts = style.split(";"); + for (String part : parts) { + part = part.trim(); + if (part.startsWith("rotation:")) { + String rotationStr = part.replace("rotation:", "").replace("f", "").trim(); + int rotationValue = Integer.parseInt(rotationStr); + + // 转换为角度(1度 = 65536) + int degrees = rotationValue / 65536; + + // 判断角度 + if (Math.abs(degrees) == 45) { + return "倾斜"; + } else { + return "水平"; + } + } + } + } catch (Exception e) { + return "解析出错:" + e.getMessage(); + } + + return "未知格式"; + } + + public static String getDrawingType(String text) { + String value =""; + if (text.contains("wp:wrapSquare")) { + value = "wp:wrapSquare" + "#" + "四周型环绕"; + } else if (text.contains("wp:wrapTight")) { + value = "wp:wrapTight" + "#" + "紧密型环绕"; + } else if (text.contains("wp:wrapThrough")) { + value = "wp:wrapThrough" + "#" + "穿越型环绕"; + } else if (text.contains("wp:wrapTopAndBottom")) { + value = "wp:wrapTopAndBottom" + "#" + "上下型环绕"; + } else if (text.contains("wp:wrapNone")) { + value = "wp:wrapNone" + "#" + "无环绕"; + } + return value; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordService.java b/src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordService.java new file mode 100644 index 0000000..7710500 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordService.java @@ -0,0 +1,20 @@ +package com.example.exam.exam.service.wpsword.word; + + + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; + +/** + * 判分逻辑集合(wps word) + * + * @author rwb + */ +public interface JudgementForWordService { + + /** + * word 判分 + * @param stuInfoVo 学生考试信息 + * @return 结果 + */ + Double autoForWpsWord(StuInfoVo stuInfoVo) throws Exception; +} diff --git a/src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordServiceImpl.java new file mode 100644 index 0000000..9a7a1b4 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpsword/word/JudgementForWordServiceImpl.java @@ -0,0 +1,115 @@ +package com.example.exam.exam.service.wpsword.word; + + +import com.example.exam.exam.controller.auto.vo.StuInfoVo; +import com.example.exam.exam.dal.*; +import com.example.exam.exam.mapper.EducationPaperQuMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.service.c.JudgementService; +import com.example.exam.exam.service.question.IExamQuestionService; +import com.example.exam.exam.service.stupaperscore.StuPaperScoreService; +import com.example.exam.exam.service.tenant.SystemTenantService; +import com.example.exam.exam.service.wpsword.JudgementWpsWordService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + + +/** + * @author REN + */ +@Service +public class JudgementForWordServiceImpl implements JudgementForWordService { + @Resource + JudgementService judgementService; + @Resource + EducationPaperSchemeMapper educationPaperSchemeMapper; + @Resource + IExamQuestionService examQuestionService; + @Resource + EducationPaperQuMapper educationPaperQuMapper; + @Resource + StuPaperScoreService stuPaperScoreService; + @Resource + JudgementWpsWordService judgementWpsWordService; + @Resource + SystemTenantService systemTenantService; + @Override + public Double autoForWpsWord(StuInfoVo stuInfoVo) throws Exception { + SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName()); + Double score = 0.0; + // 0、获取到试卷信息(试卷详情) + List educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId()); + List educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId()); + // 1、获取到学生文件路径 + String filePath = stuInfoVo.getFilePath(); + File folder = new File(filePath); + // 2、获取到学生文件 + File[] files = folder.listFiles(); + for (File file : files) { + File csFiles = new File(file.getPath()); + String quId = csFiles.getName(); + File[] csFileList = csFiles.listFiles(); + String name = csFileList[0].getName(); + if ("文字".equals(name)) { + File[] cs_file_list = csFiles.listFiles(); + String lastFilePath = cs_file_list[0].getPath(); + File[] lastFiles = cs_file_list[0].listFiles(); + // 3、根据题号进行查询试题的相关信息 + Optional result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals(name)).findFirst(); + Optional results = educationPaperQus.stream().filter(quLists -> quLists.getQuId().equals(quId)).findFirst(); + + EducationPaperScheme educationPaperScheme = result.get(); + EducationPaperQu educationPaperQu = results.get(); + String quScore = educationPaperScheme.getQuScores(); + ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId); + for (File lastFile : lastFiles) { + if (lastFile.getName().contains("文档")) { + String judgementStr = "

-----------------------------------------------------------

"; + judgementStr += "

试题序号:" + educationPaperQu.getSort() + "

"; + judgementStr += "

试题编号:" + examQuestion.getQuNum() + "

"; + judgementStr += "

试题分数:" + Double.parseDouble(quScore) + "

"; + judgementStr += "

试题名称:" + name + "

"; + SourceAndText wordpojo = judgementWpsWordService.judgementWpsWord(Double.parseDouble(quScore), lastFilePath, lastFile.getPath(), examQuestion, judgementStr); + score += wordpojo.getScore(); + judgementStr = wordpojo.getText(); + judgementStr += "

试题得分:" + wordpojo.getScore() + "

"; + // 4、需要更新学生试题得分,首先需要查询试题的数据库是否保存信息 + // 通过 quId,stuId,paperId 查询 + StuPaperScoreDO stuPaperScoreDO = stuPaperScoreService.getStuScoreByPaperIdAndQuid(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), quId); + if (stuPaperScoreDO != null) { + // 说明已经是做过该题,需要更新数据 + stuPaperScoreDO.setScore(new BigDecimal(wordpojo.getScore())); + stuPaperScoreDO.setContent(judgementStr); + stuPaperScoreDO.setSort(educationPaperQu.getSort()); + stuPaperScoreDO.setSubjectName(name); + stuPaperScoreDO.setIsTrue(wordpojo.getScore() == 0 ? 1 : wordpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreDO.setTrueScore(new BigDecimal(quScore)); + stuPaperScoreDO.setTenantId(systemTenant.getId()); + stuPaperScoreService.updateStuPaperScore(stuPaperScoreDO); + } else { + StuPaperScoreDO insertInfo = new StuPaperScoreDO(); + insertInfo.setStuId(stuInfoVo.getStuId()); + insertInfo.setPaperId(stuInfoVo.getPaperId()); + insertInfo.setQuId(quId); + insertInfo.setScore(new BigDecimal(wordpojo.getScore())); + insertInfo.setContent(judgementStr); + insertInfo.setSort(educationPaperQu.getSort()); + insertInfo.setSubjectName(name); + insertInfo.setTrueScore(new BigDecimal(quScore)); + insertInfo.setTenantId(systemTenant.getId()); + insertInfo.setIsTrue(wordpojo.getScore() == 0 ? 1 : wordpojo.getScore() == Double.parseDouble(quScore) ? 0 : 2); + stuPaperScoreService.insertStuPaperScore(insertInfo); + } + break; + } + } + } + } + return score; + } +} diff --git a/src/main/java/com/example/exam/exam/utils/HtmlAppender.java b/src/main/java/com/example/exam/exam/utils/HtmlAppender.java new file mode 100644 index 0000000..07e545e --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/HtmlAppender.java @@ -0,0 +1,53 @@ +package com.example.exam.exam.utils; + +public class HtmlAppender { + + /** + * 向原始HTML文本中追加一行内容,每行使用

标签包裹并换行。 + * + * @param originalText 原始HTML内容 + * @param newLine 要追加的新行文本 + * @return 新的HTML文本 + */ + public static String appendHtmlLine(String originalText, String newLine) { + StringBuilder result = new StringBuilder(originalText == null ? "" : originalText); + + if (newLine != null && !newLine.isEmpty()) { + result.append("

") + .append(escapeHtml(newLine)) + .append("

\n"); + } + + return result.toString(); + } + public static String appendHtmlLineMysql(String originalText, String format, Object... args) { + StringBuilder result = new StringBuilder(originalText == null ? "" : originalText); + + if (format != null && !format.isEmpty()) { + String newLine = String.format(format, args); + result.append("

") + .append(escapeHtml(newLine)) + .append("

\n"); + } + + return result.toString(); + } + + // 简单HTML转义,防止注入攻击 + private static String escapeHtml(String text) { + return text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'"); + } + + // 示例用法 + public static void main(String[] args) { + String original = ""; + original = appendHtmlLine(original, "Line 1"); + original = appendHtmlLine(original, "Line 2"); + original = appendHtmlLine(original, ""); + System.out.println(original); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/exam/exam/utils/Result.java b/src/main/java/com/example/exam/exam/utils/Result.java new file mode 100644 index 0000000..2aaf3c3 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/Result.java @@ -0,0 +1,54 @@ +package com.example.exam.exam.utils; + + +import lombok.Data; + +@Data +public class Result { + private Integer code; + private String msg; + private T data; + + /** 返回成功,携带数据(对象或列表均可) */ + public static Result success(T data) { + Result r = new Result<>(); + r.setCode(200); + r.setMsg("success"); + r.setData(data); + return r; + } + + /** 返回失败,附带错误信息 */ + public static Result fail(String msg) { + Result r = new Result<>(); + r.setCode(500); + r.setMsg(msg); + r.setData(null); + return r; + } + + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/exam/exam/utils/brower/BookmarkChecker.java b/src/main/java/com/example/exam/exam/utils/brower/BookmarkChecker.java new file mode 100644 index 0000000..3144f07 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/brower/BookmarkChecker.java @@ -0,0 +1,77 @@ +package com.example.exam.exam.utils.brower; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * 检查书签是否存在工具类 + */ +public class BookmarkChecker { + + /** + * 检查书签是否存在 + * + * @param bookmarkFile 书签文件路径 + * @param bookmarkName 要检查的书签名称 + * @return 是否存在该书签 + */ + public static boolean bookmarkExists(String bookmarkFile, String bookmarkName) { + Path path = Paths.get(bookmarkFile); + if (!Files.exists(path)) { + System.out.println("❌ 没有找到书签文件!"); + return false; + } + + JsonObject root; + try (FileReader reader = new FileReader(bookmarkFile)) { + root = JsonParser.parseReader(reader).getAsJsonObject(); + } catch (Exception e) { + System.err.println("❌ 读取书签文件失败:" + e.getMessage()); + return false; + } + + JsonObject roots = root.getAsJsonObject("roots"); + + if (roots.has("bookmark_bar")) { + if (searchBookmark(roots.getAsJsonObject("bookmark_bar"), bookmarkName)) { + return true; + } + } + if (roots.has("other")) { + if (searchBookmark(roots.getAsJsonObject("other"), bookmarkName)) { + return true; + } + } + + return false; + } + + // 递归查找书签 + private static boolean searchBookmark(JsonObject node, String nameToFind) { + if (!node.has("children")) return false; + + JsonArray children = node.getAsJsonArray("children"); + for (JsonElement element : children) { + JsonObject child = element.getAsJsonObject(); + String name = child.get("name").getAsString(); + String type = child.get("type").getAsString(); + + if ("url".equals(type) && name.equals(nameToFind)) { + return true; + } else if ("folder".equals(type) && child.has("children")) { + if (searchBookmark(child, nameToFind)) { + return true; + } + } + } + return false; + } +} + diff --git a/src/main/java/com/example/exam/exam/utils/brower/BookmarkDeleter.java b/src/main/java/com/example/exam/exam/utils/brower/BookmarkDeleter.java new file mode 100644 index 0000000..3893152 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/brower/BookmarkDeleter.java @@ -0,0 +1,88 @@ +package com.example.exam.exam.utils.brower; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.io.FileReader; +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * 删除指定书签工具类 + */ +public class BookmarkDeleter { + + /** + * 删除指定书签 + * + * @param bookmarkFile 书签文件路径 + * @param bookmarkNameToDelete 要删除的书签名称 + * @return 是否删除成功 + */ + public static boolean deleteBookmarkByName(String bookmarkFile, String bookmarkNameToDelete) { + Path path = Paths.get(bookmarkFile); + if (!Files.exists(path)) { + System.out.println("❌ 没有找到书签文件!"); + return false; + } + + JsonObject root; + try (FileReader reader = new FileReader(bookmarkFile)) { + root = JsonParser.parseReader(reader).getAsJsonObject(); + } catch (Exception e) { + System.err.println("❌ 读取书签文件失败:" + e.getMessage()); + return false; + } + + JsonObject roots = root.getAsJsonObject("roots"); + boolean deleted = false; + + if (roots.has("bookmark_bar")) { + deleted |= deleteUrlBookmark(roots.getAsJsonObject("bookmark_bar"), bookmarkNameToDelete); + } + if (roots.has("other")) { + deleted |= deleteUrlBookmark(roots.getAsJsonObject("other"), bookmarkNameToDelete); + } + + if (deleted) { + try (FileWriter writer = new FileWriter(bookmarkFile)) { + new GsonBuilder().setPrettyPrinting().create().toJson(root, writer); + } catch (Exception e) { + System.err.println("❌ 写入书签文件失败:" + e.getMessage()); + return false; + } + System.out.println("✅ 成功删除书签: " + bookmarkNameToDelete); + } else { + System.out.println("⚠️ 未找到该书签: " + bookmarkNameToDelete); + } + + return deleted; + } + + // 私有方法:只删除普通网页书签(type = url),支持递归 + private static boolean deleteUrlBookmark(JsonObject node, String nameToDelete) { + if (!node.has("children")) return false; + + JsonArray children = node.getAsJsonArray("children"); + boolean deleted = false; + + for (int i = children.size() - 1; i >= 0; i--) { + JsonObject child = children.get(i).getAsJsonObject(); + String name = child.get("name").getAsString(); + String type = child.get("type").getAsString(); + + if ("url".equals(type) && name.equals(nameToDelete)) { + children.remove(i); + deleted = true; + } else if ("folder".equals(type) && child.has("children")) { + deleted |= deleteUrlBookmark(child, nameToDelete); // 递归 + } + } + + return deleted; + } +} diff --git a/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java b/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java new file mode 100644 index 0000000..04a4140 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java @@ -0,0 +1,224 @@ +package com.example.exam.exam.utils.c; + +import javax.swing.*; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.concurrent.*; +import java.util.stream.Stream; + +/** + * C语言 相关工具类 + * + * @author pengchen + */ +public class JudgementCUtils +{ + /** + * 运行C语言代码 + * @param code C语言代码 + * @param input 测试用例插入值(如有) + * @param standard 编译版本 C99 + * @return 运行结果 + */ + public static String run_code(String pathC, String code, String input, String standard, String text) { + try { + boolean hasInput = code.contains("scanf") || code.contains("fgets") || code.contains("getchar"); + System.out.println(System.getenv("PATH")); +// String[] programCList = pathC.split("\\\\"); + + String programC = pathC + "\\program.c"; + String programOut = pathC + "\\program.out"; + // 写入 C 源码到文件 + File file = new File(programC); + try (FileWriter writer = new FileWriter(file)) { + writer.write(code); + } + + // 编译代码 + ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, programC, "-o", programOut); + compileBuilder.redirectErrorStream(true); + Process compileProcess = compileBuilder.start(); + + StringBuilder compileOutput = new StringBuilder(); + try (BufferedReader compileReader = new BufferedReader(new InputStreamReader(compileProcess.getInputStream()))) { + String line; + while ((line = compileReader.readLine()) != null) { + compileOutput.append(line).append("\n"); + } + } + + int compileResult = compileProcess.waitFor(); + String outputLower = compileOutput.toString().toLowerCase(); + if (outputLower.contains("error:") || !Files.exists(Paths.get(programOut))) { +// LogFileUtils.writeLine("❌ 编译失败:"); +// LogFileUtils.writeLine(compileOutput.toString()); + return "编译失败,输出:\n" + compileOutput.toString(); + } + + // 运行程序 + ProcessBuilder runBuilder = new ProcessBuilder(programOut); + runBuilder.redirectErrorStream(true); + Process runProcess = runBuilder.start(); + + // 输入注入(如有) + if (hasInput && input != null) { + try (BufferedWriter inputWriter = new BufferedWriter(new OutputStreamWriter(runProcess.getOutputStream()))) { + inputWriter.write(input); + inputWriter.newLine(); + inputWriter.flush(); + } + } + + // 使用线程池处理带超时的输出读取 + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(() -> { + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + } + return output.toString(); + }); + + String output; + try { + output = future.get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { +// LogFileUtils.writeLine("⏰ 程序超时,强制终止进程!"); + runProcess.destroy(); + future.cancel(true); + output = "程序运行超时(超过 5 秒)"; + } finally { + executor.shutdownNow(); + } + +// if (text != null) { +// LogFileUtils.writeLine("✅ 程序运行完成,输出:" + text); +// } else { +// LogFileUtils.writeLine("✅ 程序运行完成,输出:" + output.trim()); +// } + + return output; + + } catch (Exception ex) { + ex.printStackTrace(); +// LogFileUtils.writeLine("❌ 运行 C 代码时出错:" + ex.getMessage()); + return "运行 C 代码时出错:" + ex.getMessage(); + } + } + + /** + * 运行C语言代码,出题测试运行 + * @param code C语言代码 + * @param standard 编译版本 C99 + * @return 运行结果 + */ + public static String run_test_code(String code, String standard) { + try { + // 写入 C 语言源码到文件 + File file = new File("program.c"); + try (FileWriter writer = new FileWriter(file)) { + writer.write(code); + } + + // 编译 C 代码 + ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, "program.c", "-o", "program.out"); + compileBuilder.redirectErrorStream(true); + Process compileProcess = compileBuilder.start(); + + StringBuilder compileOutput = new StringBuilder(); + try (BufferedReader compileReader = new BufferedReader(new InputStreamReader(compileProcess.getInputStream()))) { + String line; + while ((line = compileReader.readLine()) != null) { + compileOutput.append(line).append("\n"); + } + } + + int compileResult = compileProcess.waitFor(); + + // 检查编译是否成功 + String outputLower = compileOutput.toString().toLowerCase(); + if (outputLower.contains("error:") || !Files.exists(Paths.get("program.out"))) { + return "编译失败,输出:\n" + compileOutput.toString(); + } + + // 运行程序并添加超时机制 + ProcessBuilder runBuilder = new ProcessBuilder("./program.out"); + runBuilder.redirectErrorStream(true); + Process runProcess = runBuilder.start(); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(() -> { + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + } + return output.toString(); + }); + + String output; + try { + output = future.get(5, TimeUnit.SECONDS); // 设置 5 秒超时 + } catch (TimeoutException e) { + runProcess.destroy(); + future.cancel(true); + output = "程序运行超时(超过 5 秒)"; + } finally { + executor.shutdownNow(); + } + + return output; + } catch (Exception ex) { + ex.printStackTrace(); + return "运行 C 代码时出错:" + ex.getMessage(); + } + } + + + + /** + * 读取文件代码 + * @param filePath 文件路径 + * @return 文件内容 + */ + public static String readFile(String filePath, String fileName) { + // 创建一个 Path 对象,指向包含 C 语言文件的文件夹 + Path folderPath = Paths.get(filePath); + + // 判断路径是否存在 + if (!Files.exists(folderPath)) { + return null; // 或者 return null,或者 throw new RuntimeException(...),看你的函数定义 + } + + // 使用 StringBuilder 来累积所有读取到的代码内容 + StringBuilder codeBuilder = new StringBuilder(); + + try (Stream paths = Files.walk(folderPath)) { + paths.filter(Files::isRegularFile) + .filter(path -> path.getFileName().toString().equalsIgnoreCase(fileName)) + .forEach(path -> { + try { + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + for (String line : lines) { + codeBuilder.append("\n").append(line); + System.out.println(" " + line); + } + } catch (IOException e) { + LogFileUtils.writeLine("❗ 无法读取文件: " + path); + } + }); + } catch (IOException e) { + return null; + } + return codeBuilder.toString(); + } +} diff --git a/src/main/java/com/example/exam/exam/utils/c/LogFileUtils.java b/src/main/java/com/example/exam/exam/utils/c/LogFileUtils.java new file mode 100644 index 0000000..8b84054 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/c/LogFileUtils.java @@ -0,0 +1,73 @@ +package com.example.exam.exam.utils.c; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class LogFileUtils { + + private static BufferedWriter writer; + private static final Logger log = LoggerFactory.getLogger(LogFileUtils.class); + // 日期时间格式化器(比如 2025-03-24 15:32:10) + private static final DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + /** + * 创建txt文件 + * @param path 路径 + */ + public static void createFile(String path) { + try { + File file = new File(path); + if (!file.exists()) { + file.getParentFile().mkdirs(); // 创建父目录 + file.createNewFile(); + } + writer = new BufferedWriter(new FileWriter(file, true)); // 追加写入模式 + } catch (IOException e) { + log.error(e.getMessage()); + throw new RuntimeException("无法创建日志文件: " + e.getMessage()); + } + } + + + /** + * 写入文本,自动换行 + * @param content 写入的文本 + */ + public static void writeLine(String content) { + if (writer == null) { + throw new IllegalStateException("请先调用 create() 方法初始化文件"); + } + + try { + String timestamp = LocalDateTime.now().format(formatter); + String logLine = String.format("[%s] %s", timestamp, content); + + writer.write(logLine); + writer.newLine(); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 关闭文件 + */ + public static void close() { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/example/exam/exam/utils/file/GetDifferencesBetweenFolders.java b/src/main/java/com/example/exam/exam/utils/file/GetDifferencesBetweenFolders.java new file mode 100644 index 0000000..4083b58 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/file/GetDifferencesBetweenFolders.java @@ -0,0 +1,130 @@ +package com.example.exam.exam.utils.file; + + +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 获取文件夹差异工具类 + */ +public class GetDifferencesBetweenFolders { + + // 获取两个文件夹的差异 + public static Map getDifferencesBetweenFolders( String folder1, String folder2) throws IOException { + + Path path1 = Paths.get(folder1); + Path path2 = Paths.get( folder2); + + Map files1 = listFilesAndFoldersWithAttributes(path1); + Map files2 = listFilesAndFoldersWithAttributes(path2); + + Map differences = new LinkedHashMap<>(); + + // 找出仅存在于 folder1 的文件 + for (String file : files1.keySet()) { + if (!files2.containsKey(file)) { + differences.put(file, "仅存在于 " + folder1); + } else { + String file1Attributes = files1.get(file); + String file2Attributes = files2.get(file); + if (!file1Attributes.equals(file2Attributes)) { + differences.put(file, "属性不同: " + file1Attributes + " vs " + file2Attributes); + } + } + } + + // 找出仅存在于 folder2 的文件 + for (String file : files2.keySet()) { + if (!files1.containsKey(file)) { + differences.put(file, "仅存在于 " + folder2); + } + } + + return differences; + } + + + + + + // 列出文件夹下的所有文件及其属性 + public static Map listFilesAndFoldersWithAttributes(Path folder) throws IOException { + if (!Files.exists(folder)) return Collections.emptyMap(); + try (Stream stream = Files.walk(folder)) { + return stream.collect(Collectors.toMap( + path -> folder.relativize(path).toString(), + GetDifferencesBetweenFolders::getFileAttributes, + (a, b) -> a, + LinkedHashMap::new + )); + } + } + + + public static Map listFilesAndFoldersWithAttributes(Path folder, String excludeFolderName) throws IOException { + if (!Files.exists(folder)) return Collections.emptyMap(); + + Map result = new LinkedHashMap<>(); + + Files.walkFileTree(folder, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + // 如果遇到需要排除的文件夹,跳过整个目录 + if (dir.getFileName().toString().equals(excludeFolderName) && !dir.equals(folder)) { + return FileVisitResult.SKIP_SUBTREE; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + String relativePath = folder.relativize(file).toString(); + result.put(relativePath, GetDifferencesBetweenFolders.getFileAttributes(file)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + // 也可以记录文件夹本身的属性 + if (!dir.equals(folder)) { // 排除根目录自身 + String relativePath = folder.relativize(dir).toString(); + result.put(relativePath, GetDifferencesBetweenFolders.getFileAttributes(dir)); + } + return FileVisitResult.CONTINUE; + } + }); + + return result; + } + + + + + // 返回文件和文件夹的属性 + static String getFileAttributes(Path path) { + try { + boolean isDirectory = Files.isDirectory(path); + boolean isHidden = Files.isHidden(path); + boolean isReadable = Files.isReadable(path); + + if (isDirectory) { + return String.format("隐藏: %b, 可读: %b", isHidden, isReadable); + } else { + boolean isWritable = Files.isWritable(path); + return String.format("隐藏: %b, 可读: %b, 可写: %b", + isHidden, isReadable, isWritable); + } + } catch (IOException e) { + return "无法获取属性"; + } + } + + + +} diff --git a/src/main/java/com/example/exam/exam/utils/zip/ZipUtil.java b/src/main/java/com/example/exam/exam/utils/zip/ZipUtil.java new file mode 100644 index 0000000..0e97df0 --- /dev/null +++ b/src/main/java/com/example/exam/exam/utils/zip/ZipUtil.java @@ -0,0 +1,117 @@ +package com.example.exam.exam.utils.zip; + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; + +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.DosFileAttributeView; +import java.util.Enumeration; + +public class ZipUtil { + + public static String unzipToNamedFolder(String zipFilePath) { + File zipFile = new File(zipFilePath); + + if (!zipFile.exists() || !zipFile.getName().toLowerCase().endsWith(".zip")) { + System.err.println("❌ 无效 zip 文件: " + zipFilePath); + return null; + } + + String fileNameNoExt = zipFile.getName().replaceAll("(?i)\\.zip$", ""); + File extractDir = new File(zipFile.getParentFile(), fileNameNoExt); + if (!extractDir.exists()) extractDir.mkdirs(); + + // 指定编码(GBK 用于支持中文文件名,UTF-8 用于通用 ZIP) + try (ZipFile zf = new ZipFile(zipFile, "GBK")) { + Enumeration entries = zf.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + File outFile = new File(extractDir, entry.getName()); + + // 防止 Zip 穿越攻击 + if (!outFile.getCanonicalPath().startsWith(extractDir.getCanonicalPath())) { + throw new IOException("非法路径: " + entry.getName()); + } + + if (entry.isDirectory()) { + outFile.mkdirs(); + } else { + File parent = outFile.getParentFile(); + if (!parent.exists()) parent.mkdirs(); + + try (InputStream is = zf.getInputStream(entry); + OutputStream os = new FileOutputStream(outFile)) { + + byte[] buffer = new byte[4096]; + int len; + while ((len = is.read(buffer)) > 0) { + os.write(buffer, 0, len); + } + } + } + + // 读取 Zip 中的外部文件属性(低16位,DOS属性) + int externalAttrs = (int) (entry.getExternalAttributes() & 0xFFFF); + + // DOS隐藏属性是第二位(0x02) + boolean hidden = (externalAttrs & 0x02) != 0; + // DOS只读属性是第一位(0x01) + boolean readOnly = (externalAttrs & 0x01) != 0; + + Path outPath = outFile.toPath(); + DosFileAttributeView view = Files.getFileAttributeView(outPath, DosFileAttributeView.class); + + if (view != null) { + try { + // 设置隐藏属性 + view.setHidden(hidden); + // 设置只读属性,需要用Files.setAttribute + Files.setAttribute(outPath, "dos:readonly", readOnly); + } catch (IOException e) { + System.err.println("设置文件属性失败: " + e.getMessage()); + } + } + } + System.out.println("✅ 解压完成,目录:" + extractDir.getAbsolutePath()); + return extractDir.getAbsolutePath(); + + } catch (IOException e) { + System.err.println("❌ 解压失败: " + e.getMessage()); + return null; + } + } + + + public static String downloadStudentFile(String fileUrl, String filePath) { + try { + URL url = new URL(fileUrl); + URLConnection connection = url.openConnection(); + + String fileName = new File(url.getPath()).getName(); + File dir = new File(filePath); + if (!dir.exists()) dir.mkdirs(); + + File saveFile = new File(dir, fileName); + + try (InputStream in = connection.getInputStream(); + FileOutputStream out = new FileOutputStream(saveFile)) { + + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + + System.out.println("✅ 下载成功: " + saveFile.getAbsolutePath()); + return saveFile.getAbsolutePath(); + } + } catch (IOException e) { + System.err.println("❌ 下载失败: " + e.getMessage()); + return null; + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f2a58ad --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,17 @@ +spring.datasource.url=jdbc:mysql://rm-bp1a44uap1mm20980mo.mysql.rds.aliyuncs.com:3306/pc-exam?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true +spring.datasource.username=pc_exam +spring.datasource.password=Pcexam123 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +server.port=48082 + +spring.main.banner-mode=off + +# MyBatis-Plus ?????? +mybatis-plus.configuration.map-underscore-to-camel-case=true +mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml +mybatis-plus.type-aliases-package=com.example.exam.exam.dal + + +spring.security.user.name=exam +spring.security.user.password=exam123 \ No newline at end of file diff --git a/src/main/resources/mapper/paper/EducationPaperQuMapper.xml b/src/main/resources/mapper/paper/EducationPaperQuMapper.xml new file mode 100644 index 0000000..a9e55cc --- /dev/null +++ b/src/main/resources/mapper/paper/EducationPaperQuMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + select paper_id, qu_id, sort from education_paper_qu + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/paper/EducationPaperSchemeMapper.xml b/src/main/resources/mapper/paper/EducationPaperSchemeMapper.xml new file mode 100644 index 0000000..b0e600b --- /dev/null +++ b/src/main/resources/mapper/paper/EducationPaperSchemeMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + select scheme_id, task_id,qu_title,sp_name, qu_level, keywords, point_names, qu_numbers, qu_scores, subtotal_score,sort from education_paper_scheme + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/EducationPaperMapper.xml b/src/main/resources/mapper/question/EducationPaperMapper.xml new file mode 100644 index 0000000..f36c210 --- /dev/null +++ b/src/main/resources/mapper/question/EducationPaperMapper.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select paper_id, task_id, num,counts, paper_score,roll_up, is_ab, status ,create_time,update_time,creator,updater ,deleted from education_paper + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/ExamMysqlKeywordMapper.xml b/src/main/resources/mapper/question/ExamMysqlKeywordMapper.xml new file mode 100644 index 0000000..51323a0 --- /dev/null +++ b/src/main/resources/mapper/question/ExamMysqlKeywordMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + select keyword_id, answer_id, keyword, score_rate from exam_mysql_keyword + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/ExamQuestionAnswerMapper.xml b/src/main/resources/mapper/question/ExamQuestionAnswerMapper.xml new file mode 100644 index 0000000..08e22b7 --- /dev/null +++ b/src/main/resources/mapper/question/ExamQuestionAnswerMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + select answer_id, qu_id, is_right, image, content,contentIn, score_rate,attribute,sort from exam_question_answer + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/ExamQuestionKeywordMapper.xml b/src/main/resources/mapper/question/ExamQuestionKeywordMapper.xml new file mode 100644 index 0000000..bfa279b --- /dev/null +++ b/src/main/resources/mapper/question/ExamQuestionKeywordMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + select keyword_id, qu_id, keyword, score_rate from exam_question_keyword + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/ExamQuestionMapper.xml b/src/main/resources/mapper/question/ExamQuestionMapper.xml new file mode 100644 index 0000000..3ea3206 --- /dev/null +++ b/src/main/resources/mapper/question/ExamQuestionMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select qu_id, qu_bank_id, qu_num,qu_bank_name, chapterId_dict_text,subject_name, qu_level, + content, content_text, analysis,answer, point_names, keywords, manual ,status,audit ,course_name, specialty_name , tname from exam_question + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/ExamQuestionScoreMapper.xml b/src/main/resources/mapper/question/ExamQuestionScoreMapper.xml new file mode 100644 index 0000000..e95b6c0 --- /dev/null +++ b/src/main/resources/mapper/question/ExamQuestionScoreMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + select score_id, qu_id, is_pass, is_result, is_keyword,is_compile, is_pass_score, is_result_score, is_keyword_score ,is_compile_score,keyword_cutoff,compile_cutoff from exam_question_score + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/question/SysFileMapper.xml b/src/main/resources/mapper/question/SysFileMapper.xml new file mode 100644 index 0000000..3052e51 --- /dev/null +++ b/src/main/resources/mapper/question/SysFileMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + select file_id, qu_id, url, file_type ,file_name from sys_file + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/stupaperscore/StuPaperScoreMapper.xml b/src/main/resources/mapper/stupaperscore/StuPaperScoreMapper.xml new file mode 100644 index 0000000..b8cfda5 --- /dev/null +++ b/src/main/resources/mapper/stupaperscore/StuPaperScoreMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + DELETE FROM exam_stu_paper_score WHERE stu_id = #{stuId} AND paper_id = #{paperId} + + + + \ No newline at end of file diff --git a/src/test/java/com/example/exam/exam/StuApplicationTests.java b/src/test/java/com/example/exam/exam/StuApplicationTests.java new file mode 100644 index 0000000..cd2f863 --- /dev/null +++ b/src/test/java/com/example/exam/exam/StuApplicationTests.java @@ -0,0 +1,49 @@ +package com.example.exam.exam; + +import com.example.exam.exam.dal.EducationPaperScheme; +import com.example.exam.exam.dal.ExamQuestion; +import com.example.exam.exam.mapper.EducationPaperMapper; +import com.example.exam.exam.mapper.EducationPaperSchemeMapper; +import com.example.exam.exam.mapper.ExamQuestionMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +@SpringBootTest +class StuApplicationTests { + + @Autowired + private EducationPaperMapper educationPaperMapper; + @Autowired + private ExamQuestionMapper examQuestionMapper; + @Autowired + private EducationPaperSchemeMapper educationPaperSchemeMapper; + + //根据试卷id 查询 该试卷下的 试题 和答案 (试题和答案都按顺序) + @Test + void selectPaperQuByPid() { + List examQuestions = educationPaperMapper.selectPaperQuByPaperId("6576f1dd58d74396a64bc723ec61ab91"); + for (ExamQuestion examQuestion : examQuestions) { + System.out.println(examQuestion.toString()); + } + } + + //根据试题id 查询 试题 + @Test + void selectQuById() { + ExamQuestion examQuestion = examQuestionMapper.selectExamQuestionByQuId("1c6a1832c16d4b37bae08557d05b8c64"); + System.out.println(examQuestion); + } + + //根据试卷id 返回 该试卷的 方案集合 + @Test + void selectPaperScheme(){ + List educationPaperSchemes = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId("a995d7a347d9477a90f5703d2c21afdc"); + for (EducationPaperScheme educationPaperScheme : educationPaperSchemes) { + System.out.println(educationPaperScheme.toString()); + } + } + +}
1