已实现文档浏览、视频播放功能,答题模块中简答题、判断题以及多选题的答案填充待实现;
部分异常捕获需要重做相关逻辑
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
7
.idea/encodings.xml
generated
Normal file
7
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
78
pom.xml
Normal file
78
pom.xml
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>work.slhaf</groupId>
|
||||
<artifactId>CxstudyAuto</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.36</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-java</artifactId>
|
||||
<version>4.29.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.5.17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>ocr_api20210707</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.bonigarcia</groupId>
|
||||
<artifactId>webdrivermanager</artifactId>
|
||||
<version>5.9.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>htmlunit-driver</artifactId>
|
||||
<version>4.13.0</version>
|
||||
</dependency>-->
|
||||
<!--<dependency>
|
||||
<groupId>net.sourceforge.htmlunit</groupId>
|
||||
<artifactId>htmlunit</artifactId>
|
||||
<version>2.70.0</version>
|
||||
</dependency>-->
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
21
src/main/java/work/slhaf/Main.java
Normal file
21
src/main/java/work/slhaf/Main.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package work.slhaf;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.task.cxstudy.CxstudyTask;
|
||||
import work.slhaf.util.AiUtil;
|
||||
import work.slhaf.util.OcrUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException, IOException {
|
||||
Config config = Config.load();
|
||||
AiUtil.load(config);
|
||||
OcrUtil.load(config);
|
||||
CxstudyTask.run(config);
|
||||
}
|
||||
|
||||
}
|
||||
77
src/main/java/work/slhaf/chat/ChatClient.java
Normal file
77
src/main/java/work/slhaf/chat/ChatClient.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package work.slhaf.chat;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import work.slhaf.chat.constant.Constant;
|
||||
import work.slhaf.chat.pojo.ChatBody;
|
||||
import work.slhaf.chat.pojo.ChatResponse;
|
||||
import work.slhaf.chat.pojo.Message;
|
||||
import work.slhaf.chat.pojo.PrimaryChatResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ChatClient {
|
||||
private String clientId;
|
||||
|
||||
private String url;
|
||||
private String apikey;
|
||||
private String model;
|
||||
|
||||
private int top_p;
|
||||
private int temperature;
|
||||
private int max_tokens;
|
||||
|
||||
public ChatClient(String url,String apikey,String model){
|
||||
this.url = url;
|
||||
this.apikey = apikey;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public ChatResponse runChat(List<Message> messages){
|
||||
HttpRequest request = HttpRequest.post(url);
|
||||
request.header("Content-Type", "application/json");
|
||||
request.header("Authorization", "Bearer " + apikey);
|
||||
|
||||
ChatBody body;
|
||||
if (top_p > 0) {
|
||||
body = ChatBody.builder()
|
||||
.model(model)
|
||||
.messages(messages)
|
||||
.top_p(top_p)
|
||||
.temperature(temperature)
|
||||
.max_tokens(max_tokens)
|
||||
.build();
|
||||
}else {
|
||||
body = ChatBody.builder()
|
||||
.model(model)
|
||||
.messages(messages)
|
||||
.build();
|
||||
}
|
||||
|
||||
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
|
||||
ChatResponse finalResponse;
|
||||
try {
|
||||
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
|
||||
finalResponse = ChatResponse.builder()
|
||||
.type(Constant.Response.SUCCESS)
|
||||
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
|
||||
.usageBean(primaryChatResponse.getUsage())
|
||||
.build();
|
||||
}catch (Exception e){
|
||||
finalResponse = ChatResponse.builder()
|
||||
.type(Constant.Response.ERROR)
|
||||
.message(response.getStatus()+": "+response.body())
|
||||
.build();
|
||||
}
|
||||
response.close();
|
||||
return finalResponse;
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/java/work/slhaf/chat/constant/Constant.java
Normal file
22
src/main/java/work/slhaf/chat/constant/Constant.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package work.slhaf.chat.constant;
|
||||
|
||||
public class Constant {
|
||||
|
||||
public static class Character {
|
||||
public static final String USER = "user";
|
||||
public static final String SYSTEM = "system";
|
||||
public static final String ASSISTANT = "assistant";
|
||||
}
|
||||
|
||||
public static class Model {
|
||||
public static final String DEEP_SEEK_CHAT = "deepseek-chat";
|
||||
public static final String GLM_4_FLASH = "glm-4_flash";
|
||||
public static final String GLM_4_PLUS = "glm-4_plus";
|
||||
public static final String GLM_4_0520 = "glm-4_0520";
|
||||
}
|
||||
|
||||
public static class Response {
|
||||
public static final String SUCCESS = "success";
|
||||
public static final String ERROR = "error";
|
||||
}
|
||||
}
|
||||
25
src/main/java/work/slhaf/chat/pojo/ChatBody.java
Normal file
25
src/main/java/work/slhaf/chat/pojo/ChatBody.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package work.slhaf.chat.pojo;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ChatBody {
|
||||
@NonNull
|
||||
private String model;
|
||||
@NonNull
|
||||
private List<Message> messages;
|
||||
@Builder.Default
|
||||
private int temperature = 1;
|
||||
@Builder.Default
|
||||
private int top_p = 1;
|
||||
private boolean stream;
|
||||
@Builder.Default
|
||||
private int max_tokens = 1024;
|
||||
private int presence_penalty;
|
||||
private int frequency_penalty;
|
||||
}
|
||||
16
src/main/java/work/slhaf/chat/pojo/ChatResponse.java
Normal file
16
src/main/java/work/slhaf/chat/pojo/ChatResponse.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package work.slhaf.chat.pojo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ChatResponse {
|
||||
private String type;
|
||||
private String message;
|
||||
private PrimaryChatResponse.UsageBean usageBean;
|
||||
}
|
||||
14
src/main/java/work/slhaf/chat/pojo/Message.java
Normal file
14
src/main/java/work/slhaf/chat/pojo/Message.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package work.slhaf.chat.pojo;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Message {
|
||||
@NonNull
|
||||
private String role;
|
||||
@NonNull
|
||||
private String content;
|
||||
}
|
||||
111
src/main/java/work/slhaf/chat/pojo/PrimaryChatResponse.java
Normal file
111
src/main/java/work/slhaf/chat/pojo/PrimaryChatResponse.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package work.slhaf.chat.pojo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class PrimaryChatResponse {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* object
|
||||
*/
|
||||
private String object;
|
||||
/**
|
||||
* created
|
||||
*/
|
||||
private int created;
|
||||
/**
|
||||
* model
|
||||
*/
|
||||
private String model;
|
||||
/**
|
||||
* choices
|
||||
*/
|
||||
private List<ChoicesBean> choices;
|
||||
/**
|
||||
* usage
|
||||
*/
|
||||
private UsageBean usage;
|
||||
/**
|
||||
* system_fingerprint
|
||||
*/
|
||||
private String system_fingerprint;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class UsageBean {
|
||||
/**
|
||||
* prompt_tokens
|
||||
*/
|
||||
private int prompt_tokens;
|
||||
/**
|
||||
* completion_tokens
|
||||
*/
|
||||
private int completion_tokens;
|
||||
/**
|
||||
* total_tokens
|
||||
*/
|
||||
private int total_tokens;
|
||||
/**
|
||||
* prompt_cache_hit_tokens
|
||||
*/
|
||||
private int prompt_cache_hit_tokens;
|
||||
/**
|
||||
* prompt_cache_miss_tokens
|
||||
*/
|
||||
private int prompt_cache_miss_tokens;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UsageBean{" +
|
||||
"prompt_tokens=" + prompt_tokens +
|
||||
", completion_tokens=" + completion_tokens +
|
||||
", total_tokens=" + total_tokens +
|
||||
", prompt_cache_hit_tokens=" + prompt_cache_hit_tokens +
|
||||
", prompt_cache_miss_tokens=" + prompt_cache_miss_tokens +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class ChoicesBean {
|
||||
/**
|
||||
* index
|
||||
*/
|
||||
private int index;
|
||||
/**
|
||||
* message
|
||||
*/
|
||||
private MessageBean message;
|
||||
/**
|
||||
* logprobs
|
||||
*/
|
||||
private Object logprobs;
|
||||
/**
|
||||
* finish_reason
|
||||
*/
|
||||
private String finish_reason;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class MessageBean {
|
||||
/**
|
||||
* role
|
||||
*/
|
||||
private String role;
|
||||
/**
|
||||
* content
|
||||
*/
|
||||
private String content;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/main/java/work/slhaf/config/AiConfig.java
Normal file
21
src/main/java/work/slhaf/config/AiConfig.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package work.slhaf.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class AiConfig {
|
||||
private String apikey;
|
||||
private String model;
|
||||
private String baseUrl;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return """
|
||||
AiConfig:
|
||||
apikey: '%s'
|
||||
model: '%s'
|
||||
""".formatted(apikey, model);
|
||||
}
|
||||
}
|
||||
89
src/main/java/work/slhaf/config/Config.java
Normal file
89
src/main/java/work/slhaf/config/Config.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package work.slhaf.config;
|
||||
|
||||
import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Scanner;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
public class Config {
|
||||
private CxstudyConfig cxstudyConfig;
|
||||
private OcrConfig ocrConfig;
|
||||
private AiConfig aiConfig;
|
||||
|
||||
private Config() {
|
||||
}
|
||||
|
||||
public static Config load() throws IOException {
|
||||
File file = new File("./config.json");
|
||||
boolean configExists = checkConfigExists(file);
|
||||
|
||||
if (!configExists) {
|
||||
generateConfig(file);
|
||||
}
|
||||
|
||||
return loadFromFile(file);
|
||||
}
|
||||
|
||||
private static void generateConfig(File file) throws IOException {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
AiConfig aiConfig = new AiConfig();
|
||||
System.out.println("recording ai config:");
|
||||
System.out.print("apikey: ");
|
||||
aiConfig.setApikey(sc.nextLine());
|
||||
System.out.print("baseUrl: ");
|
||||
aiConfig.setBaseUrl(sc.nextLine());
|
||||
System.out.print("model: ");
|
||||
aiConfig.setModel(sc.nextLine());
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
OcrConfig ocrConfig = new OcrConfig();
|
||||
System.out.println("recording ocr config:");
|
||||
System.out.print("accessKeyId: ");
|
||||
ocrConfig.setAccessKeyId(sc.nextLine());
|
||||
System.out.print("accessKeySecret: ");
|
||||
ocrConfig.setAccessKeySecret(sc.nextLine());
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
CxstudyConfig studyConfig = new CxstudyConfig();
|
||||
System.out.println("recording study config:");
|
||||
System.out.print("phone: ");
|
||||
studyConfig.setPhone(sc.nextLine());
|
||||
System.out.print("password: ");
|
||||
studyConfig.setPassword(sc.nextLine());
|
||||
System.out.print("target: ");
|
||||
studyConfig.setTarget(sc.nextLine());
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
Config config = new Config();
|
||||
config.cxstudyConfig = studyConfig;
|
||||
config.ocrConfig = ocrConfig;
|
||||
config.aiConfig = aiConfig;
|
||||
String configStr = JSONUtil.toJsonPrettyStr(config);
|
||||
System.out.println("this is your config file: \r\n" + configStr);
|
||||
|
||||
System.out.println("press ENTER to generate config: ");
|
||||
sc.nextLine();
|
||||
|
||||
file.createNewFile();
|
||||
FileUtils.writeStringToFile(file, configStr, StandardCharsets.UTF_8);
|
||||
System.out.println(" config generated");
|
||||
}
|
||||
|
||||
private static Config loadFromFile(File file) throws IOException {
|
||||
return JSONUtil.toBean(FileUtils.readFileToString(file, StandardCharsets.UTF_8), Config.class);
|
||||
}
|
||||
|
||||
private static boolean checkConfigExists(File file) {
|
||||
return file.exists();
|
||||
}
|
||||
}
|
||||
22
src/main/java/work/slhaf/config/CxstudyConfig.java
Normal file
22
src/main/java/work/slhaf/config/CxstudyConfig.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package work.slhaf.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class CxstudyConfig {
|
||||
private String phone;
|
||||
private String password;
|
||||
private String target;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return """
|
||||
CxstudyConfig:
|
||||
phone: '%s'
|
||||
password: '%s'
|
||||
target: '%s'
|
||||
""".formatted(phone, password, target);
|
||||
}
|
||||
}
|
||||
20
src/main/java/work/slhaf/config/OcrConfig.java
Normal file
20
src/main/java/work/slhaf/config/OcrConfig.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package work.slhaf.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class OcrConfig {
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return """
|
||||
OcrConfig:
|
||||
accessKeyId: '%s'
|
||||
accessKeySecret: '%s'
|
||||
""".formatted(accessKeyId, accessKeySecret);
|
||||
}
|
||||
}
|
||||
186
src/main/java/work/slhaf/pojo/OCRDataInfo.java
Normal file
186
src/main/java/work/slhaf/pojo/OCRDataInfo.java
Normal file
@@ -0,0 +1,186 @@
|
||||
package work.slhaf.pojo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class OCRDataInfo {
|
||||
|
||||
/**
|
||||
* algo_version
|
||||
*/
|
||||
private String algo_version;
|
||||
/**
|
||||
* 文字内容-段落
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* height
|
||||
*/
|
||||
private int height;
|
||||
/**
|
||||
* orgHeight
|
||||
*/
|
||||
private int orgHeight;
|
||||
/**
|
||||
* orgWidth
|
||||
*/
|
||||
private int orgWidth;
|
||||
/**
|
||||
* prism_version
|
||||
*/
|
||||
private String prism_version;
|
||||
/**
|
||||
* prism_wnum
|
||||
*/
|
||||
private int prism_wnum;
|
||||
/**
|
||||
* prism_wordsInfo
|
||||
*/
|
||||
private List<PrismWordsInfoBean> prism_wordsInfo;
|
||||
/**
|
||||
* width
|
||||
*/
|
||||
private int width;
|
||||
|
||||
public static class PrismWordsInfoBean {
|
||||
/**
|
||||
* angle
|
||||
*/
|
||||
private int angle;
|
||||
/**
|
||||
* direction
|
||||
*/
|
||||
private int direction;
|
||||
/**
|
||||
* height
|
||||
*/
|
||||
private int height;
|
||||
/**
|
||||
* pos
|
||||
*/
|
||||
private List<PosBean> pos;
|
||||
/**
|
||||
* prob
|
||||
*/
|
||||
private int prob;
|
||||
/**
|
||||
* width
|
||||
*/
|
||||
private int width;
|
||||
/**
|
||||
* 文字内容-行
|
||||
*/
|
||||
private String word;
|
||||
/**
|
||||
* x
|
||||
*/
|
||||
private int x;
|
||||
/**
|
||||
* y
|
||||
*/
|
||||
private int y;
|
||||
|
||||
public int getAngle() {
|
||||
return angle;
|
||||
}
|
||||
|
||||
public void setAngle(int angle) {
|
||||
this.angle = angle;
|
||||
}
|
||||
|
||||
public int getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(int direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public List<PosBean> getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public void setPos(List<PosBean> pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public int getProb() {
|
||||
return prob;
|
||||
}
|
||||
|
||||
public void setProb(int prob) {
|
||||
this.prob = prob;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public String getWord() {
|
||||
return word;
|
||||
}
|
||||
|
||||
public void setWord(String word) {
|
||||
this.word = word;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(int x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public static class PosBean {
|
||||
/**
|
||||
* x
|
||||
*/
|
||||
private int x;
|
||||
/**
|
||||
* y
|
||||
*/
|
||||
private int y;
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(int x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
334
src/main/java/work/slhaf/task/cxstudy/CxstudyTask.java
Normal file
334
src/main/java/work/slhaf/task/cxstudy/CxstudyTask.java
Normal file
@@ -0,0 +1,334 @@
|
||||
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创建完毕!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
|
||||
public class Answer {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class MultiChoiceAnswer extends Answer {
|
||||
private List<String> answers;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class SingleChoiceAnswer extends Answer {
|
||||
private String answer;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class TextAnswer extends Answer {
|
||||
private String answer;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class MultiChoiceQuestion extends Question {
|
||||
private String type = "多选题";
|
||||
private List<String> choices;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Question {
|
||||
private String title;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class SingleChoiceQuestion extends Question {
|
||||
private String type = "单选题题";
|
||||
private List<String> choices;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class TextQuestion extends Question{
|
||||
private String type = "简答题";
|
||||
}
|
||||
90
src/main/java/work/slhaf/util/AiUtil.java
Normal file
90
src/main/java/work/slhaf/util/AiUtil.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package work.slhaf.util;
|
||||
|
||||
import work.slhaf.chat.ChatClient;
|
||||
import work.slhaf.chat.constant.Constant;
|
||||
import work.slhaf.chat.pojo.ChatResponse;
|
||||
import work.slhaf.chat.pojo.Message;
|
||||
import work.slhaf.config.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AiUtil {
|
||||
private static ChatClient chatClient;
|
||||
private static final String PROMPT = """
|
||||
你是一个智能问卷系统助手。你将接收一个JSON格式的Question对象,需要根据Question的类型和内容,生成对应的Answer JSON,并将结果放入Markdown代码块中。
|
||||
|
||||
Question JSON可能包含以下类型:
|
||||
1. 简答题(TextQuestion):包含title字段和type字段
|
||||
2. 单选题(SingleChoiceQuestion):包含title字段、type字段和choices字段
|
||||
3. 多选题(MultiChoiceQuestion):包含title字段、type字段和choices字段
|
||||
|
||||
请根据以下规则生成Answer JSON:
|
||||
1. 如果Question类型是"简答题",生成TextAnswer对象,包含answer字段
|
||||
2. 如果Question类型是"单选题",生成SingleChoiceAnswer对象,包含answer字段,且answer必须是choices中的一个选项
|
||||
3. 如果Question类型是"多选题",生成MultiChoiceAnswer对象,包含answers字段,且answers必须是choices中的多个选项(至少选择一个)。注意:choices的格式为"A. 答案1","B. 答案2"等,你只需要返回选项字母(如"A"、"B"),不需要返回选项内容
|
||||
|
||||
示例1:
|
||||
输入:
|
||||
{
|
||||
"title": "请描述你的工作经历",
|
||||
"type": "简答题"
|
||||
}
|
||||
输出:
|
||||
```json
|
||||
{
|
||||
"answer": "我有5年Java开发经验,曾参与多个大型项目..."
|
||||
}
|
||||
```
|
||||
|
||||
示例2:
|
||||
输入:
|
||||
{
|
||||
"title": "你最喜欢的编程语言是?",
|
||||
"type": "单选题",
|
||||
"choices": ["A. Java", "B. Python", "C. C++"]
|
||||
}
|
||||
输出:
|
||||
```json
|
||||
{
|
||||
"answer": "B"
|
||||
}
|
||||
```
|
||||
|
||||
示例3:
|
||||
输入:
|
||||
{
|
||||
"title": "你使用过哪些数据库?",
|
||||
"type": "多选题",
|
||||
"choices": ["A. MySQL", "B. MongoDB", "C. Redis"]
|
||||
}
|
||||
输出:
|
||||
```json
|
||||
{
|
||||
"answers": ["A", "C"]
|
||||
}
|
||||
```
|
||||
|
||||
注意:两种选择题的选项内容(不包含序号)可能存在乱码,当输入的Question JSON为选择题时,你需要同时根据choices和title中的内容来确定正确选项。
|
||||
请根据接收到的Question JSON,生成对应的Answer JSON,并将结果放入Markdown代码块中。确保答案内容合理且符合问题要求。
|
||||
""";
|
||||
|
||||
private AiUtil() {}
|
||||
|
||||
public static void load(Config config) {
|
||||
String apikey = config.getAiConfig().getApikey();
|
||||
String model = config.getAiConfig().getModel();
|
||||
String baseUrl = config.getAiConfig().getBaseUrl();
|
||||
chatClient = new ChatClient(baseUrl, apikey, model);
|
||||
}
|
||||
|
||||
public static String runChat(String message) {
|
||||
List<Message> messages = new ArrayList<>();
|
||||
Message system = new Message(Constant.Character.SYSTEM,PROMPT);
|
||||
messages.add(system);
|
||||
messages.add(new Message(Constant.Character.USER,message));
|
||||
|
||||
ChatResponse chatResponse = chatClient.runChat(messages);
|
||||
return chatResponse.getMessage();
|
||||
}
|
||||
}
|
||||
84
src/main/java/work/slhaf/util/OcrUtil.java
Normal file
84
src/main/java/work/slhaf/util/OcrUtil.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package work.slhaf.util;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.aliyun.ocr_api20210707.Client;
|
||||
import com.aliyun.ocr_api20210707.models.RecognizeAdvancedRequest;
|
||||
import com.aliyun.ocr_api20210707.models.RecognizeAdvancedResponse;
|
||||
import com.aliyun.tea.TeaException;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.pojo.OCRDataInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
@Slf4j
|
||||
public class OcrUtil {
|
||||
private static Client client;
|
||||
|
||||
private OcrUtil() {}
|
||||
|
||||
public static void load(Config basicConfig) {
|
||||
//读取密钥
|
||||
String accessKeyId = basicConfig.getOcrConfig().getAccessKeyId();
|
||||
String accessKeySecret = basicConfig.getOcrConfig().getAccessKeySecret();
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(accessKeyId)
|
||||
.setAccessKeySecret(accessKeySecret);
|
||||
config.endpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
|
||||
try {
|
||||
client = new com.aliyun.ocr_api20210707.Client(config);
|
||||
} catch (Exception e) {
|
||||
log.error("创建client出错");
|
||||
e.printStackTrace();
|
||||
}
|
||||
log.info("OCR client已创建");
|
||||
|
||||
}
|
||||
|
||||
public static String getContentOfImage(File file) throws FileNotFoundException {
|
||||
//设置请求信息
|
||||
RecognizeAdvancedRequest recognizeAdvancedRequest = new RecognizeAdvancedRequest()
|
||||
.setBody(new FileInputStream(file))
|
||||
.setNeedRotate(Boolean.TRUE)
|
||||
.setNeedRotate(Boolean.TRUE);
|
||||
|
||||
try {
|
||||
//发送请求并处理回应
|
||||
RecognizeAdvancedResponse response = client.recognizeAdvancedWithOptions(recognizeAdvancedRequest, new RuntimeOptions());
|
||||
OCRDataInfo ocrDataInfo = JSONUtil.toBean(response.getBody().getData(), OCRDataInfo.class);
|
||||
if (!ocrDataInfo.getContent().isEmpty()) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
ocrDataInfo.getPrism_wordsInfo().forEach(prismWordsInfoBean -> {
|
||||
str.append(prismWordsInfoBean.getWord());
|
||||
if (!ocrDataInfo.getPrism_wordsInfo().get(ocrDataInfo.getPrism_wordsInfo().size() - 1).equals(prismWordsInfoBean)) {
|
||||
str.append("\r\n");
|
||||
}
|
||||
});
|
||||
return str.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (TeaException error) {
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
log.error(error.getData().get("Recommend").toString());
|
||||
com.aliyun.teautil.Common.assertAsString(error.message);
|
||||
return "ERROR";
|
||||
} catch (Exception _error) {
|
||||
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
log.error(error.getData().get("Recommend").toString());
|
||||
com.aliyun.teautil.Common.assertAsString(error.message);
|
||||
return "ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
13
src/test/java/ProjectTest.java
Normal file
13
src/test/java/ProjectTest.java
Normal file
@@ -0,0 +1,13 @@
|
||||
import org.junit.Test;
|
||||
import work.slhaf.config.Config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ProjectTest {
|
||||
|
||||
@Test
|
||||
public void configTest() throws IOException {
|
||||
Config config = Config.load();
|
||||
System.out.println(config.toString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user