Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
fa63a8e039
@ -4,11 +4,14 @@ import com.cmvr.common.core.controller.BaseController;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.page.TableDataInfo;
|
||||
import com.cmvr.vi.model.domain.ViCorpus;
|
||||
import com.cmvr.vi.model.vo.ViContinuousCorpusVO;
|
||||
import com.cmvr.vi.model.vo.ViCorpusVO;
|
||||
import com.cmvr.vi.service.IViCorpusService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -20,11 +23,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 语料库Controller
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
@Api(tags = "语音交互--语料库")
|
||||
@RestController
|
||||
@RequestMapping("/vi/corpus")
|
||||
@ -33,67 +31,97 @@ public class ViCorpusController extends BaseController {
|
||||
|
||||
private final IViCorpusService viCorpusService;
|
||||
|
||||
/**
|
||||
* 查询语料库列表
|
||||
*/
|
||||
@ApiOperation("查询语料库列表")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ViCorpus viCorpus) {
|
||||
startPage();
|
||||
List<ViCorpus> list = viCorpusService.selectViCorpusList(viCorpus);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 导出语料库列表
|
||||
// */
|
||||
// @ApiOperation("查询语料库列表")
|
||||
// @PreAuthorize("@ss.hasPermi('vi:corpus:export')")
|
||||
// @PostMapping("/export")
|
||||
// public void export(HttpServletResponse response, ViCorpus viCorpus) {
|
||||
// List<ViCorpus> list = viCorpusService.selectViCorpusList(viCorpus);
|
||||
// ExcelUtil<ViCorpus> util = new ExcelUtil<ViCorpus>(ViCorpus.class);
|
||||
// util.exportExcel(response, list, "语料库数据");
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取语料库详细信息
|
||||
*/
|
||||
@ApiOperation("根据id获取语料库详细信息")
|
||||
@ApiOperation("根据id获取语料详细信息")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:query')")
|
||||
@GetMapping(value = "/{corpusId}")
|
||||
public AjaxResult getInfo(@PathVariable("corpusId") Long corpusId) {
|
||||
return success(viCorpusService.selectViCorpusByCorpusId(corpusId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增语料库
|
||||
*/
|
||||
@ApiOperation("新增语料库")
|
||||
// ----------------------- 单次对话语料/唤醒语料 --------------------------
|
||||
|
||||
@ApiOperation(value = "查询单次对话语料/唤醒语料列表")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:list')")
|
||||
@PostMapping("/single/list")
|
||||
public TableDataInfo list(@Validated @RequestBody ViCorpusVO viCorpusVO) {
|
||||
startPage();
|
||||
List<ViCorpus> list = viCorpusService.selectViCorpusList(viCorpusVO);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ApiOperation("新增单次对话语料/唤醒语料")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:add')")
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody ViCorpus viCorpus) {
|
||||
return toAjax(viCorpusService.insertViCorpus(viCorpus));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改语料库
|
||||
*/
|
||||
@ApiOperation("修改语料库")
|
||||
@ApiOperation("批量新增单次对话语料/唤醒语料")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:add')")
|
||||
@PostMapping("/insertBatch")
|
||||
public AjaxResult insertBatch(@RequestBody List<ViCorpus> data) {
|
||||
return toAjax(viCorpusService.insertBatch(data));
|
||||
}
|
||||
|
||||
@ApiOperation("删除单次对话语料/唤醒语料")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:remove')")
|
||||
@DeleteMapping("/{corpusIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] corpusIds) {
|
||||
return toAjax(viCorpusService.deleteViCorpusByCorpusIds(corpusIds));
|
||||
}
|
||||
|
||||
@ApiOperation("修改单次对话语料/唤醒语料")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:edit')")
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody ViCorpus viCorpus) {
|
||||
return toAjax(viCorpusService.updateViCorpus(viCorpus));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除语料库
|
||||
*/
|
||||
@ApiOperation("删除语料库")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:remove')")
|
||||
@DeleteMapping("/{corpusIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] corpusIds) {
|
||||
return toAjax(viCorpusService.deleteViCorpusByCorpusIds(corpusIds));
|
||||
// ----------------------- 连续对话 --------------------------
|
||||
|
||||
@ApiOperation("查询连续对话列表")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:list')")
|
||||
@PostMapping("/continuous/list")
|
||||
public TableDataInfo continuousList(@Validated @RequestBody ViCorpusVO viCorpusVO) {
|
||||
startPage();
|
||||
List<ViContinuousCorpusVO> list = viCorpusService.selectContinuousList(viCorpusVO);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ApiOperation("新增连续对话")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:add')")
|
||||
@PostMapping("/continuous/insert")
|
||||
public AjaxResult add(@RequestBody ViContinuousCorpusVO viContinuousCorpusVO) {
|
||||
return toAjax(viCorpusService.insertContinuousViCorpus(viContinuousCorpusVO));
|
||||
}
|
||||
|
||||
@ApiOperation("修改连续对话")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:edit')")
|
||||
@PostMapping("/continuous/update")
|
||||
public AjaxResult update(@RequestBody ViContinuousCorpusVO viContinuousCorpusVO) {
|
||||
return toAjax(viCorpusService.updateContinuousViCorpus(viContinuousCorpusVO));
|
||||
}
|
||||
|
||||
@ApiOperation("删除连续对话")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:remove')")
|
||||
@DeleteMapping("/continuous")
|
||||
public AjaxResult continuousRemove(@RequestBody String[] parentIds) {
|
||||
return toAjax(viCorpusService.deleteViCorpusByParentIds(parentIds));
|
||||
}
|
||||
|
||||
/* @ApiOperation("批量修改")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:edit')")
|
||||
@PostMapping("/updateBatch")
|
||||
public AjaxResult updateBatch(@RequestBody List<ViCorpus> data) {
|
||||
return toAjax(viCorpusService.updateBatch(data));
|
||||
}*/
|
||||
|
||||
/* @ApiOperation("批量新增/修改")
|
||||
@PreAuthorize("@ss.hasPermi('vi:corpus:edit')")
|
||||
@PostMapping("/insertOrUpdateBatch")
|
||||
public AjaxResult insertOrUpdateBatch(@RequestBody List<ViCorpus> data) {
|
||||
return toAjax(viCorpusService.insertOrUpdateBatch(data));
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
package com.cmvr.web.controller.vi;
|
||||
|
||||
import com.cmvr.common.core.controller.BaseController;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.page.TableDataInfo;
|
||||
import com.cmvr.vi.model.domain.ViProject;
|
||||
import com.cmvr.vi.service.IViProjectService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "语音交互--项目管理")
|
||||
@RestController
|
||||
@RequestMapping("/vi/project")
|
||||
@RequiredArgsConstructor
|
||||
public class ViProjectController extends BaseController {
|
||||
|
||||
private final IViProjectService viProjectService;
|
||||
|
||||
@ApiOperation("获取项目列表")
|
||||
@PreAuthorize("@ss.hasPermi('vi:project:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ViProject viProject) {
|
||||
startPage();
|
||||
List<ViProject> list = viProjectService.selectViProjectList(viProject);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ApiOperation("根据id获取项目信息")
|
||||
@PreAuthorize("@ss.hasPermi('vi:project:query')")
|
||||
@GetMapping(value = "/{projectId}")
|
||||
public AjaxResult getInfo(@PathVariable("projectId") String projectId) {
|
||||
return success(viProjectService.selectViProjectByProjectId(projectId));
|
||||
}
|
||||
|
||||
@ApiOperation("添加项目")
|
||||
@PreAuthorize("@ss.hasPermi('vi:project:add')")
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody ViProject viProject) {
|
||||
return toAjax(viProjectService.insertViProject(viProject));
|
||||
}
|
||||
|
||||
@ApiOperation("编辑项目")
|
||||
@PreAuthorize("@ss.hasPermi('vi:project:edit')")
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody ViProject viProject) {
|
||||
return toAjax(viProjectService.updateViProject(viProject));
|
||||
}
|
||||
|
||||
@ApiOperation("删除项目")
|
||||
@PreAuthorize("@ss.hasPermi('vi:project:remove')")
|
||||
@DeleteMapping("/{projectIds}")
|
||||
public AjaxResult remove(@PathVariable String[] projectIds) {
|
||||
return toAjax(viProjectService.deleteViProjectByProjectIds(projectIds));
|
||||
}
|
||||
|
||||
// @ApiOperation("执行项目")
|
||||
@PostMapping("/execute")
|
||||
public AjaxResult execute(@RequestParam("projectId") String projectId) {
|
||||
return AjaxResult.ok(viProjectService.execute(projectId));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package com.cmvr.web.controller.vi;
|
||||
|
||||
import com.cmvr.common.core.controller.BaseController;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.page.TableDataInfo;
|
||||
import com.cmvr.vi.model.domain.ViScheme;
|
||||
import com.cmvr.vi.service.IViSchemeService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "语音交互--方案")
|
||||
@RestController
|
||||
@RequestMapping("/vi/scheme")
|
||||
@RequiredArgsConstructor
|
||||
public class ViSchemeController extends BaseController {
|
||||
private final IViSchemeService viSchemeService;
|
||||
|
||||
@ApiOperation("查询方案列表")
|
||||
@PreAuthorize("@ss.hasPermi('vi:scheme:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ViScheme viScheme) {
|
||||
startPage();
|
||||
List<ViScheme> list = viSchemeService.selectViSchemeList(viScheme);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ApiOperation("根据ID查询方案详细信息")
|
||||
@PreAuthorize("@ss.hasPermi('vi:scheme:query')")
|
||||
@GetMapping(value = "/{schemeId}")
|
||||
public AjaxResult getInfo(@PathVariable("schemeId") String schemeId) {
|
||||
return success(viSchemeService.selectViSchemeBySchemeId(schemeId));
|
||||
}
|
||||
|
||||
@ApiOperation("新增方案")
|
||||
@PreAuthorize("@ss.hasPermi('vi:scheme:add')")
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody ViScheme viScheme) {
|
||||
return toAjax(viSchemeService.insertViScheme(viScheme));
|
||||
}
|
||||
|
||||
@ApiOperation("修改方案")
|
||||
@PreAuthorize("@ss.hasPermi('vi:scheme:edit')")
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody ViScheme viScheme) {
|
||||
return toAjax(viSchemeService.updateViScheme(viScheme));
|
||||
}
|
||||
|
||||
@ApiOperation("删除方案")
|
||||
@PreAuthorize("@ss.hasPermi('vi:scheme:remove')")
|
||||
@DeleteMapping("/{schemeIds}")
|
||||
public AjaxResult remove(@PathVariable String[] schemeIds) {
|
||||
return toAjax(viSchemeService.deleteViSchemeBySchemeIds(schemeIds));
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,11 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.FileImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
@ -42,6 +46,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ -166,6 +171,7 @@ public class EdgeCameraServiceImpl implements EdgeCameraService, EdgeStreamServi
|
||||
throw new GlobalException("响应图片数据无效");
|
||||
}
|
||||
String filePath = saveImage(frame.getData(), frame.getWidth(), frame.getHeight(), false);
|
||||
log.info("彩色图片地址:{}", filePath);
|
||||
// String deepFilePath = saveImage(depthFrame.getData(), depthFrame.getWidth(), depthFrame.getHeight(), true);
|
||||
// List<String> result = new ArrayList<>();
|
||||
try {
|
||||
@ -178,7 +184,7 @@ public class EdgeCameraServiceImpl implements EdgeCameraService, EdgeStreamServi
|
||||
} catch (IOException e) {
|
||||
throw new GlobalException("图片上传失败: " + e.getMessage());
|
||||
} finally {
|
||||
deleteTempFile(filePath);
|
||||
// deleteTempFile(filePath);
|
||||
// deleteTempFile(deepFilePath);
|
||||
// todo 暂时不关闭摄像头
|
||||
// stop(terminalId, deviceId);
|
||||
@ -368,50 +374,50 @@ public class EdgeCameraServiceImpl implements EdgeCameraService, EdgeStreamServi
|
||||
|
||||
String fileName = System.currentTimeMillis() + (isDepth ? "_depth" : "") + ".jpg";
|
||||
String filePath = Paths.get(tempDir.getAbsolutePath(), fileName).toString();
|
||||
|
||||
try {
|
||||
byte[] pixels = pixelData.toByteArray();
|
||||
BufferedImage image;
|
||||
|
||||
if (isDepth) {
|
||||
// 深度图:单通道(灰度)
|
||||
// 灰度深度图
|
||||
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(
|
||||
buffer, width, height, width, 1,
|
||||
new int[]{0}, null
|
||||
);
|
||||
|
||||
ColorModel cm = new ComponentColorModel(
|
||||
ColorSpace.getInstance(ColorSpace.CS_GRAY),
|
||||
false, false,
|
||||
Transparency.OPAQUE,
|
||||
DataBuffer.TYPE_BYTE
|
||||
);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width, 1, new int[]{0}, null);
|
||||
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false,
|
||||
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
image = new BufferedImage(cm, raster, false, null);
|
||||
} else {
|
||||
// 彩色图:RGB 三通道
|
||||
// 彩色RGB
|
||||
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(
|
||||
buffer, width, height, 3 * width, 3,
|
||||
new int[]{2, 1, 0}, null
|
||||
);
|
||||
|
||||
ColorModel cm = new ComponentColorModel(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
false, false,
|
||||
Transparency.OPAQUE,
|
||||
DataBuffer.TYPE_BYTE
|
||||
);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, 3 * width, 3,
|
||||
new int[]{1, 2, 0}, null);
|
||||
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
|
||||
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
image = new BufferedImage(cm, raster, false, null);
|
||||
}
|
||||
|
||||
File output = new File(filePath);
|
||||
ImageIO.write(image, "jpg", output);
|
||||
// 设置高质量JPEG压缩
|
||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
|
||||
ImageWriter writer = writers.next();
|
||||
|
||||
try (FileImageOutputStream output = new FileImageOutputStream(new File(filePath))) {
|
||||
writer.setOutput(output);
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
if (param.canWriteCompressed()) {
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
param.setCompressionQuality(0.95f); // 压缩质量/压缩比 95%
|
||||
}
|
||||
writer.write(null, new IIOImage(image, null, null), param);
|
||||
}
|
||||
writer.dispose();
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new GlobalException(e.getMessage());
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将本地文件转换为 MultipartFile
|
||||
*/
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.cmvr.test.enums;
|
||||
|
||||
/**
|
||||
* 流程场景类型
|
||||
*/
|
||||
public enum FlowSceneTypeEnum {
|
||||
NORMAL, // 普通
|
||||
VI_WAKEUP_CORPUS, // 语音交互-唤醒场景
|
||||
VI_SINGLE_CORPUS, // 语音交互-单次对话场景
|
||||
VI_CONTINUOUS_CORPUS // 语音交互-连续对话场景
|
||||
}
|
||||
@ -174,7 +174,6 @@ public class FlowGraph {
|
||||
* 获取开始节点对象
|
||||
*/
|
||||
public FlowNodeWrapper getStartNode() {
|
||||
System.out.println("这是什么线程:" + Thread.currentThread().getName());
|
||||
String id = findStartNodeId();
|
||||
return getNode(id);
|
||||
}
|
||||
|
||||
@ -10,6 +10,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务实例状态协调器:负责同步日志记录、上下文状态、数据库状态
|
||||
*/
|
||||
@ -40,8 +42,8 @@ public class TaskInstHolder {
|
||||
|
||||
public void markRunning(String instId, String taskId, String itemId,
|
||||
String nodeId, String nodeType, String operate,
|
||||
String action, String paramsIn) {
|
||||
nodeInstService.logRunning(instId, taskId, itemId, nodeId, nodeType, operate, action, paramsIn);
|
||||
String action, String paramsIn, List<Integer> iterations) {
|
||||
nodeInstService.logRunning(instId, taskId, itemId, nodeId, nodeType, operate, action, paramsIn, iterations);
|
||||
}
|
||||
|
||||
public void markSuccess(String instId, String itemId, String nodeId, int pendingItemCount, String nodeType,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.cmvr.test.flow.control;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.cmvr.common.exception.GlobalException;
|
||||
import com.cmvr.test.enums.NodeTypeEnum;
|
||||
import com.cmvr.test.enums.TaskStatusEnum;
|
||||
@ -10,6 +11,7 @@ import com.cmvr.test.flow.context.TaskInstHolder;
|
||||
import com.cmvr.test.flow.context.TaskThreadRegistry;
|
||||
import com.cmvr.test.flow.runtime.engine.FlowItemExecutor;
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteContext;
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
|
||||
import com.cmvr.test.service.ITeNodeInstService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -17,6 +19,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
@ -118,7 +121,7 @@ public class FlowControlService {
|
||||
nodeInstService.update(instId, nodeId, TaskStatusEnum.RUNNING);
|
||||
continue;
|
||||
} else {
|
||||
nodeInstService.delete(instId, nodeId);
|
||||
nodeInstService.delete(instId, nodeId, pendingNode.getIterations());
|
||||
}
|
||||
if (graph.isSub()) {
|
||||
// 找到子图对应的loop节点
|
||||
@ -135,11 +138,21 @@ public class FlowControlService {
|
||||
// 子图中断点,先执行中断的子图节点
|
||||
executor.execute(() -> {
|
||||
CountDownLatch latch = taskThreadRegistry.getLatch(instId, loopCtx.getNode().getNodeId());
|
||||
|
||||
// 恢复时构造新的 message,带上 loopNum 和 iterations
|
||||
TaskNodeExecuteMessage resumeMessage = new TaskNodeExecuteMessage();
|
||||
BeanUtil.copyProperties(pendingNode.getRootMessage(), resumeMessage);
|
||||
resumeMessage.setLoopNum(pendingNode.getLoopIteration()); // 保留中断时的循环次数
|
||||
resumeMessage.setIterations(new ArrayList<>(pendingNode.getIterations())); // 保留中断时的路径
|
||||
|
||||
flowItemExecutor.executeNode(
|
||||
pendingNode.getGraph(), pendingNode.getNode(),
|
||||
pendingNode.getStartNodeId(), pendingNode.getStartInputDefs(),
|
||||
pendingNode.getRootMessage(), latch::countDown,
|
||||
pendingNode.getLoopIteration()
|
||||
pendingNode.getGraph(),
|
||||
pendingNode.getNode(),
|
||||
pendingNode.getStartNodeId(),
|
||||
pendingNode.getStartInputDefs(),
|
||||
resumeMessage,
|
||||
latch::countDown,
|
||||
pendingNode.getIterations()
|
||||
);
|
||||
|
||||
try {
|
||||
@ -152,11 +165,19 @@ public class FlowControlService {
|
||||
} else {
|
||||
// 普通节点直接恢复
|
||||
executor.execute(() -> {
|
||||
TaskNodeExecuteMessage resumeMessage = new TaskNodeExecuteMessage();
|
||||
BeanUtil.copyProperties(pendingNode.getRootMessage(), resumeMessage);
|
||||
resumeMessage.setLoopNum(pendingNode.getLoopIteration()); // 保留循环次数
|
||||
resumeMessage.setIterations(new ArrayList<>(pendingNode.getIterations())); // 保留路径
|
||||
|
||||
flowItemExecutor.executeNode(
|
||||
pendingNode.getGraph(), pendingNode.getNode(),
|
||||
pendingNode.getStartNodeId(), pendingNode.getStartInputDefs(),
|
||||
pendingNode.getRootMessage(), pendingNode.getOnFinished(),
|
||||
pendingNode.getLoopIteration()
|
||||
pendingNode.getGraph(),
|
||||
pendingNode.getNode(),
|
||||
pendingNode.getStartNodeId(),
|
||||
pendingNode.getStartInputDefs(),
|
||||
resumeMessage,
|
||||
pendingNode.getOnFinished(),
|
||||
pendingNode.getIterations()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.cmvr.test.flow.runtime.dispatcher;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.cmvr.common.utils.spring.SpringUtils;
|
||||
import com.cmvr.test.flow.builder.FlowGraph;
|
||||
import com.cmvr.test.flow.builder.FlowNodeWrapper;
|
||||
@ -11,6 +12,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@Slf4j
|
||||
@ -25,7 +28,9 @@ public class FlowLoopNodeHandler implements FlowNodeTypeHandler {
|
||||
String instId = message.getInstId();
|
||||
FlowGraph graph = message.getGraph();
|
||||
// 当前从第几次循环开始
|
||||
int loopNum = message.getLoopNum() < 0 ? 1 : message.getLoopNum();
|
||||
int loopNum = Math.max(message.getLoopNum(), 1);
|
||||
// 父级迭代路径
|
||||
List<Integer> parentIterations = message.getIterations();
|
||||
|
||||
String nodeId = message.getNodeId();
|
||||
FlowNodeWrapper nodeWrapper = graph.getNode(nodeId);
|
||||
@ -50,7 +55,28 @@ public class FlowLoopNodeHandler implements FlowNodeTypeHandler {
|
||||
|
||||
taskThreadRegistry.registerLatch(instId, nodeId, latch);
|
||||
FlowItemExecutor flowItemExecutor = SpringUtils.getBean(FlowItemExecutor.class);
|
||||
flowItemExecutor.executeSubGraph(subGraph, message, latch::countDown, i);
|
||||
|
||||
// 构造新的迭代路径
|
||||
// List<Integer> newIterations = new ArrayList<>(parentIterations);
|
||||
// newIterations.add(i);
|
||||
//
|
||||
// // 克隆 message,避免覆盖父级
|
||||
// TaskNodeExecuteMessage subMessage = new TaskNodeExecuteMessage();
|
||||
// BeanUtil.copyProperties(message, subMessage);
|
||||
//// subMessage.setLoopNum(i); // 当前层循环次数
|
||||
// subMessage.setIterations(newIterations); // 完整迭代路径
|
||||
|
||||
// 构造新的迭代路径
|
||||
List<Integer> newIterations = new ArrayList<>(parentIterations);
|
||||
newIterations.add(i);
|
||||
|
||||
// 克隆 message,并明确设置 loopNum
|
||||
TaskNodeExecuteMessage subMessage = new TaskNodeExecuteMessage();
|
||||
BeanUtil.copyProperties(message, subMessage);
|
||||
// subMessage.setLoopNum(i); // 当前 loop 第 i 次
|
||||
subMessage.setIterations(newIterations); // 完整路径
|
||||
|
||||
flowItemExecutor.executeSubGraph(subGraph, subMessage, latch::countDown, newIterations);
|
||||
|
||||
try {
|
||||
latch.await(); // 等待子图执行完毕后再进行下一轮
|
||||
|
||||
@ -53,12 +53,11 @@ public class FlowItemExecutor {
|
||||
String startNodeId = graph.findStartNodeId();
|
||||
List<FlowParamDef> startInputDefs = graph.getStartInputParamsDefs();
|
||||
|
||||
|
||||
NodeExecutor executor = node ->
|
||||
executeNode(graph, node, startNodeId, startInputDefs, rootMessage, onFinished, -1);
|
||||
|
||||
// 执行 start 节点(同步执行)
|
||||
executeNode(graph, graph.getStartNode(), startNodeId, startInputDefs, rootMessage, onFinished, -1);
|
||||
NodeExecutor executor = node ->
|
||||
executeNode(graph, node, startNodeId, startInputDefs, rootMessage, onFinished, new ArrayList<>());
|
||||
|
||||
executeNode(graph, graph.getStartNode(), startNodeId, startInputDefs, rootMessage, onFinished, new ArrayList<>());
|
||||
|
||||
// 调度后续节点(并发)
|
||||
flowTaskScheduler.start(graph, taskInstHolder.getContext(rootMessage.getInstId()), executor, onFinished);
|
||||
@ -70,7 +69,7 @@ public class FlowItemExecutor {
|
||||
public void executeSubGraph(FlowGraph subGraph,
|
||||
TaskNodeExecuteMessage rootMessage,
|
||||
Runnable onFinished,
|
||||
int loopIteration) {
|
||||
List<Integer> iterations) {
|
||||
|
||||
// 主图中开始节点的 inputParams
|
||||
List<FlowParamDef> startInputDefs = subGraph.getStartInputParamsDefs();
|
||||
@ -81,15 +80,16 @@ public class FlowItemExecutor {
|
||||
|
||||
NodeExecutor executor = node -> executeNode(
|
||||
subGraph, node, subStartNodeId, startInputDefs,
|
||||
rootMessage, onFinished, loopIteration
|
||||
rootMessage, onFinished, iterations
|
||||
);
|
||||
|
||||
// 执行子图起始节点
|
||||
executeNode(subGraph, subStartNode, subStartNodeId, startInputDefs,
|
||||
rootMessage, onFinished, loopIteration);
|
||||
rootMessage, onFinished, iterations);
|
||||
|
||||
// 启动子图调度
|
||||
flowTaskScheduler.subStart(subGraph, taskInstHolder.getContext(rootMessage.getInstId()), executor, onFinished, loopIteration);
|
||||
flowTaskScheduler.subStart(subGraph, taskInstHolder.getContext(rootMessage.getInstId()),
|
||||
executor, onFinished, iterations);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,20 +101,22 @@ public class FlowItemExecutor {
|
||||
List<FlowParamDef> startInputDefs,
|
||||
TaskNodeExecuteMessage rootMessage,
|
||||
Runnable onFinished,
|
||||
int loopIteration) {
|
||||
List<Integer> iterations) {
|
||||
|
||||
String instId = rootMessage.getInstId();
|
||||
String itemId = rootMessage.getItemId();
|
||||
String nodeId = node.getNodeId();
|
||||
log.info("当前执行的是 {} 节点,迭代路径={}", node.getNodeName(), iterations);
|
||||
|
||||
//注册当前线程
|
||||
registerThread(graph, node, startNodeId, startInputDefs, rootMessage, onFinished, loopIteration, instId, itemId, nodeId);
|
||||
registerThread(graph, node, startNodeId, startInputDefs, rootMessage, onFinished, iterations, instId, itemId, nodeId);
|
||||
|
||||
try {
|
||||
// 准备输入参数
|
||||
JSONObject inputParams = paramPreparer.prepare(graph, node, startNodeId, startInputDefs, rootMessage);
|
||||
|
||||
// 创建带上下文的执行消息(关键:注入 graph)
|
||||
TaskNodeExecuteMessage message = buildTaskNodeExecuteMsg(graph, node, rootMessage, nodeId, inputParams, loopIteration);
|
||||
// 创建带上下文的执行消息(关键:注入 graph + iterations)
|
||||
TaskNodeExecuteMessage message = buildTaskNodeExecuteMsg(graph, node, rootMessage, nodeId, inputParams, iterations);
|
||||
|
||||
// 执行责任链
|
||||
TaskNodeExecuteResult result = chainBuilder.build(interceptors, dispatchNodeHandler).apply(message);
|
||||
@ -126,13 +128,14 @@ public class FlowItemExecutor {
|
||||
|
||||
// 分支节点处理
|
||||
if (node.getNodeType() == NodeTypeEnum.BRANCH) {
|
||||
postHandleBranchNode(graph, node, startNodeId, startInputDefs, rootMessage, onFinished, loopIteration, nodeId, inputParams, message);
|
||||
postHandleBranchNode(graph, node, startNodeId, startInputDefs, rootMessage, onFinished, iterations, nodeId, inputParams, message);
|
||||
return;
|
||||
}
|
||||
|
||||
// END 节点 or 子图结束
|
||||
if (node.getNodeType() == NodeTypeEnum.END || node.getNodeType() == NodeTypeEnum.SUB_END) {
|
||||
try {
|
||||
log.info("{} 节点执行,释放线程", node.getNodeType());
|
||||
onFinished.run();
|
||||
} catch (Exception e) {
|
||||
log.error("onFinished 回调异常", e);
|
||||
@ -140,22 +143,24 @@ public class FlowItemExecutor {
|
||||
}
|
||||
|
||||
// 通知调度器继续调度后继节点
|
||||
flowTaskScheduler.markCompleted(message.getInstId(), nodeId, graph, n ->
|
||||
executeNode(graph, n, startNodeId, startInputDefs, rootMessage, onFinished, loopIteration), loopIteration);
|
||||
flowTaskScheduler.markCompleted(
|
||||
message.getInstId(), nodeId, graph,
|
||||
n -> executeNode(graph, n, startNodeId, startInputDefs, rootMessage, onFinished, iterations),
|
||||
iterations
|
||||
);
|
||||
} finally {
|
||||
// 如果不是暂停全中断
|
||||
TaskContext ctx = taskInstHolder.getContext(instId);
|
||||
if (ctx == null || !ctx.isPaused()) {
|
||||
taskThreadRegistry.unregisterNodeContext(instId, itemId, nodeId);
|
||||
if (node.getNodeType().equals(NodeTypeEnum.SUB_START)) {
|
||||
log.info("subStart注銷了");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TaskNodeExecuteMessage buildTaskNodeExecuteMsg(FlowGraph graph, FlowNodeWrapper node, TaskNodeExecuteMessage rootMessage, String nodeId, JSONObject inputParams, int loopIteration) {
|
||||
private static TaskNodeExecuteMessage buildTaskNodeExecuteMsg(FlowGraph graph, FlowNodeWrapper node,
|
||||
TaskNodeExecuteMessage rootMessage, String nodeId,
|
||||
JSONObject inputParams, List<Integer> iterations) {
|
||||
TaskNodeExecuteMessage message = new TaskNodeExecuteMessage();
|
||||
BeanUtil.copyProperties(rootMessage, message);
|
||||
message.setNodeId(nodeId);
|
||||
@ -163,11 +168,18 @@ public class FlowItemExecutor {
|
||||
message.setAction(node.getAction());
|
||||
message.setInputParams(inputParams);
|
||||
message.setGraph(graph);
|
||||
message.setLoopNum(loopIteration);
|
||||
// 只设置 iterations
|
||||
message.setIterations(new ArrayList<>(iterations));
|
||||
|
||||
// loopNum 不在这里计算,而是由 FlowLoopNodeHandler / resume 显式写入
|
||||
// 如果 rootMessage 里已经带了 loopNum,就沿用它
|
||||
message.setLoopNum(rootMessage.getLoopNum());
|
||||
return message;
|
||||
}
|
||||
|
||||
private void postHandleBranchNode(FlowGraph graph, FlowNodeWrapper node, String startNodeId, List<FlowParamDef> startInputDefs, TaskNodeExecuteMessage rootMessage, Runnable onFinished, int loopIteration, String nodeId, JSONObject inputParams, TaskNodeExecuteMessage message) {
|
||||
private void postHandleBranchNode(FlowGraph graph, FlowNodeWrapper node, String startNodeId,
|
||||
List<FlowParamDef> startInputDefs, TaskNodeExecuteMessage rootMessage, Runnable onFinished,
|
||||
List<Integer> iterations, String nodeId, JSONObject inputParams, TaskNodeExecuteMessage message) {
|
||||
String matchedAnchorId = FlowBranchEvaluator.evaluate(nodeId, node.getBranchConditions(), inputParams);
|
||||
FlowEdge matchedEdge = graph.getEdgeByFromAnchorId(matchedAnchorId);
|
||||
|
||||
@ -190,12 +202,13 @@ public class FlowItemExecutor {
|
||||
message.getInstId(),
|
||||
matchedEdge.getTo(),
|
||||
graph,
|
||||
n -> executeNode(graph, n, startNodeId, startInputDefs, rootMessage, onFinished, loopIteration),
|
||||
loopIteration
|
||||
n -> executeNode(graph, n, startNodeId, startInputDefs, rootMessage, onFinished, iterations),
|
||||
iterations
|
||||
);
|
||||
}
|
||||
|
||||
private void registerThread(FlowGraph graph, FlowNodeWrapper node, String startNodeId, List<FlowParamDef> startInputDefs, TaskNodeExecuteMessage rootMessage, Runnable onFinished, int loopIteration, String instId, String itemId, String nodeId) {
|
||||
private void registerThread(FlowGraph graph, FlowNodeWrapper node, String startNodeId, List<FlowParamDef> startInputDefs,
|
||||
TaskNodeExecuteMessage rootMessage, Runnable onFinished, List<Integer> iterations, String instId, String itemId, String nodeId) {
|
||||
TaskNodeExecuteContext nodeExecuteContext = new TaskNodeExecuteContext();
|
||||
nodeExecuteContext.setThread(Thread.currentThread());
|
||||
nodeExecuteContext.setGraph(graph);
|
||||
@ -204,7 +217,8 @@ public class FlowItemExecutor {
|
||||
nodeExecuteContext.setNode(node);
|
||||
nodeExecuteContext.setOnFinished(onFinished);
|
||||
nodeExecuteContext.setRootMessage(rootMessage);
|
||||
nodeExecuteContext.setLoopIteration(loopIteration);
|
||||
// nodeExecuteContext.setLoopIteration(loopIteration);
|
||||
nodeExecuteContext.setIterations(new ArrayList<>(iterations));
|
||||
taskThreadRegistry.registerNodeContext(instId, itemId, nodeId, nodeExecuteContext);
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -32,7 +33,6 @@ public class FlowTaskScheduler {
|
||||
TaskContext context,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
Runnable onFinished) {
|
||||
|
||||
String instId = context.getInstId();
|
||||
String startNodeId = graph.findStartNodeId();
|
||||
List<String> nextNodes = graph.getNextNodes(startNodeId);
|
||||
@ -44,7 +44,7 @@ public class FlowTaskScheduler {
|
||||
}
|
||||
|
||||
for (String nextId : nextNodes) {
|
||||
scheduleNode(instId, nextId, graph, executorFunc, -1);
|
||||
scheduleNode(instId, nextId, graph, executorFunc, new ArrayList<>()); // 空 iterations
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,8 +55,7 @@ public class FlowTaskScheduler {
|
||||
TaskContext context,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
Runnable onFinished,
|
||||
int loopIteration) {
|
||||
|
||||
List<Integer> iterations) {
|
||||
String instId = context.getInstId();
|
||||
FlowNodeWrapper startNode = graph.findSubStartNodeId();
|
||||
List<String> nextNodes = graph.getNextNodes(startNode.getNodeId());
|
||||
@ -67,16 +66,15 @@ public class FlowTaskScheduler {
|
||||
return;
|
||||
}
|
||||
|
||||
// 并发调度后继节点
|
||||
for (String nextId : nextNodes) {
|
||||
scheduleNode(instId, nextId, graph, executorFunc, loopIteration);
|
||||
scheduleNode(instId, nextId, graph, executorFunc, iterations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 某节点执行完成后,判断并调度其所有后继节点
|
||||
*/
|
||||
public void markCompleted(String instId,
|
||||
/* public void markCompleted(String instId,
|
||||
String nodeId,
|
||||
FlowGraph graph,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
@ -99,12 +97,38 @@ public class FlowTaskScheduler {
|
||||
scheduleNode(instId, nextId, graph, executorFunc, loopIteration);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
/**
|
||||
* 某节点执行完成后,判断并调度其所有后继节点
|
||||
*/
|
||||
public void markCompleted(String instId,
|
||||
String nodeId,
|
||||
FlowGraph graph,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
List<Integer> iterations) {
|
||||
List<String> nextNodes = graph.getNextNodes(nodeId);
|
||||
|
||||
for (String nextId : nextNodes) {
|
||||
String key = FlowSchedulerCache.buildKey(instId, nextId, iterations);
|
||||
|
||||
// 使用 Set 来记录已完成的前驱节点
|
||||
Set<String> completedPreNodes = schedulerCache.getOrInitCompletedPre(key);
|
||||
// 添加当前节点作为已完成的前驱节点
|
||||
completedPreNodes.add(nodeId);
|
||||
|
||||
int preCount = graph.getPredecessors().getOrDefault(nextId, Collections.emptyList()).size();
|
||||
|
||||
// 如果当前节点的所有前驱节点都已完成,则调度后继节点
|
||||
if (completedPreNodes.size() == preCount) {
|
||||
scheduleNode(instId, nextId, graph, executorFunc, iterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交节点执行任务(只调度一次)
|
||||
*/
|
||||
private void scheduleNode(String instId,
|
||||
/* private void scheduleNode(String instId,
|
||||
String nodeId,
|
||||
FlowGraph graph,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
@ -112,6 +136,31 @@ public class FlowTaskScheduler {
|
||||
|
||||
String key = FlowSchedulerCache.buildKey(instId, nodeId, loopIteration);
|
||||
|
||||
// 如果已经调度过,则不再重复调度
|
||||
if (schedulerCache.trySchedule(key)) {
|
||||
executor.submit(() -> {
|
||||
Thread.currentThread().setName("node-thread-" + nodeId);
|
||||
try {
|
||||
executorFunc.execute(graph.getNode(nodeId));
|
||||
} catch (Exception e) {
|
||||
if (ExceptionUtil.getRootCause(e) instanceof InterruptedException) {
|
||||
return;
|
||||
}
|
||||
log.error("节点执行异常:instId={}, nodeId={}, err={}", instId, nodeId, e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
/**
|
||||
* 提交节点执行任务(除循环只调度一次)
|
||||
*/
|
||||
private void scheduleNode(String instId,
|
||||
String nodeId,
|
||||
FlowGraph graph,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
List<Integer> iterations) {
|
||||
String key = FlowSchedulerCache.buildKey(instId, nodeId, iterations);
|
||||
|
||||
// 如果已经调度过,则不再重复调度
|
||||
if (schedulerCache.trySchedule(key)) {
|
||||
executor.submit(() -> {
|
||||
@ -132,9 +181,9 @@ public class FlowTaskScheduler {
|
||||
String nodeId,
|
||||
FlowGraph graph,
|
||||
FlowItemExecutor.NodeExecutor executorFunc,
|
||||
int loopIteration) {
|
||||
List<Integer> iterations) {
|
||||
|
||||
String key = FlowSchedulerCache.buildKey(instId, nodeId, loopIteration);
|
||||
String key = FlowSchedulerCache.buildKey(instId, nodeId, iterations);
|
||||
if (schedulerCache.trySchedule(key)) {
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
|
||||
@ -3,6 +3,7 @@ package com.cmvr.test.flow.runtime.engine.support;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@ -34,4 +35,12 @@ public class FlowSchedulerCache {
|
||||
? StrUtil.format("{}_{}_{}", instId, nodeId, loopIteration)
|
||||
: StrUtil.format("{}_{}", instId, nodeId);
|
||||
}
|
||||
|
||||
public static String buildKey(String instId, String nodeId, List<Integer> iterations) {
|
||||
StringBuilder sb = new StringBuilder(instId).append(":").append(nodeId);
|
||||
for (int it : iterations) {
|
||||
sb.append(":").append(it);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,8 @@ public class FlowLoggingInterceptor implements FlowMsgPreInterceptor {
|
||||
// todo 判断一下是否为LOOP重入
|
||||
if (!(message.getNodeType().equalsIgnoreCase(NodeTypeEnum.LOOP.getCode()) && message.getLoopNum() > 0)) {
|
||||
taskInstHolder.markRunning(message.getInstId(), message.getTaskId(), message.getItemId(), message.getNodeId(),
|
||||
message.getNodeType(), message.getAction().getOperate(), message.getAction().getAction(), message.getInputParams().toJSONString());
|
||||
message.getNodeType(), message.getAction().getOperate(), message.getAction().getAction(),
|
||||
message.getInputParams().toJSONString(), message.getIterations());
|
||||
}
|
||||
|
||||
TaskNodeExecuteResult result = next.apply(message);
|
||||
@ -43,7 +44,7 @@ public class FlowLoggingInterceptor implements FlowMsgPreInterceptor {
|
||||
return null;
|
||||
}
|
||||
|
||||
taskInstHolder.markSuccess(message.getInstId(), message.getItemId(), message.getNodeId(), message.getPendingItemCount(),message.getNodeType(),
|
||||
taskInstHolder.markSuccess(message.getInstId(), message.getItemId(), message.getNodeId(), message.getPendingItemCount(), message.getNodeType(),
|
||||
result.getOutputParams().toJSONString(), StrUtil.format("[{}] 执行成功", message.getAction()));
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
@ -5,6 +5,7 @@ import com.cmvr.test.flow.builder.FlowNodeWrapper;
|
||||
import com.cmvr.test.flow.builder.FlowParamDef;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -21,4 +22,5 @@ public class TaskNodeExecuteContext {
|
||||
private TaskNodeExecuteMessage rootMessage;
|
||||
private Runnable onFinished;
|
||||
private int loopIteration;
|
||||
private List<Integer> iterations = new ArrayList<>();
|
||||
}
|
||||
|
||||
@ -5,25 +5,38 @@ import com.cmvr.test.enums.ActionEnum;
|
||||
import com.cmvr.test.flow.builder.FlowGraph;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单个节点的执行输入消息
|
||||
*/
|
||||
@Data
|
||||
public class TaskNodeExecuteMessage {
|
||||
|
||||
/** 实例ID */
|
||||
/**
|
||||
* 实例ID
|
||||
*/
|
||||
private String instId;
|
||||
|
||||
/** 任务ID */
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
private String taskId;
|
||||
|
||||
/** 检测项ID(用于定位流程) */
|
||||
/**
|
||||
* 检测项ID(用于定位流程)
|
||||
*/
|
||||
private String itemId;
|
||||
|
||||
/** 节点ID */
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private String nodeId;
|
||||
|
||||
/** 待执行检测项数 */
|
||||
/**
|
||||
* 待执行检测项数
|
||||
*/
|
||||
private int pendingItemCount;
|
||||
|
||||
/**
|
||||
@ -37,25 +50,44 @@ public class TaskNodeExecuteMessage {
|
||||
*/
|
||||
private transient int loopNum = 0;
|
||||
|
||||
/** 节点名称 */
|
||||
/**
|
||||
* 全局迭代路径(嵌套循环时用)
|
||||
*/
|
||||
private List<Integer> iterations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/** 节点类型(START、FUNCTION、END 等) */
|
||||
/**
|
||||
* 节点类型(START、FUNCTION、END 等)
|
||||
*/
|
||||
private String nodeType;
|
||||
|
||||
/** 节点行为(动作标识符) */
|
||||
/**
|
||||
* 节点行为(动作标识符)
|
||||
*/
|
||||
private ActionEnum action;
|
||||
|
||||
/** 是否试运行 */
|
||||
/**
|
||||
* 是否试运行
|
||||
*/
|
||||
private boolean trial;
|
||||
|
||||
/** 所属终端ID */
|
||||
/**
|
||||
* 所属终端ID
|
||||
*/
|
||||
private String terminalId;
|
||||
|
||||
/** 节点入参 */
|
||||
/**
|
||||
* 节点入参
|
||||
*/
|
||||
private JSONObject inputParams;
|
||||
|
||||
/** 上游输出参数(来自上一个节点的输出) */
|
||||
/**
|
||||
* 上游输出参数(来自上一个节点的输出)
|
||||
*/
|
||||
private JSONObject upStreamOutput;
|
||||
|
||||
}
|
||||
|
||||
@ -64,8 +64,8 @@ public class EdgeCameraOperateService implements EdgeOperateService {
|
||||
// // 驾驶偏好
|
||||
// imageUrl = "http://192.168.1.100:9000/cmvr-iot/FILE/20250822/1755849654731.jpg";
|
||||
// }
|
||||
// String imageUrl = "http://192.168.1.100:9000/cmvr-iot/IMAGE/20250901/1756718594725.jpg";
|
||||
String imageUrl = edgeCameraService.getRGBDImages(terminalId, deviceId);
|
||||
String imageUrl = "http://192.168.1.100:9000/cmvr-iot/IMAGE/20250901/1756718594725.jpg";
|
||||
// String imageUrl = edgeCameraService.getRGBDImages(terminalId, deviceId);
|
||||
|
||||
JSONArray imageUrls = new JSONArray();
|
||||
if (ObjUtil.isNotEmpty(upstreamOutput)) {
|
||||
|
||||
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.cmvr.common.annotation.Excel;
|
||||
import com.cmvr.common.core.domain.BaseEntity;
|
||||
import com.cmvr.test.enums.LoopNodeTypeEnum;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
@ -49,4 +50,10 @@ public class TeDetectionItem extends BaseEntity {
|
||||
|
||||
@ApiModelProperty("检测项参数配置(JSON)")
|
||||
private String config;
|
||||
|
||||
/**
|
||||
* #{@link LoopNodeTypeEnum}
|
||||
*/
|
||||
@ApiModelProperty("场景标识,默认 NORMAL")
|
||||
private String sceneCode;
|
||||
}
|
||||
|
||||
@ -47,6 +47,9 @@ public class TeNodeInst {
|
||||
@ApiModelProperty("执行日志/备注")
|
||||
private String message;
|
||||
|
||||
@ApiModelProperty("循环节点的层级")
|
||||
private String iteration;
|
||||
|
||||
@ApiModelProperty("开始时间戳(ms)")
|
||||
private Long startTime;
|
||||
|
||||
|
||||
@ -70,4 +70,12 @@ public interface ITeDetectionItemService extends IService<TeDetectionItem> {
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteTeDetectionItemById(String id);
|
||||
|
||||
/**
|
||||
* 根据场景类型查询检测项
|
||||
*
|
||||
* @param sceneType 场景类型
|
||||
* @return 结果
|
||||
*/
|
||||
public TeDetectionItem selectTeDetectionItemBySceneType(String sceneType);
|
||||
}
|
||||
|
||||
@ -23,18 +23,19 @@ public interface ITeNodeInstService extends IService<TeNodeInst> {
|
||||
/**
|
||||
* 记录节点进入运行状态
|
||||
*
|
||||
* @param instId 任务实例 ID
|
||||
* @param taskId 任务定义 ID
|
||||
* @param itemId 检测项 ID
|
||||
* @param nodeId 节点 ID
|
||||
* @param nodeType 节点类型
|
||||
* @param operate 执行来源
|
||||
* @param action 动作类型
|
||||
* @param paramsIn 输入参数(已序列化为 JSON 字符串)
|
||||
* @param instId 任务实例 ID
|
||||
* @param taskId 任务定义 ID
|
||||
* @param itemId 检测项 ID
|
||||
* @param nodeId 节点 ID
|
||||
* @param nodeType 节点类型
|
||||
* @param operate 执行来源
|
||||
* @param action 动作类型
|
||||
* @param paramsIn 输入参数(已序列化为 JSON 字符串)
|
||||
* @param iterations 迭代次数
|
||||
*/
|
||||
void logRunning(String instId, String taskId, String itemId,
|
||||
String nodeId, String nodeType,
|
||||
String operate, String action, String paramsIn);
|
||||
String operate, String action, String paramsIn, List<Integer> iterations);
|
||||
|
||||
/**
|
||||
* 节点执行后更新输出参数等信息/记录节点成功完成
|
||||
@ -52,7 +53,7 @@ public interface ITeNodeInstService extends IService<TeNodeInst> {
|
||||
/**
|
||||
* 删除节点
|
||||
*/
|
||||
void delete(String instId, String nodeId);
|
||||
void delete(String instId, String nodeId, List<Integer> iterations);
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
|
||||
@ -12,6 +12,7 @@ import com.cmvr.test.model.vo.TeFlowViewVO;
|
||||
import com.cmvr.test.service.ITeNodeInstService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
@ -33,7 +34,7 @@ public class ITeNodeInstServiceImpl extends ServiceImpl<TeNodeInstMapper, TeNode
|
||||
@Override
|
||||
public void logRunning(String instId, String taskId, String itemId,
|
||||
String nodeId, String nodeType,
|
||||
String operate, String action, String paramsIn) {
|
||||
String operate, String action, String paramsIn, List<Integer> iterations) {
|
||||
|
||||
TeNodeInst record = new TeNodeInst();
|
||||
record.setInstId(instId);
|
||||
@ -47,6 +48,7 @@ public class ITeNodeInstServiceImpl extends ServiceImpl<TeNodeInstMapper, TeNode
|
||||
record.setParamsIn(paramsIn);
|
||||
record.setParamsOut("{}");
|
||||
record.setStartTime(System.currentTimeMillis());
|
||||
record.setIteration(getIteration(iterations));
|
||||
|
||||
this.save(record);
|
||||
}
|
||||
@ -113,19 +115,26 @@ public class ITeNodeInstServiceImpl extends ServiceImpl<TeNodeInstMapper, TeNode
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String instId, String nodeId) {
|
||||
public void delete(String instId, String nodeId, List<Integer> iterations) {
|
||||
String iteration = getIteration(iterations);
|
||||
LambdaQueryWrapper<TeNodeInst> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(TeNodeInst::getInstId, instId)
|
||||
.eq(TeNodeInst::getNodeId, nodeId);
|
||||
.eq(TeNodeInst::getNodeId, nodeId)
|
||||
.eq(TeNodeInst::getIteration, iteration);
|
||||
this.baseMapper.delete(wrapper);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getIteration(List<Integer> iterations) {
|
||||
return StrUtil.join(StrUtil.DASHED, iterations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String instId, String nodeId, TaskStatusEnum nodeStatus) {
|
||||
LambdaUpdateWrapper<TeNodeInst> wrapper = Wrappers.lambdaUpdate();
|
||||
wrapper.eq(TeNodeInst::getInstId, instId)
|
||||
.eq(TeNodeInst::getNodeId, nodeId)
|
||||
.set(TeNodeInst::getStatus,nodeStatus.name());
|
||||
.set(TeNodeInst::getStatus, nodeStatus.name());
|
||||
this.update(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
package com.cmvr.test.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.cmvr.common.core.domain.entity.SysGroup;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.test.enums.NodeTypeEnum;
|
||||
import com.cmvr.test.mapper.TeDetectionItemMapper;
|
||||
import com.cmvr.test.model.domain.TeDetectionItem;
|
||||
@ -56,11 +57,13 @@ public class TeDetectionItemServiceImpl extends ServiceImpl<TeDetectionItemMappe
|
||||
.select(SysGroup::getGroupId)
|
||||
.select(SysGroup::getGroupName)
|
||||
.leftJoin(SysGroup.class, SysGroup::getGroupId, TeDetectionItem::getGroupId)
|
||||
.like(StringUtils.isNotBlank(teDetectionItem.getDetectName()), TeDetectionItem::getDetectName, teDetectionItem.getDetectName())
|
||||
.like(StringUtils.isNotBlank(teDetectionItem.getDetectDesc()), TeDetectionItem::getDetectDesc, teDetectionItem.getDetectDesc())
|
||||
.eq(StringUtils.isNotBlank(teDetectionItem.getStatus()), TeDetectionItem::getStatus, teDetectionItem.getStatus())
|
||||
.eq(StringUtils.isNotBlank(teDetectionItem.getIsDeploy()), TeDetectionItem::getIsDeploy, teDetectionItem.getIsDeploy())
|
||||
.eq(StringUtils.isNotBlank(teDetectionItem.getDetectVersion()), TeDetectionItem::getDetectVersion, teDetectionItem.getDetectVersion());
|
||||
.eq(StrUtil.isNotBlank(teDetectionItem.getSceneCode()), TeDetectionItem::getSceneCode, teDetectionItem.getSceneCode())
|
||||
.like(StrUtil.isNotBlank(teDetectionItem.getDetectName()), TeDetectionItem::getDetectName, teDetectionItem.getDetectName())
|
||||
.like(StrUtil.isNotBlank(teDetectionItem.getDetectDesc()), TeDetectionItem::getDetectDesc, teDetectionItem.getDetectDesc())
|
||||
.eq(StrUtil.isNotBlank(teDetectionItem.getStatus()), TeDetectionItem::getStatus, teDetectionItem.getStatus())
|
||||
.eq(StrUtil.isNotBlank(teDetectionItem.getIsDeploy()), TeDetectionItem::getIsDeploy, teDetectionItem.getIsDeploy())
|
||||
.eq(StrUtil.isNotBlank(teDetectionItem.getDetectVersion()), TeDetectionItem::getDetectVersion, teDetectionItem.getDetectVersion())
|
||||
.orderByDesc(TeDetectionItem::getCreateTime);
|
||||
|
||||
// 执行查询并返回结果
|
||||
return this.baseMapper.selectJoinList(TeDetectionItemVO.class, queryWrapper);
|
||||
@ -180,4 +183,11 @@ public class TeDetectionItemServiceImpl extends ServiceImpl<TeDetectionItemMappe
|
||||
public int deleteTeDetectionItemById(String id) {
|
||||
return this.baseMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeDetectionItem selectTeDetectionItemBySceneType(String sceneType) {
|
||||
LambdaQueryWrapper<TeDetectionItem> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TeDetectionItem::getSceneCode, sceneType);
|
||||
return this.getOne(queryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,12 @@
|
||||
<artifactId>cmvr-iot-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试流程-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
package com.cmvr.vi.enums;
|
||||
|
||||
public enum ViCorpusEnum {
|
||||
WAKEUP, //唤醒
|
||||
TEST //测试
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.cmvr.vi.mapper;
|
||||
|
||||
import com.cmvr.vi.model.domain.ViProject;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
|
||||
/**
|
||||
* 语音交互-项目Mapper接口
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
public interface ViProjectMapper extends MPJBaseMapper<ViProject> {
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.cmvr.vi.mapper;
|
||||
|
||||
import com.cmvr.vi.model.domain.ViScheme;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
|
||||
/**
|
||||
* 语音交互-方案Mapper接口
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
public interface ViSchemeMapper extends MPJBaseMapper<ViScheme> {
|
||||
}
|
||||
@ -8,83 +8,55 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 语料库对象 vi_corpus
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("语料库对象")
|
||||
@ApiModel("语音交互--语料库")
|
||||
public class ViCorpus extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 语料ID
|
||||
*/
|
||||
@ApiModelProperty("语料ID")
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long corpusId;
|
||||
|
||||
/**
|
||||
* 语料名称
|
||||
*/
|
||||
@ApiModelProperty("测试语料-连续对话的父ID")
|
||||
private String parentId;
|
||||
|
||||
@Excel(name = "语料名称")
|
||||
@ApiModelProperty("语料名称")
|
||||
private String corpusName;
|
||||
|
||||
/**
|
||||
* 语料类型(WAKEUP唤醒 / TEST测试)
|
||||
*/
|
||||
@Excel(name = "语料类型", readConverterExp = "WAKEUP唤醒,TEST测试")
|
||||
@ApiModelProperty("语料类型")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 语音对应的文本
|
||||
*/
|
||||
@Excel(name = "语音对应的文本")
|
||||
@ApiModelProperty("语音对应的文本")
|
||||
private String textContent;
|
||||
|
||||
/**
|
||||
* 语音文件存放路径
|
||||
*/
|
||||
@Excel(name = "语音文件存放路径")
|
||||
@ApiModelProperty("语音文件存放路径")
|
||||
private String audioPath;
|
||||
|
||||
/**
|
||||
* 音色
|
||||
*/
|
||||
@Excel(name = "音色")
|
||||
@ApiModelProperty("音色")
|
||||
private String voiceType;
|
||||
|
||||
/**
|
||||
* 方言
|
||||
*/
|
||||
@Excel(name = "方言")
|
||||
@ApiModelProperty("方言")
|
||||
private String dialect;
|
||||
|
||||
/**
|
||||
* 预期结果(预留)
|
||||
*/
|
||||
// @Excel(name = "预期结果(预留)")
|
||||
// @Excel(name = "预期结果(预留)")
|
||||
@ApiModelProperty("预期结果(预留)")
|
||||
private String expectedResult;
|
||||
|
||||
/**
|
||||
* 同一父语料下的顺序
|
||||
*/
|
||||
@Excel(name = "同一父语料下的顺序")
|
||||
@ApiModelProperty("同一父语料下的顺序")
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
@Excel(name = "是否连续(0连续 1单次)")
|
||||
@ApiModelProperty("是否连续(0连续 1单次),测试语料时使用")
|
||||
private Integer continuous = 1;
|
||||
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
|
||||
@ApiModelProperty(value = "状态", notes = "0=正常,1=停用")
|
||||
private String status;
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
package com.cmvr.vi.model.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.cmvr.common.annotation.Excel;
|
||||
import com.cmvr.common.core.domain.BaseEntity;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ApiModel("语音交互--项目表")
|
||||
public class ViProject extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("项目ID")
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String projectId;
|
||||
|
||||
@Excel(name = "项目名称")
|
||||
@ApiModelProperty("项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Excel(name = "测试人员")
|
||||
@ApiModelProperty("测试人员")
|
||||
private String tester;
|
||||
|
||||
@Excel(name = "计划测试开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("计划测试开始时间")
|
||||
private Date planStart;
|
||||
|
||||
@Excel(name = "计划测试结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("计划测试结束时间")
|
||||
private Date planEnd;
|
||||
|
||||
@Excel(name = "项目描述")
|
||||
@ApiModelProperty("项目描述")
|
||||
private String description;
|
||||
|
||||
@Excel(name = "样品名称")
|
||||
@ApiModelProperty("样品名称")
|
||||
private String sampleName;
|
||||
|
||||
@Excel(name = "商标")
|
||||
@ApiModelProperty("商标")
|
||||
private String trademark;
|
||||
|
||||
@Excel(name = "型号规格")
|
||||
@ApiModelProperty("型号规格")
|
||||
private String modelSpec;
|
||||
|
||||
@Excel(name = "数量")
|
||||
@ApiModelProperty("数量")
|
||||
private Integer quantity;
|
||||
|
||||
@Excel(name = "委托单位")
|
||||
@ApiModelProperty("委托单位")
|
||||
private String entrustUnit;
|
||||
|
||||
@Excel(name = "生产单位")
|
||||
@ApiModelProperty("生产单位")
|
||||
private String productionUnit;
|
||||
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty("生产日期")
|
||||
private Date productionDate;
|
||||
|
||||
@Excel(name = "送样日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty("送样日期")
|
||||
private Date sampleDate;
|
||||
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
|
||||
@ApiModelProperty(value = "状态", notes = "0=正常,1=停用")
|
||||
private String status;
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.cmvr.vi.model.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.cmvr.common.annotation.Excel;
|
||||
import com.cmvr.common.core.domain.BaseEntity;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@ApiModel("语音交互--方案表")
|
||||
public class ViScheme extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("方案ID")
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String schemeId;
|
||||
|
||||
@Excel(name = "所属项目ID")
|
||||
@ApiModelProperty("所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Excel(name = "方案名称")
|
||||
@ApiModelProperty("方案名称")
|
||||
private String schemeName;
|
||||
|
||||
@ApiModelProperty("场景类型 1:唤醒场景 2: 单次对话场景 3:连续对话场景")
|
||||
private Integer sceneType;
|
||||
|
||||
@ApiModelProperty("唤醒语料parent_id")
|
||||
private String wakeId;
|
||||
|
||||
@ApiModelProperty("排序")
|
||||
private Integer sort;
|
||||
|
||||
@Excel(name = "语料ID列表")
|
||||
@ApiModelProperty(value = "语料parent_id列表,逗号分隔",notes = "单次和唤醒的parent_id和id一致,连续语料的parent_id下有多条子语料")
|
||||
private String corpusIds;
|
||||
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
|
||||
@ApiModelProperty(value = "状态", notes = "0=正常,1=停用")
|
||||
private String status;
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.cmvr.vi.model.vo;
|
||||
|
||||
import com.cmvr.vi.model.domain.ViCorpus;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel("连续对话分组VO")
|
||||
public class ViContinuousCorpusVO {
|
||||
|
||||
@ApiModelProperty("测试语料-连续对话的父ID")
|
||||
private String parentId;
|
||||
|
||||
@ApiModelProperty("语料名称")
|
||||
private String corpusName;
|
||||
|
||||
@ApiModelProperty("音色")
|
||||
private String voiceType;
|
||||
|
||||
@ApiModelProperty("方言")
|
||||
private String dialect;
|
||||
|
||||
@ApiModelProperty("子语料列表")
|
||||
private List<ViCorpus> children;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.cmvr.vi.model.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel("查询单次对话语料/唤醒语料VO")
|
||||
public class ViCorpusVO {
|
||||
|
||||
@ApiModelProperty("测试语料-连续对话的父ID")
|
||||
private String parentId;
|
||||
|
||||
@ApiModelProperty("测试语料-连续对话的父ID列表")
|
||||
private List<String> parentIds;
|
||||
|
||||
@ApiModelProperty("语料名称")
|
||||
private String corpusName;
|
||||
|
||||
@ApiModelProperty("音色")
|
||||
private String voiceType;
|
||||
|
||||
@ApiModelProperty("方言")
|
||||
private String dialect;
|
||||
|
||||
@ApiModelProperty("语音对应的文本")
|
||||
private String textContent;
|
||||
|
||||
@ApiModelProperty(value = "状态", notes = "0=正常,1=停用")
|
||||
private String status;
|
||||
|
||||
@NotEmpty(message = "语料类型不能为空")
|
||||
@ApiModelProperty(value = "语料类型", required = true, notes = "WAKEUP 唤醒,TEST 单次对话测试")
|
||||
private String type;
|
||||
}
|
||||
@ -2,6 +2,8 @@ package com.cmvr.vi.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.cmvr.vi.model.domain.ViCorpus;
|
||||
import com.cmvr.vi.model.vo.ViContinuousCorpusVO;
|
||||
import com.cmvr.vi.model.vo.ViCorpusVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -22,10 +24,18 @@ public interface IViCorpusService extends IService<ViCorpus> {
|
||||
/**
|
||||
* 查询语料库列表
|
||||
*
|
||||
* @param viCorpus 语料库
|
||||
* @param viCorpusVO 语料库
|
||||
* @return 语料库集合
|
||||
*/
|
||||
public List<ViCorpus> selectViCorpusList(ViCorpus viCorpus);
|
||||
public List<ViCorpus> selectViCorpusList(ViCorpusVO viCorpusVO);
|
||||
|
||||
/**
|
||||
* 查询连续对话列表
|
||||
*
|
||||
* @param viCorpusVO 语料库
|
||||
* @return 语料库集合
|
||||
*/
|
||||
public List<ViContinuousCorpusVO> selectContinuousList(ViCorpusVO viCorpusVO);
|
||||
|
||||
/**
|
||||
* 新增语料库
|
||||
@ -35,6 +45,14 @@ public interface IViCorpusService extends IService<ViCorpus> {
|
||||
*/
|
||||
public int insertViCorpus(ViCorpus viCorpus);
|
||||
|
||||
/**
|
||||
* 批量新增语料库
|
||||
*
|
||||
* @param viCorpus 语料库
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean insertBatch(List<ViCorpus> viCorpus);
|
||||
|
||||
/**
|
||||
* 修改语料库
|
||||
*
|
||||
@ -51,4 +69,20 @@ public interface IViCorpusService extends IService<ViCorpus> {
|
||||
*/
|
||||
public int deleteViCorpusByCorpusIds(Long[] corpusIds);
|
||||
|
||||
/**
|
||||
* 批量新增/修改语料库
|
||||
*/
|
||||
int insertOrUpdateBatch(List<ViCorpus> data);
|
||||
|
||||
/**
|
||||
* 新增连续对话
|
||||
*/
|
||||
boolean insertContinuousViCorpus(ViContinuousCorpusVO viContinuousCorpusVO);
|
||||
|
||||
/**
|
||||
* 修改连续对话
|
||||
*/
|
||||
int updateContinuousViCorpus(ViContinuousCorpusVO viContinuousCorpusVO);
|
||||
|
||||
int deleteViCorpusByParentIds(String[] parentIds);
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
package com.cmvr.vi.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.cmvr.vi.model.domain.ViProject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 语音交互-项目Service接口
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
public interface IViProjectService extends IService<ViProject> {
|
||||
/**
|
||||
* 查询语音交互-项目
|
||||
*
|
||||
* @param projectId 语音交互-项目主键
|
||||
* @return 语音交互-项目
|
||||
*/
|
||||
public ViProject selectViProjectByProjectId(String projectId);
|
||||
|
||||
/**
|
||||
* 查询语音交互-项目列表
|
||||
*
|
||||
* @param viProject 语音交互-项目
|
||||
* @return 语音交互-项目集合
|
||||
*/
|
||||
public List<ViProject> selectViProjectList(ViProject viProject);
|
||||
|
||||
/**
|
||||
* 新增语音交互-项目
|
||||
*
|
||||
* @param viProject 语音交互-项目
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertViProject(ViProject viProject);
|
||||
|
||||
/**
|
||||
* 修改语音交互-项目
|
||||
*
|
||||
* @param viProject 语音交互-项目
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateViProject(ViProject viProject);
|
||||
|
||||
/**
|
||||
* 批量删除语音交互-项目
|
||||
*
|
||||
* @param projectIds 需要删除的语音交互-项目主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteViProjectByProjectIds(String[] projectIds);
|
||||
|
||||
/**
|
||||
* 执行项目
|
||||
*/
|
||||
public String execute(String projectId);
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.cmvr.vi.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.cmvr.vi.model.domain.ViScheme;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 语音交互-方案Service接口
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
public interface IViSchemeService extends IService<ViScheme> {
|
||||
/**
|
||||
* 查询语音交互-方案
|
||||
*
|
||||
* @param schemeId 语音交互-方案主键
|
||||
* @return 语音交互-方案
|
||||
*/
|
||||
public ViScheme selectViSchemeBySchemeId(String schemeId);
|
||||
|
||||
/**
|
||||
* 查询语音交互-方案列表
|
||||
*
|
||||
* @param viScheme 语音交互-方案
|
||||
* @return 语音交互-方案集合
|
||||
*/
|
||||
public List<ViScheme> selectViSchemeList(ViScheme viScheme);
|
||||
|
||||
/**
|
||||
* 新增语音交互-方案
|
||||
*
|
||||
* @param viScheme 语音交互-方案
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertViScheme(ViScheme viScheme);
|
||||
|
||||
/**
|
||||
* 修改语音交互-方案
|
||||
*
|
||||
* @param viScheme 语音交互-方案
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateViScheme(ViScheme viScheme);
|
||||
|
||||
/**
|
||||
* 批量删除语音交互-方案
|
||||
*
|
||||
* @param schemeIds 需要删除的语音交互-方案主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteViSchemeBySchemeIds(String[] schemeIds);
|
||||
}
|
||||
@ -1,15 +1,27 @@
|
||||
package com.cmvr.vi.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.cmvr.common.utils.uuid.IdUtils;
|
||||
import com.cmvr.vi.enums.ViCorpusEnum;
|
||||
import com.cmvr.vi.mapper.ViCorpusMapper;
|
||||
import com.cmvr.vi.model.domain.ViCorpus;
|
||||
import com.cmvr.vi.model.vo.ViContinuousCorpusVO;
|
||||
import com.cmvr.vi.model.vo.ViCorpusVO;
|
||||
import com.cmvr.vi.service.IViCorpusService;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 语料库Service业务层处理
|
||||
@ -19,61 +31,193 @@ import java.util.List;
|
||||
@Service
|
||||
public class IViCorpusServiceImpl extends ServiceImpl<ViCorpusMapper, ViCorpus> implements IViCorpusService {
|
||||
|
||||
/**
|
||||
* 查询语料库
|
||||
*
|
||||
* @param corpusId 语料库主键
|
||||
* @return 语料库
|
||||
*/
|
||||
@Override
|
||||
public ViCorpus selectViCorpusByCorpusId(Long corpusId) {
|
||||
return this.getById(corpusId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询语料库列表
|
||||
*
|
||||
* @param viCorpus 语料库
|
||||
* @return 语料库
|
||||
*/
|
||||
@Override
|
||||
public List<ViCorpus> selectViCorpusList(ViCorpus viCorpus) {
|
||||
public List<ViCorpus> selectViCorpusList(ViCorpusVO viCorpusVO) {
|
||||
LambdaQueryWrapper<ViCorpus> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(ViCorpus::getType, viCorpus.getType());
|
||||
|
||||
// 如果是唤醒语料
|
||||
if (ViCorpusEnum.WAKEUP.name().equalsIgnoreCase(viCorpusVO.getType())) {
|
||||
wrapper.eq(ViCorpus::getType, ViCorpusEnum.WAKEUP);
|
||||
}
|
||||
|
||||
// 如果是单次对话
|
||||
if (ViCorpusEnum.TEST.name().equalsIgnoreCase(viCorpusVO.getType())) {
|
||||
wrapper.eq(ViCorpus::getType, ViCorpusEnum.TEST)
|
||||
.eq(ViCorpus::getContinuous, 1); // 只查单次
|
||||
}
|
||||
|
||||
// 公共条件
|
||||
wrapper.like(ObjUtil.isNotEmpty(viCorpusVO.getVoiceType()), ViCorpus::getVoiceType, viCorpusVO.getVoiceType())
|
||||
.like(ObjUtil.isNotEmpty(viCorpusVO.getDialect()), ViCorpus::getDialect, viCorpusVO.getDialect())
|
||||
.like(ObjUtil.isNotEmpty(viCorpusVO.getCorpusName()), ViCorpus::getCorpusName, viCorpusVO.getCorpusName())
|
||||
.like(ObjUtil.isNotEmpty(viCorpusVO.getTextContent()), ViCorpus::getTextContent, viCorpusVO.getTextContent())
|
||||
.eq(ObjUtil.isNotEmpty(viCorpusVO.getStatus()), ViCorpus::getStatus, viCorpusVO.getStatus())
|
||||
.in(CollUtil.isNotEmpty(viCorpusVO.getParentIds()), ViCorpus::getParentId, viCorpusVO.getParentIds())
|
||||
.orderByAsc(ViCorpus::getSortOrder)
|
||||
.orderByDesc(ViCorpus::getUpdateTime);
|
||||
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增语料库
|
||||
*
|
||||
* @param viCorpus 语料库
|
||||
* @return 结果
|
||||
*/
|
||||
|
||||
@Override
|
||||
public List<ViContinuousCorpusVO> selectContinuousList(ViCorpusVO viCorpusVO) {
|
||||
// 查询父级
|
||||
MPJLambdaWrapper<ViCorpus> wrapper = new MPJLambdaWrapper<>();
|
||||
wrapper.select(ViCorpus::getParentId)
|
||||
.select(ViCorpus::getCorpusName)
|
||||
.select(ViCorpus::getVoiceType)
|
||||
.select(ViCorpus::getDialect)
|
||||
.eq(ViCorpus::getType, ViCorpusEnum.TEST)
|
||||
.eq(ViCorpus::getContinuous, 0)
|
||||
.like(ObjUtil.isNotEmpty(viCorpusVO.getVoiceType()), ViCorpus::getVoiceType, viCorpusVO.getVoiceType())
|
||||
.like(ObjUtil.isNotEmpty(viCorpusVO.getDialect()), ViCorpus::getDialect, viCorpusVO.getDialect())
|
||||
.like(ObjUtil.isNotEmpty(viCorpusVO.getCorpusName()), ViCorpus::getCorpusName, viCorpusVO.getCorpusName())
|
||||
.in(CollUtil.isNotEmpty(viCorpusVO.getParentIds()), ViCorpus::getParentId, viCorpusVO.getParentIds())
|
||||
.groupBy(ViCorpus::getParentId, ViCorpus::getVoiceType, ViCorpus::getDialect, ViCorpus::getCorpusName);
|
||||
|
||||
List<ViContinuousCorpusVO> list = this.baseMapper.selectJoinList(ViContinuousCorpusVO.class, wrapper);
|
||||
|
||||
// 查询 children 用 parentId 关联
|
||||
for (ViContinuousCorpusVO vo : list) {
|
||||
List<ViCorpus> children = this.list(
|
||||
Wrappers.lambdaQuery(ViCorpus.class)
|
||||
.eq(ViCorpus::getParentId, vo.getParentId())
|
||||
.eq(ViCorpus::getContinuous, 0)
|
||||
.eq(ViCorpus::getType, ViCorpusEnum.TEST)
|
||||
.orderByAsc(ViCorpus::getSortOrder)
|
||||
);
|
||||
vo.setChildren(children);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int insertViCorpus(ViCorpus viCorpus) {
|
||||
viCorpus.setParentId(IdUtils.fastSimpleUUID());
|
||||
return this.baseMapper.insert(viCorpus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改语料库
|
||||
*
|
||||
* @param viCorpus 语料库
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public boolean insertBatch(List<ViCorpus> data) {
|
||||
data.forEach(v -> v.setParentId(IdUtils.fastSimpleUUID()));
|
||||
return this.saveBatch(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateViCorpus(ViCorpus viCorpus) {
|
||||
return this.baseMapper.updateById(viCorpus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除语料库
|
||||
*
|
||||
* @param corpusIds 需要删除的语料库主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteViCorpusByCorpusIds(Long[] corpusIds) {
|
||||
return this.baseMapper.deleteBatchIds(Arrays.asList(corpusIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertOrUpdateBatch(List<ViCorpus> data) {
|
||||
if (CollUtil.isEmpty(data)) {
|
||||
throw new IllegalArgumentException("data must not be null");
|
||||
}
|
||||
|
||||
// 非空 id → 更新
|
||||
List<ViCorpus> updateList = data.stream()
|
||||
.filter(item -> item.getCorpusId() != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 空 id → 新增
|
||||
List<ViCorpus> insertList = data.stream()
|
||||
.filter(item -> item.getCorpusId() == null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!insertList.isEmpty()) {
|
||||
this.saveBatch(insertList);
|
||||
}
|
||||
|
||||
// 批量更新(自定义 XML,返回影响行数)
|
||||
if (!updateList.isEmpty()) {
|
||||
for (ViCorpus viCorpus : updateList) {
|
||||
this.updateById(viCorpus);
|
||||
}
|
||||
}
|
||||
return data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insertContinuousViCorpus(ViContinuousCorpusVO viContinuousCorpusVO) {
|
||||
String parentId = IdUtils.fastSimpleUUID();
|
||||
List<ViCorpus> children = viContinuousCorpusVO.getChildren();
|
||||
children.forEach(v -> v.setParentId(parentId));
|
||||
return this.insertBatch(children);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int updateContinuousViCorpus(ViContinuousCorpusVO viContinuousCorpusVO) {
|
||||
String parentId = viContinuousCorpusVO.getParentId();
|
||||
|
||||
// parentId 下现有 corpusId
|
||||
List<Long> dbIds = this.list(new LambdaQueryWrapper<ViCorpus>()
|
||||
.select(ViCorpus::getCorpusId)
|
||||
.eq(ViCorpus::getParentId, parentId))
|
||||
.stream()
|
||||
.map(ViCorpus::getCorpusId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<ViCorpus> children = viContinuousCorpusVO.getChildren();
|
||||
|
||||
// 新增、更新、保留
|
||||
List<ViCorpus> addList = new ArrayList<>();
|
||||
List<ViCorpus> updateList = new ArrayList<>();
|
||||
Set<Long> keepIds = new HashSet<>();
|
||||
|
||||
for (ViCorpus child : children) {
|
||||
if (ObjUtil.isEmpty(child.getCorpusId())) {
|
||||
// 新增
|
||||
child.setParentId(parentId);
|
||||
addList.add(child);
|
||||
} else {
|
||||
// 更新
|
||||
updateList.add(child);
|
||||
keepIds.add(child.getCorpusId());
|
||||
}
|
||||
}
|
||||
|
||||
// 要删除的
|
||||
List<Long> removeIds = dbIds.stream()
|
||||
.filter(id -> !keepIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!addList.isEmpty()) {
|
||||
this.saveBatch(addList);
|
||||
}
|
||||
if (!updateList.isEmpty()) {
|
||||
this.updateBatchById(updateList);
|
||||
}
|
||||
if (!removeIds.isEmpty()) {
|
||||
this.removeByIds(removeIds);
|
||||
}
|
||||
|
||||
return addList.size() + updateList.size() + removeIds.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int deleteViCorpusByParentIds(String[] parentIds) {
|
||||
if (parentIds == null || parentIds.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
return this.baseMapper.delete(
|
||||
new LambdaQueryWrapper<ViCorpus>()
|
||||
.in(ViCorpus::getParentId, Arrays.asList(parentIds))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
package com.cmvr.vi.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.cmvr.vi.mapper.ViProjectMapper;
|
||||
import com.cmvr.vi.model.domain.ViProject;
|
||||
import com.cmvr.vi.service.IViProjectService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 语音交互-项目Service业务层处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
@Service
|
||||
public class ViProjectServiceImpl extends ServiceImpl<ViProjectMapper, ViProject> implements IViProjectService {
|
||||
|
||||
@Override
|
||||
public ViProject selectViProjectByProjectId(String projectId) {
|
||||
return this.getById(projectId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViProject> selectViProjectList(ViProject viProject) {
|
||||
LambdaQueryWrapper<ViProject> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(ObjUtil.isNotEmpty(viProject.getProjectName()), ViProject::getProjectName, viProject.getProjectName())
|
||||
.eq(ObjUtil.isNotEmpty(viProject.getStatus()), ViProject::getStatus, viProject.getStatus());
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertViProject(ViProject viProject) {
|
||||
return this.baseMapper.insert(viProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateViProject(ViProject viProject) {
|
||||
return this.baseMapper.updateById(viProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteViProjectByProjectIds(String[] projectIds) {
|
||||
return this.baseMapper.deleteBatchIds(Arrays.asList(projectIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(String projectId) {
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
package com.cmvr.vi.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.cmvr.common.exception.GlobalException;
|
||||
import com.cmvr.test.enums.FlowSceneTypeEnum;
|
||||
import com.cmvr.test.model.domain.TeDetectionItem;
|
||||
import com.cmvr.test.model.vo.TeQueryTaskOrchestraItemVO;
|
||||
import com.cmvr.test.model.vo.TeTaskOrchestraVO;
|
||||
import com.cmvr.test.service.ITeDetectionItemService;
|
||||
import com.cmvr.test.service.ITeTaskOrchestrationService;
|
||||
import com.cmvr.vi.mapper.ViSchemeMapper;
|
||||
import com.cmvr.vi.model.domain.ViScheme;
|
||||
import com.cmvr.vi.service.IViSchemeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 语音交互-方案Service业务层处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ViSchemeServiceImpl extends ServiceImpl<ViSchemeMapper, ViScheme> implements IViSchemeService {
|
||||
|
||||
private final ITeTaskOrchestrationService teTaskOrchestrationService;
|
||||
private final ITeDetectionItemService detectionItemService;
|
||||
|
||||
@Override
|
||||
public ViScheme selectViSchemeBySchemeId(String schemeId) {
|
||||
return this.getById(schemeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViScheme> selectViSchemeList(ViScheme viScheme) {
|
||||
LambdaQueryWrapper<ViScheme> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(ViScheme::getProjectId, viScheme.getProjectId())
|
||||
.eq(ObjUtil.isNotEmpty(viScheme.getSchemeName()), ViScheme::getSchemeName, viScheme.getSchemeName())
|
||||
.eq(ObjUtil.isNotEmpty(viScheme.getStatus()), ViScheme::getStatus, viScheme.getStatus())
|
||||
.orderByAsc(ViScheme::getSort);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertViScheme(ViScheme viScheme) {
|
||||
// 先插方案
|
||||
int insert = this.baseMapper.insert(viScheme);
|
||||
|
||||
String taskId = viScheme.getProjectId();
|
||||
Integer sort = viScheme.getSort();
|
||||
|
||||
// 获取对应场景流程id
|
||||
Integer sceneType = viScheme.getSceneType(); // 1:唤醒 2:单次对话 3:连续对话
|
||||
String key;
|
||||
switch (sceneType) {
|
||||
case 1: key = FlowSceneTypeEnum.VI_WAKEUP_CORPUS.name(); break;
|
||||
case 2: key = FlowSceneTypeEnum.VI_SINGLE_CORPUS.name(); break;
|
||||
case 3: key = FlowSceneTypeEnum.VI_CONTINUOUS_CORPUS.name(); break;
|
||||
default: throw new GlobalException("场景类型参数错误!");
|
||||
}
|
||||
TeDetectionItem item = detectionItemService.selectTeDetectionItemBySceneType(key);
|
||||
String itemId = item.getId();
|
||||
|
||||
// 查询已有编排项
|
||||
List<TeQueryTaskOrchestraItemVO> items = teTaskOrchestrationService.queryByTaskId(taskId);
|
||||
|
||||
// 插入新编排项
|
||||
TeQueryTaskOrchestraItemVO itemVO = new TeQueryTaskOrchestraItemVO();
|
||||
itemVO.setItemId(itemId);
|
||||
itemVO.setOrderNum(sort);
|
||||
items.add(itemVO);
|
||||
|
||||
// 落库并归一化
|
||||
normalizeTaskItems(taskId);
|
||||
|
||||
return insert;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateViScheme(ViScheme viScheme) {
|
||||
int update = this.baseMapper.updateById(viScheme);
|
||||
|
||||
String taskId = viScheme.getProjectId();
|
||||
|
||||
// 这里不管 sort 是否改动,都做一次归一化,避免乱序
|
||||
normalizeTaskItems(taskId);
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteViSchemeBySchemeIds(String[] schemeIds) {
|
||||
List<String> schemeIdList = Arrays.asList(schemeIds);
|
||||
|
||||
// 查要删除的方案,获取对应 taskId 和流程 itemId
|
||||
List<ViScheme> schemes = this.baseMapper.selectBatchIds(schemeIdList);
|
||||
int delete = this.baseMapper.deleteBatchIds(schemeIdList);
|
||||
|
||||
// 每个 taskId 单独归一化一次
|
||||
Map<String, List<ViScheme>> taskSchemeMap = schemes.stream()
|
||||
.collect(Collectors.groupingBy(ViScheme::getProjectId));
|
||||
|
||||
for (String taskId : taskSchemeMap.keySet()) {
|
||||
List<TeQueryTaskOrchestraItemVO> items = teTaskOrchestrationService.queryByTaskId(taskId);
|
||||
|
||||
if (items != null && !items.isEmpty()) {
|
||||
// 找出要删的流程 itemId
|
||||
List<String> deleteItemIds = taskSchemeMap.get(taskId).stream()
|
||||
.map(s -> {
|
||||
String key;
|
||||
switch (s.getSceneType()) {
|
||||
case 1: key = FlowSceneTypeEnum.VI_WAKEUP_CORPUS.name(); break;
|
||||
case 2: key = FlowSceneTypeEnum.VI_SINGLE_CORPUS.name(); break;
|
||||
case 3: key = FlowSceneTypeEnum.VI_CONTINUOUS_CORPUS.name(); break;
|
||||
default: throw new GlobalException("场景类型参数错误!");
|
||||
}
|
||||
return detectionItemService.selectTeDetectionItemBySceneType(key).getId();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 移除这些 item
|
||||
items.removeIf(i -> deleteItemIds.contains(i.getItemId()));
|
||||
|
||||
// 覆盖保存并归一化
|
||||
normalizeTaskItems(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
return delete;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 taskId 对编排表的检测项重新排序(1..N)
|
||||
*/
|
||||
private void normalizeTaskItems(String taskId) {
|
||||
List<TeQueryTaskOrchestraItemVO> items = teTaskOrchestrationService.queryByTaskId(taskId);
|
||||
if (items == null || items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 按顺序排序
|
||||
items.sort(Comparator.comparing(TeQueryTaskOrchestraItemVO::getOrderNum));
|
||||
|
||||
// 重新归一化 1..N
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.get(i).setOrderNum(i + 1);
|
||||
}
|
||||
|
||||
// 落库
|
||||
List<String> itemIds = items.stream()
|
||||
.map(TeQueryTaskOrchestraItemVO::getItemId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
TeTaskOrchestraVO orchestraVO = new TeTaskOrchestraVO();
|
||||
orchestraVO.setTaskId(taskId);
|
||||
orchestraVO.setItemIds(itemIds);
|
||||
|
||||
teTaskOrchestrationService.insertTeTaskOrchestration(orchestraVO);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user