feat: 语音交互评估

This commit is contained in:
stream 2026-02-03 10:58:38 +08:00
parent 8950f7cd06
commit 87a6cbfc6e
19 changed files with 388 additions and 195 deletions

View File

@ -4,10 +4,13 @@ import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.evaluation.model.domain.AeEvaluation;
import com.cmvr.evaluation.service.IAeEvaluationService;
import com.cmvr.test.model.domain.TeAiEvaluation;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
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.RestController;
@ -20,13 +23,20 @@ public class AeEvaluationController extends BaseController {
private final IAeEvaluationService aeEvaluationService;
// @ApiOperation("回调AI评估")
// @GetMapping("/test")
// public AjaxResult execute() {
// AeEvaluation aeEvaluation = new AeEvaluation();
// aeEvaluation.setInstId("1336caf1e80b2d23b706ad1cd05d78bd");
// aeEvaluation.setItemId("18483d62ccec13c0a336d1d605e9b06c");
// aeEvaluationService.executeEvaluation(aeEvaluation);
// return AjaxResult.ok();
// }
@ApiOperation("回调AI评估")
@GetMapping("/test")
public AjaxResult execute() {
AeEvaluation aeEvaluation = new AeEvaluation();
aeEvaluation.setInstId("1336caf1e80b2d23b706ad1cd05d78bd");
aeEvaluation.setItemId("18483d62ccec13c0a336d1d605e9b06c");
aeEvaluationService.executeEvaluation(aeEvaluation);
@PostMapping("/callback")
public AjaxResult list(@RequestBody AeEvaluation aeEvaluation) {
aeEvaluationService.callback(aeEvaluation);
return AjaxResult.ok();
}
}

View File

@ -64,7 +64,7 @@ public class EdgeMicrophoneServiceImpl implements EdgeMicrophoneService {
try {
// 获取gRPC客户端存根
MicPhoneServiceGrpc.MicPhoneServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), MicPhoneServiceGrpc.MicPhoneServiceBlockingStub.class);
String filePath = StrUtil.format("{}/{}_{}.mp4", "/home/share/assets/video", deviceId, System.currentTimeMillis());
String filePath = StrUtil.format("{}/{}_{}.wav", "/home/share/assets/video", deviceId, System.currentTimeMillis());
// 构建请求消息
MicrophoneCommand.StartMicRecordingCommand.Request request = MicrophoneCommand.StartMicRecordingCommand.Request.newBuilder()

View File

@ -2,7 +2,14 @@ package com.cmvr.common.core.minio;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import io.minio.*;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.SetBucketPolicyArgs;
import io.minio.StatObjectArgs;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -10,10 +17,10 @@ import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Date;
@Slf4j
@ -104,6 +111,40 @@ public class MinioService {
return objectName;
}
public String upload(String bucketName, String path, File file) {
String originalFilename = file.getName();
if (StringUtils.isBlank(originalFilename)) {
throw new RuntimeException("文件名为空");
}
// 生成文件名使用当前时间戳
String fileName = System.currentTimeMillis() + "";
if (originalFilename.lastIndexOf(".") >= 0) {
fileName += originalFilename.substring(originalFilename.lastIndexOf("."));
}
// 构建文件在 MinIO 中的路径
String objectName = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN) + "/" + fileName;
try (InputStream stream = Files.newInputStream(file.toPath())) {
PutObjectArgs objectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.object(path + "/" + objectName)
.stream(stream, file.length(), -1) // 传入文件流
.contentType("application/octet-stream") // 可以根据需要修改文件类型
.build();
// 上传文件到 MinIO
minioClient.putObject(objectArgs);
} catch (Exception e) {
log.error("文件上传失败,", e);
return null;
}
return objectName;
}
/**
* 文件上传
*

View File

@ -19,7 +19,7 @@ public class AeEvaluation implements Serializable {
@TableId(value = "ae_id", type = IdType.ASSIGN_UUID)
private String aeId;
@ApiModelProperty("评估类型VOICE:语音TOUCH:触控)")
@ApiModelProperty("评估类型VI_PROJECT:语音TI_PROJECT:触控)")
private String aeType;
@ApiModelProperty("流程实例ID")

View File

@ -9,4 +9,9 @@ public interface IAeEvaluationService extends IService<AeEvaluation> {
* 执行语音交互评估
*/
public void executeEvaluation(AeEvaluation evaluation);
/**
* 评估回调
*/
void callback(AeEvaluation aeEvaluation);
}

View File

@ -4,20 +4,25 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.http.CallAPIUtil;
import com.cmvr.evaluation.mapper.AeEvaluationMapper;
import com.cmvr.evaluation.model.domain.AeEvaluation;
import com.cmvr.evaluation.service.IAeEvaluationService;
import com.cmvr.llm.config.APIProperties;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.enums.RunModeEnum;
import com.cmvr.test.model.domain.TeNodeInst;
import com.cmvr.test.model.vo.TeFlowViewVO;
import com.cmvr.test.service.ITeNodeInstService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@ -30,6 +35,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeEvaluation> implements IAeEvaluationService {
@ -38,8 +44,11 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
private final APIProperties apiProperties;
private final StringRedisTemplate redisTemplate;
@Override
public void executeEvaluation(AeEvaluation evaluation) {
String aeType = evaluation.getAeType();
if (aeType.equals(RunModeEnum.VI_PROJECT.name())) {
// 语音交互评估
// 查询流程执行记录
TeFlowViewVO teFlowViewVO = new TeFlowViewVO();
teFlowViewVO.setInstId(evaluation.getInstId());
@ -71,11 +80,15 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
// 场景 1唤醒
case 1: {
String audioUrl = extractAudioUrl(view);
String videoUrl = extractVideoUrl(view);
AeEvaluation eval = new AeEvaluation();
BeanUtil.copyProperties(evaluation, eval);
eval.setAeId(IdUtil.fastSimpleUUID());
eval.setStatus(0); // 待评估
eval.setAvContent(wakeContent);
eval.setAudioPath(audioUrl);
eval.setVideoPath(videoUrl);
evaluations.add(eval);
break;
}
@ -83,6 +96,12 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
// 场景 2单次对话一层循环
case 2: {
view.stream()
.filter(e -> e.getAction().equals(ActionEnum.CAMERA_RECORDING_STOP.getAction()))
.map(x->JSONObject.parseObject(x.getParamsOut()).getJSONObject("videoUrl").getString("videoUrl"))
.collect(Collectors.toList())
.forEach(System.out::println);
Map<String, List<TeNodeInst>> iterationMap = buildSingleIterationMap(view);
for (Map.Entry<String, List<TeNodeInst>> entry : iterationMap.entrySet()) {
@ -172,6 +191,19 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
redisTemplate.opsForList().rightPush("ae:evaluation:queue", JSON.toJSONString(e));
}
runEvaluation(first);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (aeType.equals(RunModeEnum.TI_PROJECT.name())) {
// 触控交互评估
} else {
throw new GlobalException("错误的评估任务类型!仅支持语音交互和触控交互!");
}
}
private void runEvaluation(AeEvaluation first) {
// 构建评估请求
Map<String, String> body = new HashMap<>();
body.put("aeId", first.getAeId());
@ -183,9 +215,44 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
first.setStatus(1);
this.updateById(first);
} catch (Exception e) {
throw new RuntimeException(e);
}
@Override
public void callback(AeEvaluation aeEvaluation) {
String aeId = aeEvaluation.getAeId();
if (StrUtil.isEmpty(aeId)) {
throw new GlobalException("AI评估ID不能为空!");
}
String result = aeEvaluation.getResult();
if (StrUtil.isEmpty(result)) {
throw new GlobalException("AI评估结果不能为空!");
}
AeEvaluation byId = this.getById(aeId);
if (Objects.isNull(byId)) {
log.error("ID为 {} 的评估对象不存在!",aeId);
}
JSONObject jsonObject = JSON.parseObject(result);
String overallResult = jsonObject.getString("overall_result");
int status;
if (StrUtil.isNotEmpty(overallResult) && overallResult.equals("成功")) {
status = 3;
} else {
status = 4;
}
LambdaUpdateWrapper<AeEvaluation> wrapper = Wrappers.lambdaUpdate();
wrapper.eq(AeEvaluation::getAeId, aeId)
.set(AeEvaluation::getResult, result)
.set(AeEvaluation::getStatus, status); // 评估完成/失败
this.update(wrapper);
// 继续下一个评估
String str = redisTemplate.opsForList().leftPop("ae:evaluation:queue");
if (StrUtil.isEmpty(str)) {
return;
}
AeEvaluation evaluation = JSON.parseObject(str, AeEvaluation.class);
runEvaluation(evaluation);
}
private Map<String, List<TeNodeInst>> buildSingleIterationMap(List<TeNodeInst> view) {
@ -224,7 +291,7 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
private String extractAudioUrl(List<TeNodeInst> nodeList) {
TeNodeInst microPhoneInst = nodeList.stream()
.filter(e -> ActionEnum.MICROPHONE_START.getAction().equals(e.getAction()))
.filter(e -> ActionEnum.MICROPHONE_STOP.getAction().equals(e.getAction()))
.findFirst()
.orElse(null);
@ -233,13 +300,13 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
}
JSONObject paramsOut = JSONObject.parseObject(microPhoneInst.getParamsOut());
return paramsOut.getString("audioUrl");
return paramsOut.getJSONObject("audioUrl").getString("audioUrl");
}
private String extractVideoUrl(List<TeNodeInst> nodeList) {
TeNodeInst recordingInst = nodeList.stream()
.filter(e -> ActionEnum.CAMERA_RECORDING_START.getAction().equals(e.getAction()))
.filter(e -> ActionEnum.CAMERA_RECORDING_STOP.getAction().equals(e.getAction()))
.findFirst()
.orElse(null);
@ -248,6 +315,6 @@ public class AeEvaluationServiceImpl extends ServiceImpl<AeEvaluationMapper, AeE
}
JSONObject paramsOut = JSONObject.parseObject(recordingInst.getParamsOut());
return paramsOut.getString("videoUrl");
return paramsOut.getJSONObject("videoUrl").getString("videoUrl");
}
}

View File

@ -114,7 +114,7 @@ public class SecurityConfig
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/test/evaluation/callback","/flow/**","/flowise/**","/kws/**","/ws/**","/api/grpc/**","/show/**","/node-red/**","/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.antMatchers("/system/file/upload","/evaluation/callback","/flow/**","/flowise/**","/kws/**","/ws/**","/api/grpc/**","/show/**","/node-red/**","/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.cmvr.common.core.domain.entity.SysFileInfo;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.List;
public interface ISysFileService extends IService<SysFileInfo> {
@ -12,5 +13,7 @@ public interface ISysFileService extends IService<SysFileInfo> {
public String uploadFile(MultipartFile file, Integer fileType);
public String uploadFile(File file, Integer fileType);
public int deleteFileByIds(Long[] fileIds);
}

View File

@ -18,7 +18,9 @@ import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@ -75,7 +77,7 @@ public class ISysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFileInfo>
}
String url = generUrl(type, fileName);
// 保存文件
saveFileInfo(file, md5, url, fileType);
saveFileInfo(file.getOriginalFilename(),file.getSize(), md5, url, fileType);
log.info("文件上传成功,url:{}", url);
return url;
} catch (IOException e) {
@ -84,6 +86,40 @@ public class ISysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFileInfo>
}
}
@Override
public String uploadFile(File file, Integer fileType) {
try {
// 计算文件的 MD5 校验码
String md5 = DigestUtils.md5DigestAsHex(Files.newInputStream(file.toPath()));
SysFileInfo fileInfo = findByMd5(md5);
if (!Objects.isNull(fileInfo)) {
this.updateById(fileInfo);
// 如果文件已存在直接返回已上传文件的 URL
return fileInfo.getFilePath();
}
// 上传文件到 MinIO
FileType type = FileType.getByCode(fileType);
String fileName = minioService.upload(minioProps.getBucketName(), type.toString(), file);
if (StringUtils.isEmpty(fileName)) {
throw new RuntimeException("文件上传失败");
}
// 生成文件的 URL
String url = generUrl(type, fileName);
// 将文件信息保存到数据库
saveFileInfo(file.getName(),file.length(), md5, url, fileType);
log.info("文件上传成功url: {}", url);
return url;
} catch (IOException e) {
log.error("文件上传失败,{}", e.getMessage(), e);
throw new RuntimeException("文件上传失败");
}
}
@Override
public int deleteFileByIds(Long[] fileIds) {
return this.baseMapper.deleteBatchIds(Arrays.asList(fileIds));
@ -99,10 +135,10 @@ public class ISysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFileInfo>
return getOne(wrapper);
}
private void saveFileInfo(MultipartFile file, String md5, String filePath, Integer fileType) throws IOException {
private void saveFileInfo(String filename,Long size, String md5, String filePath, Integer fileType) throws IOException {
SysFileInfo fileInfo = new SysFileInfo();
fileInfo.setFileName(file.getOriginalFilename());
fileInfo.setFileSize(file.getSize());
fileInfo.setFileName(filename);
fileInfo.setFileSize(size);
fileInfo.setFileType(fileType);
fileInfo.setFilePath(filePath);
fileInfo.setMd5(md5);

View File

@ -47,8 +47,8 @@ public class TaskInstHolder {
}
public void markSuccess(String instId, String itemId, String nodeId, int pendingItemCount, String nodeType,
String paramsOut, String message) {
nodeInstService.logSuccess(instId, itemId, nodeId, paramsOut, message);
String paramsOut, String message, List<Integer> iterations) {
nodeInstService.logSuccess(instId, itemId, nodeId, paramsOut, message,iterations);
syncStatus(instId, TaskStatusEnum.SUCCESS);
// 如果没有下一个检测项要执行 且是end节点 注销任务上下文
if (pendingItemCount == 0 && nodeType.equalsIgnoreCase(NodeTypeEnum.END.getCode())) {
@ -58,8 +58,8 @@ public class TaskInstHolder {
public void markFailed(String instId, String taskId, String itemId,
String nodeId, String nodeType, String operate,
String action, String params, String message) {
nodeInstService.logFailed(instId, taskId, itemId, nodeId, nodeType, operate, action, params, message);
String action, String params, String message, List<Integer> iterations) {
nodeInstService.logFailed(instId, taskId, itemId, nodeId, nodeType, operate, action, params, message,iterations);
syncStatus(instId, TaskStatusEnum.FAILED);
taskContextManager.unregister(instId);

View File

@ -31,20 +31,24 @@ public class FlowEndNodeHandler implements FlowNodeTypeHandler {
String itemId = message.getItemId();
TaskContext context = taskInstHolder.getContext(instId);
RunModeEnum runMode = context.getRunMode();
// if (runMode.equals(RunModeEnum.NORMAL) || runMode.equals(RunModeEnum.TRIAL)) {
// // 仅语音交互和触控交互进行评估
// return TaskNodeExecuteResult.success();
// }
JSONObject jsonObject = new JSONObject();
jsonObject.put("instId", instId);
jsonObject.put("itemId", itemId);
jsonObject.put("aeType", "VOICE");
jsonObject.put("aeType", RunModeEnum.VI_PROJECT.name());
executor.submit(() -> {
Thread.currentThread().setName("evaluation-thread-" + Thread.currentThread().getId());
try {
// log.info("异步任务开始执行threadName={}, instId={}, taskId={}", Thread.currentThread().getName(), instId, taskId);
log.info("异步评估任务开始执行threadName={}, instId={}, itemId={}", Thread.currentThread().getName(), instId, itemId);
exAeEvaluationService.executeEvaluation(jsonObject);
// log.info("异步任务执行结束threadName={}, instId={}, taskId={}", Thread.currentThread().getName(), instId, taskId);
log.info("异步评估任务执行结束threadName={}, instId={}, itemId={}", Thread.currentThread().getName(), instId, itemId);
} catch (Exception e) {
// log.error("任务执行异常threadName={}, instId={}, taskId={}, 错误={}", Thread.currentThread().getName(), instId, taskId, e.getMessage(), e);
log.error("评估任务执行异常threadName={}, instId={}, itemId={}, 错误={}", Thread.currentThread().getName(), instId, itemId, e.getMessage(), e);
Thread.currentThread().interrupt(); // 保持中断状态
}
});

View File

@ -100,8 +100,8 @@ public class FlowTaskRuntimeEntry implements FlowTaskRuntimeService {
null, null,
ActionEnum.NONE.getOperate(), ActionEnum.NONE.getAction(),
JSON.toJSONString(taskExecuteTrailVO),
"试运行异常:" + StrUtil.sub(ExceptionUtil.getMessage(e), 0, 480)
);
"试运行异常:" + StrUtil.sub(ExceptionUtil.getMessage(e), 0, 480),
null);
throw new GlobalException("试运行异常:" + e.getMessage());
}
}
@ -149,8 +149,8 @@ public class FlowTaskRuntimeEntry implements FlowTaskRuntimeService {
instId, taskId, null, null, null,
ActionEnum.NONE.getOperate(), ActionEnum.NONE.getAction(),
JSON.toJSONString(originalVO),
"任务执行异常:" + StrUtil.sub(ExceptionUtil.getMessage(e), 0, 480)
);
"任务执行异常:" + StrUtil.sub(ExceptionUtil.getMessage(e), 0, 480),
null);
throw new GlobalException("任务执行异常:" + e.getMessage());
}
}

View File

@ -40,7 +40,8 @@ public class FlowExceptionInterceptor implements FlowMsgPreInterceptor {
message.getNodeType(),
message.getAction().getOperate(), message.getAction().getOperate(),
ObjUtil.isEmpty(message.getInputParams()) ? null : message.getInputParams().toJSONString(),
"执行异常:" + StrUtil.sub(ExceptionUtil.getMessage(e), 0, 480)
"执行异常:" + StrUtil.sub(ExceptionUtil.getMessage(e), 0, 480),
message.getIterations()
);
log.error("节点执行异常instId={}, nodeId={}, action={}",
message.getInstId(), message.getNodeId(), message.getAction(), e);

View File

@ -45,7 +45,7 @@ public class FlowLoggingInterceptor implements FlowMsgPreInterceptor {
}
taskInstHolder.markSuccess(message.getInstId(), message.getItemId(), message.getNodeId(), message.getPendingItemCount(), message.getNodeType(),
result.getOutputParams().toJSONString(), StrUtil.format("[{}] 执行成功", message.getAction()));
result.getOutputParams().toJSONString(), StrUtil.format("[{}] 执行成功", message.getAction()), message.getIterations());
long end = System.currentTimeMillis();
log.info("[ {} ]节点执行结束, 参数[ {} ], 循环次数[ {} ], 耗时[ {} ]", message.getAction(), message.getInputParams(), message.getLoopNum(), end - start);

View File

@ -1,13 +1,12 @@
package com.cmvr.test.flow.runtime.operator.edge;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.enums.FileType;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeCameraService;
import com.cmvr.system.service.ISysFileService;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.builder.FlowNodeWrapper;
import com.cmvr.test.flow.context.TaskInstHolder;
@ -16,7 +15,7 @@ import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.io.File;
@Service
@RequiredArgsConstructor
@ -24,6 +23,7 @@ public class EdgeCameraOperateService implements EdgeOperateService {
private final EdgeCameraService edgeCameraService;
private final TaskInstHolder taskInstHolder;
private final ISysFileService sysFileService;
@Override
public boolean supports(ActionEnum action) {
@ -94,32 +94,18 @@ public class EdgeCameraOperateService implements EdgeOperateService {
}
case CAMERA_RECORDING_START: {
// 生成视频路径
// String videoUrl = edgeCameraService.startRecording(edgeCommonVO);
// videoUrl = StrUtil.format("{}/{}", "http://10.148.108.162/system/video", StrUtil.subAfter(videoUrl, "/", true));
List<String> videoList = CollUtil.newArrayList(
"http://192.168.0.100:9000/cmvr-iot/VIDEO/20251223/1766477538765.mp4",
"http://192.168.0.100:9000/cmvr-iot/VIDEO/20251223/1766477608318.mp4",
"http://192.168.0.100:9000/cmvr-iot/VIDEO/20251223/1766477631974.mp4",
"http://192.168.0.100:9000/cmvr-iot/VIDEO/20251223/1766478299920.mp4",
"http://192.168.0.100:9000/cmvr-iot/VIDEO/20251223/1766477777557.mp4"
);
Integer index = CollUtil.getLast(message.getIterations());
String videoUrl = videoList.get(index -1);
// String videoUrl = "http://10.148.108.162/system/video/cam4_1752225205349.mp4";
// JSONArray videoUrls = new JSONArray();
// if (ObjUtil.isNotEmpty(upstreamOutput)) {
// JSONArray upstream = upstreamOutput.getJSONArray("videoUrl");
// if (ObjUtil.isNotEmpty(upstream)) {
// videoUrls.addAll(upstream);
// }
// }
//
// videoUrls.add(videoUrl);
output.put("videoUrl", videoUrl);
output.put("isPlay", false);
output.put("type", FileType.VIDEO.code());
// String videoUrl = StrUtil.format("{}/{}_{}.mp4", "/home/share/assets/video", deviceId, System.currentTimeMillis());
String videoUrl = "/home/xtkuang/Projects/models/assets/video/IMG_0524_silent.mp4";
// 创建一个新的 JSONObject 来保存视频路径和其他信息
JSONObject videoInfo = new JSONObject();
videoInfo.put("videoUrl", videoUrl);
videoInfo.put("isPlay", false); // 初始状态为不可播放
videoInfo.put("type", FileType.VIDEO.code()); // 文件类型为视频
// 将视频路径数组保存到 output
output.put("videoInfo", videoInfo);
break;
}
@ -136,11 +122,15 @@ public class EdgeCameraOperateService implements EdgeOperateService {
.orElseThrow(() -> new IllegalStateException("未找到视频录制开始节点"));
// 从上下文获取上游节点存储的参数 获取视频地址
JSONObject pre = taskInstHolder.getNodeOutParams(instId, startRecordNode.getNodeId(),message.getIterations());
JSONArray videoUrl = pre.getJSONArray("videoUrl");
output.put("videoUrl", videoUrl);
output.put("isPlay", true);
output.put("type", FileType.VIDEO.code());
JSONObject preStart = taskInstHolder.getNodeOutParams(instId, startRecordNode.getNodeId(), message.getIterations());
JSONObject videoInfo = preStart.getJSONObject("videoInfo");
videoInfo.put("isPlay", true);
// String videoUrl = videoInfo.getString("videoUrl");
// String url = sysFileService.uploadFile(new File(videoUrl), FileType.VIDEO.code());
// videoInfo.put("videoUrl", url);
// 将视频路径数组保存到 output
output.put("videoUrl", videoInfo);
break;
}
default:

View File

@ -1,20 +1,29 @@
package com.cmvr.test.flow.runtime.operator.edge;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.enums.FileType;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeMicrophoneService;
import com.cmvr.system.service.ISysFileService;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.builder.FlowNodeWrapper;
import com.cmvr.test.flow.context.TaskInstHolder;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.io.File;
@Service
@RequiredArgsConstructor
public class EdgeMicrophoneOperateService implements EdgeOperateService {
private final EdgeMicrophoneService edgeMicrophoneService;
private final TaskInstHolder taskInstHolder;
private final ISysFileService sysFileService;
@Override
public boolean supports(ActionEnum action) {
@ -37,13 +46,42 @@ public class EdgeMicrophoneOperateService implements EdgeOperateService {
switch (action) {
case MICROPHONE_START: {
// String audioPath = edgeMicrophoneService.startRecord(edgeCommonVO);
String audioPath = "xxx";
// String audioPath = StrUtil.format("{}/{}_{}.wav", "/home/share/assets/audio", deviceId, System.currentTimeMillis());
String audioPath = "/home/xtkuang/Projects/models/assets/video/IMG_0524_audio.wav";
output.put("audioUrl", audioPath);
// 创建一个新的 JSONObject 来保存音频路径和其他信息
JSONObject audioInfo = new JSONObject();
audioInfo.put("audioUrl", audioPath);
audioInfo.put("isPlay", false); // 初始状态为不可播放
audioInfo.put("type", FileType.AUDIO.code());
output.put("audioInfo", audioInfo);
break;
}
case MICROPHONE_STOP: {
// edgeMicrophoneService.stopRecord(edgeCommonVO);
// 获取视频录制开始节点的id
FlowNodeWrapper startRecordNode = message.getGraph()
.getNodeMap()
.values()
.stream()
.filter(node -> ActionEnum.MICROPHONE_START.equals(node.getAction()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("未找到音频录制开始节点"));
// 从上下文获取上游节点存储的参数 获取音频地址
JSONObject preStart = taskInstHolder.getNodeOutParams(instId, startRecordNode.getNodeId(), message.getIterations());
JSONObject audioInfo = preStart.getJSONObject("audioInfo");
audioInfo.put("isPlay", true);
// String audioUrl = audioInfo.getString("audioUrl");
// String url = sysFileService.uploadFile(new File(audioUrl), FileType.AUDIO.code());
// audioInfo.put("audioUrl", url);
// 将视频路径数组保存到 output
output.put("audioUrl", audioInfo);
break;
}

View File

@ -41,14 +41,14 @@ public interface ITeNodeInstService extends IService<TeNodeInst> {
* 节点执行后更新输出参数等信息/记录节点成功完成
*/
void logSuccess(String instId, String itemId, String nodeId,
String paramsOut, String message);
String paramsOut, String message, List<Integer> iterations);
/**
* 记录节点执行失败
*/
void logFailed(String instId, String taskId, String itemId,
String nodeId, String nodeType,
String operate, String action, String params, String message);
String operate, String action, String params, String message, List<Integer> iterations);
/**
* 删除节点

View File

@ -1,5 +1,6 @@
package com.cmvr.test.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -55,11 +56,12 @@ public class ITeNodeInstServiceImpl extends ServiceImpl<TeNodeInstMapper, TeNode
@Override
public void logSuccess(String instId, String itemId, String nodeId,
String paramsOut, String message) {
String paramsOut, String message, List<Integer> iterations) {
LambdaUpdateWrapper<TeNodeInst> update = new LambdaUpdateWrapper<>();
update.eq(TeNodeInst::getInstId, instId)
.eq(TeNodeInst::getItemId, itemId)
.eq(TeNodeInst::getNodeId, nodeId)
.eq(TeNodeInst::getIteration, getIteration(iterations))
.set(TeNodeInst::getParamsOut, paramsOut)
.set(TeNodeInst::getStatus, TaskStatusEnum.SUCCESS.name())
.set(TeNodeInst::getMessage, message)
@ -71,12 +73,13 @@ public class ITeNodeInstServiceImpl extends ServiceImpl<TeNodeInstMapper, TeNode
@Override
public void logFailed(String instId, String taskId, String itemId,
String nodeId, String nodeType,
String operate, String action, String params, String message) {
String operate, String action, String params, String message, List<Integer> iterations) {
LambdaQueryWrapper<TeNodeInst> query = Wrappers.lambdaQuery();
query.eq(TeNodeInst::getInstId, instId)
.eq(TeNodeInst::getItemId, itemId)
.eq(TeNodeInst::getNodeId, nodeId);
.eq(TeNodeInst::getNodeId, nodeId)
.eq(CollUtil.isNotEmpty(iterations), TeNodeInst::getIteration, getIteration(iterations));
TeNodeInst existing = this.getOne(query, false);

View File

@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.evaluation.model.domain.AeEvaluation;
import com.cmvr.test.enums.FlowSceneTypeEnum;
import com.cmvr.test.enums.RunModeEnum;
import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService;
@ -15,7 +14,6 @@ import com.cmvr.test.model.domain.TeDetectionItem;
import com.cmvr.test.model.domain.TeTaskConfigInfo;
import com.cmvr.test.model.domain.TeTaskInst;
import com.cmvr.test.model.vo.TeQueryProjectOrchestraItemVO;
import com.cmvr.test.model.vo.TeTaskExecuteNormalVO;
import com.cmvr.test.model.vo.TeTaskExecuteProjectVO;
import com.cmvr.test.service.ITeDetectionItemService;
import com.cmvr.test.service.ITeTaskConfigInfoService;
@ -119,19 +117,16 @@ public class ViProjectServiceImpl extends ServiceImpl<ViProjectMapper, ViProject
@Override
public String executeProject(TeTaskExecuteProjectVO taskExecuteProjectVO) {
TeTaskExecuteNormalVO normalVO = new TeTaskExecuteNormalVO();
normalVO.setTaskId(taskExecuteProjectVO.getProjectId());
TeTaskExecuteProjectVO normalVO = new TeTaskExecuteProjectVO();
normalVO.setProjectId(taskExecuteProjectVO.getProjectId());
normalVO.setRunParams(taskExecuteProjectVO.getRunParams());
normalVO.setTerminalId(taskExecuteProjectVO.getTerminalId());
String instId = flowTaskRuntimeService.executeTask(normalVO);
String instId = flowTaskRuntimeService.executeProjectTask(normalVO);
this.update(
new LambdaUpdateWrapper<ViProject>()
.eq(ViProject::getProjectId, taskExecuteProjectVO.getProjectId())
.set(ViProject::getStatus, "0")
);
// 异步执行评估
AeEvaluation aeEvaluation = new AeEvaluation();
aeEvaluation.setInstId(instId);
return instId;
}