【修改】 1、WPS相关试题,考点全对返回试题分数,2、新增邮箱判分与生成文件方法,3、优化代码,增加备注

This commit is contained in:
dlaren
2025-08-17 21:08:35 +08:00
parent 65e9accc91
commit 15541d95c4
25 changed files with 840 additions and 1227 deletions

View File

@@ -2,6 +2,7 @@ package com.example.exam.exam.controller.auto;
import com.example.exam.exam.controller.auto.vo.StuInfoVo;
import com.example.exam.exam.dal.StuPaperScoreDO;
import com.example.exam.exam.service.autoForEmailSetting.AutoForWinEmailSettingService;
import com.example.exam.exam.service.autoForWinEdgeSetting.AutoForWinEdgeSettingService;
import com.example.exam.exam.service.autoforbrower.AutoForBrowerService;
import com.example.exam.exam.service.autoforps.AutoForPsService;
@@ -56,19 +57,26 @@ public class AutoController {
AutoForPsService autoForPsService;
@Resource
AutoForWinEdgeSettingService autoForWinEdgeSettingService;
@Resource
AutoForWinEmailSettingService autoForWinEmailSettingService;
/**
* 考试完成后,获取总分
* @param stuInfoVo 学生信息
* @return 分数
*/
@GetMapping("/getStuSource")
public Result<BigDecimal> getStuSource(StuInfoVo stuInfoVo){
public Result<BigDecimal> getStuSource(StuInfoVo stuInfoVo) {
BigDecimal source = new BigDecimal(0);
List<StuPaperScoreDO> stuPaperScoreDOList = stuPaperScoreService.findByStuIDAndPaperId(stuInfoVo.getStuId(), stuInfoVo.getPaperId(), stuInfoVo.getTemporaryId());
for (StuPaperScoreDO stuPaperScoreDO : stuPaperScoreDOList){
for (StuPaperScoreDO stuPaperScoreDO : stuPaperScoreDOList) {
source = source.add(stuPaperScoreDO.getScore());
}
return Result.success(source);
}
// C语言
/**
* C语言判分
*
* @param stuInfoVo 学生信息
* @return 判分结果
*/
@@ -76,96 +84,171 @@ public class AutoController {
public Result<BigDecimal> judgementForC(@RequestBody StuInfoVo stuInfoVo) throws IOException {
return Result.success(autoService.autoForC(stuInfoVo));
}
// WPS-WORD
/**
* WPS-WORD 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws Exception 异常信息
*/
@PostMapping("/judgementForWord")
public Result<BigDecimal> judgementForWord(@RequestBody StuInfoVo stuInfoVo) throws Exception {
return Result.success(judgementForWordService.autoForWpsWord(stuInfoVo));
}
// WPS-EXCEL
/**
* WPS-EXCEL 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws Exception 异常信息
*/
@PostMapping("/judgementForExcel")
public Result<BigDecimal> judgementForExcel(@RequestBody StuInfoVo stuInfoVo) throws Exception {
return Result.success(judgementForExcelService.autoForWpsExcel(stuInfoVo));
}
// WPS-PPT
/**
* WPS-PPT 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws Exception 异常信息
*/
@PostMapping("/judgementForPptx")
public Result<BigDecimal> judgementForPptx(@RequestBody StuInfoVo stuInfoVo) throws Exception {
return Result.success(judgementForPptxService.autoForWpsPptx(stuInfoVo));
}
// Mysql
/**
* Mysql 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws SQLException 异常信息
* @throws IOException 异常信息
*/
@PostMapping("/judgementForMysql")
public Result<BigDecimal> judgementForMysql(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException {
return Result.success(autoForMysqlService.autoForMysql(stuInfoVo));
}
// Mysql文件
/**
* Mysql 文件
* @param stuInfoVo 学生信息
* @return 分数
* @throws SQLException 异常信息
* @throws IOException 异常信息
*/
@PostMapping("/judgementForMysqlFile")
public Result judgementForMysqlFile(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException {
return Result.success(autoForMysqlService.autoForMysqlFile(stuInfoVo));
}
/**
* 删除 本地学生的连接和库
* @param tNames
* @throws Exception
*
* @param tNames 表名
* @throws Exception 异常信息
*/
@PostMapping("/delMysqlConnect")
public void delete(@RequestBody List<String> tNames) throws Exception {
mysqlLocalService.delMysqlConnect(tNames);
}
// 文件处理
/**
* 文件提 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws SQLException 异常信息
* @throws IOException 异常信息
*/
@PostMapping("/judgementForFile")
public Result<BigDecimal> judgementForFile(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException {
return Result.success(autoForFileService.autoForFile(stuInfoVo));
}
// 浏览器
/**
* 浏览器题 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws SQLException 异常信息
* @throws IOException 异常信息
*/
@PostMapping("/judgementForBrower")
public Result<BigDecimal> judgementForBrower(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException {
return Result.success(autoForBrowerService.autoForBrower(stuInfoVo));
}
// windows网络设置
/**
* 将浏览器的页面写入到JSON文件中
* @param stuInfoVo 学生选择题信息
*
* @param stuInfoVo 学生信息
* @return String
*/
@PostMapping("/judgementForEdgeDummyToJson")
public Result<String> judgementForEdgeDummyToJson(@RequestBody StuInfoVo stuInfoVo){
public Result<String> judgementForEdgeDummyToJson(@RequestBody StuInfoVo stuInfoVo) {
return Result.success(autoForWinEdgeSettingService.autoForEdgeToJson(stuInfoVo));
}
/**
* 邮箱设置判分
*
* @param stuInfoVo 学生信息
* @return String
*/
@PostMapping("/judgementForEmailDummy")
public Result<BigDecimal> judgementForEmailDummy(@RequestBody StuInfoVo stuInfoVo) throws IOException {
return Result.success(autoForWinEdgeSettingService.autoForEdge(stuInfoVo));
}
/**
* 邮箱的页面写入到JSON文件中
*
* @param stuInfoVo 学生信息
* @return String
*/
@PostMapping("/judgementForEmailDummyToJson")
public Result<String> judgementForEmailDummyToJson(@RequestBody StuInfoVo stuInfoVo) {
return Result.success(autoForWinEmailSettingService.autoForEmailToJson(stuInfoVo));
}
/**
* windows网络设置判分
* @param stuInfoVo 学生选择题信息
*
* @param stuInfoVo 学生信息
* @return String
*/
@PostMapping("/judgementForEdgeDummy")
public Result<BigDecimal> judgementForEdgeDummy(@RequestBody StuInfoVo stuInfoVo) throws IOException {
return Result.success(autoForWinEdgeSettingService.autoForEdge(stuInfoVo));
return Result.success(autoForWinEmailSettingService.autoForEmail(stuInfoVo));
}
// QQ邮箱
// win10虚拟界面
//PS
/**
* PS 判分
* @param stuInfoVo 学生信息
* @return 分数
* @throws SQLException 异常信息
* @throws IOException 异常信息
*/
@PostMapping("/judgementForPS")
public Result<BigDecimal> judgementForPS(@RequestBody StuInfoVo stuInfoVo) throws SQLException, IOException {
return Result.success(autoForPsService.autoForPs(stuInfoVo));
}
// 单项选择
/**
* 将选择题写入到JSON文件中
* @param stuInfoVo 学生选择题信息
* @return String
*
* @param stuInfoVo 学生信息
* @return 分数
*/
@PostMapping("/judgementForChoiceToJson")
public Result<String> judgementForChoiceToJson(@RequestBody StuInfoVo stuInfoVo){
public Result<String> judgementForChoiceToJson(@RequestBody StuInfoVo stuInfoVo) {
return Result.success(autoForChoiceService.autoForChoiceToJson(stuInfoVo));
}
/**
* 选择题判分
* @param stuInfoVo 学生选择题信息
*
* @param stuInfoVo 学生信息
* @return String
*/
@PostMapping("/judgementForChoice")

View File

@@ -23,10 +23,10 @@ public class StuInfoVo {
// 选择题学生答案
private String choiceAnswerId;
// Edge学生设置Key
// Edge学生设置Key(邮箱也使用)
private String edgeAnswerKeys;
// Edge学生设置Value
// Edge学生设置Value(邮箱也使用)
private String edgeAnswerValues;
// 学校名称

View File

@@ -0,0 +1,16 @@
package com.example.exam.exam.service.autoForEmailSetting;
import com.example.exam.exam.controller.auto.vo.StuInfoVo;
import java.io.IOException;
import java.math.BigDecimal;
/**
* @author REN
*/
public interface AutoForWinEmailSettingService {
String autoForEmailToJson(StuInfoVo stuInfoVo);
BigDecimal autoForEmail(StuInfoVo stuInfoVo) throws IOException;
}

View File

@@ -0,0 +1,286 @@
package com.example.exam.exam.service.autoForEmailSetting;
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.HtmlAppender;
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.*;
import java.math.BigDecimal;
import java.util.*;
@Service
public class AutoForWinEmailSettingServiceImpl implements AutoForWinEmailSettingService {
@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 autoForEmailToJson(StuInfoVo stuInfoVo) {
// 写入到文件夹
// 试题ID
String quId = stuInfoVo.getQuestionId();
String edgeAnswerKey = stuInfoVo.getEdgeAnswerKeys();
String edgeAnswerValue = stuInfoVo.getEdgeAnswerValues();
// 保存ID路径
String filePath = stuInfoVo.getFilePath();
File file = new File(filePath + "/" + quId + "@EmailDummy.json");
if (!file.exists()) {
LogFileUtils.createFile(filePath + "/" + quId + "@EmailDummy.json");
}
Map<String, String> map = new HashMap<>();
map.put(edgeAnswerKey, edgeAnswerValue);
return writeMapToJson(map, filePath + "/" + quId + "@EmailDummy.json");
}
@Override
public BigDecimal autoForEmail(StuInfoVo stuInfoVo) throws IOException {
SystemTenant systemTenant = systemTenantService.getId(stuInfoVo.getSchoolName());
BigDecimal score = new BigDecimal(0);
// 获取Paper下的所有windows网络设置ID
List<ExamQuestion> examQuestions = educationPaperMapper.selectPaperQuByPaperId(stuInfoVo.getPaperId());
List<ExamQuestion> edgeList = new ArrayList<>();
for (ExamQuestion examQuestion : examQuestions) {
if ("邮箱".equals(examQuestion.getSubjectName())) {
edgeList.add(examQuestion);
}
}
// 获取所有json文件并取出试题ID
List<File> resultFile = new ArrayList<>();
File dir = new File(stuInfoVo.getFilePath());
File[] files = dir.listFiles();
if (files == null) return null;
for (File file : files) {
if (file.isFile()
&& file.getName().contains("@EmailDummy")
&& file.getName().toLowerCase().endsWith(".json")) {
resultFile.add(file);
}
}
List<String> trueFileQuid = new ArrayList<>();
List<String> noFileQuid = new ArrayList<>();
// 开始读取文件并进行拆分
for (ExamQuestion examQuestion : edgeList) {
boolean fileIsTrue = false;
for (File file : resultFile) {
String quId = file.getName().split("@")[0];
if (quId.equals(examQuestion.getQuId())) {
fileIsTrue = true;
trueFileQuid.add(quId);
break;
}
}
if (!fileIsTrue) {
noFileQuid.add(examQuestion.getQuId());
}
}
// 查询哪些文件已经进行获取到学生作答文件了
for (String str : trueFileQuid) {
// 查询到学生文件
Optional<File> fileResult = resultFile.stream().filter(resultFiles -> resultFiles.getName().contains(str)).findFirst();
if (fileResult.isPresent()) {
File file = fileResult.get();
// 创建 ObjectMapper 实例
File jsonFile = new File(file.getPath());
ObjectMapper objectMapper = new ObjectMapper();
if (jsonFile.exists()) {
Double oneScore = 0.0;
String quId = file.getName().split("@")[0];
List<EducationPaperQu> educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId());
Optional<EducationPaperQu> resultss = educationPaperQus.stream().filter(entry -> entry.getQuId().equals(quId))
.findFirst();
// 获取排序
EducationPaperQu educationPaperQu = resultss.get();
// 试题分数
List<EducationPaperScheme> educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId());
Optional<EducationPaperScheme> result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals("windows网络设置")).findFirst();
String quScore = result.get().getQuScores();
// 根据ID查询试题
ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId);
// 读取学生文件
try (FileInputStream fileInputStream = new FileInputStream(jsonFile)) {
// 读取文件并转换为 JsonNode
JsonNode rootNode = objectMapper.readTree(fileInputStream);
String jsonText = objectMapper.writeValueAsString(rootNode);
// 从 JSON 文件读取并转换为 Person 对象
Map<String, String> map = objectMapper.readValue(jsonText, Map.class);
String judgementStr = "<p>-----------------------------------------------------------</p>";
judgementStr += "<p>试题序号:" + educationPaperQu.getSort() + "</p>";
judgementStr += "<p>试题编号:" + examQuestion.getQuNum() + "</p>";
judgementStr += "<p>试题分数:" + Double.parseDouble(quScore) + "</p>";
judgementStr += "<p>试题名称: 邮箱 </p>";
// 考试进行比对
for (ExamQuestionAnswer questionAnswer : examQuestion.getAnswerList()) {
boolean flag = false;
double one_sorce = 0;
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 使用map组合成考点
String point = key + "@" + value;
if (questionAnswer.getContent().equals(point)) {
// 说明正确
flag = true;
// 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重
if (questionAnswer.getScoreRate().equals("1")) {
// 说明权重相等,直接平分分数
one_sorce = Double.parseDouble(quScore) / examQuestion.getAnswerList().size();
} else {
one_sorce = Double.parseDouble(quScore) * Double.parseDouble(questionAnswer.getScoreRate());
}
break;
}
}
oneScore += one_sorce;
score = score.add(new BigDecimal(one_sorce));
if (flag) {
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "" + questionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce);
} else {
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "" + questionAnswer.getContentIn() + " 得分失败");
}
}
judgementStr += "<p>试题得分: " + oneScore + " </p>";
StuPaperScoreDO insertInfo = new StuPaperScoreDO();
insertInfo.setStuId(stuInfoVo.getStuId());
insertInfo.setPaperId(stuInfoVo.getPaperId());
insertInfo.setQuId(quId);
insertInfo.setScore(new BigDecimal(oneScore));
insertInfo.setContent(judgementStr);
insertInfo.setSort(educationPaperQu.getSort());
insertInfo.setSubjectName("邮箱");
int isTrue = oneScore == 0 ? 1 : oneScore == Double.parseDouble(quScore) ? 0 : 2;
insertInfo.setIsTrue(isTrue);
insertInfo.setTenantId(systemTenant.getId());
insertInfo.setTemporaryId(stuInfoVo.getTemporaryId());
insertInfo.setTrueScore(new BigDecimal(quScore));
stuPaperScoreService.insertStuPaperScore(insertInfo);
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 没有作答的题目直接0分
for (String str : noFileQuid) {
String quId = str;
List<EducationPaperQu> educationPaperQus = educationPaperQuMapper.selectPaperQuListByPaperId(stuInfoVo.getPaperId());
Optional<EducationPaperQu> resultss = educationPaperQus.stream().filter(entry -> entry.getQuId().equals(quId))
.findFirst();
// 获取排序
EducationPaperQu educationPaperQu = resultss.get();
// 试题分数
List<EducationPaperScheme> educationPaperSchemeList = educationPaperSchemeMapper.selectEducationPaperTaskByPaperId(stuInfoVo.getPaperId());
Optional<EducationPaperScheme> result = educationPaperSchemeList.stream().filter(quLists -> quLists.getSpName().equals("windows网络设置")).findFirst();
String quScore = result.get().getQuScores();
// 根据ID查询试题
ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(quId);
String judgementStr = "<p>-----------------------------------------------------------</p>";
judgementStr += "<p>试题序号:" + educationPaperQu.getSort() + "</p>";
judgementStr += "<p>试题编号:" + examQuestion.getQuNum() + "</p>";
judgementStr += "<p>试题分数:" + Double.parseDouble(quScore) + "</p>";
judgementStr += "<p>试题名称: 邮箱 </p>";
judgementStr += "<p>❌ 未成功检测到作答情况</p>";
judgementStr += "<p>试题得分: 0分 </p>";
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.setTemporaryId(stuInfoVo.getTemporaryId());
insertInfo.setTrueScore(new BigDecimal(quScore));
stuPaperScoreService.insertStuPaperScore(insertInfo);
}
return score.setScale(1, BigDecimal.ROUND_HALF_UP);
}
// 将Map写入JSON文件
// 将Map追加到JSON文件
public static String writeMapToJson(Map<String, String> map, String filePath) {
ObjectMapper objectMapper = new ObjectMapper();
File file = new File(filePath);
try {
Map<String, String> existingMap = new HashMap<>();
if (file.exists() && file.length() > 0) {
try (InputStream fis = new FileInputStream(file)) {
existingMap = objectMapper.readValue(fis, Map.class);
}
}
existingMap.putAll(map);
try (OutputStream fos = new FileOutputStream(file, false);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
objectMapper.writeValue(bos, existingMap);
bos.flush(); // 强制刷新
}
// 可选占用测试
boolean released = file.renameTo(file);
System.out.println("文件释放状态: " + released);
} 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"
}
}
}

View File

@@ -40,7 +40,7 @@ public class AutoForWinEdgeSettingServiceImpl implements AutoForWinEdgeSettingSe
private EducationPaperMapper educationPaperMapper;
/**
* 自动判题选择题
* 自动判题Edge
*
* @param stuInfoVo 学生考试信息
* @return 是否通过
@@ -223,31 +223,19 @@ public class AutoForWinEdgeSettingServiceImpl implements AutoForWinEdgeSettingSe
judgementStr += "<p>试题名称: windows网络设置 </p>";
judgementStr += "<p>❌ 未成功检测到作答情况</p>";
judgementStr += "<p>试题得分: 0分 </p>";
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("windows网络设置");
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("windows网络设置");
insertInfo.setIsTrue(1);
insertInfo.setTenantId(systemTenant.getId());
insertInfo.setTrueScore(new BigDecimal(quScore));
stuPaperScoreService.insertStuPaperScore(insertInfo);
}
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("windows网络设置");
insertInfo.setIsTrue(1);
insertInfo.setTenantId(systemTenant.getId());
insertInfo.setTemporaryId(stuInfoVo.getTemporaryId());
insertInfo.setTrueScore(new BigDecimal(quScore));
stuPaperScoreService.insertStuPaperScore(insertInfo);
}
return score.setScale(1, BigDecimal.ROUND_HALF_UP);
}

View File

@@ -12,17 +12,15 @@ 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 ; // 文件路径
static String answerLogPath; // 文件路径
@Resource
private ExamQuestionAnswerMapper examQuestionAnswerMapper;
private static final DateTimeFormatter formatter =
@@ -30,6 +28,7 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
//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 {
//根据题目,查找考点
@@ -72,22 +71,22 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
// appendToFile(answerLogPath,"=== 学生提交内容stu 目录) ===");
//
// stuFiles.forEach((key, value) -> appendToFile(answerLogPath,key + " -> " + value));
appendToFile(answerLogPath,"=== 学生提交内容得分点 ===");
judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"=== 学生提交内容得分点 ===");
appendToFile(answerLogPath, "=== 学生提交内容得分点 ===");
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "=== 学生提交内容得分点 ===");
// 计算试题总分
int totalScore = answerList.stream()
.mapToInt(a -> Integer.parseInt(a.getScoreRate()))
.sum();
//这里指挥判断存在文件夹的得分点
SourceAndTextAndTotal studentScorePojo = compareStuAndTestFiles(answerList, stuFiles,score,totalScore,judgementStr);
SourceAndTextAndTotal studentScorePojo = compareStuAndTestFiles(answerList, stuFiles, score, totalScore, judgementStr);
double studentScore = studentScorePojo.getScore();
correctCount= studentScorePojo.getTotal();
correctCount = studentScorePojo.getTotal();
judgementStr = studentScorePojo.getText();
//判断收藏夹得分点
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
int currentScore = Integer.parseInt(examQuestionAnswer.getScoreRate()); // 当前得分
if ( "添加到收藏夹".equals(examQuestionAnswer.getContentIn())){
if ("添加到收藏夹".equals(examQuestionAnswer.getContentIn())) {
String bookmarkNameToDelete = examQuestionAnswer.getContent();
//检查收藏夹是否有书签
boolean isCorrect = BookmarkChecker.bookmarkExists(chromeBookmarkPath, bookmarkNameToDelete);
@@ -100,14 +99,13 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
double weightScore = ((double) currentScore / totalScore) * score;
String formattedWeightScore = String.format("%.1f", weightScore);
appendToFile(answerLogPath,"✅考点"+bookmarkNameToDelete + " -> 得分权值:" + currentScore+"-> 得分:"+formattedWeightScore);
judgementStr = HtmlAppender.appendHtmlLine(judgementStr,"✅考点"+bookmarkNameToDelete + " -> 得分权值:" + currentScore+"-> 得分:"+formattedWeightScore);
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");
} else {
appendToFile(answerLogPath, "❌考点" + bookmarkNameToDelete + " -> 得分权值:" + currentScore + "-> 得分0");
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌考点" + bookmarkNameToDelete + " -> 得分权值:" + currentScore + "-> 得分0");
}
}
@@ -117,11 +115,11 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
// 计算最终得分比例(保留两位小数)
double scoreRatio = totalScore == 0 ? 0 : (double) studentScore / totalScore;
double roundedScoreRatio = Math.round(scoreRatio*score * 100.0) / 100.0; // 四舍五入到2位小数
double roundedScoreRatio = Math.round(scoreRatio * score * 100.0) / 100.0; // 四舍五入到2位小数
if (correctCount == totalQuestions) {
roundedScoreRatio = score; // 全对,直接给满分
}
appendToFile(answerLogPath," 得分:"+roundedScoreRatio);
appendToFile(answerLogPath, " 得分:" + roundedScoreRatio);
sourceAndText.setScore(roundedScoreRatio);
sourceAndText.setText(judgementStr);
return sourceAndText;
@@ -129,7 +127,7 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
// 对比学生提交内容与试题得分点
static SourceAndTextAndTotal compareStuAndTestFiles(List<ExamQuestionAnswer> answerList, Map<String, String> stuFiles,double score,int total, String judgementStr) {
static SourceAndTextAndTotal compareStuAndTestFiles(List<ExamQuestionAnswer> answerList, Map<String, String> stuFiles, double score, int total, String judgementStr) {
int totalScore = 0; // 记录总得分
int correctCount = 0; // 完全正确的题目数量
SourceAndTextAndTotal sourceAndText = new SourceAndTextAndTotal();
@@ -147,11 +145,11 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
// 计算该考点的权重得分并保留一位小数
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");
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");
}
}
}
@@ -161,6 +159,7 @@ public class JudgementBrowerServiceImpl implements JudgementBrowerService {
sourceAndText.setTotal(correctCount);
return sourceAndText;
}
/**
* 将指定内容追加写入到指定文件中。
*

View File

@@ -1,6 +1,5 @@
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;
@@ -14,7 +13,7 @@ public interface IMysqlLocalService {
void delMysqlConnect(List<String> tNames);
SourceAndText Judgement(double sorce, File file, ExamQuestion examQuestion, String judgementStr) throws IOException, SQLException;
SourceAndText Judgement(double source, File file, ExamQuestion examQuestion, String judgementStr) throws IOException, SQLException;
void JudgementFile(File mysqlFile, ExamQuestion examQuestion);

View File

@@ -6,7 +6,6 @@ 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;
@@ -14,7 +13,6 @@ import com.example.exam.exam.utils.mysql.MySQLExporterUtil;
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;
@@ -29,7 +27,6 @@ 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;

View File

@@ -11,9 +11,9 @@ public interface PsService {
* 读取考生文件,与题型中要求进行判断
* @param path 文件路径
* @param examQuestion 试题参数
* @param sorce 试题分数
* @param source 试题分数
* @return 得分
* @throws Exception 异常
*/
SourceAndText Judgement(double sorce, String lastFilePath, String path, ExamQuestion examQuestion, String judgementStr) throws IOException;
SourceAndText Judgement(double source, String lastFilePath, String path, ExamQuestion examQuestion, String judgementStr) throws IOException;
}

View File

@@ -15,10 +15,5 @@ public interface StuPaperScoreService {
List<StuPaperScoreDO> findByStuIDAndPaperId(Long stuId, String paperId, String temporaryId);
void insertStuPaperScore(StuPaperScoreDO stuPaperScoreDO);
void updateStuPaperScore(StuPaperScoreDO stuPaperScoreDO);
StuPaperScoreDO getStuScoreByPaperIdAndQuid(Long stuId, String paperId, String quId);
void deleteStuPaperScore(Long stuId, String paperId);
}

View File

@@ -11,11 +11,8 @@ 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;
@@ -23,11 +20,12 @@ import java.util.Map;
@Service
public class FileServericeImpl implements IFileServerice {
static String answerLogPath ; // 文件路径
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();
@@ -60,21 +58,21 @@ public class FileServericeImpl implements IFileServerice {
int totalScore = answerList.stream()
.mapToInt(a -> Integer.parseInt(a.getScoreRate()))
.sum();
appendToFile(answerLogPath,"=== 学生提交内容得分点 ===");
appendToFile(answerLogPath, "=== 学生提交内容得分点 ===");
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "=== 学生提交内容得分点 ===");
// 对比学生提交内容与试题得分点
SourceAndTextAndTotal studentScorePojo = compareStuAndTestFiles(answerList, stuFiles,score,totalScore,judgementStr);
SourceAndTextAndTotal 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*score) / 100.0; // 四舍五入到2位小数
double roundedScoreRatio = Math.round(scoreRatio * 100.0 * score) / 100.0; // 四舍五入到2位小数
if (correctCount == totalQuestions) {
roundedScoreRatio = score; // 全对,直接给满分
}
appendToFile(answerLogPath,"得分:"+roundedScoreRatio);
appendToFile(answerLogPath, "得分:" + roundedScoreRatio);
// judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "得分:"+roundedScoreRatio*score);
sourceAndText.setScore(roundedScoreRatio);
sourceAndText.setText(judgementStr);
@@ -83,11 +81,11 @@ public class FileServericeImpl implements IFileServerice {
// 对比学生提交内容与试题得分点
static SourceAndTextAndTotal compareStuAndTestFiles(List<ExamQuestionAnswer> answerList, Map<String, String> stuFiles, double score, int total, String judgementStr) {
SourceAndTextAndTotal sourceAndText = new SourceAndTextAndTotal();
int correctCount = 0; // 完全正确的题目数量
int totalQuestions = answerList.size(); // 总题目数量// 总题目数量
double totalScore = 0; // 记录总得分
static SourceAndTextAndTotal compareStuAndTestFiles(List<ExamQuestionAnswer> answerList, Map<String, String> stuFiles, double score, int total, String judgementStr) {
SourceAndTextAndTotal sourceAndText = new SourceAndTextAndTotal();
int correctCount = 0; // 完全正确的题目数量
int totalQuestions = answerList.size(); // 总题目数量// 总题目数量
double totalScore = 0; // 记录总得分
for (ExamQuestionAnswer answer : answerList) {
String filePath = answer.getContent(); // 试题文件路径
String checkType = answer.getContentIn(); // 考察类型(考察删除 / 考察名称 / 考察属性)
@@ -115,18 +113,18 @@ public class FileServericeImpl implements IFileServerice {
// 计算该考点的权重得分并保留一位小数
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");
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);
sourceAndText.setTotal(correctCount);
sourceAndText.setText(judgementStr);
sourceAndText.setScore(totalScore);
sourceAndText.setTotal(correctCount);
//返回累加的总分
return sourceAndText;
return sourceAndText;
}
/**

View File

@@ -10,8 +10,6 @@ import java.io.IOException;
public interface IFileServerice {
SourceAndText Judgement(double score, File file, ExamQuestion question, String judgementStr) throws IOException;

View File

@@ -14,5 +14,5 @@ import java.util.List;
public interface JudgementWpsExcelService {
// 根据给定的分数、路径、考试题目和判断题字符串返回一个SourceAndText对象
SourceAndText judgementWpsXlsx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception;
SourceAndText judgementWpsXlsx(double source, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception;
}

View File

@@ -23,10 +23,10 @@ public class JudgementWpsExcelServiceImpl implements JudgementWpsExcelService {
@Override
public SourceAndText judgementWpsXlsx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception {
public SourceAndText judgementWpsXlsx(double source, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception {
SourceAndText sourceAndText = new SourceAndText();
List<WpsXlsxInfoVo> wpsXlsxInfos = new ArrayList<>();
boolean isAllTrue = true; // 判断考点是否全对标识
// 日志:开始判分
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Xlsx判分");
double wps_xlsx_score = 0;
@@ -36,56 +36,46 @@ public class JudgementWpsExcelServiceImpl implements JudgementWpsExcelService {
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
List<String> cell = new ArrayList<>();
String[] docxInfo = examQuestionAnswer.getContent().split("@");
// 避免越界,至少保证前 5 项
if (docxInfo.length < 5) {
continue; // 数据不完整,跳过
}
WpsXlsxInfoVo wpsXlsxInfoVo = new WpsXlsxInfoVo();
wpsXlsxInfoVo.setFirstName(docxInfo[0]);
wpsXlsxInfoVo.setIndex(docxInfo[1]);
wpsXlsxInfoVo.setFunction(docxInfo[2]);
wpsXlsxInfoVo.setExamName(docxInfo[3]);
wpsXlsxInfoVo.setExamCode(docxInfo[4]);
// 如果有第 6 个字段,加入 cell
if (docxInfo.length > 5 && docxInfo[5] != null && !docxInfo[5].isEmpty()) {
cell.add(docxInfo[5]);
}
wpsXlsxInfoVo.setCell(cell);
wpsXlsxInfos.add(wpsXlsxInfoVo);
}
// 文件转 MultipartFile
MultipartFile multipartFile = new CustomMultipartFile(new File(path));
// 获取判分结果
List<JudgementXlsxVO> judgementXlsxVOS = XlsxMaster.xlsxMaster(wpsXlsxInfos, multipartFile);
// 逐个答案进行判分
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
boolean flag = false; // 每个答案单独判断
double one_score = 0;
for (JudgementXlsxVO wordJudgementDto : judgementXlsxVOS) {
if (wordJudgementDto.getContent() != null &&
wordJudgementDto.getContent().equals(examQuestionAnswer.getContent())) {
flag = true;
// 权重分值计算
if ("1".equals(examQuestionAnswer.getScoreRate())) {
one_score = sorce / answerList.size();
one_score = source / answerList.size();
} else {
one_score = sorce * Double.parseDouble(examQuestionAnswer.getScoreRate());
one_score = source * Double.parseDouble(examQuestionAnswer.getScoreRate());
}
break; // 找到匹配立即退出
}
}
wps_xlsx_score += one_score;
if (flag) {
judgementStr = HtmlAppender.appendHtmlLine(
judgementStr,
@@ -93,14 +83,19 @@ public class JudgementWpsExcelServiceImpl implements JudgementWpsExcelService {
new BigDecimal(one_score).setScale(1, RoundingMode.HALF_UP)
);
} else {
isAllTrue = false;
judgementStr = HtmlAppender.appendHtmlLine(
judgementStr,
"" + examQuestionAnswer.getContentIn() + " 得分失败"
);
}
}
sourceAndText.setScore(wps_xlsx_score);
if (isAllTrue) {
// 如果所有考点都正确,直接给满分
sourceAndText.setScore(source);
} else {
sourceAndText.setScore(wps_xlsx_score);
}
sourceAndText.setText(judgementStr);
return sourceAndText;
}

View File

@@ -7,14 +7,14 @@ import com.example.exam.exam.controller.auto.vo.StuInfoVo;
import java.math.BigDecimal;
/**
* 判分逻辑集合wps word
* 判分逻辑集合wps excel
*
* @author rwb
*/
public interface JudgementForExcelService {
/**
* word 判分
* excel 判分
* @param stuInfoVo 学生考试信息
* @return 结果
*/

View File

@@ -10,7 +10,6 @@ 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;

View File

@@ -10,5 +10,5 @@ import java.util.List;
* @author REN
*/
public interface JudgementWpsPptxService {
SourceAndText judgementWpsPptx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception;
SourceAndText judgementWpsPptx(double source, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception;
}

View File

@@ -5,10 +5,11 @@ import com.example.exam.exam.dal.ExamQuestionAnswer;
import com.example.exam.exam.dal.SourceAndText;
import com.example.exam.exam.service.wpspptx.pptx4j.SlideMaster;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.JudgementSlidesVO;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.SlideDataInfoVO;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.WpsSlideInfoVo;
import com.example.exam.exam.utils.CustomMultipartFile;
import com.example.exam.exam.utils.HtmlAppender;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.math.BigDecimal;
@@ -23,16 +24,14 @@ import java.util.List;
public class JudgementWpsPptxServiceImpl implements JudgementWpsPptxService {
@Override
public SourceAndText judgementWpsPptx(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception {
public SourceAndText judgementWpsPptx(double source, 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判分");
Boolean isAllTrue = true;
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Pptx判分");
double wpsPptScore = 0;
List<WpsSlideInfoVo> judgementReq = new ArrayList<>();
// 3、获取答案得组成
List<ExamQuestionAnswer> answerList = examQuestion.getAnswerList();
List<ExamQuestionAnswer> answerList = examQuestion.getAnswerList();
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
WpsSlideInfoVo judgementReqVo = new WpsSlideInfoVo();
// 拆分数据、
@@ -45,7 +44,8 @@ public class JudgementWpsPptxServiceImpl implements JudgementWpsPptxService {
judgementReqVo.setMethod(pptxInfos[5]);
judgementReq.add(judgementReqVo);
}
List<JudgementSlidesVO> judgementDtos = SlideMaster.slideMaster(judgementReq, new File(path));
MultipartFile multipartFile = new CustomMultipartFile(new File(path));
List<JudgementSlidesVO> judgementDtos = SlideMaster.slideMaster(judgementReq, multipartFile);
// 4、进行关联判断
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
boolean flag = false;
@@ -56,26 +56,26 @@ public class JudgementWpsPptxServiceImpl implements JudgementWpsPptxService {
// 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重
if (examQuestionAnswer.getScoreRate().equals("1")) {
// 说明权重相等,直接平分分数
one_sorce = sorce / answerList.size();
one_sorce = source / answerList.size();
} else {
one_sorce = sorce * Double.parseDouble(examQuestionAnswer.getScoreRate());
one_sorce = source * Double.parseDouble(examQuestionAnswer.getScoreRate());
}
break;
}
}
wpsPptScore += one_sorce;
if (flag) {
// LogFileUtils.writeLine("✅" + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + one_sorce);
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "" + examQuestionAnswer.getContentIn() + " 得分成功,得分:" + new BigDecimal(one_sorce).setScale(1, RoundingMode.HALF_UP));
} else {
// LogFileUtils.writeLine("❌ " + examQuestionAnswer.getContentIn() + " 得分失败");
isAllTrue = false;
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "" + examQuestionAnswer.getContentIn() + " 得分失败");
}
}
// LogFileUtils.writeLine("✅ 结束WPS_Pptx判分试题得分" + wpsPptScore);
// 关闭已经打开得文件
// LogFileUtils.close();
sourceAndText.setScore(wpsPptScore);
if (isAllTrue) {
sourceAndText.setScore(source);
} else {
sourceAndText.setScore(wpsPptScore);
}
sourceAndText.setText(judgementStr);
return sourceAndText;
}

View File

@@ -7,14 +7,14 @@ import com.example.exam.exam.controller.auto.vo.StuInfoVo;
import java.math.BigDecimal;
/**
* 判分逻辑集合wps word
* 判分逻辑集合wps ppt
*
* @author rwb
*/
public interface JudgementForPptxService {
/**
* word 判分
* ppt 判分
* @param stuInfoVo 学生考试信息
* @return 结果
*/

View File

@@ -5,6 +5,7 @@ import org.docx4j.dml.*;
import org.pptx4j.pml.*;
import java.math.BigInteger;
import java.util.List;
public class ShapePic {
@@ -409,440 +410,46 @@ public class ShapePic {
// 动画 效果
// public static String getAnimation(org.pptx4j.pml.Shape sp, CTSlideTiming timing) throws JAXBException, XPathBinderAssociationIsPartialException {
// // 获取形状中的ID
// if (sp == null) return "无动画";
// long spId = sp.getNvSpPr().getCNvPr().getId();
// // 获取动画效果
// CTTimeNodeList timeNodeList = timing.getTnLst();
// for (Object obj : timeNodeList.getParOrSeqOrExcl()) {
// if (obj instanceof CTTLTimeNodeParallel) {
// CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj;
// CTTLCommonTimeNodeData node = par.getCTn();
// if (node.getChildTnLst().getParOrSeqOrExcl() != null) {
// CTTimeNodeList childTimeNodeList = node.getChildTnLst();
// for (Object object : childTimeNodeList.getParOrSeqOrExcl()) {
// if (object instanceof CTTLSetBehavior) {
// if (((CTTLSetBehavior) object).getCBhvr() != null) {
// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt() != null) {
// // 检查目标形状ID是否匹配
// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) {
// // 返回动画效果
//
// }
// }
// }
// }
// if (object instanceof CTTLTimeNodeSequence) {
// CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object;
// CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst();
// for (Object seqObjs : seqNodeList.getParOrSeqOrExcl()) {
// if (seqObjs instanceof CTTLSetBehavior) {
// if (((CTTLSetBehavior) seqObjs).getCBhvr() != null) {
// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt() != null) {
// // 检查目标形状ID是否匹配
// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) {
// // 返回动画效果
//
// }
// }
// }
// }
// if (seqObjs instanceof CTTLTimeNodeSequence) {
// CTTLTimeNodeSequence seqObj = (CTTLTimeNodeSequence) seqObjs;
// CTTimeNodeList seqNodeLists = seqObj.getCTn().getChildTnLst();
// for (Object seqObjss : seqNodeLists.getParOrSeqOrExcl()) {
// if (seqObjss instanceof CTTLSetBehavior) {
// if (((CTTLSetBehavior) seqObjss).getCBhvr() != null) {
// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt() != null) {
// // 检查目标形状ID是否匹配
// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) {
// // 返回动画效果
//
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
//
//
// if (node.getDur() != null && !"indefinite".equals(node.getDur())) {
// return node.getDur();
// }
// System.out.println(par.getCTn());
// }
// }
// return "待开发";
// }
public static String getAnimation(Pic sp, CTSlideTiming timing) {
if (sp == null || timing == null || timing.getTnLst() == null) return "无动画";
final String spId = String.valueOf(sp.getNvPicPr().getCNvPr().getId());
CTTimeNodeList root = timing.getTnLst();
// 先找进入类effect
String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true);
if (entrance != null) return entrance;
// 再找其它类型(包括 CTTLAnimateBehavior 的 ppt_x/ppt_y
String any = findFirstBehavior(root, spId, /*preferEntrance=*/false);
return any != null ? any : "无动画";
}
/* 递归遍历所有 time nodes返回首个命中的行为字符串 */
private static String findFirstBehavior(CTTimeNodeList list, String spId, boolean preferEntrance) {
if (list == null || list.getParOrSeqOrExcl() == null) return null;
for (Object raw : list.getParOrSeqOrExcl()) {
Object node = unwrap(raw);
// 1) 直接是某种行为Behavior
String desc = tryDescribeIfBehavior(node, spId, preferEntrance);
if (desc != null) return desc;
// 2) par / seq / excl进入子列表递归
CTTimeNodeList child = getChildList(node);
if (child != null) {
String got = findFirstBehavior(child, spId, preferEntrance);
if (got != null) return got;
}
public static String getAnimation(org.pptx4j.pml.Pic sp, CTSlideTiming timing) {
long spId = sp.getNvPicPr().getCNvPr().getId();
CTTimeNodeList ctTimeNodeList = timing.getTnLst();
if (ctTimeNodeList == null) return "无动画";
// 第一层
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "type");
if (!value.isEmpty()) {
return value;
}
return null;
return "";
}
/* 如果对象是某种 Behavior且目标命中当前 spId则返回描述否则返回 null */
private static String tryDescribeIfBehavior(Object obj, String spId, boolean preferEntrance) {
// 统一 unwrap
obj = unwrap(obj);
// --- 进入类效果Effect---
if (obj instanceof CTTLAnimateEffectBehavior) {
CTTLAnimateEffectBehavior b = (CTTLAnimateEffectBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) {
String filter = safe(b.getFilter()); // 如 "fade"、"strips(downRight)" 等
return filter.isEmpty() ? "进入效果" : ("进入效果:" + filter);
}
}
// 非优先阶段:其它类型
if (!preferEntrance) {
// 通用 Animate (B 动画关键ppt_x / ppt_y)
if (obj instanceof CTTLAnimateBehavior) {
CTTLAnimateBehavior b = (CTTLAnimateBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) {
String attr = firstAttrName(b.getCBhvr());
if ("ppt_x".equalsIgnoreCase(attr)) return "位置动画Xppt_x";
if ("ppt_y".equalsIgnoreCase(attr)) return "位置动画Yppt_y";
return attr.isEmpty() ? "数值动画Animate" : ("数值动画(" + attr + "");
}
}
if (obj instanceof CTTLAnimateMotionBehavior) {
CTTLAnimateMotionBehavior b = (CTTLAnimateMotionBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) return "路径动画Motion";
}
if (obj instanceof CTTLAnimateRotationBehavior) {
CTTLAnimateRotationBehavior b = (CTTLAnimateRotationBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) return "旋转动画";
}
if (obj instanceof CTTLAnimateScaleBehavior) {
CTTLAnimateScaleBehavior b = (CTTLAnimateScaleBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) return "缩放动画";
}
if (obj instanceof CTTLSetBehavior) {
CTTLSetBehavior b = (CTTLSetBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) {
String to = (b.getTo()!=null && b.getTo().getStrVal()!=null) ? safe(b.getTo().getStrVal().getVal()) : "";
String attr = firstAttrName(b.getCBhvr());
if (!attr.isEmpty() || !to.isEmpty()) {
return "属性设置Set" + (attr.isEmpty() ? "" : attr) + (to.isEmpty() ? "" : (" -> " + to));
public static String getAniationInfo(List<Object> objects, List<Object> upObjects, String spId, String parameter) {
for (Object object : objects) {
if (object instanceof CTTLSetBehavior) {
String spIds = ((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt().getSpid();
// 判断ID是否一致
if (spIds.equals(spId)) {
// 持续时间
if (parameter.equals("dur")) {
return ((CTTLSetBehavior) object).getCBhvr().getCTn().getDur();
}
return "属性设置Set";
}
}
if (obj instanceof CTTLAnimateColorBehavior) {
CTTLAnimateColorBehavior b = (CTTLAnimateColorBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) return "颜色动画";
}
if (obj instanceof CTTLCommandBehavior) {
CTTLCommandBehavior b = (CTTLCommandBehavior) obj;
if (isTarget(b.getCBhvr(), spId)) return "命令动画Command";
}
}
return null;
}
/* 提取并返回子节点列表par/seq/excl 三种) */
private static CTTimeNodeList getChildList(Object node) {
node = unwrap(node);
if (node instanceof CTTLTimeNodeParallel) {
CTTLTimeNodeParallel n = (CTTLTimeNodeParallel) node;
return n.getCTn() != null ? n.getCTn().getChildTnLst() : null;
}
if (node instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence n = (CTTLTimeNodeSequence) node;
return n.getCTn() != null ? n.getCTn().getChildTnLst() : null;
}
if (node instanceof CTTLTimeNodeExclusive) {
CTTLTimeNodeExclusive n = (CTTLTimeNodeExclusive) node;
return n.getCTn() != null ? n.getCTn().getChildTnLst() : null;
}
return null;
}
/* 判断行为的目标是否命中当前形状 spId */
private static boolean isTarget(CTTLCommonBehaviorData cb, String spId) {
if (cb == null || cb.getTgtEl() == null || cb.getTgtEl().getSpTgt() == null) return false;
String tgt = cb.getTgtEl().getSpTgt().getSpid();
return String.valueOf(spId).equals(tgt);
}
/* 读第一个 attrName如 ppt_x / ppt_y / style.visibility ...*/
private static String firstAttrName(CTTLCommonBehaviorData cb) {
if (cb == null || cb.getAttrNameLst() == null) return "";
java.util.List<String> names = cb.getAttrNameLst().getAttrName();
return (names != null && !names.isEmpty()) ? safe(names.get(0)) : "";
}
/* 统一解包 JAXBElement */
private static Object unwrap(Object o) {
return (o instanceof JAXBElement) ? ((JAXBElement<?>) o).getValue() : o;
}
/* 安全字符串 */
private static String safe(String s) {
return s == null ? "" : s.trim();
}
// 动画-方向
public static String getAnimateDirection(Pic sp, CTSlideTiming timing) {
String dir = getAnimation(sp, timing);
if (dir.equals("downRight")) {
return "右下";
}
if (dir.equals("downLeft")) {
return "左下";
}
if (dir.equals("upRight")) {
return "右上";
}
if (dir.equals("upLeft")) {
return "左上";
}
if (dir.equals("right")) {
return "向右";
}
if (dir.equals("left")) {
return "向左";
}
if (dir.equals("down")) {
return "向下";
}
if (dir.equals("up")) {
return "向上";
}
if (dir.equals("fromTop")) {
return "从顶部";
}
if (dir.equals("fromBottom")) {
return "从底部";
}
if (dir.equals("fromLeft")) {
return "从左侧";
}
if (dir.equals("fromRight")) {
return "从右侧";
}
return "";
}
// 动画 触发方式
public static String getAnimateTrigger(Pic sp, CTSlideTiming timing) {
// 获取形状中的ID
if (sp == null) return "无动画";
long spId = sp.getNvPicPr().getCNvPr().getId();
// 获取动画效果
CTTimeNodeList timeNodeList = timing.getTnLst();
for (Object obj : timeNodeList.getParOrSeqOrExcl()) {
if (obj instanceof CTTLTimeNodeParallel) {
CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj;
CTTLCommonTimeNodeData node = par.getCTn();
if (node.getChildTnLst().getParOrSeqOrExcl() != null) {
CTTimeNodeList childTimeNodeList = node.getChildTnLst();
for (Object object : childTimeNodeList.getParOrSeqOrExcl()) {
if (object instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object;
if (seq.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getEvt().value();
}
}
} else {
CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst();
for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) {
if (seqObj instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj;
if (seqs.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getEvt().value();
}
}
} else {
CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst();
for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) {
if (seqObjs instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs;
if (seqss.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getEvt().value();
}
}
}
}
}
}
} else {
if (seqObj instanceof CTTLTimeNodeParallel) {
CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj;
if (seqs.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getEvt().value();
}
}
} else {
CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst();
for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) {
if (seqObjs instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs;
if (seqss.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getEvt().value();
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return "";
}
// 动画 持续时间
public static String getAnimationDuration(Pic sp, CTSlideTiming timing) {
if (sp == null || timing == null || timing.getTnLst() == null) return "无动画";
final String spId = String.valueOf(sp.getNvPicPr().getCNvPr().getId());
CTTimeNodeList root = timing.getTnLst();
// 先找“进入类效果effect
String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true);
if (entrance != null) return entrance;
// 找不到进入类再找其它类型运动、旋转、缩放、Set 等)
String any = findFirstBehavior(root, spId, /*preferEntrance=*/false);
return any != null ? any : "无动画";
}
// 动画 延迟
public static String getAnimationDelay(Pic sp, CTSlideTiming timing) {
// 获取形状中的ID
if (sp == null) return "无动画";
long spId = sp.getNvPicPr().getCNvPr().getId();
// 获取动画效果
CTTimeNodeList timeNodeList = timing.getTnLst();
for (Object obj : timeNodeList.getParOrSeqOrExcl()) {
if (obj instanceof CTTLTimeNodeParallel) {
CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj;
CTTLCommonTimeNodeData node = par.getCTn();
if (node.getChildTnLst().getParOrSeqOrExcl() != null) {
CTTimeNodeList childTimeNodeList = node.getChildTnLst();
for (Object object : childTimeNodeList.getParOrSeqOrExcl()) {
if (object instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object;
if (seq.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getDelay();
}
}
} else {
CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst();
for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) {
if (seqObj instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj;
if (seqs.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getDelay();
}
}
} else {
CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst();
for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) {
if (seqObjs instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs;
if (seqss.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getDelay();
}
}
}
}
}
}
} else {
if (seqObj instanceof CTTLTimeNodeParallel) {
CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj;
if (seqs.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getDelay();
}
}
} else {
CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst();
for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) {
if (seqObjs instanceof CTTLTimeNodeSequence) {
CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs;
if (seqss.getCTn().getStCondLst() != null) {
CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst();
for (CTTLTimeCondition o : stCondLst.getCond()) {
if (o.getEvt() != null) {
return o.getDelay();
}
}
}
}
}
}
}
// 向上获取延迟
for (Object upObject : upObjects) {
if (upObject instanceof CTTLTimeNodeParallel) {
// 获取动画效果
if (parameter.equals("type")) {
Integer type = ((CTTLTimeNodeParallel) upObject).getCTn().getPresetSubtype();
return String.valueOf(type);
}
// 触发方式
if (parameter.equals("function")) {
return ((CTTLTimeNodeParallel) upObject).getCTn().getNodeType().value();
}
// 获取延迟方法
for (Object condObject : ((CTTLTimeNodeParallel) upObject).getCTn().getStCondLst().getCond()) {
if (condObject instanceof CTTLTimeCondition) {
// 获取延迟
if (parameter.equals("delay")) {
return ((CTTLTimeCondition) condObject).getDelay();
}
}
}
@@ -850,10 +457,68 @@ public class ShapePic {
}
}
}
if (object instanceof CTTLTimeNodeParallel obj) {
String result = getAniationInfo(obj.getCTn().getChildTnLst().getParOrSeqOrExcl(), objects, spId, parameter);
if (!result.isEmpty()) {
return result;
}
}
if (object instanceof CTTLTimeNodeSequence obj) {
String result = getAniationInfo(obj.getCTn().getChildTnLst().getParOrSeqOrExcl(), objects, spId, parameter);
if (!result.isEmpty()) {
return result;
}
}
}
return "";
}
// 动画-方向
public static String getAnimateDirection(org.pptx4j.pml.Pic sp, CTSlideTiming timing) {
return "待开发";
}
// 动画 触发方式
public static String getAnimateTrigger(org.pptx4j.pml.Pic sp, CTSlideTiming timing) {
long spId = sp.getNvPicPr().getCNvPr().getId();
CTTimeNodeList ctTimeNodeList = timing.getTnLst();
if (ctTimeNodeList == null) return "无动画";
// 第一层
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "function");
if (!value.isEmpty()) {
return value;
}
return "";
}
/// 形状动作,鼠标单机-动作
// 动画 持续时间
public static String getAnimationDuration(org.pptx4j.pml.Pic sp, CTSlideTiming timing) {
long spId = sp.getNvPicPr().getCNvPr().getId();
CTTimeNodeList ctTimeNodeList = timing.getTnLst();
if (ctTimeNodeList == null) return "无动画";
// 第一层
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "dur");
if (!value.isEmpty()) {
return value;
}
return "";
}
}
// 动画 延迟
public static String getAnimationDelay(org.pptx4j.pml.Pic sp, CTSlideTiming timing) {
long spId = sp.getNvPicPr().getCNvPr().getId();
CTTimeNodeList ctTimeNodeList = timing.getTnLst();
if (ctTimeNodeList == null) return "无动画";
// 第一层
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "delay");
if (!value.isEmpty()) {
return value;
}
return "";
}
/// 形状动作,鼠标单机-动作
}

View File

@@ -1,97 +0,0 @@
package com.example.exam.exam.service.wpspptx.pptx4j;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.SlideDataInfoVO;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.xmlbeans.XmlCursor;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SlideConversion {
// 大纲
// 1、获取第一层目录 幻灯片 - 母版 - 设置 - 其他
// 2、获取第二层目录 第x页 - 幻灯片母版/备注母版 - 幻灯片设置 - 文件操作
// 3、获取第三层目录 幻灯片设置/形状 - 幻灯片母版 - 幻灯片设置 - 文件操作
public static List<SlideDataInfoVO> SlideDataInfos(MultipartFile file) throws Exception {
List<SlideDataInfoVO> dataInfoVOS = new ArrayList<>();
try (InputStream inputStream = file.getInputStream();
XMLSlideShow pptxXml = new XMLSlideShow(inputStream)) {
int index = 0;
String firstId = getStringRandom();
setSlideDataInfo(firstId, "", "幻灯片", "slide", "", false, dataInfoVOS);
for (XSLFSlide slides : pptxXml.getSlides()){
index += 1;
String secondId = getStringRandom();
setSlideDataInfo(secondId, firstId, ""+index+"", "sld", String.valueOf(index), true, dataInfoVOS);
// 第三层
String thirdId = getStringRandom();
int indexCnvPr = 1;
setSlideDataInfo(thirdId, secondId, "幻灯片设置", "slideSetting", String.valueOf(index) + "_0", true, dataInfoVOS);
XmlCursor spCursor = slides.getXmlObject().newCursor();
spCursor.selectPath("declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//p:sp");
while (spCursor.toNextSelection()) {
XmlCursor cNvPrXml = spCursor.newCursor();
cNvPrXml.selectPath("declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//p:cNvPr/@name");
if (cNvPrXml.toNextSelection()) {
String name = cNvPrXml.getTextValue();
String fourId = getStringRandom();
setSlideDataInfo(fourId, secondId, "形状"+indexCnvPr+"->"+name, "shape", String.valueOf(index) + "_" + String.valueOf(indexCnvPr), true, dataInfoVOS);
indexCnvPr += 1;
}
}
XmlCursor picCursor = slides.getXmlObject().newCursor();
System.out.println(picCursor.xmlText());
picCursor.selectPath("declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//p:pic");
int indexCnvPrPic = 1;
while (picCursor.toNextSelection()) {
String fourId = getStringRandom();
setSlideDataInfo(fourId, secondId, "图像"+indexCnvPrPic+"->图像", "shapePic", String.valueOf(indexCnvPrPic), true, dataInfoVOS);
indexCnvPrPic += 1;
}
}
// 设置
String firstIds = getStringRandom();
setSlideDataInfo(firstIds, "", "设置", "presentation", String.valueOf(0), false, dataInfoVOS);
String secondIds = getStringRandom();
setSlideDataInfo(secondIds, firstIds, "幻灯片设置", "presentation", String.valueOf(0), true, dataInfoVOS);
// 母版
// String dFirstId = getStringRandom();
// setPptxInfo("母版", "p:sld", "p:sld", filePath, dFirstId, "0", pptxInfoReqVos);
} catch (Exception e) {
throw new RuntimeException(e);
}
return dataInfoVOS;
}
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 void setSlideDataInfo(String id, String parentId, String text,
String type, String index, boolean isTrue, List<SlideDataInfoVO> dataInfoVOS) throws Exception {
SlideDataInfoVO dataInfo = new SlideDataInfoVO();
dataInfo.setId(id);
dataInfo.setParentId(parentId);
dataInfo.setName(text);
dataInfo.setType(type);
dataInfo.setIndex(index);
dataInfo.setClick(isTrue);
dataInfoVOS.add(dataInfo);
}
}

View File

@@ -21,15 +21,14 @@ import java.util.List;
public class SlideMaster {
public static List<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, File file) throws IOException, Docx4JException {
public static List<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, MultipartFile file) throws IOException, Docx4JException {
// File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx");
List<JudgementSlidesVO> judgementSlidesVOS = new ArrayList<>();
// 1、获取想要判断的文件地址文件流
// try (InputStream inputStream = file.getInputStream()) {
try {
try (InputStream inputStream = file.getInputStream()) {
// 加载 .pptx 文件
PresentationMLPackage ppt =
(PresentationMLPackage) OpcPackage.load(file);
(PresentationMLPackage) OpcPackage.load(inputStream);
// 你可以在这里处理 ppt比如读取文字、页数等
for (WpsSlideInfoVo wpsSlideInfoVo : wpsSlideInfoVos) {
// 幻灯片位置(第几张幻灯片)
@@ -45,7 +44,6 @@ public class SlideMaster {
String examCode = wpsSlideInfoVo.getExamCode();
// 方式方法
String method = wpsSlideInfoVo.getMethod();
String content = wpsSlideInfoVo.getSlideIndex() + "@" + firstName + "@" + function + "@" + examName + "@" + examCode + "@" + method;
// 查询幻灯片图片
List<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();

View File

@@ -18,9 +18,9 @@ public interface JudgementWpsWordService {
* 读取考生文件,与题型中要求进行判断
* @param path 文件路径
* @param examQuestion 试题参数
* @param sorce 试题分数
* @param source 试题分数
* @return 得分
* @throws Exception 异常
*/
SourceAndText judgementWpsWord(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception;
SourceAndText judgementWpsWord(double source, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception;
}

View File

@@ -9,8 +9,6 @@ import com.example.exam.exam.service.wpsword.docx4j.vo.JudgementWordsVO;
import com.example.exam.exam.service.wpsword.docx4j.vo.WpsDocxInfoVo;
import com.example.exam.exam.utils.CustomMultipartFile;
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 org.springframework.web.multipart.MultipartFile;
@@ -27,14 +25,13 @@ import java.util.List;
public class JudgementWpsWordServiceImpl implements JudgementWpsWordService {
@Override
public SourceAndText judgementWpsWord(double sorce, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception {
public SourceAndText judgementWpsWord(double source, String pathC, String path, ExamQuestion examQuestion, String judgementStr) throws Exception {
SourceAndText sourceAndText = new SourceAndText();
List<WpsDocxInfoVo> wpsDocxInfos = new ArrayList<>();
boolean isAllTrue = true; // 判断考点是否全对标识
// 日志:开始判分
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Word判分");
double wps_word_sorce = 0;
// 获取答案组成
List<ExamQuestionAnswer> answerList = examQuestion.getAnswerList();
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
@@ -50,13 +47,10 @@ public class JudgementWpsWordServiceImpl implements JudgementWpsWordService {
wpsDocxInfos.add(wpsDocxInfoVo);
}
}
// 将文件转成 MultipartFile
MultipartFile multipartFile = new CustomMultipartFile(new File(path));
// 获取文档检测结果
List<JudgementWordsVO> judgementWordsVOS = DocxMaster.docxMaster(wpsDocxInfos, multipartFile);
// 判分
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
boolean flag = false; // 每个答案单独判断
@@ -69,16 +63,15 @@ public class JudgementWpsWordServiceImpl implements JudgementWpsWordService {
flag = true;
// 计算分数
if ("1".equals(examQuestionAnswer.getScoreRate())) {
one_sorce = sorce / answerList.size();
one_sorce = source / answerList.size();
} else {
one_sorce = sorce * Double.parseDouble(examQuestionAnswer.getScoreRate());
one_sorce = source * Double.parseDouble(examQuestionAnswer.getScoreRate());
}
break; // 找到匹配立即跳出
}
}
// 分数相加
wps_word_sorce += one_sorce;
if (flag) {
judgementStr = HtmlAppender.appendHtmlLine(
judgementStr,
@@ -86,14 +79,19 @@ public class JudgementWpsWordServiceImpl implements JudgementWpsWordService {
new BigDecimal(one_sorce).setScale(1, RoundingMode.HALF_UP)
);
} else {
isAllTrue = false; // 有一个考点不对,标识为不全对
judgementStr = HtmlAppender.appendHtmlLine(
judgementStr,
"" + examQuestionAnswer.getContentIn() + " 得分失败"
);
}
}
sourceAndText.setScore(wps_word_sorce);
if (isAllTrue) {
// 如果全对,分数为总分
sourceAndText.setScore(source);
} else {
sourceAndText.setScore(wps_word_sorce);
}
sourceAndText.setText(judgementStr);
return sourceAndText;
}