maojian 2 months ago
parent
commit
f0c246ee44
  1. 1
      asr-service/.settings/org.eclipse.core.resources.prefs
  2. 183
      asr-service/pom.xml
  3. 13
      asr-service/src/main/java/com/bw/App.java
  4. 19
      asr-service/src/main/java/com/bw/asr/Application.java
  5. 37
      asr-service/src/main/java/com/bw/asr/cache/ConfigCache.java
  6. 39
      asr-service/src/main/java/com/bw/asr/controller/TaskReceiveController.java
  7. 42
      asr-service/src/main/java/com/bw/asr/entity/AppResultDoc.java
  8. 45
      asr-service/src/main/java/com/bw/asr/entity/Constants.java
  9. 52
      asr-service/src/main/java/com/bw/asr/entity/VideoResult.java
  10. 206
      asr-service/src/main/java/com/bw/asr/handler/MainHandler.java
  11. 18
      asr-service/src/main/java/com/bw/asr/service/AsrTaskService.java
  12. 17
      asr-service/src/main/java/com/bw/asr/service/TaskReceiveService.java
  13. 553
      asr-service/src/main/java/com/bw/asr/service/impl/AsrTaskServiceImpl.java
  14. 55
      asr-service/src/main/java/com/bw/asr/service/impl/TaskReceiveServiceImpl.java
  15. 177
      asr-service/src/main/java/com/bw/asr/utils/DateUtil.java
  16. 1045
      asr-service/src/main/java/com/bw/asr/utils/DownLoadUtil.java
  17. 41
      asr-service/src/main/java/com/bw/asr/utils/FileUtil.java
  18. 45
      asr-service/src/main/java/com/bw/asr/utils/SpringBootKafka.java
  19. 23
      asr-service/src/main/java/com/bw/asr/utils/ThrowMessageUtil.java
  20. 52
      asr-service/src/main/resources/bootstrap.yml
  21. 36
      asr-service/src/main/resources/logback-spring.xml
  22. 38
      asr-service/src/test/java/com/bw/AppTest.java
  23. 40
      funasr-service/.classpath
  24. 1
      funasr-service/.gitignore
  25. 23
      funasr-service/.project
  26. 4
      funasr-service/.settings/org.eclipse.core.resources.prefs
  27. 9
      funasr-service/.settings/org.eclipse.jdt.core.prefs
  28. 4
      funasr-service/.settings/org.eclipse.m2e.core.prefs
  29. 213
      funasr-service/pom.xml
  30. 19
      funasr-service/src/main/java/com/bw/funasr/Application.java
  31. 37
      funasr-service/src/main/java/com/bw/funasr/cache/ConfigCache.java
  32. 39
      funasr-service/src/main/java/com/bw/funasr/controller/TaskReceiveController.java
  33. 42
      funasr-service/src/main/java/com/bw/funasr/entity/AppResultDoc.java
  34. 34
      funasr-service/src/main/java/com/bw/funasr/entity/Constants.java
  35. 206
      funasr-service/src/main/java/com/bw/funasr/handler/MainHandler.java
  36. 17
      funasr-service/src/main/java/com/bw/funasr/service/FunasrTaskService.java
  37. 190
      funasr-service/src/main/java/com/bw/funasr/service/Impl/FunasrTaskServiceImpl.java
  38. 55
      funasr-service/src/main/java/com/bw/funasr/service/Impl/TaskReceiveServiceImpl.java
  39. 17
      funasr-service/src/main/java/com/bw/funasr/service/TaskReceiveService.java
  40. 177
      funasr-service/src/main/java/com/bw/funasr/utils/DateUtil.java
  41. 1045
      funasr-service/src/main/java/com/bw/funasr/utils/DownLoadUtil.java
  42. 41
      funasr-service/src/main/java/com/bw/funasr/utils/FileUtil.java
  43. 23
      funasr-service/src/main/java/com/bw/funasr/utils/ThrowMessageUtil.java
  44. 52
      funasr-service/src/main/resources/bootstrap.yml
  45. 36
      funasr-service/src/main/resources/logback-spring.xml
  46. 1
      pom.xml

1
asr-service/.settings/org.eclipse.core.resources.prefs

@ -1,4 +1,3 @@
eclipse.preferences.version=1 eclipse.preferences.version=1
encoding//src/main/java=UTF-8 encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8 encoding/<project>=UTF-8

183
asr-service/pom.xml

@ -22,5 +22,188 @@
<version>3.8.1</version> <version>3.8.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<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>3.14.9</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>
<!-- Fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.21</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-quanmiaolightapp20240801</artifactId>
<version>2.0.33</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-aimiaobi20230801</artifactId>
<version>1.0.89</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
</dependencies> </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.asr.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> </project>

13
asr-service/src/main/java/com/bw/App.java

@ -1,13 +0,0 @@
package com.bw;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}

19
asr-service/src/main/java/com/bw/asr/Application.java

@ -0,0 +1,19 @@
package com.bw.asr;
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);
}
}

37
asr-service/src/main/java/com/bw/asr/cache/ConfigCache.java

@ -0,0 +1,37 @@
package com.bw.asr.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>> taskQueue = new LinkedBlockingDeque<Map<String,Object>>();
/****结果队列****/
public static LinkedBlockingDeque<Map<String, Object>> resultQueue = 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失败---");
}
}
}

39
asr-service/src/main/java/com/bw/asr/controller/TaskReceiveController.java

@ -0,0 +1,39 @@
package com.bw.asr.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.asr.service.TaskReceiveService;
import lombok.extern.slf4j.Slf4j;
/**
* 任务接收控制层
* @author jian.mao
* @date 2025年1月14日
* @description
*/
@Controller
@RequestMapping("/task")
@Slf4j
public class TaskReceiveController {
@Resource
private TaskReceiveService taskReceiveService;
@PostMapping("/put")
@ResponseBody
public String put(@RequestBody String param){
String response = taskReceiveService.put(param);
return response;
}
@RequestMapping(value = "/hello", method = RequestMethod.GET)
@ResponseBody
public String hello(String param, String token) {
return "123";
}
}

42
asr-service/src/main/java/com/bw/asr/entity/AppResultDoc.java

@ -0,0 +1,42 @@
package com.bw.asr.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;
/***前端输入参数 ***/
private Map<String, Object> input;
}

45
asr-service/src/main/java/com/bw/asr/entity/Constants.java

@ -0,0 +1,45 @@
package com.bw.asr.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 ERROR = "error";
public static final String TRACE = "trace";
public static final String WORKSPACEID = "workspaceId";
public static final String VIDEOURL = "videoUrl";
public static final String VIDEOMODELID = "videoModelId";
public static final String SNAPSHOTINTERVAL = "snapshotInterval";//1-10秒
public static final String LANGUAGE = "language";
public static final String METHODNAME = "methodName";//userdefined
public static final String INTERVAL = "interval";
public static final String STARTTIME = "startTime";
public static final String ENDTIME = "endTime";
public static final String DATA_KEY = "videoCaptionInfo";
public static final String SUBCONTENT = "subContent";
public static final String SUBTITLES = "subtitles";
public static final String VIDEOCONTENTS = "videoContents";
public static final String JOBID = "jobId";
/**
* 任务id
*/
public static final String TASKID = "taskId";
}

52
asr-service/src/main/java/com/bw/asr/entity/VideoResult.java

@ -0,0 +1,52 @@
package com.bw.asr.entity;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 视频解析结果实体 - 根据阿里云实际数据结构更新
*/
@Data
public class VideoResult {
// 基础信息
private String taskId;
private String taskStatus;
private String errorMessage;
private Long createTime = System.currentTimeMillis();
private List<Map<String, Object>> subtitles; // 字幕列表
private Integer captionCount; // 字幕数量
private String headerInfo; // header信息
// 辅助字段
private String videoUrl; // 视频URL
// 格式化方法
public String getFormattedCreateTime() {
if (createTime != null) {
return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date(createTime));
}
return "未知时间";
}
/**
* 获取第一条字幕如果有
*/
public Map<String, Object> getFirstSubtitle() {
if (subtitles != null && !subtitles.isEmpty()) {
return subtitles.get(0);
}
return null;
}
/**
* 获取最后一条字幕如果有
*/
public Map<String, Object> getLastSubtitle() {
if (subtitles != null && !subtitles.isEmpty()) {
return subtitles.get(subtitles.size() - 1);
}
return null;
}
}

206
asr-service/src/main/java/com/bw/asr/handler/MainHandler.java

@ -0,0 +1,206 @@
package com.bw.asr.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.asr.cache.ConfigCache;
import com.bw.asr.service.AsrTaskService;
import com.bw.asr.utils.DateUtil;
import com.bw.asr.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.task-queue-path}")
private String taskPath;
@Value("${task.result-task-queue-path}")
private String resultTaskPath;
@Autowired
private AsrTaskService asrTaskService;
/***线程池参数***/
@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 consumerThread = new Thread(() -> {
while (true) {
try {
// 从队列中获取任务
Map<String, Object> task = ConfigCache.taskQueue.take();
// 提交给线程池执行
executor.execute(() -> createTask(task));
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
log.error("任务消费线程被中断");
break;
}
}
});
consumerThread.start();
log.info("创建任务消费线程启动-----");
//消费结果任务队列数据
Thread resultConsumerThread = new Thread(() -> {
while (true) {
try {
// 从队列中获取任务
Map<String, Object> task = ConfigCache.resultQueue.take();
// 提交给线程池执行
executor.execute(() -> getResult(task));
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
log.error("任务消费线程被中断");
break;
}
DateUtil.sleep(3000);
}
});
resultConsumerThread.start();
log.info("结果任务消费线程启动-----");
//启动加载缓存任务
readTask(taskPath, ConfigCache.taskQueue);
readTask(resultTaskPath, ConfigCache.resultQueue);
//停止处理
waitDown();
}
/**
* 创建任务执行方法
* @param task
*/
private void createTask(Map<String, Object> task) {
asrTaskService.create(task);
}
private void getResult(Map<String, Object> task) {
asrTaskService.parse(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.taskQueue.size() > 0) {
try {
Map<String, Object> task = ConfigCache.taskQueue.take();
FileUtil.writeFile(taskPath, JSONObject.toJSONString(task));
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
log.info("taskQueue write is file end");
break;
}
}
while (true) {
if (ConfigCache.resultQueue.size() > 0) {
try {
Map<String, Object> task = ConfigCache.resultQueue.take();
FileUtil.writeFile(resultTaskPath, JSONObject.toJSONString(task));
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
log.info("taskQueue write is file end");
break;
}
}
}
}

18
asr-service/src/main/java/com/bw/asr/service/AsrTaskService.java

@ -0,0 +1,18 @@
package com.bw.asr.service;
import java.util.Map;
public interface AsrTaskService {
/**
* ocr远端任务
* @param task
*/
public void create(Map<String, Object> task);
/**
* 解析结果
* @param task
*/
public void parse(Map<String, Object> task);
}

17
asr-service/src/main/java/com/bw/asr/service/TaskReceiveService.java

@ -0,0 +1,17 @@
package com.bw.asr.service;
/**
* 任务接收服务层
* @author jian.mao
* @date 2025年1月14日
* @description
*/
public interface TaskReceiveService {
/**
* 任务新增
* @param dataJson
* @return
*/
public String put(String dataJson);
}

553
asr-service/src/main/java/com/bw/asr/service/impl/AsrTaskServiceImpl.java

@ -0,0 +1,553 @@
package com.bw.asr.service.impl;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.core.http.HttpClient;
import com.aliyun.httpcomponent.httpclient.ApacheAsyncHttpClientBuilder;
import com.aliyun.sdk.gateway.pop.Configuration;
import com.aliyun.sdk.gateway.pop.auth.SignatureVersion;
import com.aliyun.sdk.service.quanmiaolightapp20240801.AsyncClient;
import com.aliyun.sdk.service.quanmiaolightapp20240801.models.*;
import darabonba.core.client.ClientOverrideConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import com.bw.asr.entity.Constants;
import com.bw.asr.entity.VideoResult;
import com.bw.asr.service.AsrTaskService;
import com.bw.asr.utils.DownLoadUtil;
import com.bw.asr.utils.FileUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.bw.asr.entity.AppResultDoc;
import com.bw.asr.cache.ConfigCache;
import lombok.extern.slf4j.Slf4j;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
/**
* 视频解析服务实现类
*/
@Service
@Slf4j
@RefreshScope
public class AsrTaskServiceImpl implements AsrTaskService {
@Value("${file.path-prefix}")
private String downloadFilePathPrefix;
@Value("${aliyun.access-key-id}")
private String accessKeyId;
@Value("${aliyun.access-key-secret}")
private String accessKeySecret;
@Value("${aliyun.workspace-id}")
private String workspaceId;
@Value("${video.analysis.model-id}")
private String modelId;
@Value("${video.analysis.video-model-id}")
private String videoModelId;
@Value("${api.save-url}")
private String saveUrl;
// 语言映射表中文 -> 英文
private static final Map<String, String> LANGUAGE_MAPPING = new HashMap<>();
static {
LANGUAGE_MAPPING.put("中文", "chinese");
LANGUAGE_MAPPING.put("法语", "french");
LANGUAGE_MAPPING.put("英语", "english");
LANGUAGE_MAPPING.put("日语", "japanese");
LANGUAGE_MAPPING.put("中英文自由说", "chineseEnglishFreely");
LANGUAGE_MAPPING.put("阿拉伯语", "arabic");
LANGUAGE_MAPPING.put("韩语", "korean");
LANGUAGE_MAPPING.put("马来语", "malay");
LANGUAGE_MAPPING.put("泰语", "thai");
LANGUAGE_MAPPING.put("葡萄牙语", "portuguese");
LANGUAGE_MAPPING.put("西班牙语", "spanish");
LANGUAGE_MAPPING.put("印尼语", "indonesian");
LANGUAGE_MAPPING.put("越南语", "vietnamese");
}
/**
* 获取阿里云客户端
*/
private AsyncClient getClient() {
try {
Credential credential = Credential.builder()
.accessKeyId(accessKeyId)
.accessKeySecret(accessKeySecret)
.build();
HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(30))
.responseTimeout(Duration.ofSeconds(60))
.ignoreSSL(false)
.build();
return AsyncClient.builder()
.credentialsProvider(StaticCredentialProvider.create(credential))
.serviceConfiguration(Configuration.create().setSignatureVersion(SignatureVersion.V3))
.overrideConfiguration(ClientOverrideConfiguration.create()
.setProtocol("HTTPS")
.setEndpointOverride("quanmiaolightapp.cn-beijing.aliyuncs.com"))
.httpClient(httpClient)
.build();
} catch (Exception e) {
log.error("初始化阿里云客户端失败", e);
return null;
}
}
/**
* 提交视频分析任务
*/
@Override
public void create(Map<String, Object> task) {
try {
//源文件链接
String videoUrl = task.get(Constants.VIDEOURL).toString();
String url = videoUrl.replace("10.8.0.16", "182.92.120.176");
String inputLanguage = task.get(Constants.LANGUAGE).toString();
// 映射语言参数
String language = mapLanguage(inputLanguage);
// 构建请求
SubmitVideoAnalysisTaskRequest request = buildVideoAnalysisRequest(url,language);
// 提交任务
AsyncClient client = getClient();
CompletableFuture<SubmitVideoAnalysisTaskResponse> responseFuture =
client.submitVideoAnalysisTask(request);
SubmitVideoAnalysisTaskResponse response = responseFuture.get();
if (response != null && response.getBody().getSuccess() &&
response.getBody().getData() != null) {
String jobId = response.getBody().getData().getTaskId();
log.info("视频解析任务提交成功,任务ID:{}", (String)task.get(Constants.TASKID));
task.put(Constants.JOBID, URLEncoder.encode(jobId.replace("\"", ""), "UTF-8"));
//任务创建成功放到监控结果队列中
ConfigCache.resultQueue.put(task);
} else {
throw new RuntimeException("视频解析任务提交失败");
}
} catch (Throwable e) {
log.error("提交视频解析任务异常", 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);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
}
/**
* 构建视频理解请求
*/
private SubmitVideoAnalysisTaskRequest buildVideoAnalysisRequest(String videoUrl,String language) {
return SubmitVideoAnalysisTaskRequest.builder()
.workspaceId(workspaceId)
.videoUrl(videoUrl)
.videoModelId(videoModelId)
.language(language)
.generateOptions(Arrays.asList("videoAnalysis", "videoGenerate"))
.videoModelCustomPromptTemplate("# 角色\n你是一名专业的视频字幕提取员\n\n# 任务\n提取视频中的所有对话和字幕内容\n\n# 输入\n{videoAsrText}\n\n# 输出格式\n时间戳:00:00:00 - 内容:这里是字幕内容")
.textProcessTasks(Collections.singletonList(
SubmitVideoAnalysisTaskRequest.TextProcessTasks.builder()
.modelId(modelId)
.modelCustomPromptTemplate("# 角色\n视频内容分析师\n\n# 输入\n{videoAnalysisText}\n{videoAsrText}\n\n# 任务\n提取视频字幕并标记时间戳\n\n# 输出格式\n=== 视频字幕时间轴 ===")
.build()
))
.frameSampleMethod(SubmitVideoAnalysisTaskRequest.FrameSampleMethod.builder()
.methodName("standard")
.build())
.build();
}
@Override
public void parse(Map<String, Object> task) {
ObjectMapper objectMapper = new ObjectMapper();
// TODO Auto-generated method stub
try {
String jobId = (String) task.get(Constants.JOBID);
//获取视频解析结果
VideoResult videoResult = getVideoAnalysisResult(jobId);
System.out.println("result ---------------" +videoResult);
log.info("获取解析结果-jobId:{},result:{}",jobId,videoResult);
if (videoResult != null) {
String status = videoResult.getTaskStatus();
// 1. 检查headerInfo中是否有错误信息
Object headerInfo = videoResult.getHeaderInfo();
String errorMessage = "";
if (headerInfo != null && headerInfo.toString().contains("errorCode")) {
log.error("阿里云返回错误信息: {}", headerInfo);
errorMessage = extractErrorMessageFromHeader(headerInfo.toString());
}
if ("SUCCESSED".equals(status)) {
// String content = printSimpleCaptions(videoResult);
// String jsonString = objectMapper.writeValueAsString(videoResult.getSubtitles());
// if(jsonString.isEmpty() || jsonString == null || "[]".equals(jsonString.trim())) {
// jsonString = "未解析到字幕信息";
// }
List<Map<String, Object>> subtitles = videoResult.getSubtitles();
// 检查字幕是否为空
if (subtitles == null || subtitles.isEmpty()) {
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);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
//成功 发送结果
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.SUBCONTENT, subtitles);//videoResult.getSubtitles()
result.put(Constants.SUBTITLES, videoResult.getCaptionCount());
entity.setResult(result);
entity.setStatus(1);
entity.setDel(0);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}else if("FAILED".equals(status) || "CANCELED".equals(status)) {
System.out.println("视频解析失败: " + videoResult.getErrorMessage());
//发送失败结果
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);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}else if("PENDING".equals(status) && !errorMessage.isEmpty() && errorMessage !=null){
log.error("任务处于PENDING状态但有错误信息,立即处理。taskId: {}, 错误: {}", (String)task.get(Constants.TASKID), errorMessage);
//发送失败结果
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, errorMessage);
entity.setResult(result);
entity.setStatus(2);
entity.setDel(0);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}else {
//识别中 -- 放回队列
ConfigCache.resultQueue.put(task);
}
}
} catch (Throwable e) {
// TODO: handle exception
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);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
}
/**
* 获取视频分析结果
*/
public VideoResult getVideoAnalysisResult(String taskId) {
try {
AsyncClient client = getClient();
GetVideoAnalysisTaskRequest request = GetVideoAnalysisTaskRequest.builder()
.taskId(taskId)
.workspaceId(workspaceId)
.build();
CompletableFuture<GetVideoAnalysisTaskResponse> responseFuture =
client.getVideoAnalysisTask(request);
GetVideoAnalysisTaskResponse response = responseFuture.get();
if (response != null && response.getBody().getSuccess() &&
response.getBody().getData() != null) {
return parseVideoAnalysisResult(response.getBody().getData(), taskId);
}
} catch (Exception e) {
log.error("获取视频解析结果异常", e);
}
return null;
}
/**
* 解析视频分析结果
*/
private VideoResult parseVideoAnalysisResult(GetVideoAnalysisTaskResponseBody.Data data, String taskId) {
VideoResult result = new VideoResult();
result.setTaskId(taskId);
result.setTaskStatus(data.getTaskStatus());
try {
String jsonStr = JSON.toJSONString(data);
JSONObject json = JSON.parseObject(jsonStr);
// 提取错误信息
if (json.containsKey("errorMessage")) {
result.setErrorMessage(json.getString("errorMessage"));
}
// 提取payload中的数据
if (json.containsKey("payload")) {
JSONObject payload = json.getJSONObject("payload");
if (payload.containsKey("output")) {
JSONObject output = payload.getJSONObject("output");
extractCaptionsFromOutput(output, result);
}
}
// 提取header信息
if (json.containsKey("header")) {
JSONObject header = json.getJSONObject("header");
result.setHeaderInfo(JSON.toJSONString(header));
}
} catch (Exception e) {
log.error("解析视频分析结果异常", e);
}
return result;
}
/**
* 从output中提取字幕
*/
private void extractCaptionsFromOutput(JSONObject output, VideoResult result) {
try {
// 提取视频字幕结果
if (output.containsKey("videoCaptionResult")) {
JSONObject captionResult = output.getJSONObject("videoCaptionResult");
if (captionResult.getBooleanValue("generateFinished") &&
captionResult.containsKey("videoCaptions")) {
JSONArray captions = captionResult.getJSONArray("videoCaptions");
List<Map<String, Object>> subtitleList = new ArrayList<>();
for (int i = 0; i < captions.size(); i++) {
JSONObject caption = captions.getJSONObject(i);
Map<String, Object> subtitle = new HashMap<>();
// subtitle.put("startTime", caption.getLong("startTime"));
// subtitle.put("endTime", caption.getLong("endTime"));
subtitle.put("startTimeFormat", caption.getString("startTimeFormat"));
subtitle.put("endTimeFormat", caption.getString("endTimeFormat"));
subtitle.put("text", caption.getString("text"));
// if (caption.containsKey("speaker")) {
// subtitle.put("speaker", caption.getString("speaker"));
// }
subtitleList.add(subtitle);
}
result.setSubtitles(subtitleList);
result.setCaptionCount(subtitleList.size());
}
}
} catch (Exception e) {
log.error("提取字幕异常", e);
}
}
/**
* 打印简化的字幕格式
*/
private String printSimpleCaptions(VideoResult result) {
if (result.getSubtitles() != null && !result.getSubtitles().isEmpty()) {
StringBuilder captionsBuilder = new StringBuilder();
for (Map<String, Object> subtitle : result.getSubtitles()) {
String startTime = (String) subtitle.get("startTimeFormat");
String endTime = (String) subtitle.get("endTimeFormat");
String text = (String) subtitle.get("text");
String content = "[" + startTime + " - " + endTime + "] "+ text;
// 添加到构建器
captionsBuilder.append(content).append("\n");
// 同时输出到控制台如果需要的话
System.out.println(content);
}
// 返回所有字幕内容
return captionsBuilder.toString();
} else {
System.out.println("未检测到字幕内容");
return "未检测到字幕内容";
}
}
/**
* 从headerInfo中提取错误信息
*/
private String extractErrorMessageFromHeader(String headerInfo) {
try {
JSONObject json = JSON.parseObject(headerInfo);
// 尝试提取errorMessage
if (json.containsKey("errorMessage")) {
String errorMsg = json.getString("errorMessage");
return errorMsg.length() > 200 ?
errorMsg.substring(0, 200) + "..." : errorMsg;
}
if (json.containsKey("eventInfo")) {
return json.getString("eventInfo");
}
if (json.containsKey("errorCode")) {
return "错误代码: " + json.getString("errorCode");
}
} catch (Exception e) {
log.warn("解析headerInfo失败", e);
}
// 如果解析失败返回原始信息的前100个字符
return headerInfo.length() > 100 ?
headerInfo.substring(0, 100) + "..." : headerInfo;
}
/**
* 映射语言参数中文 -> 英文
*/
private String mapLanguage(String inputLanguage) {
if (inputLanguage == null || inputLanguage.trim().isEmpty()) {
return "chinese"; // 默认中文
}
String trimmed = inputLanguage.trim().toLowerCase();
// 首先尝试精确匹配
for (Map.Entry<String, String> entry : LANGUAGE_MAPPING.entrySet()) {
if (trimmed.equals(entry.getKey().toLowerCase())) {
return entry.getValue();
}
}
// 尝试包含匹配支持更灵活的输入
for (Map.Entry<String, String> entry : LANGUAGE_MAPPING.entrySet()) {
if (trimmed.contains(entry.getKey().toLowerCase())) {
log.debug("语言包含匹配: {} -> {}", trimmed, entry.getValue());
return entry.getValue();
}
}
// 默认返回中文
log.warn("无法识别的语言输入: {}, 使用默认值: chinese", inputLanguage);
return "chinese";
}
/**
* 测试方法
*/
// public static void main(String[] args) {
// try {
// System.out.println("=== 视频字幕提取测试 ===\n");
//
// // 创建服务实例
// AsrTaskServiceImpl service = new AsrTaskServiceImpl();
//
// // 设置配置
// service.accessKeyId = "LTAI5tDbzNNXnSg9r6ncQi2x";
// service.accessKeySecret = "f8aX6BM8ZiQbEfL1o4gcvX2PsziCw3";
// service.workspaceId = "llm-7qk6vvzgxq7qhl8e";
// service.modelId = "quanmiao-llm-plus";
// service.videoModelId = "quanmiao-vl-plus";
// service.language = "chinese";
//
// // 测试视频URL
// String testVideoUrl = "https://vd9.bdstatic.com/mda-rmggrjqeweys54r8/mb/cae_h264/1765977811688977696/mda-rmggrjqeweys54r8.mp4";
//
// // 创建任务
// Map<String, Object> task = new HashMap<>();
// task.put("videoUrl", testVideoUrl);
//
// System.out.println("提交视频分析任务...");
// System.out.println("视频URL: " + testVideoUrl);
//
// // 提交任务
// service.create(task);
//
// // 等待任务处理实际使用中可以通过回调或轮询获取结果
// System.out.println("任务已提交,正在处理中...");
//
// // 为了测试我们可以等待一段时间后手动获取结果
// Thread.sleep(30000); // 等待30秒
//
// String taskId = (String) task.get("taskId");
// if (taskId != null) {
// System.out.println("\n任务ID: " + taskId);
//
// // 获取简化字幕
// System.out.println("\n=== 提取到的字幕 ===");
// String captions = service.getSimplifiedCaptions(taskId);
// System.out.println(captions);
// }
//
// } catch (Exception e) {
// System.err.println("测试失败: " + e.getMessage());
// e.printStackTrace();
// }
// }
}

55
asr-service/src/main/java/com/bw/asr/service/impl/TaskReceiveServiceImpl.java

@ -0,0 +1,55 @@
package com.bw.asr.service.impl;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.bw.asr.cache.ConfigCache;
import com.bw.asr.entity.Constants;
import com.bw.asr.service.TaskReceiveService;
import lombok.extern.slf4j.Slf4j;
/**
* 任务接收服务层实现类
* @author jian.mao
* @date 2025年1月14日
* @description
*/
@Service
@Slf4j
public class TaskReceiveServiceImpl implements TaskReceiveService {
@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.taskQueue.putFirst(task);
}else{
ConfigCache.taskQueue.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);
}
}

177
asr-service/src/main/java/com/bw/asr/utils/DateUtil.java

@ -0,0 +1,177 @@
package com.bw.asr.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);
}
}

1045
asr-service/src/main/java/com/bw/asr/utils/DownLoadUtil.java
File diff suppressed because it is too large
View File

41
asr-service/src/main/java/com/bw/asr/utils/FileUtil.java

@ -0,0 +1,41 @@
package com.bw.asr.utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* 文件工具类
* @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();
}
}
}

45
asr-service/src/main/java/com/bw/asr/utils/SpringBootKafka.java

@ -0,0 +1,45 @@
package com.bw.asr.utils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
/**
* @PROJECT_NAME: companybusinesscrawl
* @DESCRIPTION:SpringBootKafka 工具类
* @DATE: 2023/4/6 11:09
*/
@Slf4j
@Component
public class SpringBootKafka {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
/**
* 自定义topicKafkaTemplate
*/
/**
* public static final String TOPIC = "companyBussTest";
**/
public void send(String topic, String message) {
//发送消息
ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, message);
future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
@Override
public void onFailure(Throwable throwable) {
//发送失败的处理
log.info(topic + " - 生产者 发送消息失败:" + throwable.getMessage());
}
@Override
public void onSuccess(SendResult<String, Object> stringObjectSendResult) {
//成功的处理
log.info(topic + " - 生产者 发送消息成功" );
}
});
}
}

23
asr-service/src/main/java/com/bw/asr/utils/ThrowMessageUtil.java

@ -0,0 +1,23 @@
package com.bw.asr.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();
}
}

52
asr-service/src/main/resources/bootstrap.yml

@ -0,0 +1,52 @@
# ==================== 必须文件:bootstrap.yml ====================
# 这个文件用于配置Nacos客户端,优先级最高
spring:
application:
name: asr-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

36
asr-service/src/main/resources/logback-spring.xml

@ -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}/asr-service.log
</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log-path}/asr-service.log.%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>

38
asr-service/src/test/java/com/bw/AppTest.java

@ -1,38 +0,0 @@
package com.bw;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}

40
funasr-service/.classpath

@ -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>

1
funasr-service/.gitignore

@ -0,0 +1 @@
/target/

23
funasr-service/.project

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>funasr-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>

4
funasr-service/.settings/org.eclipse.core.resources.prefs

@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding/<project>=UTF-8

9
funasr-service/.settings/org.eclipse.jdt.core.prefs

@ -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

4
funasr-service/.settings/org.eclipse.m2e.core.prefs

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

213
funasr-service/pom.xml

@ -0,0 +1,213 @@
<?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>funasr-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>funasr-service</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<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>3.14.9</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>
<!-- Fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.21</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-quanmiaolightapp20240801</artifactId>
<version>2.0.33</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-aimiaobi20230801</artifactId>
<version>1.0.89</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.14.3</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.funasr.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>

19
funasr-service/src/main/java/com/bw/funasr/Application.java

@ -0,0 +1,19 @@
package com.bw.funasr;
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);
}
}

37
funasr-service/src/main/java/com/bw/funasr/cache/ConfigCache.java

@ -0,0 +1,37 @@
package com.bw.funasr.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>> taskQueue = new LinkedBlockingDeque<Map<String,Object>>();
/****结果队列****/
public static LinkedBlockingDeque<Map<String, Object>> resultQueue = 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失败---");
}
}
}

39
funasr-service/src/main/java/com/bw/funasr/controller/TaskReceiveController.java

@ -0,0 +1,39 @@
package com.bw.funasr.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.funasr.service.TaskReceiveService;
import lombok.extern.slf4j.Slf4j;
/**
* 任务接收控制层
* @author jian.mao
* @date 2025年1月14日
* @description
*/
@Controller
@RequestMapping("/task")
@Slf4j
public class TaskReceiveController {
@Resource
private TaskReceiveService taskReceiveService;
@PostMapping("/put")
@ResponseBody
public String put(@RequestBody String param){
String response = taskReceiveService.put(param);
return response;
}
@RequestMapping(value = "/hello", method = RequestMethod.GET)
@ResponseBody
public String hello(String param, String token) {
return "123";
}
}

42
funasr-service/src/main/java/com/bw/funasr/entity/AppResultDoc.java

@ -0,0 +1,42 @@
package com.bw.funasr.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;
/***前端输入参数 ***/
private Map<String, Object> input;
}

34
funasr-service/src/main/java/com/bw/funasr/entity/Constants.java

@ -0,0 +1,34 @@
package com.bw.funasr.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 ERROR = "error";
public static final String VIDEOURL = "videoUrl";
public static final String VOICECONTENT = "voiceContent";
public static final String TRACE = "trace";
public static final String JOBID = "jobId";
/**
* 任务id
*/
public static final String TASKID = "taskId";
}

206
funasr-service/src/main/java/com/bw/funasr/handler/MainHandler.java

@ -0,0 +1,206 @@
package com.bw.funasr.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.funasr.cache.ConfigCache;
import com.bw.funasr.service.FunasrTaskService;
import com.bw.funasr.utils.DateUtil;
import com.bw.funasr.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.task-queue-path}")
private String taskPath;
@Value("${task.result-task-queue-path}")
private String resultTaskPath;
@Autowired
private FunasrTaskService funasrTaskService;
/***线程池参数***/
@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 consumerThread = new Thread(() -> {
while (true) {
try {
// 从队列中获取任务
Map<String, Object> task = ConfigCache.taskQueue.take();
// 提交给线程池执行
executor.execute(() -> createTask(task));
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
log.error("任务消费线程被中断");
break;
}
}
});
consumerThread.start();
log.info("创建任务消费线程启动-----");
//消费结果任务队列数据
Thread resultConsumerThread = new Thread(() -> {
while (true) {
try {
// 从队列中获取任务
Map<String, Object> task = ConfigCache.resultQueue.take();
// 提交给线程池执行
executor.execute(() -> getResult(task));
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
log.error("任务消费线程被中断");
break;
}
DateUtil.sleep(3000);
}
});
resultConsumerThread.start();
log.info("结果任务消费线程启动-----");
//启动加载缓存任务
readTask(taskPath, ConfigCache.taskQueue);
readTask(resultTaskPath, ConfigCache.resultQueue);
//停止处理
waitDown();
}
/**
* 创建任务执行方法
* @param task
*/
private void createTask(Map<String, Object> task) {
funasrTaskService.create(task);
}
private void getResult(Map<String, Object> task) {
funasrTaskService.parse(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.taskQueue.size() > 0) {
try {
Map<String, Object> task = ConfigCache.taskQueue.take();
FileUtil.writeFile(taskPath, JSONObject.toJSONString(task));
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
log.info("taskQueue write is file end");
break;
}
}
while (true) {
if (ConfigCache.resultQueue.size() > 0) {
try {
Map<String, Object> task = ConfigCache.resultQueue.take();
FileUtil.writeFile(resultTaskPath, JSONObject.toJSONString(task));
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
log.info("taskQueue write is file end");
break;
}
}
}
}

17
funasr-service/src/main/java/com/bw/funasr/service/FunasrTaskService.java

@ -0,0 +1,17 @@
package com.bw.funasr.service;
import java.util.Map;
public interface FunasrTaskService {
/**
* ocr远端任务
* @param task
*/
public void create(Map<String, Object> task);
/**
* 解析结果
* @param task
*/
public void parse(Map<String, Object> task);
}

190
funasr-service/src/main/java/com/bw/funasr/service/Impl/FunasrTaskServiceImpl.java

@ -0,0 +1,190 @@
package com.bw.funasr.service.Impl;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import com.alibaba.dashscope.audio.asr.transcription.Transcription;
import com.alibaba.dashscope.audio.asr.transcription.TranscriptionParam;
import com.alibaba.dashscope.audio.asr.transcription.TranscriptionQueryParam;
import com.alibaba.dashscope.audio.asr.transcription.TranscriptionResult;
import com.alibaba.dashscope.audio.asr.transcription.TranscriptionTaskResult;
import com.alibaba.fastjson2.JSONObject;
import com.bw.funasr.service.FunasrTaskService;
import com.bw.funasr.entity.AppResultDoc;
import com.bw.funasr.utils.DownLoadUtil;
import com.bw.funasr.cache.ConfigCache;
import com.bw.funasr.entity.Constants;
import com.google.gson.*;
import lombok.extern.slf4j.Slf4j;
/**
* 语音转文字服务实现类
*/
@Service
@Slf4j
@RefreshScope
public class FunasrTaskServiceImpl implements FunasrTaskService{
@Value("${aliyun.apiKey}")
private String apiKey;
@Value("${aliyun.model}")
private String model;
@Value("${aliyun.apiUrl}")
private String apiUrl;
@Value("${api.save-url}")
private String saveUrl;
@Override
public void create(Map<String, Object> task) {
// TODO Auto-generated method stub
String videoUrl = task.get(Constants.VIDEOURL).toString();
String url = videoUrl.replace("10.8.0.16", "182.92.120.176");
com.alibaba.dashscope.utils.Constants.baseHttpApiUrl = apiUrl;
// 创建转写请求参数
TranscriptionParam param =
TranscriptionParam.builder()
.apiKey(apiKey)
.model(model)
// .parameter("language_hints", new String[]{"zh", "en"})//language_hints为可选参数用于指定待识别音频的语言代码
.fileUrls(Arrays.asList(url))
.build();
try {
Transcription transcription = new Transcription();
// 提交转写请求
TranscriptionResult result = transcription.asyncCall(param);
System.out.println("RequestId: " + result.getRequestId());
String jobId = result.getTaskId();
log.info("语音解析任务提交成功,任务ID:{}", (String)task.get(Constants.TASKID));
task.put(Constants.JOBID, URLEncoder.encode(jobId.replace("\"", ""), "UTF-8"));
//任务创建成功放到监控结果队列中
ConfigCache.resultQueue.put(task);
} catch (Exception e) {
log.error("提交视频解析任务异常", 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);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
}
@Override
public void parse(Map<String, Object> task) {
// TODO Auto-generated method stub
try {
String jobId = (String) task.get(Constants.JOBID);
TranscriptionQueryParam queryParam = TranscriptionQueryParam.builder()
.apiKey(apiKey)
.taskId(jobId)
.build();
Transcription transcription = new Transcription();
// 提交转写请求
TranscriptionResult result = transcription.wait(queryParam);
String taskStatus = result.getTaskStatus().toString();
log.info("获取解析结果-jobId:{},status:{}",jobId,taskStatus);
if ("SUCCEEDED".equals(taskStatus)) {
// 获取转写结果
List<TranscriptionTaskResult> taskResultList = result.getResults();
if (taskResultList != null && taskResultList.size() > 0) {
for (TranscriptionTaskResult taskResult : taskResultList) {
String transcriptionUrl = taskResult.getTranscriptionUrl();
HttpURLConnection connection = (HttpURLConnection) new URL(transcriptionUrl).openConnection();
connection.setRequestMethod("GET");
connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonObject jsonResult = gson.fromJson(reader, JsonObject.class);
System.out.println(gson.toJson(jsonResult));
JsonArray transcripts = jsonResult.getAsJsonArray("transcripts");
if (transcripts != null && transcripts.size() > 0) {
JsonObject firstTranscript = transcripts.get(0).getAsJsonObject();
String transcriptText = firstTranscript.get("text").getAsString();
//成功 发送结果
log.info("发送解析结果-taskId{}",task.get(Constants.TASKID));
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> result1 = new HashMap<String, Object>(16);
result1.put(Constants.VOICECONTENT, transcriptText);
entity.setResult(result1);
entity.setStatus(1);
entity.setDel(0);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
}
}
}else if("PENDING".equals(taskStatus) || "RUNNING".equals(taskStatus)){
//识别中 -- 放回队列
ConfigCache.resultQueue.put(task);
}else {
log.error("语音转文字任务失败,jobId: {}, status: {}", jobId, taskStatus);
//获取错误信息
JsonObject outputJson = result.getOutput();
String message = outputJson.get("message").getAsString();
//发送失败结果
AppResultDoc entity = new AppResultDoc();
entity.setTaskId((String)task.get(Constants.TASKID));
entity.setAppId((Integer) task.get(Constants.ID));
entity.setCreateTime(System.currentTimeMillis());
Map<String, Object> resultMap = new HashMap<>(16);
resultMap.put(Constants.ERROR, message);
entity.setResult(resultMap);
entity.setStatus(2);
entity.setDel(0);
entity.setInput(task);
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
} 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);
entity.setInput(task);
//回传给api服务保存
DownLoadUtil.doPost(saveUrl, JSONObject.toJSONString(entity));
}
}
}

55
funasr-service/src/main/java/com/bw/funasr/service/Impl/TaskReceiveServiceImpl.java

@ -0,0 +1,55 @@
package com.bw.funasr.service.Impl;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.bw.funasr.cache.ConfigCache;
import com.bw.funasr.entity.Constants;
import com.bw.funasr.service.TaskReceiveService;
import lombok.extern.slf4j.Slf4j;
/**
* 任务接收服务层实现类
* @author jian.mao
* @date 2025年1月14日
* @description
*/
@Service
@Slf4j
public class TaskReceiveServiceImpl implements TaskReceiveService {
@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.taskQueue.putFirst(task);
}else{
ConfigCache.taskQueue.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);
}
}

17
funasr-service/src/main/java/com/bw/funasr/service/TaskReceiveService.java

@ -0,0 +1,17 @@
package com.bw.funasr.service;
/**
* 任务接收服务层
* @author jian.mao
* @date 2025年1月14日
* @description
*/
public interface TaskReceiveService {
/**
* 任务新增
* @param dataJson
* @return
*/
public String put(String dataJson);
}

177
funasr-service/src/main/java/com/bw/funasr/utils/DateUtil.java

@ -0,0 +1,177 @@
package com.bw.funasr.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);
}
}

1045
funasr-service/src/main/java/com/bw/funasr/utils/DownLoadUtil.java
File diff suppressed because it is too large
View File

41
funasr-service/src/main/java/com/bw/funasr/utils/FileUtil.java

@ -0,0 +1,41 @@
package com.bw.funasr.utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* 文件工具类
* @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();
}
}
}

23
funasr-service/src/main/java/com/bw/funasr/utils/ThrowMessageUtil.java

@ -0,0 +1,23 @@
package com.bw.funasr.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();
}
}

52
funasr-service/src/main/resources/bootstrap.yml

@ -0,0 +1,52 @@
# ==================== 必须文件:bootstrap.yml ====================
# 这个文件用于配置Nacos客户端,优先级最高
spring:
application:
name: funasr-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

36
funasr-service/src/main/resources/logback-spring.xml

@ -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}/funasr-service.log
</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log-path}/funasr-service.log.%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>

1
pom.xml

@ -11,6 +11,7 @@
<module>asr-service</module> <module>asr-service</module>
<module>translate-service</module> <module>translate-service</module>
<module>ai-service</module> <module>ai-service</module>
<module>funasr-service</module>
<module>document-convert-service</module> <module>document-convert-service</module>
</modules> </modules>

Loading…
Cancel
Save