feat: 触控交互(部分)

This commit is contained in:
stream 2026-02-04 10:01:33 +08:00
parent e3e5626e20
commit e58ac937ec
20 changed files with 808 additions and 83 deletions

View File

@ -4,11 +4,9 @@ import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult; import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.evaluation.model.domain.AeEvaluation; import com.cmvr.evaluation.model.domain.AeEvaluation;
import com.cmvr.evaluation.service.IAeEvaluationService; import com.cmvr.evaluation.service.IAeEvaluationService;
import com.cmvr.test.model.domain.TeAiEvaluation;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -23,20 +21,12 @@ public class AeEvaluationController extends BaseController {
private final IAeEvaluationService aeEvaluationService; 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评估") @ApiOperation("回调AI评估")
@PostMapping("/callback") @PostMapping("/callback")
public AjaxResult list(@RequestBody AeEvaluation aeEvaluation) { public AjaxResult list(@RequestBody AeEvaluation aeEvaluation) {
aeEvaluationService.callback(aeEvaluation); aeEvaluationService.callback(aeEvaluation);
return AjaxResult.ok(); return AjaxResult.ok();
} }
} }

View File

@ -0,0 +1,87 @@
package com.cmvr.web.controller.ti;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.core.page.TableDataInfo;
import com.cmvr.test.model.vo.TeTaskExecuteProjectVO;
import com.cmvr.ti.model.domain.TiProject;
import com.cmvr.ti.service.ITiProjectService;
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("/ti/project")
@RequiredArgsConstructor
public class TiProjectController extends BaseController {
private final ITiProjectService tiProjectService;
@ApiOperation("获取项目列表")
@PreAuthorize("@ss.hasPermi('ti:project:list')")
@GetMapping("/list")
public TableDataInfo list(TiProject tiProject) {
startPage();
List<TiProject> list = tiProjectService.selectTiProjectList(tiProject);
return getDataTable(list);
}
@ApiOperation("根据id获取项目信息")
@PreAuthorize("@ss.hasPermi('ti:project:query')")
@GetMapping(value = "/{projectId}")
public AjaxResult getInfo(@PathVariable("projectId") String projectId) {
return success(tiProjectService.selectTiProjectByProjectId(projectId));
}
@ApiOperation("添加项目")
@PreAuthorize("@ss.hasPermi('ti:project:add')")
@PostMapping
public AjaxResult add(@RequestBody TiProject tiProject) {
return toAjax(tiProjectService.insertTiProject(tiProject));
}
@ApiOperation("编辑项目")
@PreAuthorize("@ss.hasPermi('ti:project:edit')")
@PutMapping
public AjaxResult edit(@RequestBody TiProject tiProject) {
return toAjax(tiProjectService.updateTiProject(tiProject));
}
@ApiOperation("删除项目")
@PreAuthorize("@ss.hasPermi('ti:project:remove')")
@DeleteMapping("/{projectIds}")
public AjaxResult remove(@PathVariable String[] projectIds) {
return toAjax(tiProjectService.deleteTiProjectByProjectIds(projectIds));
}
@ApiOperation("查询项目执行时运行参数")
@GetMapping("/queryExecuteRunParams")
public AjaxResult queryExecuteRunParams(@RequestParam("projectId") String projectId) {
return AjaxResult.ok(tiProjectService.queryExecuteRunParams(projectId));
}
@ApiOperation("执行项目")
@PostMapping("/execute")
public AjaxResult execute(@RequestBody TeTaskExecuteProjectVO taskExecuteProjectVO) {
return AjaxResult.ok(tiProjectService.executeProject(taskExecuteProjectVO));
}
@ApiOperation("查询最近一次项目执行实例id")
@GetMapping("/queryExecuteInstId/{projectId}")
public AjaxResult queryExecuteInstId(@PathVariable("projectId") String projectId) {
return AjaxResult.ok(tiProjectService.queryExecuteInstId(projectId));
}
}

View File

@ -1,6 +1,6 @@
server: server:
# 服务器的HTTP端口默认为8080 # 服务器的HTTP端口默认为8080
port: 13081 port: 13080
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
@ -120,9 +120,11 @@ api:
INTENT_RECOGNITION: # 意图识别 INTENT_RECOGNITION: # 意图识别
app-id: d3c98sp0gdon0fcf6l00 app-id: d3c98sp0gdon0fcf6l00
app-key: d3c98vp0gdon0fcf6n20 app-key: d3c98vp0gdon0fcf6n20
TOUCH_COORDINATES: # 意图识别
app-id: d5thge2cktmipk78h82g
app-key: d1ebtabnjkflk4gmhikg
evaluation: http://192.168.0.18:8000/analyze evaluation: http://192.168.0.18:8000/analyze
flowise: flowise:
tts: 192.168.0.222:8080/tts/ tts: 192.168.0.222:8080/tts/
start: http://192.168.0.108:3000/api/v1/prediction/f99329e9-b33d-437d-90ed-69eaa8a05418 start: http://192.168.0.108:3000/api/v1/prediction/f99329e9-b33d-437d-90ed-69eaa8a05418

View File

@ -23,6 +23,7 @@ public enum ActionEnum {
BRANCH("NONE", "BRANCH", "分支"), BRANCH("NONE", "BRANCH", "分支"),
SUB_END("NONE", "SUB_END", "子流程结束"), SUB_END("NONE", "SUB_END", "子流程结束"),
SLEEP("NONE", "SLEEP", "延迟节点"), SLEEP("NONE", "SLEEP", "延迟节点"),
HTTP("NONE", "HTTP", "HTTP调用"),
// AI评估 // AI评估
AI_EVALUATION_PRE("AE", "AI_EVALUATION_PRE", "AI评估预处理"), AI_EVALUATION_PRE("AE", "AI_EVALUATION_PRE", "AI评估预处理"),
// AI评估 // AI评估
@ -66,6 +67,9 @@ public enum ActionEnum {
INTENT_RECOGNITION("LLM", "INTENT_RECOGNITION", "意图识别"), INTENT_RECOGNITION("LLM", "INTENT_RECOGNITION", "意图识别"),
GENERATE_ADVANCED_AUDIO("LLM", "GENERATE_ADVANCED_AUDIO", "tts语音合成"), GENERATE_ADVANCED_AUDIO("LLM", "GENERATE_ADVANCED_AUDIO", "tts语音合成"),
// 触控交互
TI_PATH_SEARCH("EDGE", "TI_PATH_SEARCH", "路径搜索"),
TI_TOUCH_COORDINATES("EDGE", "TI_TOUCH_COORDINATES", "获取触控二维坐标")
; ;

View File

@ -0,0 +1,96 @@
package com.cmvr.test.flow.runtime.dispatcher;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component("HTTP")
@RequiredArgsConstructor
public class FlowHttpNodeHandler implements FlowNodeTypeHandler {
@Override
public TaskNodeExecuteResult handle(TaskNodeExecuteMessage message) {
try {
JSONObject inputParams = message.getInputParams();
// 1. 必填参数校验
String url = inputParams.getString("url");
if (url == null || url.isEmpty()) {
return TaskNodeExecuteResult.failure("HTTP url is required");
}
String method = inputParams.getString("method");
if (method == null) {
method = "POST";
}
int timeout = inputParams.getIntValue("timeout", 100000);
JSONObject headers = inputParams.getJSONObject("headers");
Object bodyCfg = inputParams.get("body");
// 2. 构造 HTTP 请求
HttpRequest request = HttpRequest.of(url)
.method(Method.valueOf(method.toUpperCase()))
.timeout(timeout);
// 3. headers可选
if (headers != null) {
for (String key : headers.keySet()) {
String value = headers.getString(key);
if (value != null) {
request.header(key, value);
}
}
}
// 4. body可选JSON
if (bodyCfg != null) {
request.header("Content-Type", "application/json");
request.body(bodyCfg.toString());
}
log.info("[FLOW][HTTP] {} {}", method, url);
// 5. 执行请求
HttpResponse response = request.execute();
if (!response.isOk()) {
log.error("[FLOW][HTTP] 调用失败status={}", response.getStatus());
return TaskNodeExecuteResult.failure("HTTP status=" + response.getStatus());
}
// 6. 原样解析返回JSON / String
Object resp;
String respBody = response.body();
try {
resp = JSON.parse(respBody);
} catch (Exception e) {
resp = respBody;
}
JSONObject output = new JSONObject();
output.put("result", resp);
log.info(
"[FLOW][HTTP] success, nodeId={}, responseType={}",
message.getNodeId(),
resp.getClass().getSimpleName()
);
return TaskNodeExecuteResult.success(output);
} catch (Exception e) {
log.error("[FLOW][HTTP] 执行异常", e);
return TaskNodeExecuteResult.failure(e.getMessage());
}
}
}

View File

@ -15,8 +15,6 @@ import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.File;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class EdgeCameraOperateService implements EdgeOperateService { public class EdgeCameraOperateService implements EdgeOperateService {

View File

@ -0,0 +1,257 @@
package com.cmvr.test.flow.runtime.operator.edge.ti;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.config.properties.MinioProperties;
import com.cmvr.common.core.minio.MinioService;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.http.CallAPIUtil;
import com.cmvr.llm.config.APIProperties;
import com.cmvr.llm.config.AgentConfig;
import com.cmvr.llm.util.LargeModelFileUploadUtil;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import com.cmvr.test.flow.runtime.operator.edge.EdgeOperateService;
import com.cmvr.test.service.ex.ExTiVehicleFunctionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class TiTouchOperateService implements EdgeOperateService {
private final ExTiVehicleFunctionService exTiVehicleFunctionService;
private final MinioService minioService;
private final MinioProperties minioProps;
private final APIProperties apiProperties;
// 设置运动模式
public static final List<String> DRIVE_MODE_PATH = Arrays.asList("主页", "设置", "驾驶模式", "运动模式");
// 打开空调
public static final List<String> OPEN_AC_PATH = Arrays.asList("主页", "空调", "打开");
// 所有路径集合
public static final List<List<String>> ALL_PATHS =
Arrays.asList(
OPEN_AC_PATH,
DRIVE_MODE_PATH
);
@Override
public boolean supports(ActionEnum action) {
return action.name().startsWith("TI_");
}
@Override
public TaskNodeExecuteResult execute(TaskNodeExecuteMessage message) {
ActionEnum action = message.getAction();
JSONObject inputParams = message.getInputParams();
JSONObject output = new JSONObject();
switch (action) {
case TI_PATH_SEARCH: {
// 当前界面
String currentLocation = inputParams.getString("currentLocation");
// 查询功能
long funcId = inputParams.getLongValue("funcId");
JSONObject funcObj = exTiVehicleFunctionService.queryById(funcId);
String funcKey = funcObj.getString("funcKey");
String iconUrl = funcObj.getString("iconUrl");
// ========== 根据当前界面匹配路径 ==========
List<String> matchedPath = null;
for (List<String> path : ALL_PATHS) {
if (path.contains(funcKey)) {
matchedPath = path;
break;
}
}
if (matchedPath == null) {
throw new GlobalException("未匹配到可用路径");
}
// ========== 计算下一步 ==========
int index = matchedPath.indexOf(currentLocation);
String nextLocation;
boolean isEnd;
if (index == -1 || index == matchedPath.size() - 1) {
nextLocation = currentLocation;
isEnd = true;
} else {
nextLocation = matchedPath.get(index + 1);
isEnd = (index + 1 == matchedPath.size() - 1);
}
// 输出
output.put("nextLocation", nextLocation);
output.put("isEnd", isEnd);
output.put("iconUrl", iconUrl);
break;
}
case TI_TOUCH_COORDINATES: {
// 页面图片(相机)
String imageUrl = inputParams.getString("imageUrl");
String pageImageUrl = uploadImageFromMinio(imageUrl);
// icon图片
String iconUrl = inputParams.getString("iconUrl");
String iconImageUrl = uploadImageFromMinio(iconUrl);
// 获取触控坐标
String touchCoordinates = getTouchCoordinates(pageImageUrl, iconImageUrl);
output.put("touchCoordinates", touchCoordinates);
break;
}
default:
throw new GlobalException("不支持的触控操作类型: " + action);
}
return TaskNodeExecuteResult.success(output);
}
private String getTouchCoordinates(String pageImageUrl, String iconImageUrl) {
AgentConfig agentConfig = apiProperties.getAgentConfig(ActionEnum.TI_TOUCH_COORDINATES.getAction());
Map<String, String> params = new HashMap<>();
params.put("input_image", pageImageUrl);
params.put("icon_image", iconImageUrl);
Map<String, String> headers = new HashMap<>();
headers.put("Apikey", agentConfig.getAppKey());
Map<String, String> body = new HashMap<>();
body.put("AppKey", agentConfig.getAppKey());
body.put("AppID", agentConfig.getAppId());
body.put("InputData", JSON.toJSONString(params));
body.put("UserID", "18888888888");
String response = CallAPIUtil.doPostJson(apiProperties.getWorkFlowUrl(), headers, body);
JSONObject resObject1 = JSON.parseObject(response);
if (resObject1.containsKey("runId")) {
String processId = resObject1.getString("runId");
return queryResultByRunId(processId, agentConfig);
} else {
throw new GlobalException("未获取到 runId响应内容" + response);
}
}
/**
* 根据 MinIO 文件访问地址上传到商道返回商道文件地址
*/
private String uploadImageFromMinio(String url) {
try {
String bucketName = minioProps.getBucketName();
String prefix = StrPool.SLASH + bucketName + StrPool.SLASH;
String objectName = StrUtil.removePrefix(
url,
StrUtil.subBefore(url, prefix, true) + prefix
);
File imageFile = minioService.getFile(bucketName, objectName);
byte[] imageBytes = FileUtil.readBytes(imageFile);
return LargeModelFileUploadUtil.uploadFile(imageBytes);
} catch (Exception e) {
log.error("上传 MinIO 文件到商道失败url={}", url, e);
throw new GlobalException("图片上传失败");
}
}
private String queryResultByRunId(String processId, AgentConfig agentConfig) {
Map<String, String> headers = new HashMap<>();
headers.put("Apikey", agentConfig.getAppKey());
Map<String, String> body = new HashMap<>();
body.put("AppKey", agentConfig.getAppKey());
body.put("AppID", agentConfig.getAppId());
body.put("RunID", processId);
body.put("UserID", "18888888888");
String responseBody;
JSONObject endNode;
int failCount = 0;
int maxFail = 40;
while (true) {
responseBody = CallAPIUtil.doPostJson(apiProperties.getQueryResultUrl(), headers, body);
JSONObject jsonObject = JSON.parseObject(responseBody);
String status = jsonObject.getString("status");
if ("success".equals(status)) {
JSONObject nodes = jsonObject.getJSONObject("nodes");
if (nodes == null) {
throw new GlobalException("响应中 nodes 为空:" + responseBody);
}
endNode = findEndNode(nodes);
if (endNode != null) {
break;
} else {
throw new GlobalException("未找到 nodeType=end 的节点:" + nodes);
}
} else if ("failed".equals(status)) {
// 如果失败
throw new GlobalException(responseBody);
} else {
// 处理其他状态码
failCount++;
if (failCount > maxFail) {
throw new GlobalException("达到最大重试次数API状态仍未成功" + status);
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new GlobalException("轮询被中断");
}
}
// 解析 output 内容并清洗
JSONObject output = endNode.getJSONObject("output");
String res = output.getString("content");
if (StrUtil.isEmpty(res)) {
throw new GlobalException("未找到坐标内容");
}
return res;
}
/**
* nodes 中提取 nodeType=end 的节点
*/
private JSONObject findEndNode(JSONObject nodes) {
for (Map.Entry<String, Object> entry : nodes.entrySet()) {
JSONObject node = (JSONObject) entry.getValue();
if ("end".equalsIgnoreCase(node.getString("nodeType"))) {
return node;
}
}
return null;
}
}

View File

@ -0,0 +1,9 @@
package com.cmvr.test.service.ex;
import com.alibaba.fastjson2.JSONObject;
public interface ExTiProjectService {
public JSONObject queryById(String id);
}

View File

@ -0,0 +1,12 @@
package com.cmvr.test.service.ex;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
public interface ExTiVehicleFunctionService {
public JSONArray queryByIds(String ids);
public JSONObject queryById(Long id);
}

View File

@ -0,0 +1,7 @@
package com.cmvr.ti.mapper;
import com.cmvr.ti.model.domain.TiProject;
import com.github.yulichang.base.MPJBaseMapper;
public interface TiProjectMapper extends MPJBaseMapper<TiProject> {
}

View File

@ -0,0 +1,85 @@
package com.cmvr.ti.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 lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("触控交互--项目表")
public class TiProject 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;
@ApiModelProperty(value = "状态", notes = "状态0已执行 1未执行")
private String status;
@ApiModelProperty(value = "评价id")
private Long indicatorId;
@ApiModelProperty(value = "测试功能id列表")
private String testfuncs;
}

View File

@ -1,65 +0,0 @@
CREATE TABLE `te_vehicle_config`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`oem` VARCHAR(50) NOT NULL COMMENT '车企名称',
`model` VARCHAR(50) NOT NULL COMMENT '车型名称',
`version` VARCHAR(50) NOT NULL COMMENT '车机版本',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='触控交互-车辆配置表';
DROP TABLE IF EXISTS `ti_vehicle_ui`;
CREATE TABLE `ti_vehicle_ui`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`vehicle_config_id` BIGINT(20) NOT NULL COMMENT '所属车辆配置IDte_vehicle_config.id',
`ui_key` VARCHAR(50) NOT NULL COMMENT '界面字典键值(来自 sys_dict_data.value',
`ui_url` VARCHAR(255) DEFAULT NULL COMMENT '界面截图URL',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='触控交互-车机界面表';
DROP TABLE IF EXISTS `ti_vehicle_function`;
CREATE TABLE `ti_vehicle_function`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`vehicle_config_id` BIGINT(20) NOT NULL COMMENT '关联车辆配置IDte_vehicle_config.id',
`ui_id` BIGINT(20) NOT NULL COMMENT '所属界面IDti_vehicle_ui.id',
`func_key` VARCHAR(50) NOT NULL COMMENT '功能字典键值(来自 sys_dict_data.value',
`icon_url` VARCHAR(255) DEFAULT NULL COMMENT '图标URL可选',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='触控交互-车机功能表';

View File

@ -0,0 +1,29 @@
package com.cmvr.ti.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmvr.test.model.domain.TeTaskInst;
import com.cmvr.test.model.vo.TeQueryProjectOrchestraItemVO;
import com.cmvr.test.model.vo.TeTaskExecuteProjectVO;
import com.cmvr.ti.model.domain.TiProject;
import java.util.List;
public interface ITiProjectService extends IService<TiProject> {
public TiProject selectTiProjectByProjectId(String projectId);
public List<TiProject> selectTiProjectList(TiProject tiProject);
public int insertTiProject(TiProject tiProject);
public int updateTiProject(TiProject tiProject);
public int deleteTiProjectByProjectIds(String[] projectIds);
public List<TeQueryProjectOrchestraItemVO> queryExecuteRunParams(String projectId);
public String executeProject(TeTaskExecuteProjectVO taskExecuteProjectVO);
public TeTaskInst getTeTaskInst(String projectId);
public String queryExecuteInstId(String projectId);
}

View File

@ -18,6 +18,8 @@ public interface ITiVehicleFunctionService extends IService<TiVehicleFunction> {
*/ */
public TiVehicleFunction selectTiVehicleFunctionById(Long id); public TiVehicleFunction selectTiVehicleFunctionById(Long id);
public List<TiVehicleFunction> queryByIds(List<Long> ids);
/** /**
* 查询车辆功能列表 * 查询车辆功能列表
* *

View File

@ -0,0 +1,19 @@
package com.cmvr.ti.service.ex;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.test.service.ex.ExTiProjectService;
import com.cmvr.ti.service.ITiProjectService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ExTiProjectServiceImpl implements ExTiProjectService {
private final ITiProjectService tiProjectService;
@Override
public JSONObject queryById(String id) {
return JSONObject.from(tiProjectService.getById(id));
}
}

View File

@ -0,0 +1,33 @@
package com.cmvr.ti.service.ex;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.test.service.ex.ExTiVehicleFunctionService;
import com.cmvr.ti.model.domain.TiVehicleFunction;
import com.cmvr.ti.service.ITiVehicleFunctionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class ExTiVehicleFunctionServiceImpl implements ExTiVehicleFunctionService {
private final ITiVehicleFunctionService tiVehicleFunctionService;
@Override
public JSONArray queryByIds(String ids) {
List<TiVehicleFunction> tiVehicleFunctions = tiVehicleFunctionService.listByIds(StrUtil.split(StrPool.COMMA, ids));
return JSONArray.from(tiVehicleFunctions);
}
@Override
public JSONObject queryById(Long id) {
return JSONObject.from(tiVehicleFunctionService.getById(id));
}
}

View File

@ -0,0 +1,124 @@
package com.cmvr.ti.service.impl;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.test.enums.RunModeEnum;
import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService;
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.TeTaskExecuteProjectVO;
import com.cmvr.test.service.ITeTaskConfigInfoService;
import com.cmvr.ti.mapper.TiProjectMapper;
import com.cmvr.ti.model.domain.TiProject;
import com.cmvr.ti.model.domain.TiVehicleFunction;
import com.cmvr.ti.service.ITiProjectService;
import com.cmvr.ti.service.ITiVehicleFunctionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class TiProjectServiceImpl extends ServiceImpl<TiProjectMapper, TiProject> implements ITiProjectService {
private final ITeTaskConfigInfoService taskConfigInfoService;
private final FlowTaskRuntimeService flowTaskRuntimeService;
private final ITiVehicleFunctionService tiVehicleFunctionService;
@Override
public TiProject selectTiProjectByProjectId(String projectId) {
return this.getById(projectId);
}
@Override
public List<TiProject> selectTiProjectList(TiProject tiProject) {
LambdaQueryWrapper<TiProject> wrapper = Wrappers.lambdaQuery();
wrapper.eq(ObjUtil.isNotEmpty(tiProject.getProjectName()), TiProject::getProjectName, tiProject.getProjectName())
.eq(ObjUtil.isNotEmpty(tiProject.getStatus()), TiProject::getStatus, tiProject.getStatus());
return this.list(wrapper);
}
@Override
public int insertTiProject(TiProject tiProject) {
// 校验功能ID是否属于同一车型配置
List<Long> ids = StrUtil.split(tiProject.getTestfuncs(), CharPool.COMMA)
.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
List<TiVehicleFunction> functions = tiVehicleFunctionService.queryByIds(ids);
if (functions.size() != ids.size()) {
throw new GlobalException("存在无效的功能ID");
}
if (functions.stream()
.map(TiVehicleFunction::getVehicleConfigId)
.distinct()
.count() != 1) {
throw new GlobalException("功能ID不属于同一车型配置");
}
int insert = this.baseMapper.insert(tiProject);
TeTaskConfigInfo taskConfigInfo = new TeTaskConfigInfo();
taskConfigInfo.setRunMode(RunModeEnum.TI_PROJECT.name());
taskConfigInfo.setId(tiProject.getProjectId());
taskConfigInfo.setStatus("0");
taskConfigInfo.setTaskName(StrUtil.format("{}_{}", RunModeEnum.TI_PROJECT, tiProject.getProjectName()));
taskConfigInfoService.save(taskConfigInfo);
return insert;
}
@Override
public int updateTiProject(TiProject tiProject) {
return this.baseMapper.updateById(tiProject);
}
@Override
public int deleteTiProjectByProjectIds(String[] projectIds) {
taskConfigInfoService.deleteTeTaskConfigInfoByIds(projectIds);
return this.baseMapper.deleteBatchIds(Arrays.asList(projectIds));
}
@Override
public List<TeQueryProjectOrchestraItemVO> queryExecuteRunParams(String projectId) {
return Collections.emptyList();
}
@Override
public String executeProject(TeTaskExecuteProjectVO taskExecuteProjectVO) {
TeTaskExecuteProjectVO normalVO = new TeTaskExecuteProjectVO();
normalVO.setProjectId(taskExecuteProjectVO.getProjectId());
normalVO.setRunParams(taskExecuteProjectVO.getRunParams());
normalVO.setTerminalId(taskExecuteProjectVO.getTerminalId());
String instId = flowTaskRuntimeService.executeProjectTask(normalVO);
this.update(
new LambdaUpdateWrapper<TiProject>()
.eq(TiProject::getProjectId, taskExecuteProjectVO.getProjectId())
.set(TiProject::getStatus, "0")
);
return instId;
}
@Override
public TeTaskInst getTeTaskInst(String projectId) {
return null;
}
@Override
public String queryExecuteInstId(String projectId) {
return "";
}
}

View File

@ -14,6 +14,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -21,13 +22,19 @@ import java.util.List;
*/ */
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class TiVehicleFunctionServiceImpl extends ServiceImpl<TiVehicleFunctionMapper, TiVehicleFunction> implements ITiVehicleFunctionService { public class TiVehicleFunctionServiceImpl extends ServiceImpl<TiVehicleFunctionMapper, TiVehicleFunction>
implements ITiVehicleFunctionService {
@Override @Override
public TiVehicleFunction selectTiVehicleFunctionById(Long id) { public TiVehicleFunction selectTiVehicleFunctionById(Long id) {
return this.getById(id); return this.getById(id);
} }
@Override
public List<TiVehicleFunction> queryByIds(List<Long> ids) {
return this.listByIds(ids);
}
@Override @Override
public List<TiVehicleFunction> selectTiVehicleFunctionList(TiVehicleFunctionVO tiVehicleFunctionVO) { public List<TiVehicleFunction> selectTiVehicleFunctionList(TiVehicleFunctionVO tiVehicleFunctionVO) {
LambdaQueryWrapper<TiVehicleFunction> wrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<TiVehicleFunction> wrapper = Wrappers.lambdaQuery();

View File

@ -1,6 +1,7 @@
package com.cmvr.vi.service; package com.cmvr.vi.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.cmvr.test.model.domain.TeTaskInst;
import com.cmvr.test.model.vo.TeQueryProjectOrchestraItemVO; import com.cmvr.test.model.vo.TeQueryProjectOrchestraItemVO;
import com.cmvr.test.model.vo.TeTaskExecuteProjectVO; import com.cmvr.test.model.vo.TeTaskExecuteProjectVO;
import com.cmvr.vi.model.domain.ViProject; import com.cmvr.vi.model.domain.ViProject;
@ -63,9 +64,12 @@ public interface IViProjectService extends IService<ViProject> {
*/ */
public String executeProject(TeTaskExecuteProjectVO taskExecuteProjectVO); public String executeProject(TeTaskExecuteProjectVO taskExecuteProjectVO);
public TeTaskInst getTeTaskInst(String projectId);
/** /**
* 查询最近一次项目执行记录 * 查询最近一次项目执行记录
*/ */
public String queryExecuteInstId(String projectId); public String queryExecuteInstId(String projectId);
Object queryEvaluation(String projectId);
} }

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmvr.common.exception.GlobalException; import com.cmvr.common.exception.GlobalException;
import com.cmvr.evaluation.service.IAeEvaluationService;
import com.cmvr.test.enums.FlowSceneTypeEnum; import com.cmvr.test.enums.FlowSceneTypeEnum;
import com.cmvr.test.enums.RunModeEnum; import com.cmvr.test.enums.RunModeEnum;
import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService; import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService;
@ -44,6 +45,7 @@ public class ViProjectServiceImpl extends ServiceImpl<ViProjectMapper, ViProject
private final ITeDetectionItemService detectionItemService; private final ITeDetectionItemService detectionItemService;
private final ITeTaskConfigInfoService taskConfigInfoService; private final ITeTaskConfigInfoService taskConfigInfoService;
private final ITeTaskInstService taskInstService; private final ITeTaskInstService taskInstService;
private final IAeEvaluationService aeEvaluationService;
@Override @Override
public ViProject selectViProjectByProjectId(String projectId) { public ViProject selectViProjectByProjectId(String projectId) {
@ -132,6 +134,12 @@ public class ViProjectServiceImpl extends ServiceImpl<ViProjectMapper, ViProject
@Override @Override
public String queryExecuteInstId(String projectId) { public String queryExecuteInstId(String projectId) {
TeTaskInst latestTaskInst = getTeTaskInst(projectId);
return latestTaskInst.getId();
}
@Override
public TeTaskInst getTeTaskInst(String projectId) {
LambdaQueryWrapper<ViProject> wrapperProject = Wrappers.lambdaQuery(ViProject.class); LambdaQueryWrapper<ViProject> wrapperProject = Wrappers.lambdaQuery(ViProject.class);
wrapperProject.eq(ViProject::getProjectId, projectId) wrapperProject.eq(ViProject::getProjectId, projectId)
.eq(ViProject::getStatus, "0"); .eq(ViProject::getStatus, "0");
@ -144,6 +152,23 @@ public class ViProjectServiceImpl extends ServiceImpl<ViProjectMapper, ViProject
.orderByDesc(TeTaskInst::getCreateTime) .orderByDesc(TeTaskInst::getCreateTime)
.last("LIMIT 1"); .last("LIMIT 1");
TeTaskInst latestTaskInst = taskInstService.getOne(wrapper); TeTaskInst latestTaskInst = taskInstService.getOne(wrapper);
return latestTaskInst.getId(); return latestTaskInst;
}
@Override
public Object queryEvaluation(String projectId) {
// 查询项目信息
ViProject project = getById(projectId);
// 查询对应的实例id
String instId = queryExecuteInstId(projectId);
// 查询对应检测项信息
List<TeQueryProjectOrchestraItemVO> items = queryExecuteRunParams(projectId);
// 查询对应评估信息
// aeEvaluationService
return null;
} }
} }