答题模块完成;

将CxstudyTask调整为普通类, 通过initialize入口进行初始化;
调整目录结构;
细化异常处理逻辑;
添加了配置: 是否启用AI答题、是否启用无头模式;
配置文件中可选择Selenium驱动,但除firefox外均未经测试;
This commit is contained in:
2025-03-25 12:34:10 +08:00
parent b3d27983e0
commit 3ac5678deb
23 changed files with 621 additions and 424 deletions

1
.gitignore vendored
View File

@@ -37,3 +37,4 @@ build/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
/config.json /config.json
/config.json.bak

View File

@@ -2,7 +2,7 @@ package work.slhaf;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import work.slhaf.config.Config; import work.slhaf.config.Config;
import work.slhaf.task.cxstudy.CxstudyTask; import work.slhaf.task.CxstudyTask;
import work.slhaf.util.AiUtil; import work.slhaf.util.AiUtil;
import work.slhaf.util.OcrUtil; import work.slhaf.util.OcrUtil;
@@ -13,9 +13,13 @@ public class Main {
public static void main(String[] args) throws InterruptedException, IOException { public static void main(String[] args) throws InterruptedException, IOException {
Config config = Config.load(); Config config = Config.load();
AiUtil.load(config); if (config.isAiEnable()){
OcrUtil.load(config); AiUtil.load(config.getAiConfig());
CxstudyTask.run(config); OcrUtil.load(config.getOcrConfig());
}
CxstudyTask cxstudyTask = CxstudyTask.initialize(config);
cxstudyTask.run();
} }
} }

View File

@@ -3,7 +3,6 @@ package work.slhaf.chat;
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import work.slhaf.chat.constant.Constant; import work.slhaf.chat.constant.Constant;
@@ -12,7 +11,6 @@ import work.slhaf.chat.pojo.ChatResponse;
import work.slhaf.chat.pojo.Message; import work.slhaf.chat.pojo.Message;
import work.slhaf.chat.pojo.PrimaryChatResponse; import work.slhaf.chat.pojo.PrimaryChatResponse;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Data @Data
@@ -57,19 +55,14 @@ public class ChatClient {
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute(); HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
ChatResponse finalResponse; ChatResponse finalResponse;
try {
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class); PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
finalResponse = ChatResponse.builder() finalResponse = ChatResponse.builder()
.type(Constant.Response.SUCCESS) .type(Constant.Response.SUCCESS)
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent()) .message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
.usageBean(primaryChatResponse.getUsage()) .usageBean(primaryChatResponse.getUsage())
.build(); .build();
}catch (Exception e){
finalResponse = ChatResponse.builder()
.type(Constant.Response.ERROR)
.message(response.getStatus()+": "+response.body())
.build();
}
response.close(); response.close();
return finalResponse; return finalResponse;
} }

View File

@@ -6,9 +6,9 @@ import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class AiConfig { public class AiConfig {
private String apikey; private String apikey = "";
private String model; private String model = "";
private String baseUrl; private String baseUrl = "";
@Override @Override
public String toString() { public String toString() {

View File

@@ -1,7 +1,5 @@
package work.slhaf.config; package work.slhaf.config;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -10,11 +8,16 @@ import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner; import java.util.Scanner;
@Data @Data
@Slf4j @Slf4j
public class Config { public class Config {
private String browser;
private boolean aiEnable;
private boolean headless;
private CxstudyConfig cxstudyConfig; private CxstudyConfig cxstudyConfig;
private OcrConfig ocrConfig; private OcrConfig ocrConfig;
private AiConfig aiConfig; private AiConfig aiConfig;
@@ -23,7 +26,7 @@ public class Config {
} }
public static Config load() throws IOException { public static Config load() throws IOException {
File file = new File("./config.json"); File file = new File("config.json");
boolean configExists = checkConfigExists(file); boolean configExists = checkConfigExists(file);
if (!configExists) { if (!configExists) {
@@ -36,8 +39,60 @@ public class Config {
private static void generateConfig(File file) throws IOException { private static void generateConfig(File file) throws IOException {
Scanner sc = new Scanner(System.in); Scanner sc = new Scanner(System.in);
System.out.println("\r\n---------------\r\n"); System.out.println("\r\n---------------\r\n");
System.out.println("开始记录配置文件: ");
Config config = new Config();
System.out.println("选择Selenium驱动必须与安装的浏览器保持一致。该程序开发时使用firefox驱动其余浏览器驱动未经过测试");
System.out.println("可选值: edge, firefox, safari, chrome");
List<String> browsers = new ArrayList<>();
browsers.add("edge");
browsers.add("firefox");
browsers.add("chrome");
String browser;
System.out.print("输入你的浏览器名称: ");
while (true) {
browser = sc.nextLine();
if (browsers.contains(browser)){
config.setBrowser(browser);
break;
}
System.out.println("可选值为: edge, firefox, chrome");
System.out.print("输入你的浏览器名称: " );
}
System.out.println("\r\n---------------\r\n");
System.out.println("是否显示GUI界面第一次运行建议选择显示测试没有问题后再修改配置文件为隐藏GUI界面");
System.out.print("输入 y (启用) 或 n (禁用) : ");
String headlessStr = "";
boolean headless;
while (true) {
headlessStr = sc.nextLine();
if (headlessStr.equals("y") || headlessStr.equals("n")){
headless = headlessStr.equals("y");
break;
}
System.out.print("输入 y (启用) 或 n (禁用) : ");
}
config.setHeadless(headless);
System.out.println("\r\n---------------\r\n");
String aiEnableStr;
boolean aiEnable;
System.out.print("是否启用答题功能y / n: ");
while (true) {
aiEnableStr = sc.nextLine();
if (aiEnableStr.equals("y") || aiEnableStr.equals("n")) {
aiEnable = aiEnableStr.equals("y");
break;
}
System.out.println("请输入 y (启用) 或 n (禁用) : ");
}
config.setAiEnable(aiEnable);
AiConfig aiConfig = new AiConfig(); AiConfig aiConfig = new AiConfig();
System.out.println("recording ai config:"); OcrConfig ocrConfig = new OcrConfig();
if (aiEnable) {
System.out.println("记录AI配置: ");
System.out.print("apikey: "); System.out.print("apikey: ");
aiConfig.setApikey(sc.nextLine()); aiConfig.setApikey(sc.nextLine());
System.out.print("baseUrl: "); System.out.print("baseUrl: ");
@@ -46,37 +101,36 @@ public class Config {
aiConfig.setModel(sc.nextLine()); aiConfig.setModel(sc.nextLine());
System.out.println("\r\n---------------\r\n"); System.out.println("\r\n---------------\r\n");
OcrConfig ocrConfig = new OcrConfig(); System.out.println("记录OCR用于题目识别配置: ");
System.out.println("recording ocr config:");
System.out.print("accessKeyId: "); System.out.print("accessKeyId: ");
ocrConfig.setAccessKeyId(sc.nextLine()); ocrConfig.setAccessKeyId(sc.nextLine());
System.out.print("accessKeySecret: "); System.out.print("accessKeySecret: ");
ocrConfig.setAccessKeySecret(sc.nextLine()); ocrConfig.setAccessKeySecret(sc.nextLine());
}
System.out.println("\r\n---------------\r\n"); System.out.println("\r\n---------------\r\n");
CxstudyConfig studyConfig = new CxstudyConfig(); CxstudyConfig studyConfig = new CxstudyConfig();
System.out.println("recording study config:"); System.out.println("记录学习通配置: ");
System.out.print("phone: "); System.out.print("手机号: ");
studyConfig.setPhone(sc.nextLine()); studyConfig.setPhone(sc.nextLine());
System.out.print("password: "); System.out.print("密码: ");
studyConfig.setPassword(sc.nextLine()); studyConfig.setPassword(sc.nextLine());
System.out.print("target: "); System.out.print("目标课程: ");
studyConfig.setTarget(sc.nextLine()); studyConfig.setTarget(sc.nextLine());
System.out.println("\r\n---------------\r\n"); System.out.println("\r\n---------------\r\n");
Config config = new Config();
config.cxstudyConfig = studyConfig; config.cxstudyConfig = studyConfig;
config.ocrConfig = ocrConfig; config.ocrConfig = ocrConfig;
config.aiConfig = aiConfig; config.aiConfig = aiConfig;
String configStr = JSONUtil.toJsonPrettyStr(config); String configStr = JSONUtil.toJsonPrettyStr(config);
System.out.println("this is your config file: \r\n" + configStr); System.out.println("配置文件如下: \r\n" + configStr);
System.out.println("配置文件将位于: "+file.getAbsolutePath());
System.out.println("press ENTER to generate config: "); System.out.println("输入 ENTER 以创建配置文件: ");
sc.nextLine(); sc.nextLine();
file.createNewFile(); file.createNewFile();
FileUtils.writeStringToFile(file, configStr, StandardCharsets.UTF_8); FileUtils.writeStringToFile(file, configStr, StandardCharsets.UTF_8);
System.out.println(" config generated"); System.out.println("配置文件生成完毕");
} }
private static Config loadFromFile(File file) throws IOException { private static Config loadFromFile(File file) throws IOException {

View File

@@ -6,9 +6,9 @@ import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class CxstudyConfig { public class CxstudyConfig {
private String phone; private String phone = "";
private String password; private String password = "";
private String target; private String target = "";
@Override @Override
public String toString() { public String toString() {

View File

@@ -6,8 +6,8 @@ import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class OcrConfig { public class OcrConfig {
private String accessKeyId; private String accessKeyId = "";
private String accessKeySecret; private String accessKeySecret = "";
@Override @Override
public String toString() { public String toString() {

View File

@@ -0,0 +1,7 @@
package work.slhaf.exception;
public class AiRequestException extends RuntimeException {
public AiRequestException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package work.slhaf.exception;
public class AiResponseFormatException extends RuntimeException {
public AiResponseFormatException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package work.slhaf.exception;
public class OcrRequestException extends RuntimeException {
public OcrRequestException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,4 @@
package work.slhaf.pojo.answer;
public class Answer {
}

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.answer; package work.slhaf.pojo.answer;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.answer; package work.slhaf.pojo.answer;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.answer; package work.slhaf.pojo.answer;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.question; package work.slhaf.pojo.question;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.question; package work.slhaf.pojo.question;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.question; package work.slhaf.pojo.question;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package work.slhaf.task.cxstudy.pojo.question; package work.slhaf.pojo.question;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -0,0 +1,457 @@
package work.slhaf.task;
import cn.hutool.json.JSONUtil;
import io.github.bonigarcia.wdm.WebDriverManager;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import work.slhaf.config.Config;
import work.slhaf.exception.AiRequestException;
import work.slhaf.exception.AiResponseFormatException;
import work.slhaf.exception.OcrRequestException;
import work.slhaf.pojo.answer.Answer;
import work.slhaf.pojo.answer.MultiChoiceAnswer;
import work.slhaf.pojo.answer.SingleChoiceAnswer;
import work.slhaf.pojo.answer.TextAnswer;
import work.slhaf.pojo.question.MultiChoiceQuestion;
import work.slhaf.pojo.question.Question;
import work.slhaf.pojo.question.SingleChoiceQuestion;
import work.slhaf.pojo.question.TextQuestion;
import work.slhaf.util.AiUtil;
import work.slhaf.util.OcrUtil;
import java.io.File;
import java.io.FileNotFoundException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Getter
@Setter
public class CxstudyTask {
private WebDriver driver;
private Actions actions;
private WebDriverWait wait;
private JavascriptExecutor executor;
private String phone;
private String passwd;
private String targetClassName;
private boolean aiEnable;
private CxstudyTask() {
}
public void run() throws InterruptedException {
login();
while (true) {
try {
switchPage();
//登录、跳转页面
Thread.sleep(2000);
List<WebElement> toBeFinished = getTodoTasks();
int chapterCount = 0;
while (!toBeFinished.isEmpty()) {
//记录元素对应文本
toBeFinished.getFirst().click();
chapterCount++;
log.info("处理第{}节", chapterCount);
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
int subTasksNum = driver.findElements(By.xpath(".//div[@class='ans-attach-ct']")).size();
log.info("发现{}个任务", subTasksNum);
for (int i = 0; i < subTasksNum; i++) {
//检测frame内部元素
//frame分为三层:
// 第一层index: 6 保存所有含有内容的frame
// 第二层index: i 视频层 i->prev_video_left
// 第三层index: 0->newTestTitle, 2->fileBox
handleVideoTask(i);
if (aiEnable) {
handleTestTask(i);
}
handleDocTask(i);
}
driver.get(driver.getCurrentUrl());
driver.switchTo().defaultContent();
toBeFinished = getTodoTasks();
}
log.info("作答完毕!");
driver.quit();
break;
}catch (AiRequestException | OcrRequestException e) {
log.error(e.getMessage());
driver.quit();
break;
}catch (WebDriverException e) {
log.error("\r\n---------------\r\n");
log.error(e.getLocalizedMessage());
log.error("\r\n---------------\r\n");
log.error("Something went wrong, will retry later..");
driver.get("https://i.chaoxing.com/");
Thread.sleep(5000);
switchPage();
}
}
}
private void handleDocTask(int i) throws InterruptedException {
try {
driver.switchTo().frame(i).switchTo().frame(2);
log.info("开始处理文档浏览...");
long lastHeight = (long) executor.executeScript("return document.body.scrollHeight;");
long currentHeight = 0;
int step = 2000;
while (currentHeight < lastHeight) {
executor.executeScript("window.scrollBy(0, arguments[0]);", step);
Thread.sleep(600);
currentHeight = (long) executor.executeScript("return window.scrollY + window.innerHeight;");
lastHeight = (long) executor.executeScript("return document.body.scrollHeight;");
}
log.info("文档浏览任务完成!");
} catch (WebDriverException ignored) {
} finally {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
}
}
private void handleTestTask(int i) {
try {
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(i));
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='singleQuesId']")));
log.info("开始处理测验...");
//所有题目元素
List<WebElement> questions = driver.findElements(By.xpath(".//div[@class='singleQuesId']"));
log.info("发现{}个题目...", questions.size());
for (WebElement question : questions) {
log.info("处理第{}个题目...", questions.indexOf(question));
//获取题目截图通过OCR获取题目信息
WebElement questionTitleElement = question.findElement(By.xpath(".//div[@class='TiMu newTiMu']"));
File screenshot = questionTitleElement.getScreenshotAs(OutputType.FILE);
String questionTitle = OcrUtil.getContentOfImage(screenshot);
log.info("题目信息: {}", questionTitle);
try {
//尝试获取单选选项
//li role='radio'
question.findElement(By.xpath(".//li[@role='radio']"));
handleSingleChoiceQuestion(question, questionTitle);
continue;
} catch (WebDriverException ignored) {
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
//尝试获取复选框
//li role='checkbox'
question.findElement(By.xpath(".//li[@role='checkbox']"));
handleMultiChoiceQuestion(question, questionTitle);
continue;
} catch (WebDriverException ignored) {
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
//尝试跳转到简答题输入框
driver.switchTo().frame("ueditor_0");
TextQuestion textQuestion = new TextQuestion();
textQuestion.setTitle(questionTitle);
Answer answer = getAnswer(textQuestion);
TextAnswer textAnswer = (TextAnswer) answer;
WebElement inputArea = driver.findElement(By.xpath(".//body[@contenteditable='true']"));
//输入答案,并检测是否正确输入
while (Objects.equals(inputArea.getText(), "")) {
inputArea.click();
inputArea.sendKeys(textAnswer.getAnswer());
}
driver.switchTo().parentFrame();
driver.switchTo().parentFrame();
} catch (WebDriverException ignored) {
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
log.info("答题任务完成!");
//点击提交按钮,等待并点击弹窗
driver.findElement(By.xpath(".//a[@class='btnSubmit workBtnIndex']")).click();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//a[@id='popok']"))).click();
} catch (WebDriverException ignored) {
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} finally {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
}
}
private void handleMultiChoiceQuestion(WebElement question, String questionTitle) throws InterruptedException {
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='checkbox']"));
MultiChoiceQuestion multiChoiceQuestion = new MultiChoiceQuestion();
multiChoiceQuestion.setTitle(questionTitle);
List<String> choices = getChoices(choiceElements);
multiChoiceQuestion.setChoices(choices);
Answer answer = getAnswer(multiChoiceQuestion);
MultiChoiceAnswer multiChoiceAnswer = (MultiChoiceAnswer) answer;
List<String> answers = multiChoiceAnswer.getAnswers();
//输入答案,并检测是否正确输入
String script = getCheckScript(question);
while (Objects.equals(executor.executeScript(script), "")) {
for (String answerContent : answers) {
log.info("答案: {}", answerContent);
String xpath = ".//span[@data='" + answerContent + "']";
WebElement clickElement = question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath(".."));
String check = clickElement.getDomAttribute("aria-checked");
// question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath("..")).click();
while (check == null || check.equals("false")) {
clickElement.click();
check = clickElement.getDomAttribute("aria-checked");
}
}
}
}
private void handleSingleChoiceQuestion(WebElement question, String questionTitle) throws InterruptedException {
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='radio']"));
SingleChoiceQuestion singleChoiceQuestion = new SingleChoiceQuestion();
singleChoiceQuestion.setTitle(questionTitle);
List<String> choices = getChoices(choiceElements);
singleChoiceQuestion.setChoices(choices);
Answer primaryAnswer = getAnswer(singleChoiceQuestion);
SingleChoiceAnswer answer = (SingleChoiceAnswer) primaryAnswer;
String answerContent = answer.getAnswer();
log.info("答案: {}", answerContent);
if (choices.size() == 2) {
answerContent = answerContent.equals("A") ? "true" : "false";
}
String xpath = ".//span[@data='" + answerContent + "']";
//输入答案,并检测是否正确输入
String script = getCheckScript(question);
while (Objects.equals(executor.executeScript(script), "")) {
question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath("..")).click();
}
}
private String getCheckScript(WebElement question) {
WebElement finalInputElement = question.findElement(By.xpath(".//input[@type='hidden']"));
String id = finalInputElement.getDomAttribute("id");
return "return document.getElementById('" + id + "').value;";
}
private List<String> getChoices(List<WebElement> choiceElements) {
List<String> choices = new ArrayList<>();
for (WebElement choiceElement : choiceElements) {
String label = choiceElement.findElement(By.tagName("label")).getText();
String content = choiceElement.findElement(By.tagName("a")).getText();
choices.add(String.format("%s. %s", label, content));
}
return choices;
}
private Answer getAnswer(Question question) throws InterruptedException {
Answer answer = null;
int tryTimes = 0;
while (tryTimes < 3) {
try {
String questionStr = JSONUtil.toJsonPrettyStr(question);
String responseStr = AiUtil.runChat(questionStr);
// 使用正则表达式提取Markdown代码块中的JSON内容
String jsonPattern = "```[\\s\\S]*?\\n([\\s\\S]*?)\\n```";
Pattern pattern = Pattern.compile(jsonPattern);
Matcher matcher = pattern.matcher(responseStr);
String jsonStr = "";
if (matcher.find()) {
jsonStr = matcher.group(1).trim();
}
if (jsonStr.isEmpty()) {
throw new AiResponseFormatException("AI 回答格式不匹配: " + jsonStr);
}
if (question instanceof SingleChoiceQuestion) {
answer = JSONUtil.toBean(jsonStr, SingleChoiceAnswer.class);
} else if (question instanceof MultiChoiceQuestion) {
answer = JSONUtil.toBean(jsonStr, MultiChoiceAnswer.class);
} else if (question instanceof TextQuestion) {
answer = JSONUtil.toBean(jsonStr, TextAnswer.class);
}
break;
} catch (AiResponseFormatException e) {
log.warn(e.getLocalizedMessage());
log.warn("3s后重试...");
Thread.sleep(3000);
tryTimes++;
}
}
return answer;
}
private void handleVideoTask(int i) throws InterruptedException {
try {
driver.switchTo().frame(i);
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='prev_video_left']")));
log.info("开始处理视频播放...");
WebElement play = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@class='vjs-big-play-button']")));
Thread.sleep(2000);
play.click();
WebElement video = driver.findElement(By.id("video_html5_api"));
//获取总时长
String totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
// 获取视频的位置信息
Point location = video.getLocation();
Dimension size = video.getSize();
int videoX = location.getX();
int videoY = location.getY();
int videoWidth = size.getWidth();
int videoHeight = size.getHeight();
// 确定鼠标起始位置:进入视频区域
int startX = videoX + videoWidth / 4;
int startY = videoY + videoHeight / 4;
actions.moveToElement(video, startX, startY).perform();
Thread.sleep(500);
int step = 50; // 每次移动步长
while (true) {
for (int j = 0; j < 20; j++) {
int offsetX = (int) (Math.random() * step) - step / 2; // 随机左右移动
int offsetY = (int) (Math.random() * step) - step / 2; // 随机上下移动
try {
actions.moveByOffset(offsetX, offsetY).perform();
}catch (Exception e) {
actions.moveToElement(video, startX, startY).perform();
}
Thread.sleep(200);
}
String currentTime = driver.findElement(By.xpath(".//span[@class='vjs-current-time-display']")).getText();
if (currentTime.isEmpty()){
actions.moveToElement(video, startX, startY).perform();
currentTime = driver.findElement(By.xpath(".//span[@class='vjs-current-time-display']")).getText();
}
if (totalTime.equals("0:00") || totalTime.isEmpty()) {
totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
} else if (currentTime.equals(totalTime)) {
break;
}
log.info(currentTime + "/" + totalTime);
//恢复暂停
try {
WebElement playingButton = driver.findElement(By.xpath(".//button[@class='vjs-play-control vjs-control vjs-button vjs-paused']"));
playingButton.click();
} catch (WebDriverException ignored) {
}
}
log.info("视频播放任务完成!");
} catch (WebDriverException ignored) {
} finally {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
}
}
private List<WebElement> getTodoTasks() {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@class='posCatalog_select']")));
List<WebElement> toBeFinished = new ArrayList<>();
for (WebElement webElement : driver.findElements(By.xpath("//div[@class='posCatalog_select']"))) {
try {
webElement.findElement(By.xpath(".//span[@class='orangeNew']"));
toBeFinished.add(webElement);
} catch (WebDriverException ignored) {
}
}
log.info("获取到{}个任务", toBeFinished.size());
return toBeFinished;
}
private void login() {
log.info("登录中...");
driver.get("https://i.chaoxing.com/");
WebElement phoneInput = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input[@id='phone']")));
// WebElement phoneInput = driver.findElement();
phoneInput.sendKeys(phone);
WebElement passwdInput = driver.findElement(By.xpath("//input[@id='pwd']"));
passwdInput.sendKeys(passwd);
WebElement submitInput = driver.findElement(By.xpath("//button[@id='loginBtn']"));
submitInput.click();
log.info("登录成功!");
}
private void switchPage() throws InterruptedException {
log.info("跳转中...");
System.out.println(driver.getTitle());
Thread.sleep(5);
try {
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@name='课程']"))).click();
}catch (TimeoutException e){
driver.findElement(By.xpath("//div[@name='课程']")).click();
}
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
String xpath = ".//span[@title='" + targetClassName + "']";
String targetClassHref = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(xpath))).findElement(By.xpath("..")).getDomAttribute("href");
driver.get(targetClassHref);
driver.findElement(By.xpath("//a[@title='章节']")).click();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@role='link']"))).findElement(By.xpath("..")).click();
}
public static CxstudyTask initialize(Config config) {
log.info("任务创建中...");
WebDriverManager.firefoxdriver().clearDriverCache().setup();
WebDriver driver = switch (config.getBrowser()) {
case "firefox" -> config.isHeadless() ? new FirefoxDriver(new FirefoxOptions().addArguments("--headless")) : new FirefoxDriver();
case "edge" ->config.isHeadless() ? new EdgeDriver(new EdgeOptions().addArguments("--headless")) : new EdgeDriver();
case "chrome" ->config.isHeadless() ? new ChromeDriver(new ChromeOptions().addArguments("--headless")) : new ChromeDriver();
default -> throw new IllegalStateException("不支持的浏览器驱动!: " + config.getBrowser());
};
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
Actions actions = new Actions(driver);
JavascriptExecutor executor = (JavascriptExecutor) driver;
CxstudyTask cxstudyTask = getCxstudyTask(config, driver, wait, actions, executor);
log.info("任务创建完毕!");
return cxstudyTask;
}
private static CxstudyTask getCxstudyTask(Config config, WebDriver driver, WebDriverWait wait, Actions actions, JavascriptExecutor executor) {
CxstudyTask cxstudyTask = new CxstudyTask();
cxstudyTask.setDriver(driver);
cxstudyTask.setWait(wait);
cxstudyTask.setActions(actions);
cxstudyTask.setExecutor(executor);
cxstudyTask.setPhone(config.getCxstudyConfig().getPhone());
cxstudyTask.setPasswd(config.getCxstudyConfig().getPassword());
cxstudyTask.setTargetClassName(config.getCxstudyConfig().getTarget());
cxstudyTask.setAiEnable(config.isAiEnable());
return cxstudyTask;
}
}

View File

@@ -1,334 +0,0 @@
package work.slhaf.task.cxstudy;
import cn.hutool.json.JSONUtil;
import io.github.bonigarcia.wdm.WebDriverManager;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import work.slhaf.config.Config;
import work.slhaf.task.cxstudy.pojo.answer.Answer;
import work.slhaf.task.cxstudy.pojo.answer.MultiChoiceAnswer;
import work.slhaf.task.cxstudy.pojo.answer.SingleChoiceAnswer;
import work.slhaf.task.cxstudy.pojo.answer.TextAnswer;
import work.slhaf.task.cxstudy.pojo.question.MultiChoiceQuestion;
import work.slhaf.task.cxstudy.pojo.question.SingleChoiceQuestion;
import work.slhaf.task.cxstudy.pojo.question.Question;
import work.slhaf.task.cxstudy.pojo.question.TextQuestion;
import work.slhaf.util.AiUtil;
import work.slhaf.util.OcrUtil;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class CxstudyTask {
static WebDriver driver;
static Actions actions;
static WebDriverWait wait;
public static void run(Config config) throws InterruptedException {
String phone = config.getCxstudyConfig().getPhone();
String passwd = config.getCxstudyConfig().getPassword();
String targetClassName = config.getCxstudyConfig().getTarget();
while (true) {
try {
//注册driver
initialize();
//登录、跳转页面
loginAndSwitch(phone, passwd, targetClassName);
Thread.sleep(2000);
List<WebElement> toBeFinished = getTodoTasks();
int chapterCount = 0;
while (!toBeFinished.isEmpty()) {
//记录元素对应文本
toBeFinished.getFirst().click();
chapterCount++;
log.info("处理第{}节", chapterCount);
// toBeFinished.get(14).click();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
int subTasksNum = driver.findElements(By.xpath(".//div[@class='ans-attach-ct']")).size();
for (int i = 0; i < subTasksNum; i++) {
//检测frame内部元素
//frame分为三层:
// 第一层index: 6 保存所有含有内容的frame
// 第二层index: i 视频层 i->prev_video_left
// 第三层index: 0->newTestTitle, 2->fileBox
// int taskContainersNum = driver.findElements(By.xpath(".//div[@class='ans-attach-ct']")).size();
// log.info("发现{}个任务", taskContainersNum);
// for (int t = 0; t < taskContainersNum; t++) {
// handleVideoTask(i);
handleTestTask(i);
// handleDocTask(i);
// }
}
driver.get(driver.getCurrentUrl());
driver.switchTo().defaultContent();
toBeFinished = getTodoTasks();
}
driver.quit();
break;
} catch (Exception e) {
log.error("\r\n---------------\r\n");
log.error(e.getLocalizedMessage());
log.error("\r\n---------------\r\n");
driver.quit();
log.error("Something went wrong, will retry later..");
Thread.sleep(5000);
}
}
}
private static void handleDocTask(int i) {
try {
driver.switchTo().frame(i).switchTo().frame(2);
log.info("开始处理文档浏览...");
JavascriptExecutor js = (JavascriptExecutor) driver;
long lastHeight = (long) js.executeScript("return document.body.scrollHeight;");
long currentHeight = 0;
int step = 2000;
while (currentHeight < lastHeight) {
js.executeScript("window.scrollBy(0, arguments[0]);", step);
Thread.sleep(600);
currentHeight = (long) js.executeScript("return window.scrollY + window.innerHeight;");
lastHeight = (long) js.executeScript("return document.body.scrollHeight;");
}
log.info("文档浏览任务完成!");
} catch (Exception ignored) {
} finally {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
}
}
private static void handleTestTask(int i) {
try {
driver.switchTo().frame(i).switchTo().frame(0);
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='singleQuesId']")));
log.info("开始处理测验...");
//所有题目元素
List<WebElement> questions = driver.findElements(By.xpath(".//div[@class='singleQuesId']"));
log.info("发现{}个题目...", questions.size());
for (WebElement question : questions) {
log.info("处理第{}个题目...", questions.indexOf(question));
//获取题目截图通过OCR获取题目信息
WebElement questionTitleElement = question.findElement(By.xpath(".//div[@class='TiMu newTiMu']"));
File screenshot = questionTitleElement.getScreenshotAs(OutputType.FILE);
String questionTitle = OcrUtil.getContentOfImage(screenshot);
log.info("题目信息: {}", questionTitle);
try {
//尝试获取单选选项
//li role='radio'
question.findElement(By.xpath(".//li[@role='radio']"));
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='radio']"));
SingleChoiceQuestion singleChoiceQuestion = new SingleChoiceQuestion();
singleChoiceQuestion.setTitle(questionTitle);
List<String> choices = getChoices(choiceElements);
singleChoiceQuestion.setChoices(choices);
Answer primaryAnswer = getAnswer(singleChoiceQuestion);
SingleChoiceAnswer answer = (SingleChoiceAnswer) primaryAnswer;
String answerContent = answer.getAnswer();
log.info("答案: {}", answerContent);
String xpath = ".//span[@data='" + answerContent + "']";
question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath("..")).click();
continue;
} catch (Exception ignored) {
}
try {
//尝试获取复选框
//li role='checkbox'
question.findElement(By.xpath(".//li[@role='checkbox']"));
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='checkbox']"));
MultiChoiceQuestion multiChoiceQuestion = new MultiChoiceQuestion();
multiChoiceQuestion.setTitle(questionTitle);
List<String> choices = getChoices(choiceElements);
multiChoiceQuestion.setChoices(choices);
continue;
} catch (Exception ignored) {
}
try {
//尝试跳转到简答题输入框
driver.switchTo().frame("ueditor_0");
driver.findElement(By.tagName("p")).sendKeys("answer");
driver.switchTo().parentFrame();
} catch (Exception ignored) {
}
}
log.info("答题任务完成!");
} catch (Exception ignored) {
} finally {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
}
}
private static List<String> getChoices(List<WebElement> choiceElements) {
List<String> choices = new ArrayList<>();
for (WebElement choiceElement : choiceElements) {
String label = choiceElement.findElement(By.tagName("label")).getText();
String content = choiceElement.findElement(By.tagName("a")).getText();
choices.add(String.format("%s. %s", label, content));
}
return choices;
}
private static Answer getAnswer(Question question) throws InterruptedException {
Answer answer = null;
int tryTimes = 0;
while (tryTimes < 3) {
try {
String questionStr = JSONUtil.toJsonPrettyStr(question);
String responseStr = AiUtil.runChat(questionStr);
// 使用正则表达式提取Markdown代码块中的JSON内容
String jsonPattern = "```[\\s\\S]*?\\n([\\s\\S]*?)\\n```";
Pattern pattern = Pattern.compile(jsonPattern);
Matcher matcher = pattern.matcher(responseStr);
String jsonStr = "";
if (matcher.find()) {
jsonStr = matcher.group(1).trim();
}
if (jsonStr.isEmpty()) {
throw new Exception("AI 回答格式不匹配: " + jsonStr);
}
if (question instanceof SingleChoiceQuestion) {
answer = JSONUtil.toBean(jsonStr, SingleChoiceAnswer.class);
} else if (question instanceof MultiChoiceQuestion) {
answer = JSONUtil.toBean(jsonStr, MultiChoiceAnswer.class);
} else if (question instanceof TextQuestion) {
answer = JSONUtil.toBean(jsonStr, TextAnswer.class);
}
break;
} catch (Exception e) {
log.error(e.getLocalizedMessage());
log.error("3s后重试...");
Thread.sleep(3000);
tryTimes++;
}
}
return answer;
}
private static void handleVideoTask(int i) {
try {
driver.switchTo().frame(i);
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='prev_video_left']")));
log.info("开始处理视频播放...");
WebElement play = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@class='vjs-big-play-button']")));
Thread.sleep(2000);
play.click();
WebElement video = driver.findElement(By.id("video_html5_api"));
//获取总时长
String totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
// 获取视频的位置信息
Point location = video.getLocation();
Dimension size = video.getSize();
int videoX = location.getX();
int videoY = location.getY();
int videoWidth = size.getWidth();
int videoHeight = size.getHeight();
// 确定鼠标起始位置:进入视频区域
int startX = videoX + videoWidth / 4;
int startY = videoY + videoHeight / 4;
actions.moveToElement(video, startX, startY).perform();
Thread.sleep(500);
int step = 10; // 每次移动步长
while (true) {
for (int j = 0; j < 20; j++) {
int offsetX = (int) (Math.random() * step) - step / 2; // 随机左右移动
int offsetY = (int) (Math.random() * step) - step / 2; // 随机上下移动
actions.moveByOffset(offsetX, offsetY).perform();
Thread.sleep(200);
}
String currentTime = driver.findElement(By.xpath(".//span[@class='vjs-current-time-display']")).getText();
if (totalTime.equals("0:00") || totalTime.isEmpty()) {
totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
} else if (currentTime.equals(totalTime)) {
log.info(totalTime);
break;
}
log.info(currentTime + "/" + totalTime);
//恢复暂停
try {
WebElement playingButton = driver.findElement(By.xpath(".//button[@class='vjs-play-control vjs-control vjs-button vjs-paused']"));
playingButton.click();
} catch (Exception ignored) {
}
}
log.info("视频播放任务完成!");
} catch (Exception ignored) {
} finally {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
}
}
private static List<WebElement> getTodoTasks() {
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@class='posCatalog_select']")));
List<WebElement> toBeFinished = new ArrayList<>();
for (WebElement webElement : driver.findElements(By.xpath("//div[@class='posCatalog_select']"))) {
try {
webElement.findElement(By.xpath(".//span[@class='orangeNew']"));
toBeFinished.add(webElement);
} catch (Exception ignored) {
}
}
log.info("获取到{}个任务", toBeFinished.size());
return toBeFinished;
}
private static void loginAndSwitch(String phone, String passwd, String targetClassName) {
log.info("登录中...");
driver.get("https://i.chaoxing.com/");
WebElement phoneInput = driver.findElement(By.xpath("//input[@id='phone']"));
phoneInput.sendKeys(phone);
WebElement passwdInput = driver.findElement(By.xpath("//input[@id='pwd']"));
passwdInput.sendKeys(passwd);
WebElement submitInput = driver.findElement(By.xpath("//button[@id='loginBtn']"));
submitInput.click();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@name='课程']"))).click();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
String xpath = ".//span[@title='" + targetClassName + "']";
String targetClassHref = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(xpath))).findElement(By.xpath("..")).getDomAttribute("href");
driver.get(targetClassHref);
driver.findElement(By.xpath("//a[@title='章节']")).click();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@role='link']"))).findElement(By.xpath("..")).click();
log.info("登录成功!");
}
private static void initialize() {
WebDriverManager.firefoxdriver().setup();
driver = new FirefoxDriver();
actions = new Actions(driver);
wait = new WebDriverWait(driver, Duration.ofSeconds(5));
log.info("webdriver创建完毕!");
}
}

View File

@@ -1,4 +0,0 @@
package work.slhaf.task.cxstudy.pojo.answer;
public class Answer {
}

View File

@@ -1,14 +1,17 @@
package work.slhaf.util; package work.slhaf.util;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.chat.ChatClient; import work.slhaf.chat.ChatClient;
import work.slhaf.chat.constant.Constant; import work.slhaf.chat.constant.Constant;
import work.slhaf.chat.pojo.ChatResponse; import work.slhaf.chat.pojo.ChatResponse;
import work.slhaf.chat.pojo.Message; import work.slhaf.chat.pojo.Message;
import work.slhaf.config.Config; import work.slhaf.config.AiConfig;
import work.slhaf.exception.AiRequestException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@Slf4j
public class AiUtil { public class AiUtil {
private static ChatClient chatClient; private static ChatClient chatClient;
private static final String PROMPT = """ private static final String PROMPT = """
@@ -65,16 +68,16 @@ public class AiUtil {
} }
``` ```
注意两种选择题的选项内容不包含序号可能存在乱码当输入的Question JSON为选择题时你需要同时根据choices和title中的内容来确定正确选项。 注意两种选择题的选项内容不包含序号可能存在乱码当输入的Question JSON为选择题时你需要同时根据choices和title中的内容来确定正确选项,同时确保答案中选项数量符合题意
请根据接收到的Question JSON生成对应的Answer JSON并将结果放入Markdown代码块中。确保答案内容合理且符合问题要求。 请根据接收到的Question JSON生成对应的Answer JSON并将结果放入Markdown代码块中。确保答案内容合理且符合问题要求。
"""; """;
private AiUtil() {} private AiUtil() {}
public static void load(Config config) { public static void load(AiConfig config) {
String apikey = config.getAiConfig().getApikey(); String apikey = config.getApikey();
String model = config.getAiConfig().getModel(); String model = config.getModel();
String baseUrl = config.getAiConfig().getBaseUrl(); String baseUrl = config.getBaseUrl();
chatClient = new ChatClient(baseUrl, apikey, model); chatClient = new ChatClient(baseUrl, apikey, model);
} }
@@ -83,8 +86,13 @@ public class AiUtil {
Message system = new Message(Constant.Character.SYSTEM,PROMPT); Message system = new Message(Constant.Character.SYSTEM,PROMPT);
messages.add(system); messages.add(system);
messages.add(new Message(Constant.Character.USER,message)); messages.add(new Message(Constant.Character.USER,message));
ChatResponse chatResponse;
ChatResponse chatResponse = chatClient.runChat(messages); try {
chatResponse = chatClient.runChat(messages);
}catch (Exception e) {
log.error(e.getMessage());
throw new AiRequestException("ai答题运行出错请检查配置或者关闭ai答题");
}
return chatResponse.getMessage(); return chatResponse.getMessage();
} }
} }

View File

@@ -7,7 +7,8 @@ import com.aliyun.ocr_api20210707.models.RecognizeAdvancedResponse;
import com.aliyun.tea.TeaException; import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions; import com.aliyun.teautil.models.RuntimeOptions;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import work.slhaf.config.Config; import work.slhaf.config.OcrConfig;
import work.slhaf.exception.OcrRequestException;
import work.slhaf.pojo.OCRDataInfo; import work.slhaf.pojo.OCRDataInfo;
import java.io.File; import java.io.File;
@@ -20,10 +21,10 @@ public class OcrUtil {
private OcrUtil() {} private OcrUtil() {}
public static void load(Config basicConfig) { public static void load(OcrConfig basicConfig) {
//读取密钥 //读取密钥
String accessKeyId = basicConfig.getOcrConfig().getAccessKeyId(); String accessKeyId = basicConfig.getAccessKeyId();
String accessKeySecret = basicConfig.getOcrConfig().getAccessKeySecret(); String accessKeySecret = basicConfig.getAccessKeySecret();
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config() com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
.setAccessKeyId(accessKeyId) .setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret); .setAccessKeySecret(accessKeySecret);
@@ -67,18 +68,10 @@ public class OcrUtil {
log.error(error.getMessage()); log.error(error.getMessage());
// 诊断地址 // 诊断地址
log.error(error.getData().get("Recommend").toString()); log.error(error.getData().get("Recommend").toString());
com.aliyun.teautil.Common.assertAsString(error.message); throw new OcrRequestException("OCR请求出错检查配置或者关闭ai答题");
return "ERROR"; } catch (Exception e) {
} catch (Exception _error) { log.error(e.getMessage());
TeaException error = new TeaException(_error.getMessage(), _error); throw new OcrRequestException("OCR请求出错检查配置或者关闭ai答题"); }
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
log.error(error.getMessage());
// 诊断地址
log.error(error.getData().get("Recommend").toString());
com.aliyun.teautil.Common.assertAsString(error.message);
return "ERROR";
}
} }
} }