diff --git a/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/evaluation/AeEvaluationController.java b/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/evaluation/AeEvaluationController.java index 7c352bf..d7daae3 100644 --- a/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/evaluation/AeEvaluationController.java +++ b/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/evaluation/AeEvaluationController.java @@ -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(); } } diff --git a/cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/service/impl/EdgeMicrophoneServiceImpl.java b/cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/service/impl/EdgeMicrophoneServiceImpl.java index dee167a..57eeb79 100644 --- a/cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/service/impl/EdgeMicrophoneServiceImpl.java +++ b/cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/service/impl/EdgeMicrophoneServiceImpl.java @@ -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() diff --git a/cmvr-iot-common/src/main/java/com/cmvr/common/core/minio/MinioService.java b/cmvr-iot-common/src/main/java/com/cmvr/common/core/minio/MinioService.java index b287c1a..b46edc3 100644 --- a/cmvr-iot-common/src/main/java/com/cmvr/common/core/minio/MinioService.java +++ b/cmvr-iot-common/src/main/java/com/cmvr/common/core/minio/MinioService.java @@ -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; + } + /** * 文件上传 * diff --git a/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/model/domain/AeEvaluation.java b/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/model/domain/AeEvaluation.java index 916fe63..930bbce 100644 --- a/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/model/domain/AeEvaluation.java +++ b/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/model/domain/AeEvaluation.java @@ -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") diff --git a/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/IAeEvaluationService.java b/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/IAeEvaluationService.java index 586563c..b6df08d 100644 --- a/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/IAeEvaluationService.java +++ b/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/IAeEvaluationService.java @@ -9,4 +9,9 @@ public interface IAeEvaluationService extends IService { * 执行语音交互评估 */ public void executeEvaluation(AeEvaluation evaluation); + + /** + * 评估回调 + */ + void callback(AeEvaluation aeEvaluation); } diff --git a/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/impl/AeEvaluationServiceImpl.java b/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/impl/AeEvaluationServiceImpl.java index 6c15c47..8e9f23e 100644 --- a/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/impl/AeEvaluationServiceImpl.java +++ b/cmvr-iot-evaluation/src/main/java/com/cmvr/evaluation/service/impl/AeEvaluationServiceImpl.java @@ -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 implements IAeEvaluationService { @@ -38,154 +44,215 @@ public class AeEvaluationServiceImpl extends ServiceImpl view = teNodeInstService.view(teFlowViewVO); - List view = teNodeInstService.view(teFlowViewVO); + // 解析 VI_SCHEME 参数 + TeNodeInst viSchemeInst = view.stream() + .filter(e -> ActionEnum.VI_SCHEME.getAction().equals(e.getAction())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("未找到 VI_SCHEME 节点")); - // 解析 VI_SCHEME 参数 - TeNodeInst viSchemeInst = view.stream() - .filter(e -> ActionEnum.VI_SCHEME.getAction().equals(e.getAction())) - .findFirst() - .orElseThrow(() -> new IllegalStateException("未找到 VI_SCHEME 节点")); + JSONObject paramsOut = JSONObject.parseObject(viSchemeInst.getParamsOut()); - JSONObject paramsOut = JSONObject.parseObject(viSchemeInst.getParamsOut()); + Integer sceneType = paramsOut.getInteger("sceneType"); - Integer sceneType = paramsOut.getInteger("sceneType"); + // 唤醒语料 + JSONObject wakeCorpus = paramsOut.getJSONObject("wakeCorpus"); + String wakeContent = wakeCorpus != null ? wakeCorpus.getString("textContent") : null; - // 唤醒语料 - JSONObject wakeCorpus = paramsOut.getJSONObject("wakeCorpus"); - String wakeContent = wakeCorpus != null ? wakeCorpus.getString("textContent") : null; + // 测试语料 + JSONArray testCorpusArray = paramsOut.getJSONArray("testCorpus"); - // 测试语料 - JSONArray testCorpusArray = paramsOut.getJSONArray("testCorpus"); + // 构建评估任务列表 + List evaluations = new LinkedList<>(); - // 构建评估任务列表 - List evaluations = new LinkedList<>(); - - switch (sceneType) { - - // 场景 1:唤醒 - case 1: { - AeEvaluation eval = new AeEvaluation(); - BeanUtil.copyProperties(evaluation, eval); - eval.setAeId(IdUtil.fastSimpleUUID()); - eval.setStatus(0); // 待评估 - eval.setAvContent(wakeContent); - evaluations.add(eval); - break; - } - - // 场景 2:单次对话(一层循环) - case 2: { - - Map> iterationMap = buildSingleIterationMap(view); - - for (Map.Entry> entry : iterationMap.entrySet()) { - - String iterationKey = entry.getKey(); // "1" / "2" / ... - List nodeList = entry.getValue(); - - int index = Integer.parseInt(iterationKey) - 1; - JSONObject corpus = testCorpusArray.getJSONObject(index); - - String content = StrUtil.join(", ", - wakeContent, - corpus.getString("textContent") - ); - - String audioUrl = extractAudioUrl(nodeList); - String videoUrl = extractVideoUrl(nodeList); + switch (sceneType) { + // 场景 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(content); + eval.setStatus(0); // 待评估 + eval.setAvContent(wakeContent); eval.setAudioPath(audioUrl); eval.setVideoPath(videoUrl); - evaluations.add(eval); + break; } - break; - } - // 场景 3:连续对话(嵌套循环) - case 3: { + // 场景 2:单次对话(一层循环) + case 2: { - Map> ContinuousIterationMap = buildContinuousIterationMap(view); - for (Map.Entry> entry : ContinuousIterationMap.entrySet()) { + 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); - String key = entry.getKey(); - List value = entry.getValue(); + Map> iterationMap = buildSingleIterationMap(view); - // 每一个 outer iteration 代表一次完整连续对话 - StringBuilder contentBuilder = new StringBuilder(wakeContent); + for (Map.Entry> entry : iterationMap.entrySet()) { + String iterationKey = entry.getKey(); // "1" / "2" / ... + List nodeList = entry.getValue(); - int corpusIndex = Integer.parseInt(key) - 1; - JSONArray corpus = testCorpusArray.getJSONArray(corpusIndex); + int index = Integer.parseInt(iterationKey) - 1; + JSONObject corpus = testCorpusArray.getJSONObject(index); - String corpusText = corpus.stream() - .filter(Objects::nonNull) - .map(obj -> ((JSONObject) obj).getString("textContent")) - .filter(StrUtil::isNotEmpty) - .collect(Collectors.joining(", ")); + String content = StrUtil.join(", ", + wakeContent, + corpus.getString("textContent") + ); - if (StrUtil.isNotEmpty(corpusText)) { - contentBuilder.append(", ").append(corpusText); + String audioUrl = extractAudioUrl(nodeList); + String videoUrl = extractVideoUrl(nodeList); + + AeEvaluation eval = new AeEvaluation(); + BeanUtil.copyProperties(evaluation, eval); + eval.setAeId(IdUtil.fastSimpleUUID()); + eval.setStatus(0); + eval.setAvContent(content); + eval.setAudioPath(audioUrl); + eval.setVideoPath(videoUrl); + + evaluations.add(eval); } - - String audioUrl = extractAudioUrl(value); - String videoUrl = extractVideoUrl(value); - - AeEvaluation eval = new AeEvaluation(); - BeanUtil.copyProperties(evaluation, eval); - eval.setAeId(IdUtil.fastSimpleUUID()); - eval.setStatus(0); - eval.setAvContent(contentBuilder.toString()); - eval.setAudioPath(audioUrl); - eval.setVideoPath(videoUrl); - - evaluations.add(eval); + break; } - break; + + // 场景 3:连续对话(嵌套循环) + case 3: { + + Map> ContinuousIterationMap = buildContinuousIterationMap(view); + for (Map.Entry> entry : ContinuousIterationMap.entrySet()) { + + String key = entry.getKey(); + List value = entry.getValue(); + + // 每一个 outer iteration 代表一次完整连续对话 + StringBuilder contentBuilder = new StringBuilder(wakeContent); + + + int corpusIndex = Integer.parseInt(key) - 1; + JSONArray corpus = testCorpusArray.getJSONArray(corpusIndex); + + String corpusText = corpus.stream() + .filter(Objects::nonNull) + .map(obj -> ((JSONObject) obj).getString("textContent")) + .filter(StrUtil::isNotEmpty) + .collect(Collectors.joining(", ")); + + if (StrUtil.isNotEmpty(corpusText)) { + contentBuilder.append(", ").append(corpusText); + } + + String audioUrl = extractAudioUrl(value); + String videoUrl = extractVideoUrl(value); + + AeEvaluation eval = new AeEvaluation(); + BeanUtil.copyProperties(evaluation, eval); + eval.setAeId(IdUtil.fastSimpleUUID()); + eval.setStatus(0); + eval.setAvContent(contentBuilder.toString()); + eval.setAudioPath(audioUrl); + eval.setVideoPath(videoUrl); + + evaluations.add(eval); + } + break; + } + + default: + throw new IllegalArgumentException("未知 sceneType:" + sceneType); } - default: - throw new IllegalArgumentException("未知 sceneType:" + sceneType); - } + try { + // 入库 + this.saveBatch(evaluations); - try { - // 入库 - this.saveBatch(evaluations); + // 提取第一个对象出来 + AeEvaluation first = CollUtil.getFirst(evaluations); + List rest = CollUtil.sub(evaluations, 1, evaluations.size()); + // 剩下的先入队列 + for (AeEvaluation e : rest) { + redisTemplate.opsForList().rightPush("ae:evaluation:queue", JSON.toJSONString(e)); + } - // 提取第一个对象出来 - AeEvaluation first = CollUtil.getFirst(evaluations); - List rest = CollUtil.sub(evaluations, 1, evaluations.size()); - // 剩下的先入队列 - for (AeEvaluation e : rest) { - redisTemplate.opsForList().rightPush("ae:evaluation:queue", JSON.toJSONString(e)); + runEvaluation(first); + } catch (Exception e) { + throw new RuntimeException(e); } - - // 构建评估请求 - Map body = new HashMap<>(); - body.put("aeId", first.getAeId()); - body.put("audioPath", first.getAudioPath()); - body.put("videoPath", first.getVideoPath()); - body.put("content", first.getAvContent()); - body.put("aeType", first.getAeType()); - CallAPIUtil.doPostJson(apiProperties.getEvaluation(), null, body); - - first.setStatus(1); - this.updateById(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 body = new HashMap<>(); + body.put("aeId", first.getAeId()); + body.put("audioPath", first.getAudioPath()); + body.put("videoPath", first.getVideoPath()); + body.put("content", first.getAvContent()); + body.put("aeType", first.getAeType()); + CallAPIUtil.doPostJson(apiProperties.getEvaluation(), null, body); + + first.setStatus(1); + this.updateById(first); + } + + @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 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> buildSingleIterationMap(List view) { @@ -224,7 +291,7 @@ public class AeEvaluationServiceImpl extends ServiceImpl 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 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 { @@ -12,5 +13,7 @@ public interface ISysFileService extends IService { public String uploadFile(MultipartFile file, Integer fileType); + public String uploadFile(File file, Integer fileType); + public int deleteFileByIds(Long[] fileIds); } diff --git a/cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/ISysFileServiceImpl.java b/cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/ISysFileServiceImpl.java index 869b77b..d96f58e 100644 --- a/cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/ISysFileServiceImpl.java +++ b/cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/ISysFileServiceImpl.java @@ -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 } 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 } } + @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 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); diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/context/TaskInstHolder.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/context/TaskInstHolder.java index fff8232..ebb0566 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/context/TaskInstHolder.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/context/TaskInstHolder.java @@ -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 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 iterations) { + nodeInstService.logFailed(instId, taskId, itemId, nodeId, nodeType, operate, action, params, message,iterations); syncStatus(instId, TaskStatusEnum.FAILED); taskContextManager.unregister(instId); diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowEndNodeHandler.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowEndNodeHandler.java index 72b1bd2..d8dec2e 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowEndNodeHandler.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowEndNodeHandler.java @@ -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(); // 保持中断状态 } }); diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskRuntimeEntry.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskRuntimeEntry.java index c4e29ef..22bb8bc 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskRuntimeEntry.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskRuntimeEntry.java @@ -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()); } } diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowExceptionInterceptor.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowExceptionInterceptor.java index f2356d5..26b85bb 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowExceptionInterceptor.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowExceptionInterceptor.java @@ -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); diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowLoggingInterceptor.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowLoggingInterceptor.java index 9f0e665..d10f4eb 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowLoggingInterceptor.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/interceptor/FlowLoggingInterceptor.java @@ -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); diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java index 938b742..5350972 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java @@ -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 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: diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeMicrophoneOperateService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeMicrophoneOperateService.java index dd7119a..c1650e4 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeMicrophoneOperateService.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeMicrophoneOperateService.java @@ -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; } diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/service/ITeNodeInstService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/service/ITeNodeInstService.java index 5ac529a..783724a 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/service/ITeNodeInstService.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/service/ITeNodeInstService.java @@ -41,14 +41,14 @@ public interface ITeNodeInstService extends IService { * 节点执行后更新输出参数等信息/记录节点成功完成 */ void logSuccess(String instId, String itemId, String nodeId, - String paramsOut, String message); + String paramsOut, String message, List 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 iterations); /** * 删除节点 diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/service/impl/ITeNodeInstServiceImpl.java b/cmvr-iot-test/src/main/java/com/cmvr/test/service/impl/ITeNodeInstServiceImpl.java index 28c8d2d..552b0a6 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/service/impl/ITeNodeInstServiceImpl.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/service/impl/ITeNodeInstServiceImpl.java @@ -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 iterations) { LambdaUpdateWrapper 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 iterations) { LambdaQueryWrapper 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); diff --git a/cmvr-iot-vi/src/main/java/com/cmvr/vi/service/impl/ViProjectServiceImpl.java b/cmvr-iot-vi/src/main/java/com/cmvr/vi/service/impl/ViProjectServiceImpl.java index 05ef100..20b0590 100644 --- a/cmvr-iot-vi/src/main/java/com/cmvr/vi/service/impl/ViProjectServiceImpl.java +++ b/cmvr-iot-vi/src/main/java/com/cmvr/vi/service/impl/ViProjectServiceImpl.java @@ -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() .eq(ViProject::getProjectId, taskExecuteProjectVO.getProjectId()) .set(ViProject::getStatus, "0") ); - // 异步执行评估 - AeEvaluation aeEvaluation = new AeEvaluation(); - aeEvaluation.setInstId(instId); return instId; }