41 changed files with 2995 additions and 4 deletions
-
40ai-service/.classpath
-
1ai-service/.gitignore
-
23ai-service/.project
-
5ai-service/.settings/org.eclipse.core.resources.prefs
-
9ai-service/.settings/org.eclipse.jdt.core.prefs
-
4ai-service/.settings/org.eclipse.m2e.core.prefs
-
195ai-service/pom.xml
-
19ai-service/src/main/java/com/bw/ai/Application.java
-
37ai-service/src/main/java/com/bw/ai/cache/ConfigCache.java
-
32ai-service/src/main/java/com/bw/ai/emotionextract/controller/EmotionTaskReceiveController.java
-
18ai-service/src/main/java/com/bw/ai/emotionextract/service/EmotionExtractService.java
-
14ai-service/src/main/java/com/bw/ai/emotionextract/service/EmotionTaskReceiveService.java
-
160ai-service/src/main/java/com/bw/ai/emotionextract/service/impl/EmotionExtractServiceImpl.java
-
50ai-service/src/main/java/com/bw/ai/emotionextract/service/impl/EmotionTaskReceiveServiceImpl.java
-
38ai-service/src/main/java/com/bw/ai/entity/AppResultDoc.java
-
110ai-service/src/main/java/com/bw/ai/entity/Constants.java
-
219ai-service/src/main/java/com/bw/ai/handler/MainHandler.java
-
39ai-service/src/main/java/com/bw/ai/textextract/controller/TextTaskReceiveController.java
-
19ai-service/src/main/java/com/bw/ai/textextract/service/TextExtractService.java
-
17ai-service/src/main/java/com/bw/ai/textextract/service/TextTaskReceiveService.java
-
139ai-service/src/main/java/com/bw/ai/textextract/service/impl/TextExtractServiceImpl.java
-
55ai-service/src/main/java/com/bw/ai/textextract/service/impl/TextTaskReceiveServiceImpl.java
-
48ai-service/src/main/java/com/bw/ai/utils/DataUtil.java
-
177ai-service/src/main/java/com/bw/ai/utils/DateUtil.java
-
1004ai-service/src/main/java/com/bw/ai/utils/DownLoadUtil.java
-
27ai-service/src/main/java/com/bw/ai/utils/EncryptionUtil.java
-
104ai-service/src/main/java/com/bw/ai/utils/FileUtil.java
-
99ai-service/src/main/java/com/bw/ai/utils/GPTResultParseUtil.java
-
33ai-service/src/main/java/com/bw/ai/utils/OtherUtils.java
-
18ai-service/src/main/java/com/bw/ai/utils/QueueUtil.java
-
23ai-service/src/main/java/com/bw/ai/utils/ThrowMessageUtil.java
-
52ai-service/src/main/resources/bootstrap.yml
-
26ai-service/src/main/resources/emotionPrompt
-
36ai-service/src/main/resources/logback-spring.xml
-
30ai-service/src/main/resources/moodPrompt
-
38ai-service/src/main/resources/opinionPrompt
-
35ai-service/src/main/resources/prompt
-
1ocr-service/src/main/java/com/bw/ocr/handler/MainHandler.java
-
1pom.xml
-
1translate-service/src/main/java/com/bw/translate/handler/MainHandler.java
-
3translate-service/src/main/java/com/bw/translate/service/impl/TranslateTaskServiceImpl.java
@ -0,0 +1,40 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<classpath> |
||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java"> |
||||
|
<attributes> |
||||
|
<attribute name="optional" value="true"/> |
||||
|
<attribute name="maven.pomderived" value="true"/> |
||||
|
</attributes> |
||||
|
</classpathentry> |
||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> |
||||
|
<attributes> |
||||
|
<attribute name="maven.pomderived" value="true"/> |
||||
|
<attribute name="optional" value="true"/> |
||||
|
</attributes> |
||||
|
</classpathentry> |
||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> |
||||
|
<attributes> |
||||
|
<attribute name="optional" value="true"/> |
||||
|
<attribute name="maven.pomderived" value="true"/> |
||||
|
<attribute name="test" value="true"/> |
||||
|
</attributes> |
||||
|
</classpathentry> |
||||
|
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"> |
||||
|
<attributes> |
||||
|
<attribute name="maven.pomderived" value="true"/> |
||||
|
<attribute name="test" value="true"/> |
||||
|
<attribute name="optional" value="true"/> |
||||
|
</attributes> |
||||
|
</classpathentry> |
||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> |
||||
|
<attributes> |
||||
|
<attribute name="maven.pomderived" value="true"/> |
||||
|
</attributes> |
||||
|
</classpathentry> |
||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> |
||||
|
<attributes> |
||||
|
<attribute name="maven.pomderived" value="true"/> |
||||
|
</attributes> |
||||
|
</classpathentry> |
||||
|
<classpathentry kind="output" path="target/classes"/> |
||||
|
</classpath> |
||||
@ -0,0 +1 @@ |
|||||
|
/target/ |
||||
@ -0,0 +1,23 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<projectDescription> |
||||
|
<name>ai-service</name> |
||||
|
<comment></comment> |
||||
|
<projects> |
||||
|
</projects> |
||||
|
<buildSpec> |
||||
|
<buildCommand> |
||||
|
<name>org.eclipse.jdt.core.javabuilder</name> |
||||
|
<arguments> |
||||
|
</arguments> |
||||
|
</buildCommand> |
||||
|
<buildCommand> |
||||
|
<name>org.eclipse.m2e.core.maven2Builder</name> |
||||
|
<arguments> |
||||
|
</arguments> |
||||
|
</buildCommand> |
||||
|
</buildSpec> |
||||
|
<natures> |
||||
|
<nature>org.eclipse.jdt.core.javanature</nature> |
||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature> |
||||
|
</natures> |
||||
|
</projectDescription> |
||||
@ -0,0 +1,5 @@ |
|||||
|
eclipse.preferences.version=1 |
||||
|
encoding//src/main/java=UTF-8 |
||||
|
encoding//src/main/resources=UTF-8 |
||||
|
encoding//src/test/java=UTF-8 |
||||
|
encoding/<project>=UTF-8 |
||||
@ -0,0 +1,9 @@ |
|||||
|
eclipse.preferences.version=1 |
||||
|
org.eclipse.jdt.core.compiler.codegen.methodParameters=generate |
||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 |
||||
|
org.eclipse.jdt.core.compiler.compliance=1.8 |
||||
|
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled |
||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning |
||||
|
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore |
||||
|
org.eclipse.jdt.core.compiler.release=disabled |
||||
|
org.eclipse.jdt.core.compiler.source=1.8 |
||||
@ -0,0 +1,4 @@ |
|||||
|
activeProfiles= |
||||
|
eclipse.preferences.version=1 |
||||
|
resolveWorkspaceProjects=true |
||||
|
version=1 |
||||
@ -0,0 +1,195 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<project |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" |
||||
|
xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
<parent> |
||||
|
<groupId>com.bw</groupId> |
||||
|
<artifactId>opai-service-center</artifactId> |
||||
|
<version>0.0.1-SNAPSHOT</version> |
||||
|
</parent> |
||||
|
<groupId>com.bw</groupId> |
||||
|
<artifactId>ai-service</artifactId> |
||||
|
<version>0.0.1-SNAPSHOT</version> |
||||
|
<name>ai-service</name> |
||||
|
<url>http://maven.apache.org</url> |
||||
|
<properties> |
||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
|
</properties> |
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba.cloud</groupId> |
||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.cloud</groupId> |
||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba.cloud</groupId> |
||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-actuator</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>fastjson</artifactId> |
||||
|
<version>2.0.17</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.httpcomponents</groupId> |
||||
|
<artifactId>httpclient</artifactId> |
||||
|
<version>4.5.3</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.httpcomponents</groupId> |
||||
|
<artifactId>httpmime</artifactId> |
||||
|
<version>4.5.13</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>commons-lang</groupId> |
||||
|
<artifactId>commons-lang</artifactId> |
||||
|
<version>2.6</version> |
||||
|
</dependency> |
||||
|
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> |
||||
|
<dependency> |
||||
|
<groupId>com.squareup.okhttp3</groupId> |
||||
|
<artifactId>okhttp</artifactId> |
||||
|
<version>4.9.3</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.kafka</groupId> |
||||
|
<artifactId>spring-kafka</artifactId> |
||||
|
</dependency> |
||||
|
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.poi</groupId> |
||||
|
<artifactId>poi</artifactId> |
||||
|
<version>4.1.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.apache.poi</groupId> |
||||
|
<artifactId>poi-ooxml</artifactId> |
||||
|
<version>4.1.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.apache.poi</groupId> |
||||
|
<artifactId>poi-scratchpad</artifactId> |
||||
|
<version>4.1.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 复杂 OOXML 强烈建议 --> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.poi</groupId> |
||||
|
<artifactId>poi-ooxml-schemas</artifactId> |
||||
|
<version>4.1.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
</dependencies> |
||||
|
<build> |
||||
|
<!-- <pluginManagement> --><!-- lock down plugins versions to avoid using Maven defaults (may be |
||||
|
moved |
||||
|
to parent pom) --> |
||||
|
<plugins> |
||||
|
<!-- clean lifecycle, see |
||||
|
https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> |
||||
|
<plugin> |
||||
|
<artifactId>maven-clean-plugin</artifactId> |
||||
|
<version>3.1.0</version> |
||||
|
</plugin> |
||||
|
<!-- default lifecycle, jar packaging: see |
||||
|
https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> |
||||
|
<plugin> |
||||
|
<artifactId>maven-resources-plugin</artifactId> |
||||
|
<version>3.0.2</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.8.0</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-surefire-plugin</artifactId> |
||||
|
<version>2.22.1</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||
|
<version>3.0.2</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-install-plugin</artifactId> |
||||
|
<version>2.5.2</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-deploy-plugin</artifactId> |
||||
|
<version>2.8.2</version> |
||||
|
</plugin> |
||||
|
<!-- site lifecycle, see |
||||
|
https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> |
||||
|
<plugin> |
||||
|
<artifactId>maven-site-plugin</artifactId> |
||||
|
<version>3.7.1</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-project-info-reports-plugin</artifactId> |
||||
|
<version>3.0.0</version> |
||||
|
</plugin> |
||||
|
<!-- spring-boot-maven-plugin插件就是打包spring boot应用的 --> |
||||
|
|
||||
|
<plugin> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
|
<configuration> |
||||
|
<mainClass>com.bw.ai.Application</mainClass> |
||||
|
<layout>ZIP</layout> |
||||
|
<includes> |
||||
|
<include> |
||||
|
<groupId>${project.groupId}</groupId> |
||||
|
<artifactId>${project.artifactId}</artifactId> |
||||
|
</include> |
||||
|
</includes> |
||||
|
</configuration> |
||||
|
<executions> |
||||
|
<execution> |
||||
|
<goals> |
||||
|
<goal>repackage</goal> |
||||
|
</goals> |
||||
|
</execution> |
||||
|
</executions> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-dependency-plugin</artifactId> |
||||
|
<version>3.1.1</version> |
||||
|
<executions> |
||||
|
<execution> |
||||
|
<id>copy</id> |
||||
|
<phase>package</phase> |
||||
|
<goals> |
||||
|
<goal>copy-dependencies</goal> |
||||
|
</goals> |
||||
|
<configuration> |
||||
|
<type>jar</type> |
||||
|
<includeTypes>jar</includeTypes> |
||||
|
<includeScope>runtime</includeScope> |
||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory> |
||||
|
</configuration> |
||||
|
</execution> |
||||
|
</executions> |
||||
|
</plugin> |
||||
|
</plugins> |
||||
|
<!-- </pluginManagement> --> |
||||
|
</build> |
||||
|
</project> |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.bw.ai; |
||||
|
|
||||
|
import org.springframework.boot.SpringApplication; |
||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 系统接口启动类 |
||||
|
* @author jian.mao |
||||
|
* @date 2025年12月30日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@SpringBootApplication |
||||
|
public class Application { |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
SpringApplication.run(Application.class, args); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
package com.bw.ai.cache; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.LinkedBlockingDeque; |
||||
|
|
||||
|
/** |
||||
|
* @author jian.mao |
||||
|
* @date 2022年11月11日 |
||||
|
* @description 静态变量类 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class ConfigCache { |
||||
|
|
||||
|
/**启动条件**/ |
||||
|
public static boolean isStart = true; |
||||
|
/*****文本抽取任务队列*****/ |
||||
|
public static LinkedBlockingDeque<Map<String, Object>> textExtractTaskQueue = new LinkedBlockingDeque<Map<String,Object>>(); |
||||
|
/****情感抽取任务队列*****/ |
||||
|
public static LinkedBlockingDeque<Map<String, Object>> emotionExtractTaskQueue = new LinkedBlockingDeque<Map<String,Object>>(); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 队列录入任务 |
||||
|
* @param queue |
||||
|
* @param task |
||||
|
*/ |
||||
|
public static void putQueue(LinkedBlockingDeque<Map<String, Object>> queue,Map<String, Object> task){ |
||||
|
//next app 写入队列准备调出 |
||||
|
try { |
||||
|
queue.put(task); |
||||
|
} catch (InterruptedException e) { |
||||
|
log.error("队列写入data失败---"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
package com.bw.ai.emotionextract.controller; |
||||
|
|
||||
|
import javax.annotation.Resource; |
||||
|
|
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import com.bw.ai.emotionextract.service.EmotionTaskReceiveService; |
||||
|
|
||||
|
/** |
||||
|
* 情感抽取控制层接口 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月16日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@RestController |
||||
|
@RequestMapping("/emotion") |
||||
|
|
||||
|
public class EmotionTaskReceiveController { |
||||
|
|
||||
|
@Resource |
||||
|
private EmotionTaskReceiveService emotionTaskReceiveService; |
||||
|
@PostMapping("/put") |
||||
|
@ResponseBody |
||||
|
public String put(@RequestBody String param){ |
||||
|
String response = emotionTaskReceiveService.put(param); |
||||
|
return response; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package com.bw.ai.emotionextract.service; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 情感抽取执行接口 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月16日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public interface EmotionExtractService { |
||||
|
|
||||
|
/** |
||||
|
* 情感抽取 |
||||
|
* @param task |
||||
|
*/ |
||||
|
public void emotionExtract(Map<String, Object> task); |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
package com.bw.ai.emotionextract.service; |
||||
|
|
||||
|
/**\ |
||||
|
* 情感抽取服务层接口 |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月16日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public interface EmotionTaskReceiveService { |
||||
|
|
||||
|
public String put(String param); |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
package com.bw.ai.emotionextract.service.impl; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bw.ai.emotionextract.service.EmotionExtractService; |
||||
|
import com.bw.ai.entity.AppResultDoc; |
||||
|
import com.bw.ai.entity.Constants; |
||||
|
import com.bw.ai.utils.DownLoadUtil; |
||||
|
import com.bw.ai.utils.FileUtil; |
||||
|
import com.bw.ai.utils.GPTResultParseUtil; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
@RefreshScope |
||||
|
public class EmotionExtractServiceImpl implements EmotionExtractService { |
||||
|
|
||||
|
@Value("${api.module-url}") |
||||
|
private String moduleUrl; |
||||
|
|
||||
|
@Value("${api.param.model}") |
||||
|
private String model; |
||||
|
@Value("${api.param.max-tokens}") |
||||
|
private Integer maxTokens; |
||||
|
|
||||
|
@Value("${api.save-url}") |
||||
|
private String saveUrl; |
||||
|
|
||||
|
@Value("${prompt.emotion.path}") |
||||
|
private String emotionPrompt; |
||||
|
|
||||
|
@Value("${prompt.mood.path}") |
||||
|
private String moodPrompt; |
||||
|
|
||||
|
@Value("${prompt.opinion.path}") |
||||
|
private String opinionPrompt; |
||||
|
|
||||
|
@Value("${api.param.authorization}") |
||||
|
private String authorization; |
||||
|
|
||||
|
@Value("${file.path-prefix}") |
||||
|
private String downloadFilePathPrefix; |
||||
|
|
||||
|
@Override |
||||
|
public void emotionExtract(Map<String, Object> task) { |
||||
|
|
||||
|
// TODO Auto-generated method stub |
||||
|
try { |
||||
|
int referenceType = (int) task.get(Constants.REFERENCE_TYPE); |
||||
|
int emotionType = (int) task.get(Constants.EMOTION_TYPE); |
||||
|
//获取参考内容 |
||||
|
String reference = null; |
||||
|
if(referenceType == 1) { |
||||
|
/****referenceType==1 文本输入类型************/ |
||||
|
reference = (String) task.get(Constants.REFERENCE); |
||||
|
}else { |
||||
|
/****referenceType==2 文件上传类型************/ |
||||
|
reference = (String) task.get(Constants.REFERENCE); |
||||
|
//下载文件 |
||||
|
String format = reference.replaceAll(".*\\.", Constants.EMPTY); |
||||
|
String fileName = UUID.randomUUID().toString() + "." + format; |
||||
|
String downloadFilePath = downloadFilePathPrefix + fileName; |
||||
|
DownLoadUtil.downloadFile(reference, downloadFilePath); |
||||
|
//删除文件 |
||||
|
FileUtil.delFile(downloadFilePath); |
||||
|
reference = FileUtil.readFile(downloadFilePath); |
||||
|
} |
||||
|
//获取 |
||||
|
String question = null; |
||||
|
if(emotionType == 1) { |
||||
|
/******type==1 情感分析类型*************/ |
||||
|
question = FileUtil.readPrompt(emotionPrompt).replace(Constants.PRE_CONTENT_REP,reference) ; |
||||
|
}else if(emotionType == 2) { |
||||
|
/******type==2 情绪识别类型*************/ |
||||
|
question = FileUtil.readPrompt(moodPrompt).replace(Constants.PRE_CONTENT_REP,reference) ; |
||||
|
}else { |
||||
|
/******type==3 意见提取类型*************/ |
||||
|
question = FileUtil.readPrompt(opinionPrompt).replace(Constants.PRE_CONTENT_REP,reference) ; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
//获取抽取子字段and prompt |
||||
|
Map<String, Object> res = modelRequest(question); |
||||
|
if(res.containsKey(Constants.CHOICES)) { |
||||
|
List<Map<String, Object>> choices = (List<Map<String, Object>>) res.get(Constants.CHOICES); |
||||
|
Map<String, Object> resMessage = (Map<String, Object>) choices.get(0).get(Constants.MESSAGE); |
||||
|
String modelContent = (String) resMessage.get(Constants.CONTENT); |
||||
|
//解析结果 |
||||
|
Object parseGptRes = GPTResultParseUtil.parse(modelContent); |
||||
|
//组装页面结果 |
||||
|
Map<String, Object> result = new HashMap<String, Object>(16); |
||||
|
result.put(Constants.EMOTION_TYPE, emotionType); |
||||
|
result.put(Constants.EMOTION_RESULT, parseGptRes); |
||||
|
//成功 发送结果 |
||||
|
AppResultDoc entity = new AppResultDoc(); |
||||
|
entity.setTaskId((String)task.get(Constants.TASKID)); |
||||
|
entity.setAppId((Integer)task.get(Constants.ID)); |
||||
|
long now = System.currentTimeMillis(); |
||||
|
entity.setCreateTime(now); |
||||
|
entity.setResult(result); |
||||
|
entity.setStatus(1); |
||||
|
entity.setDel(0); |
||||
|
//回传给api服务保存 |
||||
|
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity)); |
||||
|
}else { |
||||
|
throw new Exception("请求结果解析失败"); |
||||
|
} |
||||
|
} catch (Throwable e) { |
||||
|
log.error("情感抽取任务异常。e:",e); |
||||
|
//失败直接发送结果 |
||||
|
AppResultDoc entity = new AppResultDoc(); |
||||
|
entity.setTaskId((String)task.get(Constants.TASKID)); |
||||
|
entity.setAppId((Integer)task.get(Constants.ID)); |
||||
|
long now = System.currentTimeMillis(); |
||||
|
entity.setCreateTime(now); |
||||
|
Map<String, Object> result = new HashMap<String, Object>(16); |
||||
|
result.put(Constants.ERROR, "文本抽取异常"); |
||||
|
entity.setResult(result); |
||||
|
entity.setStatus(2); |
||||
|
entity.setDel(0); |
||||
|
//回传给api服务保存 |
||||
|
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
private Map<String, Object> modelRequest(String question){ |
||||
|
Map<String, Object> headers = new HashMap<String, Object>(16); |
||||
|
|
||||
|
headers.put(Constants.AUTHORIZATION, authorization); |
||||
|
headers.put("Content-Type", "application/json"); |
||||
|
Map<String, Object> param = new HashMap<String, Object>(16); |
||||
|
param.put(Constants.MODEL, model); |
||||
|
param.put(Constants.MAX_TOKENS, maxTokens); |
||||
|
|
||||
|
List<Map<String, Object>> messages = new ArrayList<Map<String, Object>>(); |
||||
|
Map<String, Object> message = new HashMap<String, Object>(16); |
||||
|
message.put(Constants.ROLE, Constants.USER); |
||||
|
message.put(Constants.CONTENT, question); |
||||
|
messages.add(message); |
||||
|
param.put(Constants.MESSAGES, messages); |
||||
|
log.info("请求体:{}",param); |
||||
|
String modelResStr = DownLoadUtil.doPost(moduleUrl, JSONObject.toJSONString(param),headers); |
||||
|
log.info("模型请求结果:{}",modelResStr); |
||||
|
JSONObject res = JSONObject.parseObject(modelResStr); |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
package com.bw.ai.emotionextract.service.impl; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bw.ai.cache.ConfigCache; |
||||
|
import com.bw.ai.emotionextract.service.EmotionTaskReceiveService; |
||||
|
import com.bw.ai.entity.Constants; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
|
||||
|
public class EmotionTaskReceiveServiceImpl implements EmotionTaskReceiveService { |
||||
|
|
||||
|
@Override |
||||
|
public String put(String param) { |
||||
|
Map<String, Object> response = new HashMap<>(16); |
||||
|
int code = 200; |
||||
|
String message = "success"; |
||||
|
Map<String, Object> task = null; |
||||
|
try { |
||||
|
task = JSONObject.parseObject(param); |
||||
|
} catch (Exception e) { |
||||
|
log.error("参数结构不合法,", e); |
||||
|
code = 100010; |
||||
|
message = "参数不合法"; |
||||
|
} |
||||
|
// 写入队列 |
||||
|
try { |
||||
|
if(task.containsKey(Constants.TRACE) && (boolean)task.get(Constants.TRACE)){ |
||||
|
ConfigCache.emotionExtractTaskQueue.putFirst(task); |
||||
|
}else{ |
||||
|
ConfigCache.emotionExtractTaskQueue.put(task); |
||||
|
} |
||||
|
} catch (InterruptedException e) { |
||||
|
log.error("情感抽取任务写入队列异常,", e); |
||||
|
code = 100011; |
||||
|
message = "情感抽取任务写入队列失败"; |
||||
|
} |
||||
|
response.put(Constants.CODE, code); |
||||
|
response.put(Constants.MESSAGE, message); |
||||
|
return JSONObject.toJSONString(response); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
package com.bw.ai.entity; |
||||
|
|
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* ES 索引:opai_app_result |
||||
|
* 应用执行结果文档 |
||||
|
* |
||||
|
* @author jian.mao |
||||
|
*/ |
||||
|
@Data |
||||
|
public class AppResultDoc implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
/** 任务ID */ |
||||
|
private String taskId; |
||||
|
|
||||
|
/** 应用id */ |
||||
|
private Integer appId; |
||||
|
|
||||
|
/** 状态 0 进行中,1成功,2失败 */ |
||||
|
private Integer status; |
||||
|
|
||||
|
/** 创建时间(毫秒时间戳) */ |
||||
|
private Long createTime; |
||||
|
|
||||
|
/** 执行结果(可索引) */ |
||||
|
private Map<String, Object> result; |
||||
|
|
||||
|
/** 逻辑删除标识:0-未删除 1-已删除 */ |
||||
|
private Integer del; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,110 @@ |
|||||
|
package com.bw.ai.entity; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 常量实体类 |
||||
|
* @author jian.mao |
||||
|
* @date 2022年11月15日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public class Constants { |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 空字符串常量 |
||||
|
*/ |
||||
|
public static final String EMPTY = ""; |
||||
|
|
||||
|
/************************应用参数*************************************/ |
||||
|
public static final String CODE = "code"; |
||||
|
public static final String ID = "id"; |
||||
|
public static final String MESSAGE = "message"; |
||||
|
/******************************api使用*******************************/ |
||||
|
public static final String CONTENT = "content"; |
||||
|
public static final String ERROR = "error"; |
||||
|
public static final String TRACE = "trace"; |
||||
|
|
||||
|
/** |
||||
|
* 任务id |
||||
|
*/ |
||||
|
public static final String TASKID = "taskId"; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 模型参数 model |
||||
|
*/ |
||||
|
public static final String MODEL = "model"; |
||||
|
|
||||
|
/** |
||||
|
* 模型参数 max_tokens |
||||
|
*/ |
||||
|
public static final String MAX_TOKENS = "max_tokens"; |
||||
|
|
||||
|
/** |
||||
|
* 模型参数 messages |
||||
|
*/ |
||||
|
public static final String MESSAGES = "messages"; |
||||
|
|
||||
|
/** |
||||
|
* 模型参数 user |
||||
|
*/ |
||||
|
public static final String USER = "user"; |
||||
|
|
||||
|
/** |
||||
|
* 模型参数 role |
||||
|
*/ |
||||
|
public static final String ROLE = "role"; |
||||
|
|
||||
|
/** |
||||
|
* 模型返回结果参数 choices |
||||
|
*/ |
||||
|
public static final String CHOICES = "choices"; |
||||
|
|
||||
|
/** |
||||
|
* 模型认证参数 |
||||
|
*/ |
||||
|
public static final String AUTHORIZATION = "authorization"; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 参考资料类型 |
||||
|
*/ |
||||
|
public static final String REFERENCE_TYPE = "referenceType"; |
||||
|
|
||||
|
/** |
||||
|
* 情感抽取类型 |
||||
|
*/ |
||||
|
public static final String EMOTION_TYPE = "emotionType"; |
||||
|
|
||||
|
/** |
||||
|
* 参考资料 |
||||
|
*/ |
||||
|
public static final String REFERENCE = "reference"; |
||||
|
|
||||
|
/** |
||||
|
* 文本抽取规则 key |
||||
|
*/ |
||||
|
public static final String PRE_RULES_REP = "{{rules}}"; |
||||
|
|
||||
|
/** |
||||
|
* 文本抽取 参考资料 key |
||||
|
*/ |
||||
|
public static final String PRE_CONTENT_REP = "{{content}}"; |
||||
|
|
||||
|
/** |
||||
|
* 字段key |
||||
|
*/ |
||||
|
public static final String FIELD = "field"; |
||||
|
|
||||
|
/** |
||||
|
* 抽取字段 key |
||||
|
*/ |
||||
|
public static final String EXTRACT_FIELDS = "extractFields"; |
||||
|
|
||||
|
/** |
||||
|
* 情感抽取结果 |
||||
|
*/ |
||||
|
public static final String EMOTION_RESULT = "emotionResult"; |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,219 @@ |
|||||
|
package com.bw.ai.handler; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.IOException; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.LinkedBlockingDeque; |
||||
|
import java.util.concurrent.LinkedBlockingQueue; |
||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
import org.apache.commons.io.FileUtils; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.boot.ApplicationArguments; |
||||
|
import org.springframework.boot.ApplicationRunner; |
||||
|
import org.springframework.core.annotation.Order; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bw.ai.cache.ConfigCache; |
||||
|
import com.bw.ai.emotionextract.service.EmotionExtractService; |
||||
|
import com.bw.ai.textextract.service.TextExtractService; |
||||
|
import com.bw.ai.utils.FileUtil; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @author jian.mao |
||||
|
* @date 2025年1月13日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@Component |
||||
|
@Order(value = 1) |
||||
|
@Slf4j |
||||
|
public class MainHandler implements ApplicationRunner { |
||||
|
|
||||
|
@Value("${task.textextract.task-queue-path}") |
||||
|
private String textExtractTaskPath; |
||||
|
|
||||
|
@Value("${task.emotionextract.task-queue-path}") |
||||
|
private String emotionExtractTaskPath; |
||||
|
@Autowired |
||||
|
private TextExtractService textExtractService; |
||||
|
@Autowired |
||||
|
private EmotionExtractService emotionExtractService; |
||||
|
/***线程池参数***/ |
||||
|
@Value("${threadPool.corePoolSize}") |
||||
|
private int corePoolSize; |
||||
|
@Value("${threadPool.maximumPoolSize}") |
||||
|
private int maximumPoolSize; |
||||
|
@Value("${threadPool.keepAliveTime}") |
||||
|
private long keepAliveTime; |
||||
|
@Value("${threadPool.queueSize}") |
||||
|
private int queueSize; |
||||
|
|
||||
|
/** |
||||
|
*执行入口 |
||||
|
*/ |
||||
|
@Override |
||||
|
public void run(ApplicationArguments args) throws Exception { |
||||
|
|
||||
|
/*********************************************文本抽取********************************************************/ |
||||
|
//线程池方式 |
||||
|
ThreadPoolExecutor executor = new ThreadPoolExecutor( |
||||
|
corePoolSize, |
||||
|
maximumPoolSize, |
||||
|
keepAliveTime, |
||||
|
TimeUnit.SECONDS, |
||||
|
new LinkedBlockingQueue<>(queueSize), |
||||
|
new ThreadPoolExecutor.CallerRunsPolicy() |
||||
|
); |
||||
|
//文本抽取任务消费者 |
||||
|
Thread textConsumerThread = new Thread(() -> { |
||||
|
while (ConfigCache.isStart) { |
||||
|
try { |
||||
|
// 从队列中获取任务 |
||||
|
Map<String, Object> task = ConfigCache.textExtractTaskQueue.take(); |
||||
|
// 提交给线程池执行 |
||||
|
executor.execute(() -> textExtract(task)); |
||||
|
} catch (InterruptedException e) { |
||||
|
// 恢复中断状态 |
||||
|
Thread.currentThread().interrupt(); |
||||
|
log.error("文本抽取任务消费线程被中断"); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
textConsumerThread.start(); |
||||
|
log.info("文本抽取任务任务消费线程启动-----"); |
||||
|
|
||||
|
/********************************************情感抽取********************************************************/ |
||||
|
//情感抽取线程池方式 |
||||
|
ThreadPoolExecutor emotionExecutor = new ThreadPoolExecutor( |
||||
|
corePoolSize, |
||||
|
maximumPoolSize, |
||||
|
keepAliveTime, |
||||
|
TimeUnit.SECONDS, |
||||
|
new LinkedBlockingQueue<>(queueSize), |
||||
|
new ThreadPoolExecutor.CallerRunsPolicy() |
||||
|
); |
||||
|
//情感抽取任务消费者 |
||||
|
Thread emotionConsumerThread = new Thread(() -> { |
||||
|
while (ConfigCache.isStart) { |
||||
|
try { |
||||
|
// 从队列中获取任务 |
||||
|
Map<String, Object> task = ConfigCache.emotionExtractTaskQueue.take(); |
||||
|
// 提交给线程池执行 |
||||
|
emotionExecutor.execute(() -> emotionExtract(task)); |
||||
|
} catch (InterruptedException e) { |
||||
|
// 恢复中断状态 |
||||
|
Thread.currentThread().interrupt(); |
||||
|
log.error("情感抽取任务消费线程被中断"); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
emotionConsumerThread.start(); |
||||
|
log.info("情感抽取任务任务消费线程启动-----"); |
||||
|
|
||||
|
//启动加载缓存任务 |
||||
|
readTask(textExtractTaskPath, ConfigCache.textExtractTaskQueue); |
||||
|
readTask(emotionExtractTaskPath, ConfigCache.emotionExtractTaskQueue); |
||||
|
//停止处理 |
||||
|
waitDown(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建任务执行方法 |
||||
|
* @param task |
||||
|
*/ |
||||
|
private void textExtract(Map<String, Object> task) { |
||||
|
textExtractService.extract(task); |
||||
|
} |
||||
|
|
||||
|
private void emotionExtract(Map<String, Object> task) { |
||||
|
emotionExtractService.emotionExtract(task); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/****************************************************************load******************************************************************************/ |
||||
|
/** |
||||
|
* 加载文件中的任务 |
||||
|
* @param path 文件地址 |
||||
|
* @param queue 队列 |
||||
|
*/ |
||||
|
@SuppressWarnings("unchecked") |
||||
|
public static void readTask(String path, LinkedBlockingDeque<Map<String, Object>> queue) { |
||||
|
File file = new File(path); |
||||
|
if (file.exists()) { |
||||
|
List<String> tasks = null; |
||||
|
try { |
||||
|
tasks = FileUtils.readLines(file, "UTF-8"); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
for (String taskStr : tasks) { |
||||
|
Map<String, Object> task = JSONObject.parseObject(taskStr); |
||||
|
try { |
||||
|
queue.put(task); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
file.delete(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/*******************************************************************stop************************************************************************/ |
||||
|
|
||||
|
/** |
||||
|
* 结束触发钩子 |
||||
|
*/ |
||||
|
public void waitDown() { |
||||
|
Runtime.getRuntime().addShutdownHook(new Thread() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
// 停止线程 |
||||
|
ConfigCache.isStart = false; |
||||
|
log.info("stop-------"); |
||||
|
writeTsskToFile(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 任务持久化到硬盘 |
||||
|
*/ |
||||
|
public void writeTsskToFile() { |
||||
|
while (true) { |
||||
|
if (ConfigCache.textExtractTaskQueue.size() > 0) { |
||||
|
try { |
||||
|
Map<String, Object> task = ConfigCache.textExtractTaskQueue.take(); |
||||
|
FileUtil.writeFile(textExtractTaskPath, JSONObject.toJSONString(task)); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} else { |
||||
|
log.info("taskQueue write is file end"); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
while (true) { |
||||
|
if (ConfigCache.emotionExtractTaskQueue.size() > 0) { |
||||
|
try { |
||||
|
Map<String, Object> task = ConfigCache.emotionExtractTaskQueue.take(); |
||||
|
FileUtil.writeFile(emotionExtractTaskPath, JSONObject.toJSONString(task)); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} else { |
||||
|
log.info("taskQueue write is file end"); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
package com.bw.ai.textextract.controller; |
||||
|
|
||||
|
import javax.annotation.Resource; |
||||
|
|
||||
|
import org.springframework.stereotype.Controller; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
|
||||
|
import com.bw.ai.textextract.service.TextTaskReceiveService; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
/** |
||||
|
* 文本抽取 任务接收控制层 |
||||
|
* @author jian.mao |
||||
|
* @date 2025年1月14日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@Controller |
||||
|
@RequestMapping("/task") |
||||
|
@Slf4j |
||||
|
public class TextTaskReceiveController { |
||||
|
@Resource |
||||
|
private TextTaskReceiveService textTaskReceiveService; |
||||
|
@PostMapping("/put") |
||||
|
@ResponseBody |
||||
|
public String put(@RequestBody String param){ |
||||
|
String response = textTaskReceiveService.put(param); |
||||
|
return response; |
||||
|
} |
||||
|
@RequestMapping(value = "/hello", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public String hello(String param, String token) { |
||||
|
return "123"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.bw.ai.textextract.service; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 抽取服务层接口 |
||||
|
* @author jian.mao |
||||
|
* @date 2025年2月18日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public interface TextExtractService { |
||||
|
|
||||
|
/** |
||||
|
* 抽取远端执行方法 |
||||
|
* @param task |
||||
|
*/ |
||||
|
public void extract(Map<String, Object> task); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
package com.bw.ai.textextract.service; |
||||
|
|
||||
|
/** |
||||
|
* 任务接收服务层 |
||||
|
* @author jian.mao |
||||
|
* @date 2025年1月14日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public interface TextTaskReceiveService { |
||||
|
|
||||
|
/** |
||||
|
* 任务新增 |
||||
|
* @param dataJson |
||||
|
* @return |
||||
|
*/ |
||||
|
public String put(String dataJson); |
||||
|
} |
||||
@ -0,0 +1,139 @@ |
|||||
|
package com.bw.ai.textextract.service.impl; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bw.ai.entity.AppResultDoc; |
||||
|
import com.bw.ai.entity.Constants; |
||||
|
import com.bw.ai.textextract.service.TextExtractService; |
||||
|
import com.bw.ai.utils.DownLoadUtil; |
||||
|
import com.bw.ai.utils.FileUtil; |
||||
|
import com.bw.ai.utils.GPTResultParseUtil; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
@RefreshScope |
||||
|
public class TextExtractServiceImpl implements TextExtractService { |
||||
|
|
||||
|
@Value("${api.module-url}") |
||||
|
private String moduleUrl; |
||||
|
|
||||
|
@Value("${api.param.model}") |
||||
|
private String model; |
||||
|
@Value("${api.param.max-tokens}") |
||||
|
private Integer maxTokens; |
||||
|
|
||||
|
@Value("${api.save-url}") |
||||
|
private String saveUrl; |
||||
|
|
||||
|
@Value("${prompt.path}") |
||||
|
private String promptPath; |
||||
|
|
||||
|
@Value("${api.param.authorization}") |
||||
|
private String authorization; |
||||
|
|
||||
|
@Value("${file.path-prefix}") |
||||
|
private String downloadFilePathPrefix; |
||||
|
|
||||
|
@Override |
||||
|
public void extract(Map<String, Object> task) { |
||||
|
// TODO Auto-generated method stub |
||||
|
try { |
||||
|
int referenceType = (int) task.get(Constants.REFERENCE_TYPE); |
||||
|
//获取参考内容 |
||||
|
String reference = null; |
||||
|
if(referenceType == 1) { |
||||
|
/****referenceType==1 文本输入类型************/ |
||||
|
reference = (String) task.get(Constants.REFERENCE); |
||||
|
}else { |
||||
|
/****referenceType==2 文件上传类型************/ |
||||
|
reference = (String) task.get(Constants.REFERENCE); |
||||
|
//下载文件 |
||||
|
String format = reference.replaceAll(".*\\.", Constants.EMPTY); |
||||
|
String fileName = UUID.randomUUID().toString() + "." + format; |
||||
|
String downloadFilePath = downloadFilePathPrefix + fileName; |
||||
|
DownLoadUtil.downloadFile(reference, downloadFilePath); |
||||
|
//删除文件 |
||||
|
FileUtil.delFile(downloadFilePath); |
||||
|
reference = FileUtil.readFile(downloadFilePath); |
||||
|
} |
||||
|
//获取抽取子字段and prompt |
||||
|
List<Map<String, Object>> extractFields = (List<Map<String, Object>>) task.get(Constants.EXTRACT_FIELDS); |
||||
|
String question = FileUtil.readPrompt(promptPath).replace(Constants.PRE_RULES_REP, JSONObject.toJSONString(extractFields)).replace(Constants.PRE_CONTENT_REP,reference) ; |
||||
|
Map<String, Object> res = modelRequest(question); |
||||
|
if(res.containsKey(Constants.CHOICES)) { |
||||
|
List<Map<String, Object>> choices = (List<Map<String, Object>>) res.get(Constants.CHOICES); |
||||
|
Map<String, Object> resMessage = (Map<String, Object>) choices.get(0).get(Constants.MESSAGE); |
||||
|
String modelContent = (String) resMessage.get(Constants.CONTENT); |
||||
|
//获取返回字段 |
||||
|
Map<String, Object> fields = new HashMap<String, Object>(16); |
||||
|
for (Map<String, Object> map : extractFields) { |
||||
|
fields.put((String)map.get(Constants.FIELD), map.get(Constants.FIELD)); |
||||
|
} |
||||
|
//解析结果 |
||||
|
Map<String, Object> parseGptRes = GPTResultParseUtil.parseGPTResult(fields, modelContent); |
||||
|
//成功 发送结果 |
||||
|
AppResultDoc entity = new AppResultDoc(); |
||||
|
entity.setTaskId((String)task.get(Constants.TASKID)); |
||||
|
entity.setAppId((Integer)task.get(Constants.ID)); |
||||
|
long now = System.currentTimeMillis(); |
||||
|
entity.setCreateTime(now); |
||||
|
entity.setResult(parseGptRes); |
||||
|
entity.setStatus(1); |
||||
|
entity.setDel(0); |
||||
|
//回传给api服务保存 |
||||
|
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity)); |
||||
|
}else { |
||||
|
throw new Exception("请求结果解析失败"); |
||||
|
} |
||||
|
} catch (Throwable e) { |
||||
|
log.error("文本抽取任务异常。e:",e); |
||||
|
//失败直接发送结果 |
||||
|
AppResultDoc entity = new AppResultDoc(); |
||||
|
entity.setTaskId((String)task.get(Constants.TASKID)); |
||||
|
entity.setAppId((Integer)task.get(Constants.ID)); |
||||
|
long now = System.currentTimeMillis(); |
||||
|
entity.setCreateTime(now); |
||||
|
Map<String, Object> result = new HashMap<String, Object>(16); |
||||
|
result.put(Constants.ERROR, "文本抽取异常"); |
||||
|
entity.setResult(result); |
||||
|
entity.setStatus(2); |
||||
|
entity.setDel(0); |
||||
|
//回传给api服务保存 |
||||
|
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
private Map<String, Object> modelRequest(String question){ |
||||
|
Map<String, Object> headers = new HashMap<String, Object>(16); |
||||
|
|
||||
|
headers.put(Constants.AUTHORIZATION, authorization); |
||||
|
headers.put("Content-Type", "application/json"); |
||||
|
Map<String, Object> param = new HashMap<String, Object>(16); |
||||
|
param.put(Constants.MODEL, model); |
||||
|
param.put(Constants.MAX_TOKENS, maxTokens); |
||||
|
|
||||
|
List<Map<String, Object>> messages = new ArrayList<Map<String, Object>>(); |
||||
|
Map<String, Object> message = new HashMap<String, Object>(16); |
||||
|
message.put(Constants.ROLE, Constants.USER); |
||||
|
message.put(Constants.CONTENT, question); |
||||
|
messages.add(message); |
||||
|
param.put(Constants.MESSAGES, messages); |
||||
|
log.info("请求体:{}",param); |
||||
|
String modelResStr = DownLoadUtil.doPost(moduleUrl, JSONObject.toJSONString(param),headers); |
||||
|
log.info("模型请求结果:{}",modelResStr); |
||||
|
JSONObject res = JSONObject.parseObject(modelResStr); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
package com.bw.ai.textextract.service.impl; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bw.ai.cache.ConfigCache; |
||||
|
import com.bw.ai.entity.Constants; |
||||
|
import com.bw.ai.textextract.service.TextTaskReceiveService; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
/** |
||||
|
* 任务接收服务层实现类 |
||||
|
* @author jian.mao |
||||
|
* @date 2025年1月14日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class TextTaskReceiveServiceImpl implements TextTaskReceiveService { |
||||
|
|
||||
|
@Override |
||||
|
public String put(String dataJson) { |
||||
|
Map<String, Object> response = new HashMap<>(16); |
||||
|
int code = 200; |
||||
|
String message = "success"; |
||||
|
Map<String, Object> task = null; |
||||
|
try { |
||||
|
task = JSONObject.parseObject(dataJson); |
||||
|
} catch (Exception e) { |
||||
|
log.error("参数结构不合法,", e); |
||||
|
code = 100010; |
||||
|
message = "参数不合法"; |
||||
|
} |
||||
|
// 写入队列 |
||||
|
try { |
||||
|
if(task.containsKey(Constants.TRACE) && (boolean)task.get(Constants.TRACE)){ |
||||
|
ConfigCache.textExtractTaskQueue.putFirst(task); |
||||
|
}else{ |
||||
|
ConfigCache.textExtractTaskQueue.put(task); |
||||
|
} |
||||
|
} catch (InterruptedException e) { |
||||
|
log.error("文本抽取任务写入队列异常,", e); |
||||
|
code = 100011; |
||||
|
message = "文本抽取任务写入队列失败"; |
||||
|
} |
||||
|
response.put(Constants.CODE, code); |
||||
|
response.put(Constants.MESSAGE, message); |
||||
|
return JSONObject.toJSONString(response); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.alibaba.fastjson.JSONPath; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author:jinming |
||||
|
* @className:DataUtil |
||||
|
* @version:1.0 |
||||
|
* @description: 获取dataValue的值 |
||||
|
* @Date:2023/11/1 9:54 |
||||
|
*/ |
||||
|
public class DataUtil { |
||||
|
/** |
||||
|
* |
||||
|
* @param key 传入的key |
||||
|
* @param dataMap 数据map |
||||
|
* @return 根据传入的参数进行判断解析,返回正确的dataValue |
||||
|
*/ |
||||
|
public static Object getValue(String key, Map dataMap) { |
||||
|
Object dataValue; |
||||
|
String isJson = "#json#"; |
||||
|
if (key.contains(isJson)) { |
||||
|
//进行第一次拆分,获取#json#前面的部分 |
||||
|
String[] keySplit = key.split(isJson); |
||||
|
String firstDataKey = keySplit[0]; |
||||
|
String[] firstDataKeySplit = firstDataKey.split(":"); |
||||
|
//取出前半部分对应的JSON数据并转换为JSONObject |
||||
|
String dataJson = (String) dataMap.get(firstDataKeySplit[0]); |
||||
|
JSONObject dataJsonObject = JSON.parseObject(dataJson); |
||||
|
//根据key的后半部分取出对应JSONObject中的值 |
||||
|
String firstDataKeyJson = (String) JSONPath.eval(dataJsonObject, firstDataKeySplit[1]); |
||||
|
String secDataKey = keySplit[1]; |
||||
|
JSONObject firstDataJsonObject = JSON.parseObject(firstDataKeyJson); |
||||
|
dataValue = JSONPath.eval(firstDataJsonObject, secDataKey); |
||||
|
return dataValue; |
||||
|
} |
||||
|
String[] keySplit = key.split(":"); |
||||
|
String jsonPath = keySplit[1]; |
||||
|
String dataJson = (String) dataMap.get(keySplit[0]); |
||||
|
JSONObject dataJsonObject = JSON.parseObject(dataJson); |
||||
|
dataValue = JSONPath.eval(dataJsonObject, jsonPath); |
||||
|
return dataValue; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,177 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
|
||||
|
import java.math.BigInteger; |
||||
|
import java.security.MessageDigest; |
||||
|
import java.security.NoSuchAlgorithmException; |
||||
|
import java.text.ParseException; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.time.LocalDateTime; |
||||
|
import java.time.format.DateTimeFormatter; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
|
||||
|
/** |
||||
|
* 日期工具类 |
||||
|
* |
||||
|
* @author jian.mao |
||||
|
* @date 2022年11月15日 |
||||
|
* @description |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class DateUtil { |
||||
|
|
||||
|
/** |
||||
|
* @return |
||||
|
*/ |
||||
|
public static String getTimeStrForNow() { |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH"); |
||||
|
return sdf.format(new Date()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static String getTimeStrForDay(long time) { |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); |
||||
|
|
||||
|
return sdf.format(new Date(time * 1000)); |
||||
|
} |
||||
|
|
||||
|
public static String getTimeStrForDay() { |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); |
||||
|
|
||||
|
return sdf.format(new Date()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static String getDateTime() { |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||
|
String time = sdf.format(new Date()); |
||||
|
return time; |
||||
|
} |
||||
|
|
||||
|
public static String getDateTime(Long timestap) { |
||||
|
|
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||
|
String time = sdf.format(new Date(timestap)); |
||||
|
return time; |
||||
|
} |
||||
|
|
||||
|
public static String getDate(Long timestap) { |
||||
|
|
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
||||
|
String time = sdf.format(new Date(timestap)); |
||||
|
return time; |
||||
|
} |
||||
|
|
||||
|
public static String getDateTimeForMonth() { |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM"); |
||||
|
String time = sdf.format(new Date()); |
||||
|
return time; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 休眠 |
||||
|
* |
||||
|
* @param millis 毫秒 |
||||
|
*/ |
||||
|
public static void sleep(long millis) { |
||||
|
try { |
||||
|
Thread.sleep(millis); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 1. @Description:时间戳转时间 |
||||
|
* 2. @Author: ying.zhao |
||||
|
* 3. @Date: 2023/3/28 |
||||
|
*/ |
||||
|
|
||||
|
public static String timestampToDate(String time) { |
||||
|
int thirteen = 13; |
||||
|
int ten = 10; |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||
|
// if (time.length() == thirteen) { |
||||
|
if (time.length() > ten) { |
||||
|
return sdf.format(new Date(Long.parseLong(time))); |
||||
|
} else { |
||||
|
return sdf.format(new Date(Integer.parseInt(time) * 1000L)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static String parseCreated(String jsonTime){ |
||||
|
String formattedDateTime = getDateTime(); |
||||
|
try { |
||||
|
// 使用fastjson解析JSON数据 |
||||
|
JSONObject jsonObject = JSON.parseObject(jsonTime); |
||||
|
// 获取日期和时间的值 |
||||
|
JSONObject dateObject = jsonObject.getJSONObject("date"); |
||||
|
int day = dateObject.getIntValue("day"); |
||||
|
int month = dateObject.getIntValue("month"); |
||||
|
int year = dateObject.getIntValue("year"); |
||||
|
|
||||
|
JSONObject timeObject = jsonObject.getJSONObject("time"); |
||||
|
int hour = timeObject.getIntValue("hour"); |
||||
|
int minute = timeObject.getIntValue("minute"); |
||||
|
int second = timeObject.getIntValue("second"); |
||||
|
|
||||
|
// 创建LocalDateTime对象 |
||||
|
LocalDateTime dateTime = LocalDateTime.of(year, month, day, hour, minute, second); |
||||
|
|
||||
|
// 定义日期时间格式化器 |
||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
||||
|
|
||||
|
// 格式化日期时间 |
||||
|
formattedDateTime = dateTime.format(formatter); |
||||
|
} catch (Exception e) { |
||||
|
log.info("日期转换失败:{}",e); |
||||
|
} |
||||
|
return formattedDateTime; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 字符串转换日期 |
||||
|
* @param format |
||||
|
* @param date |
||||
|
* @return |
||||
|
*/ |
||||
|
public static Date strToDate(String format,String date){ |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat(format); |
||||
|
if (date == null || date.equals("")){ |
||||
|
return new Date(); |
||||
|
}else{ |
||||
|
Date ru = null; |
||||
|
try { |
||||
|
ru = sdf.parse(date); |
||||
|
} catch (ParseException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return ru; |
||||
|
} |
||||
|
} |
||||
|
/** |
||||
|
* 日期格式话 |
||||
|
* @param format 日期格式 |
||||
|
* @param dater 要转换的日期,默认当前时间 |
||||
|
* @return |
||||
|
*/ |
||||
|
public static String FormatDate(String format,Date date){ |
||||
|
String fromatDate = null; |
||||
|
SimpleDateFormat sdf = new SimpleDateFormat(format); |
||||
|
if (date == null){ |
||||
|
fromatDate = sdf.format(new Date()); |
||||
|
}else{ |
||||
|
fromatDate = sdf.format(date); |
||||
|
} |
||||
|
return fromatDate; |
||||
|
} |
||||
|
public static void main(String[] args) { |
||||
|
String time = timestampToDate("955814400000"); |
||||
|
System.out.println(time); |
||||
|
} |
||||
|
} |
||||
1004
ai-service/src/main/java/com/bw/ai/utils/DownLoadUtil.java
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,27 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import java.security.MessageDigest; |
||||
|
import java.security.NoSuchAlgorithmException; |
||||
|
|
||||
|
/** |
||||
|
* @author jian.mao |
||||
|
* @date 2023年3月10日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public class EncryptionUtil { |
||||
|
public static String md5(String text) { |
||||
|
try { |
||||
|
MessageDigest md = MessageDigest.getInstance("MD5"); |
||||
|
md.update(text.getBytes()); |
||||
|
byte[] bytes = md.digest(); |
||||
|
StringBuilder sb = new StringBuilder(); |
||||
|
for (byte b : bytes) { |
||||
|
sb.append(String.format("%02x", b & 0xff)); |
||||
|
} |
||||
|
return sb.toString(); |
||||
|
} catch (NoSuchAlgorithmException e) { |
||||
|
e.printStackTrace(); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileInputStream; |
||||
|
import java.io.FileWriter; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import org.apache.commons.io.FileUtils; |
||||
|
import org.apache.poi.hwpf.HWPFDocument; |
||||
|
import org.apache.poi.hwpf.extractor.WordExtractor; |
||||
|
import org.apache.poi.xwpf.extractor.XWPFWordExtractor; |
||||
|
import org.apache.poi.xwpf.usermodel.XWPFDocument; |
||||
|
|
||||
|
/** |
||||
|
* 文件工具类 |
||||
|
* @author jian.mao |
||||
|
* @date 2023年7月14日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public class FileUtil { |
||||
|
|
||||
|
/** |
||||
|
* 数据写入文件 |
||||
|
* @param Path 文件路径 |
||||
|
* @param result 数据 |
||||
|
* @throws IOException |
||||
|
*/ |
||||
|
public static void writeFile(String path,String result){ |
||||
|
try { |
||||
|
FileWriter fw = new FileWriter(path,true); |
||||
|
fw.write(result+"\n"); |
||||
|
fw.flush(); |
||||
|
fw.close(); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void delFile(String path) { |
||||
|
try { |
||||
|
File file = new File(path); |
||||
|
file.delete(); |
||||
|
} catch (Exception e) { |
||||
|
// TODO: handle exception |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 读取prompt |
||||
|
* @param path |
||||
|
* @return |
||||
|
*/ |
||||
|
public static String readPrompt(String path){ |
||||
|
StringBuffer prompt = new StringBuffer(); |
||||
|
File file = new File(path); |
||||
|
if(file.exists()){ |
||||
|
List<String> tasks = null; |
||||
|
try { |
||||
|
tasks = FileUtils.readLines(file,"UTF-8"); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
for (String taskStr : tasks) { |
||||
|
prompt.append(taskStr); |
||||
|
} |
||||
|
} |
||||
|
return prompt.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解析word、txt文档 |
||||
|
* @param filePath 文件路径 |
||||
|
* @return |
||||
|
* @throws IOException |
||||
|
*/ |
||||
|
public static String readFile(String filePath) throws IOException { |
||||
|
InputStream inputStream = new FileInputStream(filePath); |
||||
|
String fileTypeDoc = "doc"; |
||||
|
String fileTypeDocx = "docx"; |
||||
|
String fileTypetxt = "txt"; |
||||
|
if (filePath.endsWith(fileTypeDoc)) { |
||||
|
try (HWPFDocument document = new HWPFDocument(inputStream)) { |
||||
|
WordExtractor extractor = new WordExtractor(document); |
||||
|
return extractor.getText(); |
||||
|
} |
||||
|
} else if (filePath.endsWith(fileTypeDocx)) { |
||||
|
try (XWPFDocument document = new XWPFDocument(inputStream)) { |
||||
|
XWPFWordExtractor extractor = new XWPFWordExtractor(document); |
||||
|
return extractor.getText(); |
||||
|
} |
||||
|
} else if (filePath.endsWith(fileTypetxt)) { |
||||
|
StringBuffer sb = new StringBuffer(); |
||||
|
for (String line : FileUtils.readLines(new File(filePath))) { |
||||
|
sb.append(line); |
||||
|
} |
||||
|
return sb.toString(); |
||||
|
}else { |
||||
|
throw new IllegalArgumentException("Unsupported file format"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.alibaba.fastjson.JSONException; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.regex.Matcher; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
/** |
||||
|
* gpt解析工具类 |
||||
|
* |
||||
|
* @author jian.mao |
||||
|
* @date 2026年1月15日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public class GPTResultParseUtil { |
||||
|
public static Map<String, Object> parseGPTResult(Map<String, Object> output, String gptContent) { |
||||
|
Map<String, Object> jsonResult = new HashMap<>(); |
||||
|
try { |
||||
|
// 替换```json, ``` 和 \n |
||||
|
String jsonContent = gptContent.replace("```json", "").replace("```", "").replace("\n", ""); |
||||
|
JSONObject jsonGPT = JSON.parseObject(jsonContent); |
||||
|
if(output != null) { |
||||
|
for (String key : output.keySet()) { |
||||
|
if (jsonGPT.containsKey(key)) { |
||||
|
jsonResult.put(key, jsonGPT.get(key)); |
||||
|
} |
||||
|
} |
||||
|
}else { |
||||
|
return jsonGPT; |
||||
|
} |
||||
|
return jsonResult; |
||||
|
} catch (JSONException e) { |
||||
|
try { |
||||
|
// 直接解析失败,使用正则表达式匹配外层的 {} |
||||
|
Pattern pattern = Pattern.compile("\\{.*\\}", Pattern.DOTALL); |
||||
|
Matcher matcher = pattern.matcher(gptContent.replace("\n", "")); |
||||
|
if (matcher.find()) { |
||||
|
JSONObject jsonGPT = JSON.parseObject(matcher.group()); |
||||
|
if(output != null) { |
||||
|
for (String key : output.keySet()) { |
||||
|
if (jsonGPT.containsKey(key)) { |
||||
|
jsonResult.put(key, jsonGPT.get(key)); |
||||
|
} |
||||
|
} |
||||
|
}else { |
||||
|
return jsonGPT; |
||||
|
} |
||||
|
return jsonResult; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} catch (Exception ex) { |
||||
|
ex.printStackTrace(); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static Object parse(String gptContent) { |
||||
|
if (gptContent == null || gptContent.trim().isEmpty()) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// 1. 清理 GPT 常见包裹 |
||||
|
String content = gptContent.replace("```json", "").replace("```", "").trim(); |
||||
|
|
||||
|
// 2. 优先尝试直接解析(fastjson 自动识别对象 / 数组) |
||||
|
return JSON.parse(content); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
// 3. 兜底:尝试从文本中截取 JSON |
||||
|
try { |
||||
|
String cleaned = gptContent.replace("\n", "").trim(); |
||||
|
|
||||
|
// 先数组 |
||||
|
Pattern arrayPattern = Pattern.compile("\\[.*\\]", Pattern.DOTALL); |
||||
|
Matcher arrayMatcher = arrayPattern.matcher(cleaned); |
||||
|
if (arrayMatcher.find()) { |
||||
|
return JSON.parseArray(arrayMatcher.group()); |
||||
|
} |
||||
|
|
||||
|
// 再对象 |
||||
|
Pattern objectPattern = Pattern.compile("\\{.*\\}", Pattern.DOTALL); |
||||
|
Matcher objectMatcher = objectPattern.matcher(cleaned); |
||||
|
if (objectMatcher.find()) { |
||||
|
return JSON.parseObject(objectMatcher.group()); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception ignored) { |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import java.security.MessageDigest; |
||||
|
|
||||
|
/** |
||||
|
* 其他工具类 |
||||
|
* @author jian.mao |
||||
|
* @date 2023年9月19日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public class OtherUtils { |
||||
|
|
||||
|
|
||||
|
|
||||
|
public static String getMd5(String string) { |
||||
|
try { |
||||
|
MessageDigest md5 = MessageDigest.getInstance("MD5"); |
||||
|
byte[] bs = md5.digest(string.getBytes("UTF-8")); |
||||
|
StringBuilder sb = new StringBuilder(40); |
||||
|
for (byte x : bs) { |
||||
|
if ((x & 0xff) >> 4 == 0) { |
||||
|
sb.append("0").append(Integer.toHexString(x & 0xff)); |
||||
|
} else { |
||||
|
sb.append(Integer.toHexString(x & 0xff)); |
||||
|
} |
||||
|
} |
||||
|
return sb.toString(); |
||||
|
} catch (Exception e) { |
||||
|
|
||||
|
return "nceaform" + System.currentTimeMillis(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.LinkedBlockingDeque; |
||||
|
|
||||
|
/** |
||||
|
* @author:jinming |
||||
|
* @className:QueueUtil |
||||
|
* @version:1.0 |
||||
|
* @description: |
||||
|
* @Date:2023/7/13 15:00 |
||||
|
*/ |
||||
|
public class QueueUtil { |
||||
|
|
||||
|
public static LinkedBlockingDeque<Map<String, Object>> taskQueue = new LinkedBlockingDeque<Map<String, Object>>(); |
||||
|
|
||||
|
public static LinkedBlockingDeque<String> sendQueue = new LinkedBlockingDeque<String>(); |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.bw.ai.utils; |
||||
|
|
||||
|
import java.io.PrintWriter; |
||||
|
import java.io.StringWriter; |
||||
|
|
||||
|
/** |
||||
|
* @author jian.mao |
||||
|
* @date 2023年3月22日 |
||||
|
* @description |
||||
|
*/ |
||||
|
public class ThrowMessageUtil { |
||||
|
|
||||
|
/** |
||||
|
* 获取异常信息 |
||||
|
* @param t |
||||
|
* @return |
||||
|
*/ |
||||
|
public static String getErrmessage(Throwable t){ |
||||
|
StringWriter stringWriter=new StringWriter(); |
||||
|
t.printStackTrace(new PrintWriter(stringWriter,true)); |
||||
|
return stringWriter.getBuffer().toString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
# ==================== 必须文件:bootstrap.yml ==================== |
||||
|
# 这个文件用于配置Nacos客户端,优先级最高 |
||||
|
spring: |
||||
|
application: |
||||
|
name: ai-service # 服务名,对应Nacos中的Data ID |
||||
|
|
||||
|
cloud: |
||||
|
nacos: |
||||
|
# ======== 配置中心 ======== |
||||
|
config: |
||||
|
server-addr: 127.0.0.1:8848 # Nacos地址 |
||||
|
username: nacos # 用户名 |
||||
|
password: nacos # 密码 |
||||
|
group: public_dev # 分组 |
||||
|
namespace: opai # 命名空间(默认public) |
||||
|
file-extension: yaml # 配置文件格式 |
||||
|
timeout: 5000 # 超时时间(ms) |
||||
|
|
||||
|
# 核心配置:开启动态刷新 |
||||
|
refresh-enabled: true # 必须为true! |
||||
|
|
||||
|
# 主配置文件(从Nacos加载) |
||||
|
data-id: ${spring.application.name}.${spring.cloud.nacos.config.file-extension} |
||||
|
|
||||
|
# 共享配置文件(可选) |
||||
|
shared-configs[0]: |
||||
|
data-id: application.yaml # 公共配置 |
||||
|
group: public_dev # 公共分组 |
||||
|
namespace: opai |
||||
|
refresh: true # 公共配置也要刷新 |
||||
|
|
||||
|
# 扩展配置(可选) |
||||
|
# extension-configs[0]: |
||||
|
# data-id: datasource.yaml |
||||
|
# group: dev |
||||
|
# refresh: true |
||||
|
|
||||
|
# ======== 服务发现 ======== |
||||
|
discovery: |
||||
|
server-addr: ${spring.cloud.nacos.config.server-addr} |
||||
|
username: ${spring.cloud.nacos.config.username} |
||||
|
password: ${spring.cloud.nacos.config.password} |
||||
|
group: ${spring.cloud.nacos.config.group} |
||||
|
namespace: ${spring.cloud.nacos.config.namespace} |
||||
|
|
||||
|
logging: |
||||
|
level: |
||||
|
root: info |
||||
|
com.alibaba.nacos.client.config.impl: WARN |
||||
|
file: |
||||
|
path: ../logs |
||||
|
|
||||
@ -0,0 +1,26 @@ |
|||||
|
你是一位专业的情感分析专家,具备丰富的情绪识别与统计分析经验。 |
||||
|
|
||||
|
你的任务是:根据我提供的【参考内容】,分析整体情感倾向,并以百分比形式给出情感分布结果。 |
||||
|
|
||||
|
情感类别仅限以下三种: |
||||
|
- 正面(Positive) |
||||
|
- 负面(Negative) |
||||
|
- 中性(Neutral) |
||||
|
|
||||
|
输出要求: |
||||
|
1. 只允许返回 JSON 格式结果,不得输出任何解释、说明或多余文本。 |
||||
|
2. JSON 的 key 必须使用英文。 |
||||
|
3. 所有数值必须为百分比格式,保留一位小数,并以 % 结尾。 |
||||
|
4. confidence 表示本次情感分析结果的整体可信度。 |
||||
|
5. 返回的 JSON 结构必须严格符合以下格式,不允许新增、删除或修改字段名称。 |
||||
|
|
||||
|
返回格式: |
||||
|
{ |
||||
|
"positive": "xx.x%", |
||||
|
"negative": "xx.x%", |
||||
|
"neutral": "xx.x%", |
||||
|
"confidence": "xx.x%" |
||||
|
} |
||||
|
|
||||
|
参考内容: |
||||
|
{{content}} |
||||
@ -0,0 +1,36 @@ |
|||||
|
<configuration> |
||||
|
<!-- 属性文件:在properties文件中找到对应的配置项 --> |
||||
|
<springProperty scope="context" name="log-path" source="logging.file.path"/> |
||||
|
<!--<springProperty scope="context" name="logging.level" source="logging.level.com.bfd"/>--> |
||||
|
<!-- 默认的控制台日志输出,一般生产环境都是后台启动,这个没太大作用 --> |
||||
|
<!-- <appender name="STDOUT" |
||||
|
class="ch.qos.logback.core.ConsoleAppender"> |
||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
||||
|
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %line %-5level %logger{50} - %msg%n</Pattern> |
||||
|
</encoder> |
||||
|
</appender> --> |
||||
|
|
||||
|
<appender name="GLMAPPER-LOGGERONE" |
||||
|
class="ch.qos.logback.core.rolling.RollingFileAppender"> |
||||
|
<append>true</append> |
||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> |
||||
|
<level>${logging.level}</level> |
||||
|
</filter> |
||||
|
<file> |
||||
|
${log-path}/ai-service.log |
||||
|
</file> |
||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
||||
|
<FileNamePattern>${log-path}/ai-service.%d{yyyy-MM-dd}</FileNamePattern> |
||||
|
<MaxHistory>7</MaxHistory> |
||||
|
</rollingPolicy> |
||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %line %-5level %logger{50} - %msg%n</pattern> |
||||
|
<charset>UTF-8</charset> |
||||
|
</encoder> |
||||
|
</appender> |
||||
|
|
||||
|
<root level="info"> |
||||
|
<appender-ref ref="GLMAPPER-LOGGERONE"/> |
||||
|
<!-- <appender-ref ref="STDOUT"/> --> |
||||
|
</root> |
||||
|
</configuration> |
||||
@ -0,0 +1,30 @@ |
|||||
|
你是一位专业的情绪识别专家,具备丰富的情绪理解与分析经验。 |
||||
|
|
||||
|
你的任务是:根据我提供的【参考内容】,对文本中体现的情绪进行识别,并给出各类情绪所占的百分比强度。 |
||||
|
|
||||
|
需要识别的情绪类型仅限以下几种: |
||||
|
- 快乐(Joy) |
||||
|
- 信任(Trust) |
||||
|
- 恐惧(Fear) |
||||
|
- 惊讶(Surprise) |
||||
|
- 悲伤(Sadness) |
||||
|
- 愤怒(Anger) |
||||
|
|
||||
|
输出要求: |
||||
|
1. 只允许返回 JSON 格式结果,不得输出任何解释、说明或多余文本。 |
||||
|
2. JSON 的 key 必须使用英文,且字段名称必须严格一致。 |
||||
|
3. 所有数值必须为百分比格式,并以 % 结尾。 |
||||
|
4. 返回的 JSON 结构必须严格符合以下格式,不允许新增、删除或修改字段。 |
||||
|
|
||||
|
返回格式: |
||||
|
{ |
||||
|
"Joy": "xx%", |
||||
|
"Trust": "xx%", |
||||
|
"FearFear": "xx%", |
||||
|
"Surprise": "xx%", |
||||
|
"Sadness": "xx%", |
||||
|
"Anger": "xx%" |
||||
|
} |
||||
|
|
||||
|
参考内容: |
||||
|
{{content}} |
||||
@ -0,0 +1,38 @@ |
|||||
|
你是一名专业的意见挖掘与情感分析专家,擅长从文本中识别观点性陈述,并结合语义理解对其情感倾向与主题进行精准分类。 |
||||
|
|
||||
|
你的任务是:根据我提供的【参考内容】,提取其中明确表达主观观点或评价的意见陈述,并对每条意见进行情感与主题分类。 |
||||
|
|
||||
|
情感类型仅限以下三种: |
||||
|
- 正面 |
||||
|
- 负面 |
||||
|
- 中性 |
||||
|
|
||||
|
输出要求: |
||||
|
1. 只允许返回 JSON 数组结构,不得输出任何解释、说明或多余文本。 |
||||
|
2. 每条意见必须包含 emotion、opinion、topic 三个字段,字段名必须使用英文。 |
||||
|
3. emotion 的取值只能是:正面、负面、中性。 |
||||
|
4. opinion 使用英文原文表达,保持原句语义,不进行扩写或主观改写。 |
||||
|
5. topic 使用英文短语,对意见所属主题进行概括(如 Product Quality、Customer Service、Pricing 等)。 |
||||
|
6. 若存在多条意见,请按出现顺序依次输出;若未识别到有效意见,请返回空数组 []。 |
||||
|
|
||||
|
返回结构示例(结构必须一致): |
||||
|
[ |
||||
|
{ |
||||
|
"emotion": "正面", |
||||
|
"opinion": "The product quality is excellent and exceeds expectations.", |
||||
|
"topic": "Product Quality" |
||||
|
}, |
||||
|
{ |
||||
|
"emotion": "负面", |
||||
|
"opinion": "Customer service response time could be improved significantly.", |
||||
|
"topic": "Customer Service" |
||||
|
}, |
||||
|
{ |
||||
|
"emotion": "中性", |
||||
|
"opinion": "Pricing seems competitive compared to similar offerings in the market.", |
||||
|
"topic": "Pricing" |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
参考内容: |
||||
|
{{content}} |
||||
@ -0,0 +1,35 @@ |
|||||
|
你是一个“结构化信息抽取引擎”,不是聊天助手。 |
||||
|
|
||||
|
你将收到两个部分: |
||||
|
1. 抽取规则(JSON数组) |
||||
|
2. 待分析的原始文本 |
||||
|
|
||||
|
你的任务是: |
||||
|
严格按照抽取规则,从原始文本中抽取信息,并生成一个 JSON 对象返回。 |
||||
|
|
||||
|
【强制规则】 |
||||
|
- 只允许输出 JSON |
||||
|
- 不得输出任何解释、提示、markdown、代码块或多余文字 |
||||
|
- JSON 的 key 必须与抽取规则中的 field 完全一致 |
||||
|
- 如果某字段未找到,返回 null |
||||
|
- 如果一个字段有多个结果,返回数组 |
||||
|
- 如果只有一个结果,返回字符串 |
||||
|
- 所有值必须来自原文,不允许编造 |
||||
|
|
||||
|
-------------------------------- |
||||
|
【抽取规则】 |
||||
|
{{rules}} |
||||
|
|
||||
|
-------------------------------- |
||||
|
【原始文本】 |
||||
|
{{content}} |
||||
|
|
||||
|
-------------------------------- |
||||
|
【你必须输出的格式示例】(仅示例,不要照抄) |
||||
|
{ |
||||
|
"time": "...", |
||||
|
"people": "...", |
||||
|
"summary": "..." |
||||
|
} |
||||
|
|
||||
|
现在开始抽取。 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue