master
parent
822e679f0f
commit
5bcb914139
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
||||||
|
</project>
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
*.log.gz
|
||||||
|
logs/
|
||||||
|
*/logs/
|
||||||
|
*/*/logs/
|
||||||
|
|
||||||
|
# Temp file
|
||||||
|
*.temp
|
||||||
|
temp
|
||||||
|
*/temp
|
||||||
|
|
||||||
|
# IDEA profile dir
|
||||||
|
.idea/
|
||||||
|
*/.idea/
|
||||||
|
*/*/.idea/
|
||||||
|
|
||||||
|
# IDEA project file
|
||||||
|
*.iml
|
||||||
|
*/*.iml
|
||||||
|
*/*/*.iml
|
||||||
|
*/*/*/*.iml
|
||||||
|
|
||||||
|
target/
|
||||||
|
*/target/
|
||||||
|
*/*/target/
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
Junit test
|
||||||
|
*/src/test/java/
|
||||||
|
*/src/test/resources/
|
||||||
|
|
||||||
|
dist/
|
||||||
|
*.jar
|
||||||
|
|
|
@ -108,6 +108,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.northking.cctp</groupId>
|
<groupId>net.northking.cctp</groupId>
|
||||||
<artifactId>cctp-test-element-core</artifactId>
|
<artifactId>cctp-test-element-core</artifactId>
|
||||||
|
<version>1.0.2-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.northking.cctp</groupId>
|
<groupId>net.northking.cctp</groupId>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import net.northking.cctp.se.exec.constant.AtuExecConstant;
|
||||||
import net.northking.cctp.se.file.Attachment;
|
import net.northking.cctp.se.file.Attachment;
|
||||||
import net.northking.cctp.se.lifecycle.EngineRuntime;
|
import net.northking.cctp.se.lifecycle.EngineRuntime;
|
||||||
import net.northking.cctp.se.log.bean.StepLog;
|
import net.northking.cctp.se.log.bean.StepLog;
|
||||||
|
import net.northking.cctp.se.plan.AtuTaskExecHeartbeatSchedule;
|
||||||
import net.northking.cctp.se.plan.bean.AutoTask;
|
import net.northking.cctp.se.plan.bean.AutoTask;
|
||||||
import net.northking.cctp.se.plan.bean.QuoteData;
|
import net.northking.cctp.se.plan.bean.QuoteData;
|
||||||
import net.northking.cctp.se.plan.bean.TaskExecResult;
|
import net.northking.cctp.se.plan.bean.TaskExecResult;
|
||||||
|
@ -444,6 +445,11 @@ public class DefaultExecThread implements AtuExecThread{
|
||||||
try {
|
try {
|
||||||
RabbitTemplate rabbitTemplate = SpringUtil.getBean(RabbitTemplate.class);
|
RabbitTemplate rabbitTemplate = SpringUtil.getBean(RabbitTemplate.class);
|
||||||
rabbitTemplate.convertAndSend(AtuExecConstant.TASK_EXEC_RESULT, JsonUtils.toJson(result));
|
rabbitTemplate.convertAndSend(AtuExecConstant.TASK_EXEC_RESULT, JsonUtils.toJson(result));
|
||||||
|
|
||||||
|
if (!AtuExecConstant.TASK_START.equalsIgnoreCase(type)) {
|
||||||
|
log.debug("清理正在执行中任务数据");
|
||||||
|
AtuTaskExecHeartbeatSchedule.execTaskMap.remove(result.getTaskId());
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("任务状态信息更新失败;", e);
|
log.error("任务状态信息更新失败;", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,20 +107,23 @@ public class EngineRegisterService {
|
||||||
|
|
||||||
private void setPcInfoLinux(CdEngineInfoRegisterDto dto, ScriptEngineInfo engineInfo) throws Exception{
|
private void setPcInfoLinux(CdEngineInfoRegisterDto dto, ScriptEngineInfo engineInfo) throws Exception{
|
||||||
CdPcDevice pcInfo = new CdPcDevice();
|
CdPcDevice pcInfo = new CdPcDevice();
|
||||||
pcInfo.setDeviceName(InetAddress.getLocalHost().getHostName());
|
|
||||||
pcInfo.setDomainName("设备域名");
|
|
||||||
pcInfo.setIpAddr(getLinuxIp());
|
pcInfo.setIpAddr(getLinuxIp());
|
||||||
pcInfo.setOsArch(System.getProperty("os.arch"));
|
if (atuServerConfig.getIsPcExecutor()) {
|
||||||
pcInfo.setOsName(System.getProperty("os.name"));
|
pcInfo.setDeviceName(InetAddress.getLocalHost().getHostName());
|
||||||
pcInfo.setRamSize(getMemory());
|
pcInfo.setDomainName("设备域名");
|
||||||
|
pcInfo.setOsArch(System.getProperty("os.arch"));
|
||||||
|
pcInfo.setOsName(System.getProperty("os.name"));
|
||||||
|
pcInfo.setRamSize(getMemory());
|
||||||
|
if (!StringUtils.isBlank(atuServerConfig.getRemoteProtocol())) {
|
||||||
|
pcInfo.setRemoteProtocol(atuServerConfig.getRemoteProtocol());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isBlank(atuServerConfig.getRemotePort())) {
|
||||||
|
pcInfo.setRemotePort(atuServerConfig.getRemotePort());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
engineInfo.setOsName(System.getProperty("os.name"));
|
||||||
dto.setCdPcDevice(pcInfo);
|
dto.setCdPcDevice(pcInfo);
|
||||||
if (!StringUtils.isBlank(atuServerConfig.getRemoteProtocol())) {
|
|
||||||
pcInfo.setRemoteProtocol(atuServerConfig.getRemoteProtocol());
|
|
||||||
}
|
|
||||||
if (!StringUtils.isBlank(atuServerConfig.getRemotePort())) {
|
|
||||||
pcInfo.setRemotePort(atuServerConfig.getRemotePort());
|
|
||||||
}
|
|
||||||
engineInfo.setOsName(pcInfo.getOsName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setEngInfoLinux(CdEngineInfoRegisterDto dto) {
|
private void setEngInfoLinux(CdEngineInfoRegisterDto dto) {
|
||||||
|
|
|
@ -94,16 +94,6 @@ public class LifecycleSchedule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 运行时管理
|
|
||||||
* 初始延迟时间 10 秒,间隔 60秒
|
|
||||||
*/
|
|
||||||
@Scheduled(initialDelay = 15 * 1000L, fixedDelay = 60 * 1000L)
|
|
||||||
public void manage()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deployNotice(Object object) {
|
private void deployNotice(Object object) {
|
||||||
byte[] json;
|
byte[] json;
|
||||||
try{
|
try{
|
||||||
|
|
|
@ -9,5 +9,9 @@ public class MQConstant {
|
||||||
*/
|
*/
|
||||||
public static final String ENGINE_HEARTBEAT = "engine.heartbeat";
|
public static final String ENGINE_HEARTBEAT = "engine.heartbeat";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务执行心跳
|
||||||
|
*/
|
||||||
|
public static final String TASK_EXEC_HEARTBEAT_QUEUE = "plan.task.exec.heartbeat";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,12 +79,7 @@ public class AtuPlanTaskSchedule {
|
||||||
logger.error("未开启移动执行线程");
|
logger.error("未开启移动执行线程");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.debug("移动线程池执行情况: {}", ExecutorPool.getMobilePool().getActiveCount() + "/" +
|
|
||||||
ExecutorPool.getMobilePool().getCorePoolSize());
|
|
||||||
if (!ExecutorPool.isMobileAvailable()){
|
|
||||||
logger.error("移动线程池中可执行线程已占满");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<String> list = new ArrayList<>();
|
List<String> list = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
list = enginePlanInfoService.queryAllDeviceId(TYPE_MOBILE);
|
list = enginePlanInfoService.queryAllDeviceId(TYPE_MOBILE);
|
||||||
|
@ -92,43 +87,53 @@ public class AtuPlanTaskSchedule {
|
||||||
logger.error("引擎获取执行设备信息失败设备:",e);
|
logger.error("引擎获取执行设备信息失败设备:",e);
|
||||||
}
|
}
|
||||||
if(list.size()>0){
|
if(list.size()>0){
|
||||||
//获取引擎注册后的设备id
|
logger.debug("委托执行的设备数量:{}", list.size());
|
||||||
EngineRuntime engineRuntime = EngineRuntime.getRuntime();
|
for (String taskDeviceId : list) {
|
||||||
ScriptEngineInfo info = engineRuntime.getEngineInfo();
|
logger.debug("移动线程池执行情况: {}", ExecutorPool.getMobilePool().getActiveCount() + "/" +
|
||||||
Map<String,Object> paramMap = new HashMap<>();
|
ExecutorPool.getMobilePool().getCorePoolSize());
|
||||||
paramMap.put("id",info.getEngineId());
|
if (!ExecutorPool.isMobileAvailable()){
|
||||||
paramMap.put("deviceList",list);
|
logger.error("移动线程池中可执行线程已占满");
|
||||||
paramMap.put("type","1");
|
return;
|
||||||
paramMap.put("userBy","engine");
|
}
|
||||||
String url = atuServerConfig.getServerUrl() + atuServerConfig.getTryAcqurieDeviceUrl();
|
//获取引擎注册后的设备id
|
||||||
try {
|
EngineRuntime engineRuntime = EngineRuntime.getRuntime();
|
||||||
ResponseEntity<ResultWrapper> result = restTemplate.postForEntity(url, paramMap, ResultWrapper.class);
|
ScriptEngineInfo info = engineRuntime.getEngineInfo();
|
||||||
if(HttpStatus.OK.equals(result.getStatusCode())){
|
Map<String,Object> paramMap = new HashMap<>();
|
||||||
ResultWrapper deviceResult = result.getBody();
|
paramMap.put("id",info.getEngineId());
|
||||||
if (deviceResult != null && deviceResult.isSuccess()) {
|
List<String> tryAcqurieDeviceIdList = Collections.singletonList(taskDeviceId);
|
||||||
List<Map<String,String>> body = (List<Map<String,String>>)deviceResult.getData();
|
paramMap.put("deviceList",tryAcqurieDeviceIdList);
|
||||||
if(!CollectionUtils.isEmpty(body)){
|
paramMap.put("type","1");
|
||||||
logger.debug("移动任务请求占用成功移动端设备信息:"+JSON.toJSONString(body));
|
paramMap.put("userBy","engine");
|
||||||
for (int i = 0; i < body.size(); i++) {
|
String url = atuServerConfig.getServerUrl() + atuServerConfig.getTryAcqurieDeviceUrl();
|
||||||
Map<String, String> deviceMap = body.get(i);
|
try {
|
||||||
String deviceId = deviceMap.get("id");
|
ResponseEntity<ResultWrapper> result = restTemplate.postForEntity(url, paramMap, ResultWrapper.class);
|
||||||
String deviceToken = deviceMap.get("token");
|
if(HttpStatus.OK.equals(result.getStatusCode())){
|
||||||
//获取设备对应的一条计划信息
|
ResultWrapper deviceResult = result.getBody();
|
||||||
boolean isUsed = getTaskFromQueue(deviceId, deviceToken,TYPE_MOBILE);
|
if (deviceResult != null && deviceResult.isSuccess()) {
|
||||||
if(!isUsed){
|
List<Map<String,String>> body = (List<Map<String,String>>)deviceResult.getData();
|
||||||
//无任务 释放设备
|
if(!CollectionUtils.isEmpty(body)){
|
||||||
logger.debug("无任务释放移动端设备:"+deviceId);
|
logger.debug("移动任务请求占用成功移动端设备信息:"+JSON.toJSONString(body));
|
||||||
releaseDevice(deviceToken);
|
for (int i = 0; i < body.size(); i++) {
|
||||||
|
Map<String, String> deviceMap = body.get(i);
|
||||||
|
String deviceId = deviceMap.get("id");
|
||||||
|
String deviceToken = deviceMap.get("token");
|
||||||
|
//获取设备对应的一条计划信息
|
||||||
|
boolean isUsed = getTaskFromQueue(deviceId, deviceToken,TYPE_MOBILE);
|
||||||
|
if(!isUsed){
|
||||||
|
//无任务 释放设备
|
||||||
|
logger.debug("无任务释放移动端设备:"+deviceId);
|
||||||
|
releaseDevice(deviceToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.error("请求占用设备失败,原因:",e);
|
||||||
|
//释放所有设备
|
||||||
|
releaseDevices(e, info.getEngineId(), tryAcqurieDeviceIdList);
|
||||||
}
|
}
|
||||||
}catch (Exception e){
|
|
||||||
logger.error("请求占用设备失败,原因:",e);
|
|
||||||
//释放所有设备
|
|
||||||
releaseDevices(e, info.getEngineId(), list);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,12 +343,16 @@ public class AtuPlanTaskSchedule {
|
||||||
task.setAppDownloadUrl(atuServerConfig.getServerUrl() + atuServerConfig.getAppDownloadUrl());
|
task.setAppDownloadUrl(atuServerConfig.getServerUrl() + atuServerConfig.getAppDownloadUrl());
|
||||||
logger.debug("任务信息:" + JSON.toJSONString(task));
|
logger.debug("任务信息:" + JSON.toJSONString(task));
|
||||||
planDeviceService.sendTask2Exec(task, deviceToken);
|
planDeviceService.sendTask2Exec(task, deviceToken);
|
||||||
|
AtuTaskExecHeartbeatSchedule.execTaskMap.put(task.getTaskId(), task.getCaseType());
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.debug("消息处理异常,回滚至队列中,消息为:{}", msg);
|
logger.debug("消息处理异常,回滚至队列中,消息为:{}", msg);
|
||||||
rabbitTemplate.convertAndSend(queueName, msg);
|
rabbitTemplate.convertAndSend(queueName, msg);
|
||||||
throw new ExecuteException("获取队列任务并推送执行异常, " + e.getMessage());
|
throw new ExecuteException("获取队列任务并推送执行异常, " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
logger.debug("队列[{}]中无任务信息,修改该委托顺序", queueName);
|
||||||
|
enginePlanInfoService.updatePlanInfoSort(planInfo.getId());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package net.northking.cctp.se.plan;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import net.northking.cctp.se.lifecycle.constant.MQConstant;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.core.Queue;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class AtuTaskExecHeartbeatSchedule {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AtuTaskExecHeartbeatSchedule.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RabbitAdmin rabbitAdmin;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录正在执行中的任务,key:任务id,value:任务类型
|
||||||
|
*/
|
||||||
|
public static ConcurrentHashMap<String, String> execTaskMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init(){
|
||||||
|
Queue queue = new Queue(MQConstant.TASK_EXEC_HEARTBEAT_QUEUE);
|
||||||
|
rabbitAdmin.declareQueue(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送任务执行心跳
|
||||||
|
*/
|
||||||
|
@Scheduled(initialDelay = 10 * 1000,fixedDelay = 60 * 1000)
|
||||||
|
public void sendTaskExecHeartbeat(){
|
||||||
|
if (execTaskMap.size() <= 0){
|
||||||
|
logger.debug("无正在执行的任务...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
rabbitTemplate.convertAndSend(MQConstant.TASK_EXEC_HEARTBEAT_QUEUE, JSONUtil.toJsonStr(execTaskMap));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("发送任务执行心跳异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,11 @@ public class EnginePlanInfo {
|
||||||
*/
|
*/
|
||||||
private Date createdTime;
|
private Date createdTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 委托排序
|
||||||
|
*/
|
||||||
|
private Long sort;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -126,4 +131,12 @@ public class EnginePlanInfo {
|
||||||
public void setCreatedTime(Date createdTime) {
|
public void setCreatedTime(Date createdTime) {
|
||||||
this.createdTime = createdTime;
|
this.createdTime = createdTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getSort() {
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSort(Long sort) {
|
||||||
|
this.sort = sort;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,4 +46,9 @@ public interface EnginePlanInfoService {
|
||||||
*/
|
*/
|
||||||
void uploadFinishedFile();
|
void uploadFinishedFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新计划顺序
|
||||||
|
* @param id 计划id
|
||||||
|
*/
|
||||||
|
void updatePlanInfoSort(String id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ public class EnginePlanInfoServiceImpl implements EnginePlanInfoService {
|
||||||
info.setBrowserType(s.getBrowserType());
|
info.setBrowserType(s.getBrowserType());
|
||||||
}
|
}
|
||||||
info.setCreatedTime(new Date());
|
info.setCreatedTime(new Date());
|
||||||
|
info.setSort(0l);
|
||||||
repository.save(info);
|
repository.save(info);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
@ -178,4 +179,9 @@ public class EnginePlanInfoServiceImpl implements EnginePlanInfoService {
|
||||||
}
|
}
|
||||||
logger.debug("检查是否存在未上传的任务结果文件结束。。。。");
|
logger.debug("检查是否存在未上传的任务结果文件结束。。。。");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePlanInfoSort(String id) {
|
||||||
|
repository.updatePlanInfoSort(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface EnginePlanInfoRepository extends JpaRepository<EnginePlanInfo,String> {
|
public interface EnginePlanInfoRepository extends JpaRepository<EnginePlanInfo,String> {
|
||||||
|
|
||||||
@Query(value = "select * from engine_plan_info where device_id = :deviceId and type = :type order by priority desc ,created_time asc"
|
@Query(value = "select * from engine_plan_info where device_id = :deviceId and type = :type order by priority desc , sort asc , created_time asc"
|
||||||
,nativeQuery = true)
|
,nativeQuery = true)
|
||||||
List<EnginePlanInfo> getOneInfoByDeviceId(@Param("deviceId") String deviceId,@Param("type") String type);
|
List<EnginePlanInfo> getOneInfoByDeviceId(@Param("deviceId") String deviceId,@Param("type") String type);
|
||||||
|
|
||||||
|
@ -35,4 +35,8 @@ public interface EnginePlanInfoRepository extends JpaRepository<EnginePlanInfo,S
|
||||||
,nativeQuery = true)
|
,nativeQuery = true)
|
||||||
List<EnginePlanInfo> selectAllPlanInfo();
|
List<EnginePlanInfo> selectAllPlanInfo();
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Modifying
|
||||||
|
@Query(value = "update engine_plan_info set sort = sort + 1 where id = :id",nativeQuery = true)
|
||||||
|
void updatePlanInfoSort(@Param("id") String id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1514,16 +1514,25 @@ public class ScriptRuntimeExecutor implements ScriptExecutor {
|
||||||
* @param stepRet 步骤结果
|
* @param stepRet 步骤结果
|
||||||
*/
|
*/
|
||||||
private void snapshot(StepExecuteResult stepRet, boolean snapshotFlag, String msg) {
|
private void snapshot(StepExecuteResult stepRet, boolean snapshotFlag, String msg) {
|
||||||
|
String contentSnapshotPath = this.getVariableValue(DebugerConst.ReplyConst.MOBILE_STEP_SNAPSHOT);
|
||||||
if (snapshotFlag) {
|
if (snapshotFlag) {
|
||||||
log.info(msg);
|
log.info(msg);
|
||||||
try {
|
try {
|
||||||
DeviceConnection deviceConnection = (DeviceConnection) this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN);
|
DeviceConnection deviceConnection = (DeviceConnection) this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN);
|
||||||
String tenantId = this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString();
|
String tenantId = this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString();
|
||||||
String imageUrl = deviceConnection.snapshotAllScreen(tenantId, currentTaskId);
|
String imageUrl = deviceConnection.snapshotAllScreen(tenantId, currentTaskId);
|
||||||
stepRet.setActualImgUri(imageUrl);
|
if (StringUtils.isNotBlank(contentSnapshotPath)) {
|
||||||
|
stepRet.setActualImgUri(contentSnapshotPath);
|
||||||
|
} else {
|
||||||
|
stepRet.setActualImgUri(imageUrl);
|
||||||
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("截图失败:", ex);
|
log.error("截图失败:", ex);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (StringUtils.isNotBlank(contentSnapshotPath)) {
|
||||||
|
stepRet.setActualImgUri(contentSnapshotPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1567,7 +1576,7 @@ public class ScriptRuntimeExecutor implements ScriptExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doLog(LogLevel logLevel, String msg) {
|
private void doLog(LogLevel logLevel, String msg) {
|
||||||
if (listener != null) {
|
if (listener != null && this.stepLog != null) {
|
||||||
this.stepLog.setLevel(logLevel.name());
|
this.stepLog.setLevel(logLevel.name());
|
||||||
this.stepLog.setLog(msg);
|
this.stepLog.setLog(msg);
|
||||||
this.stepLog.setStepId(this.currentStepId);
|
this.stepLog.setStepId(this.currentStepId);
|
||||||
|
|
|
@ -36,8 +36,8 @@ public class HttpUtils {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||||
factory.setConnectTimeout(15000);
|
factory.setConnectTimeout(30000);
|
||||||
factory.setReadTimeout(15000);
|
factory.setReadTimeout(30000);
|
||||||
restTemplate = new RestTemplate(factory);
|
restTemplate = new RestTemplate(factory);
|
||||||
|
|
||||||
SimpleClientHttpRequestFactory downloadFactory = new SimpleClientHttpRequestFactory();
|
SimpleClientHttpRequestFactory downloadFactory = new SimpleClientHttpRequestFactory();
|
||||||
|
|
|
@ -677,7 +677,7 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService<AtuPlanInfo>
|
||||||
public Map<String, Object> checkDeviceOffline(String planId, String batchId) {
|
public Map<String, Object> checkDeviceOffline(String planId, String batchId) {
|
||||||
// allOffline 0-在线,1-部分离线,2-全部离线
|
// allOffline 0-在线,1-部分离线,2-全部离线
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
if (batchId == null) {
|
if (StrUtil.isEmpty(batchId)) {
|
||||||
batchId = IdUtil.simpleUUID();
|
batchId = IdUtil.simpleUUID();
|
||||||
}
|
}
|
||||||
result.put(PlanConstant.DEVICE_OFFLINE_KEY, PlanConstant.DEVICE_OFFLINE_PORTION);
|
result.put(PlanConstant.DEVICE_OFFLINE_KEY, PlanConstant.DEVICE_OFFLINE_PORTION);
|
||||||
|
@ -2543,11 +2543,13 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService<AtuPlanInfo>
|
||||||
// 不是手动执行时,校验设备是否离线
|
// 不是手动执行时,校验设备是否离线
|
||||||
if (!PlanConstant.TRIGGER_TYPE_MANUAL.equals(atuPlanRunDto.getTriggerType())){
|
if (!PlanConstant.TRIGGER_TYPE_MANUAL.equals(atuPlanRunDto.getTriggerType())){
|
||||||
Map<String,Object> offlineMap = checkDeviceOffline(atuPlanRunDto.getPlanId(),atuPlanRunDto.getBatchId());
|
Map<String,Object> offlineMap = checkDeviceOffline(atuPlanRunDto.getPlanId(),atuPlanRunDto.getBatchId());
|
||||||
|
logger.debug("校验设备是否离线结果: {}", JSONUtil.toJsonStr(offlineMap));
|
||||||
Object allOfflineObj = offlineMap.get(PlanConstant.DEVICE_OFFLINE_KEY);
|
Object allOfflineObj = offlineMap.get(PlanConstant.DEVICE_OFFLINE_KEY);
|
||||||
if (allOfflineObj != null && PlanConstant.DEVICE_OFFLINE_ALL == Integer.parseInt(allOfflineObj.toString())) {
|
if (allOfflineObj != null && PlanConstant.DEVICE_OFFLINE_ALL == Integer.parseInt(allOfflineObj.toString())) {
|
||||||
logger.error("该计划绑定的设备已全部离线");
|
logger.error("该计划绑定的设备已全部离线");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
atuPlanRunDto.setBatchId(offlineMap.get(PlanConstant.PLAN_BATCH_ID).toString());
|
||||||
}
|
}
|
||||||
// 1.查询计划信息
|
// 1.查询计划信息
|
||||||
AtuPlanInfo planInfo = this.atuPlanInfoService.findByPrimaryKey(atuPlanRunDto.getPlanId());
|
AtuPlanInfo planInfo = this.atuPlanInfoService.findByPrimaryKey(atuPlanRunDto.getPlanId());
|
||||||
|
@ -2875,6 +2877,7 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService<AtuPlanInfo>
|
||||||
redisTemplate.delete(RedisConstant.PLAN_BATCH_OFFLINE_DEVICE + batchId);
|
redisTemplate.delete(RedisConstant.PLAN_BATCH_OFFLINE_DEVICE + batchId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.debug("离线设备id集合: {}", JSONUtil.toJsonStr(offlineDeviceIdList));
|
||||||
|
|
||||||
List<AtuPlanBatchDeviceLink> batchDeviceList = new ArrayList<>();
|
List<AtuPlanBatchDeviceLink> batchDeviceList = new ArrayList<>();
|
||||||
// 1.1.获取PC与移动设备集合
|
// 1.1.获取PC与移动设备集合
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package net.northking.cctp.executePlan.api.service;
|
||||||
|
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
|
||||||
|
|
||||||
|
public interface AtuPlanSceneCaseTaskApiService {
|
||||||
|
|
||||||
|
void sceneCaseHandle(AtuTaskExecResultDto taskExecResult, String clusterKeyPrefix);
|
||||||
|
|
||||||
|
void queryNextNodeInfo(AtuTaskExecResultDto taskExecResult, AtuPlanSceneCaseTask planSceneCaseTask, AtuPlanTask planTask);
|
||||||
|
|
||||||
|
void associatedActualImgAndVideo(AtuTaskExecResultDto taskExecResult);
|
||||||
|
|
||||||
|
String parseMobilePerformanceFile(AtuTaskExecResultDto taskExecResult);
|
||||||
|
}
|
|
@ -0,0 +1,426 @@
|
||||||
|
package net.northking.cctp.executePlan.api.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import net.northking.cctp.common.enums.FileBusinessTypeEnum;
|
||||||
|
import net.northking.cctp.common.http.ResultWrapper;
|
||||||
|
import net.northking.cctp.common.s3.FileDownloadException;
|
||||||
|
import net.northking.cctp.common.s3.NKFile;
|
||||||
|
import net.northking.cctp.common.s3.SimpleStorageService;
|
||||||
|
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
||||||
|
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||||
|
import net.northking.cctp.executePlan.constants.RedisConstant;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanInfo;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanInfoService;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||||
|
import net.northking.cctp.executePlan.dto.planBatch.AppPerInfo;
|
||||||
|
import net.northking.cctp.executePlan.dto.planBatch.DevicePerInfo;
|
||||||
|
import net.northking.cctp.executePlan.dto.planBatch.MobileTaskPerformanceDto;
|
||||||
|
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNextNodeDto;
|
||||||
|
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNodeExecDto;
|
||||||
|
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNodeInfoDto;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.*;
|
||||||
|
import net.northking.cctp.executePlan.feign.AttachmentFeignClient;
|
||||||
|
import net.northking.cctp.executePlan.feign.PublicFeignClient;
|
||||||
|
import net.northking.cctp.executePlan.utils.MinioPathUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AtuPlanSceneCaseTaskApiServiceImpl implements AtuPlanSceneCaseTaskApiService{
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AtuPlanSceneCaseTaskApiServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanInfoService planInfoService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanBatchApiService planBatchApiService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanTaskApiService planTaskApiService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PublicFeignClient publicFeignClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AttachmentFeignClient attachmentFeignClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SimpleStorageService simpleStorageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景用例处理
|
||||||
|
* @param taskExecResult 任务执行结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void sceneCaseHandle(AtuTaskExecResultDto taskExecResult, String clusterKeyPrefix){
|
||||||
|
AtuPlanSceneCaseTask planSceneCaseTask = planSceneCaseTaskService.findByPrimaryKey(taskExecResult.getTaskId());
|
||||||
|
if (ObjectUtil.isNull(planSceneCaseTask)){
|
||||||
|
logger.error("场景节点任务[" + taskExecResult.getTaskId() + "]信息不存在");
|
||||||
|
// 根据批次和用例id查询任务信息
|
||||||
|
AtuPlanTask query = new AtuPlanTask();
|
||||||
|
query.setBatchId(taskExecResult.getBatchId());
|
||||||
|
query.setCaseId(taskExecResult.getCaseId());
|
||||||
|
List<AtuPlanTask> taskList = planTaskApiService.query(query);
|
||||||
|
if (CollUtil.isEmpty(taskList)){
|
||||||
|
logger.error("根据批次id[{}]与用例id[{}]无法查询到该任务", query.getBatchId(), query.getCaseId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
planTaskApiService.taskExecFailUpdate(taskList.get(0), PlanConstant.TASK_EXECUTE_FAIL_STATUS, "场景节点任务信息不存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//开始执行同步更新计划任务表信息
|
||||||
|
AtuPlanTask planTask = planTaskApiService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
||||||
|
if (ObjectUtil.isNull(planTask)) {
|
||||||
|
logger.error("任务[" + planSceneCaseTask.getTaskId() + "]信息不存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断是否开始执行
|
||||||
|
if (ObjectUtil.equal(PlanConstant.TASK_START_EXECUTE_STATUS, taskExecResult.getStatus())){
|
||||||
|
planSceneCaseTask.setStartTime(taskExecResult.getCurrentTime());
|
||||||
|
planSceneCaseTask.setEngineId(taskExecResult.getEngineId());
|
||||||
|
planSceneCaseTask.setDeviceId(taskExecResult.getDeviceId());
|
||||||
|
planSceneCaseTask.setAppId(taskExecResult.getAppId());
|
||||||
|
planSceneCaseTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||||
|
Date now = new Date();
|
||||||
|
planSceneCaseTask.setLastHeartbeatTime(now);
|
||||||
|
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||||
|
// 判断是否首节点
|
||||||
|
AtuPlanSceneCaseTask countParams = new AtuPlanSceneCaseTask();
|
||||||
|
countParams.setTaskId(planSceneCaseTask.getTaskId());
|
||||||
|
long count = planSceneCaseTaskService.count(countParams);
|
||||||
|
if (count == 1) {
|
||||||
|
|
||||||
|
planTask.setStartTime(taskExecResult.getCurrentTime());
|
||||||
|
planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||||
|
planTask.setLastHeartbeatTime(now);
|
||||||
|
planTaskApiService.updateByPrimaryKey(planTask);
|
||||||
|
|
||||||
|
if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
|
||||||
|
clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
|
||||||
|
logger.debug("批次[" + planTask.getBatchId() + "]开始执行,更新计划的状态");
|
||||||
|
planInfoService.updatePlanByLastBatchId(planTask.getBatchId(), PlanConstant.PLAN_EXECUTING_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新批次统计数据
|
||||||
|
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else {
|
||||||
|
// 获取下一节点信息
|
||||||
|
queryNextNodeInfo(taskExecResult, planSceneCaseTask, planTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询下一节点信息
|
||||||
|
* @param taskExecResult 任务执行结果
|
||||||
|
* @param planSceneCaseTask 场景用例节点任务
|
||||||
|
* @param planTask 任务信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void queryNextNodeInfo(AtuTaskExecResultDto taskExecResult, AtuPlanSceneCaseTask planSceneCaseTask,
|
||||||
|
AtuPlanTask planTask){
|
||||||
|
|
||||||
|
planSceneCaseTask.setStatus(taskExecResult.getStatus());
|
||||||
|
planSceneCaseTask.setEndTime(taskExecResult.getCurrentTime());
|
||||||
|
planSceneCaseTask.setErrorMsg(taskExecResult.getMessage());
|
||||||
|
planSceneCaseTask.setVideoUrl(taskExecResult.getVideoPath());
|
||||||
|
planSceneCaseTask.setExecResultFile(taskExecResult.getFilePath());
|
||||||
|
logger.debug("更新当前节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
||||||
|
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||||
|
|
||||||
|
AtuSceneNextNodeDto sceneNextNodeDto = new AtuSceneNextNodeDto();
|
||||||
|
//传递计划任务表id
|
||||||
|
sceneNextNodeDto.setTaskId(planSceneCaseTask.getTaskId());
|
||||||
|
sceneNextNodeDto.setCaseId(taskExecResult.getCaseId());
|
||||||
|
sceneNextNodeDto.setNodeId(planSceneCaseTask.getNodeId());
|
||||||
|
sceneNextNodeDto.setSuccess(ObjectUtil.equal(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS, taskExecResult.getStatus()));
|
||||||
|
sceneNextNodeDto.setOutputArgs(taskExecResult.getOutputs());
|
||||||
|
sceneNextNodeDto.setScriptId(planTask.getScriptId());
|
||||||
|
sceneNextNodeDto.setSceneScriptUrl(planTask.getScriptJson());
|
||||||
|
ResultWrapper<AtuSceneNodeExecDto> nextNodeResult;
|
||||||
|
try {
|
||||||
|
logger.debug("查询场景下一节点信息,参数为 => " + JSONUtil.toJsonStr(sceneNextNodeDto));
|
||||||
|
//捕获异常信息
|
||||||
|
nextNodeResult = publicFeignClient.getNextNode(sceneNextNodeDto);
|
||||||
|
if (!nextNodeResult.isSuccess()){
|
||||||
|
logger.error("获取下一节点信息结果异常," + nextNodeResult.getMessage());
|
||||||
|
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_EXECUTE_FAIL_STATUS,
|
||||||
|
nextNodeResult.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取下一节点信息异常:",e);
|
||||||
|
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_EXECUTE_FAIL_STATUS,
|
||||||
|
"获取下一节点信息异常" + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("查询场景下一节点信息,返回结果为 => " + JSONUtil.toJsonStr(nextNodeResult));
|
||||||
|
AtuSceneNodeExecDto sceneNodeExecDto = nextNodeResult.getData();
|
||||||
|
|
||||||
|
//更新url到表里
|
||||||
|
if (sceneNodeExecDto != null && sceneNodeExecDto.getSceneScriptUrl() != null) {
|
||||||
|
planTask.setScriptJson(sceneNodeExecDto.getSceneScriptUrl());
|
||||||
|
planTaskApiService.updateByPrimaryKey(planTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtuSceneNodeInfoDto nodeInfo = sceneNodeExecDto.getNodeInfo();
|
||||||
|
// 节点不管成功或者失败,若没有连接到结束节点,默认最后节点为结束,节点信息不包含lineId
|
||||||
|
planSceneCaseTask.setNextNodeId(nodeInfo.getNodeId());
|
||||||
|
planSceneCaseTask.setLineId(nodeInfo.getLineId());
|
||||||
|
|
||||||
|
// 记录性能数据
|
||||||
|
if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planSceneCaseTask.getNodeType())
|
||||||
|
|| PlanConstant.SCRIPT_TYPE_IOS.equals(planSceneCaseTask.getNodeType())) {
|
||||||
|
logger.debug("处理场景移动端性能文件数据");
|
||||||
|
planSceneCaseTask.setPerDataPath(parseMobilePerformanceFile(taskExecResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("更新当前节点中下一节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
||||||
|
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||||
|
// 关联文件,使用 CompletableFuture 复用线程
|
||||||
|
CompletableFuture.runAsync(() -> associatedActualImgAndVideo(taskExecResult));
|
||||||
|
// future.get();
|
||||||
|
// 判断是否结束节点
|
||||||
|
if (sceneNodeExecDto.getEnd()){
|
||||||
|
logger.info("场景任务执行结束....");
|
||||||
|
//判断任务状态是否未非引擎返回的状态 取消/超时
|
||||||
|
if(ObjectUtil.equal(PlanConstant.TASK_CANCEL_STATUS, planTask.getStatus())
|
||||||
|
||ObjectUtil.equal(PlanConstant.TASK_TIMEOUT_STATUS, planTask.getStatus())){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
planTask.setEndTime(planSceneCaseTask.getEndTime());
|
||||||
|
if (StrUtil.isEmpty(nodeInfo.getLineId())) {
|
||||||
|
planTask.setStatus(planSceneCaseTask.getStatus());
|
||||||
|
planTask.setErrorMsg(planSceneCaseTask.getErrorMsg());
|
||||||
|
}else{
|
||||||
|
// 节点执行结束且连接了结束节点,则该场景任务设置成功执行
|
||||||
|
planTask.setStatus(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS);
|
||||||
|
}
|
||||||
|
logger.debug("更新场景任务为结束");
|
||||||
|
planTaskApiService.updateByPrimaryKey(planTask);
|
||||||
|
|
||||||
|
// 更新批次统计数据
|
||||||
|
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||||
|
// 删除节点更新时的场景任务文件
|
||||||
|
String noDeleteFileId = "/" + MinioPathUtils.idToPath(sceneNodeExecDto.getSceneScriptUrl())[1];
|
||||||
|
attachmentFeignClient.clearSceneTaskAttachments(planTask.getScriptId(), FileBusinessTypeEnum.SCENE_TASK_REFRESH_FILE.getCode(), noDeleteFileId);
|
||||||
|
}else {
|
||||||
|
// 新增下一节点任务信息
|
||||||
|
try {
|
||||||
|
createNextNodeTask(planSceneCaseTask, nodeInfo, taskExecResult, planTask.getEnvId());
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.error("创建下一节点任务信息失败", e);
|
||||||
|
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_EXECUTE_FAIL_STATUS,
|
||||||
|
"创建下一节点任务信息失败" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建下一节点任务
|
||||||
|
* @param planSceneCaseTask 场景用例节点任务
|
||||||
|
* @param nodeInfo 节点信息
|
||||||
|
* @param taskExecResult 任务执行结果
|
||||||
|
*/
|
||||||
|
private void createNextNodeTask(AtuPlanSceneCaseTask planSceneCaseTask, AtuSceneNodeInfoDto nodeInfo,
|
||||||
|
AtuTaskExecResultDto taskExecResult, String envId){
|
||||||
|
logger.debug("保存下一节点信息");
|
||||||
|
logger.debug("节点信息:" + JSONUtil.toJsonStr(nodeInfo));
|
||||||
|
AtuPlanSceneCaseTask nextNodeTask = new AtuPlanSceneCaseTask();
|
||||||
|
nextNodeTask.setId(IdUtil.simpleUUID());
|
||||||
|
nextNodeTask.setTaskId(planSceneCaseTask.getTaskId());
|
||||||
|
nextNodeTask.setScriptId(nodeInfo.getScriptId());
|
||||||
|
nextNodeTask.setVersionId(nodeInfo.getVersionId());
|
||||||
|
nextNodeTask.setVersionName(nodeInfo.getVersionName());
|
||||||
|
nextNodeTask.setScriptName(nodeInfo.getScriptName());
|
||||||
|
nextNodeTask.setScriptJson(nodeInfo.getScriptPath());
|
||||||
|
nextNodeTask.setNodeId(nodeInfo.getNodeId());
|
||||||
|
nextNodeTask.setNodeType(nodeInfo.getNodeType());
|
||||||
|
nextNodeTask.setStatus(PlanConstant.TASK_WAIT_EXECUTE_STATUS);
|
||||||
|
if (CollUtil.isNotEmpty(nodeInfo.getCaseParam())) {
|
||||||
|
nextNodeTask.setNodeParams(JSONUtil.toJsonStr(nodeInfo.getCaseParam()));
|
||||||
|
}
|
||||||
|
nextNodeTask.setPrevNodeId(planSceneCaseTask.getNodeId());
|
||||||
|
nextNodeTask.setCreatedTime(new Date());
|
||||||
|
planSceneCaseTaskService.insert(nextNodeTask);
|
||||||
|
|
||||||
|
// 查询计划信息
|
||||||
|
AtuPlanInfo planInfo = planInfoService.findByPrimaryKey(taskExecResult.getPlanId());
|
||||||
|
if (ObjectUtil.isNull(planInfo)){
|
||||||
|
logger.error("计划[" + taskExecResult.getPlanId() + "]信息为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("生成任务并推送至队列");
|
||||||
|
AtuTaskExecDto atuTaskExecDto = new AtuTaskExecDto();
|
||||||
|
atuTaskExecDto.setTenantId(planInfo.getTenantId());
|
||||||
|
atuTaskExecDto.setProjectId(planInfo.getProjectId());
|
||||||
|
atuTaskExecDto.setEnvId(envId);
|
||||||
|
atuTaskExecDto.setPlanId(planInfo.getId());
|
||||||
|
atuTaskExecDto.setBatchId(taskExecResult.getBatchId());
|
||||||
|
atuTaskExecDto.setTaskId(nextNodeTask.getId());
|
||||||
|
atuTaskExecDto.setCaseId(taskExecResult.getCaseId());
|
||||||
|
atuTaskExecDto.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
|
||||||
|
atuTaskExecDto.setScriptPath(nodeInfo.getScriptPath());
|
||||||
|
atuTaskExecDto.setCaseParams(nodeInfo.getCaseParam());
|
||||||
|
atuTaskExecDto.setAppSet(planInfo.getAppSet());
|
||||||
|
atuTaskExecDto.setScreenRecordSet(planInfo.getScreenRecordSet());
|
||||||
|
atuTaskExecDto.setScreenshotSet(planInfo.getScreenshotSet());
|
||||||
|
atuTaskExecDto.setFailRetryNum(planInfo.getFailRetryCount());
|
||||||
|
|
||||||
|
atuTaskExecDto.setAppId(planTaskApiService.queryAppId(planInfo.getId(), nodeInfo.getPlatformType(),
|
||||||
|
nodeInfo.getAppPackage(), nodeInfo.getNodeType()));
|
||||||
|
|
||||||
|
// 发送任务执行信息至消息队列
|
||||||
|
planTaskApiService.sendToQueue(atuTaskExecDto.getBatchId(), atuTaskExecDto, nodeInfo.getNodeType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void associatedActualImgAndVideo(AtuTaskExecResultDto taskExecResult) {
|
||||||
|
try {
|
||||||
|
if (StringUtils.hasText(taskExecResult.getFilePath())) {
|
||||||
|
InputStream inputStream = simpleStorageService.downloadAsStream(NKSecurityContext.getTenantId(), taskExecResult.getFilePath());
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
List<String> fileUriList = new ArrayList<>();
|
||||||
|
String line = null;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
StepExecuteResult stepExecuteResult = JSONUtil.toBean(line, StepExecuteResult.class);
|
||||||
|
String actualImgUri = stepExecuteResult.getActualImgUri();
|
||||||
|
if (StringUtils.hasText(actualImgUri)) {
|
||||||
|
fileUriList.add(actualImgUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileUriList.add(taskExecResult.getFilePath());
|
||||||
|
if (StringUtils.hasText(taskExecResult.getVideoPath())) {
|
||||||
|
fileUriList.add(taskExecResult.getVideoPath());
|
||||||
|
}
|
||||||
|
attachmentFeignClient.associatedFiles(fileUriList, taskExecResult.getTaskId());
|
||||||
|
}
|
||||||
|
} catch (FileDownloadException e) {
|
||||||
|
logger.error("文件下载异常", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("文件读取异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseMobilePerformanceFile(AtuTaskExecResultDto taskExecResult){
|
||||||
|
String path = "";
|
||||||
|
DevicePerInfo devicePerInfo = taskExecResult.getDevicePerInfo();
|
||||||
|
logger.debug("设备性能数据结果 => {}", JSONUtil.toJsonStr(devicePerInfo) );
|
||||||
|
if (devicePerInfo == null){
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
AtuPlanInfo planInfo = planInfoService.findByBatchId(taskExecResult.getBatchId());
|
||||||
|
MobileTaskPerformanceDto taskPerInfo = new MobileTaskPerformanceDto();
|
||||||
|
taskPerInfo.setDeviceId(taskExecResult.getDeviceId());
|
||||||
|
taskPerInfo.setAppId(taskExecResult.getAppId());
|
||||||
|
String devicePerInfoAddress = devicePerInfo.getDevicePerInfoAddress();
|
||||||
|
logger.info("任务ID:{},设备性能数据地址:{}", taskExecResult.getTaskId(), devicePerInfoAddress);
|
||||||
|
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
||||||
|
DevicePerInfoDataDto deviceData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
||||||
|
planInfo.getTenantId(), devicePerInfoAddress, DevicePerInfoDataDto.class);
|
||||||
|
logger.debug("设备性能数据 => {}", JSONUtil.toJsonStr(deviceData));
|
||||||
|
if (deviceData != null){
|
||||||
|
taskPerInfo.setDeviceCpuList(filterData(deviceData.getCpuList()));
|
||||||
|
taskPerInfo.setDeviceMemoList(filterData(deviceData.getMemoList()));
|
||||||
|
taskPerInfo.setDeviceFlowList(filterData(deviceData.getFlowList()));
|
||||||
|
taskPerInfo.setDeviceTemperatureList(filterData(deviceData.getTemperatureList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppPerInfo appPerInfo = devicePerInfo.getAppPerInfo();
|
||||||
|
if (appPerInfo != null) {
|
||||||
|
// 安装时间
|
||||||
|
taskPerInfo.setAppInstallTime(appPerInfo.getAppInstallTime());
|
||||||
|
// 启动时间
|
||||||
|
taskPerInfo.setAppActiveTime(appPerInfo.getAppActiveTime());
|
||||||
|
|
||||||
|
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
||||||
|
logger.info("任务ID:{},app性能数据地址:{}", taskExecResult.getTaskId(), appPerInfoAddress);
|
||||||
|
if (StrUtil.isNotBlank(appPerInfoAddress)) {
|
||||||
|
AppPerInfoDataDto appData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
||||||
|
planInfo.getTenantId(), appPerInfoAddress, AppPerInfoDataDto.class);
|
||||||
|
logger.debug("app性能数据 => {}", JSONUtil.toJsonStr(appData) );
|
||||||
|
if (appData != null) {
|
||||||
|
taskPerInfo.setAppCpuList(filterData(appData.getCpuList()));
|
||||||
|
taskPerInfo.setAppMemoList(filterData(appData.getMemoList()));
|
||||||
|
taskPerInfo.setAppFlowList(filterData(appData.getFlowList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("上传新的性能数据");
|
||||||
|
logger.debug("MobileTaskPerformanceDto => {}", JSONUtil.toJsonStr(taskPerInfo) );
|
||||||
|
File file = MinioPathUtils.objectToJsonFile(taskPerInfo);
|
||||||
|
try {
|
||||||
|
NKFile nkFile = simpleStorageService.upload(planInfo.getTenantId(), file, true, taskExecResult.getTaskId(), FileBusinessTypeEnum.MOBILE_TASK_PERFORMANCE_SUMMARY);
|
||||||
|
path = nkFile.getId();
|
||||||
|
logger.debug("保存任务性能数据存放地址 => {}", path);
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.error("文件上传异常", e);
|
||||||
|
}
|
||||||
|
// 删除临时性能文件
|
||||||
|
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
||||||
|
String[] pathUrl = MinioPathUtils.idToPath(devicePerInfoAddress);
|
||||||
|
try {
|
||||||
|
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("文件删除删除失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
||||||
|
if (StrUtil.isNotBlank(appPerInfoAddress)){
|
||||||
|
String[] pathUrl = MinioPathUtils.idToPath(appPerInfoAddress);
|
||||||
|
try {
|
||||||
|
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("文件删除删除失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤小于等于0的数据
|
||||||
|
* @param data 需过滤的数据
|
||||||
|
* @return 结果集
|
||||||
|
*/
|
||||||
|
private List<Double> filterData(List<Double> data){
|
||||||
|
if (data == null || data.size() <= 0){
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return data.stream().filter(num -> num > 0).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -57,6 +57,13 @@ public interface AtuPlanTaskApiService extends ExcelService
|
||||||
*/
|
*/
|
||||||
String insert(AtuPlanTask task);
|
String insert(AtuPlanTask task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id更新任务信息
|
||||||
|
* @param task 任务信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updateByPrimaryKey(AtuPlanTask task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计划批次停止
|
* 计划批次停止
|
||||||
* @param batchId 批次ID
|
* @param batchId 批次ID
|
||||||
|
@ -186,4 +193,17 @@ public interface AtuPlanTaskApiService extends ExcelService
|
||||||
List<String> getTaskIdsByScriptIdsAndPlanId(List<String> oldScriptIds, String planId);
|
List<String> getTaskIdsByScriptIdsAndPlanId(List<String> oldScriptIds, String planId);
|
||||||
|
|
||||||
void getActualImagePath(List<String> deletedFilePath, String execResultFile);
|
void getActualImagePath(List<String> deletedFilePath, String execResultFile);
|
||||||
|
/**
|
||||||
|
* 查询执行超时N秒任务
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return 任务集合
|
||||||
|
*/
|
||||||
|
List<AtuPlanTask> queryExecTimeoutTask(int timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询任务扩展信息
|
||||||
|
* @param taskId 任务id
|
||||||
|
* @return 任务扩展信息
|
||||||
|
*/
|
||||||
|
AtuPlanTaskExtendDto queryTaskExtendById(String taskId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,6 +348,11 @@ public class AtuPlanTaskApiServiceImpl extends AbstractExcelService<AtuPlanTask>
|
||||||
return task.getId();
|
return task.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int updateByPrimaryKey(AtuPlanTask task) {
|
||||||
|
return this.atuPlanTaskService.updateByPrimaryKey(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计划批次停止
|
* 计划批次停止
|
||||||
|
@ -1669,6 +1674,16 @@ public class AtuPlanTaskApiServiceImpl extends AbstractExcelService<AtuPlanTask>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AtuPlanTask> queryExecTimeoutTask(int timeout) {
|
||||||
|
return atuPlanTaskService.queryExecTimeoutTask(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AtuPlanTaskExtendDto queryTaskExtendById(String taskId) {
|
||||||
|
return atuPlanTaskService.queryTaskExtendById(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, String> getRetryTaskInputInfo(Map<String, List<AtuPlanTask>> envScriptTaskMap,
|
private Map<String, String> getRetryTaskInputInfo(Map<String, List<AtuPlanTask>> envScriptTaskMap,
|
||||||
Map<String, List<String>> envScriptMap) {
|
Map<String, List<String>> envScriptMap) {
|
||||||
Map<String, String> taskParamsMap = new HashMap<>();
|
Map<String, String> taskParamsMap = new HashMap<>();
|
||||||
|
|
|
@ -76,4 +76,9 @@ public class RabbitConstant {
|
||||||
* 批次统计数据更新队列
|
* 批次统计数据更新队列
|
||||||
*/
|
*/
|
||||||
public static final String BATCH_SUM_UPDATE_QUEUE = "execute.plan.batch.sum.update";
|
public static final String BATCH_SUM_UPDATE_QUEUE = "execute.plan.batch.sum.update";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务执行心跳
|
||||||
|
*/
|
||||||
|
public static final String TASK_EXEC_HEARTBEAT_QUEUE = "plan.task.exec.heartbeat";
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package net.northking.cctp.executePlan.consumer;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||||
|
import net.northking.cctp.executePlan.constants.RabbitConstant;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.core.AmqpAdmin;
|
||||||
|
import org.springframework.amqp.core.Message;
|
||||||
|
import org.springframework.amqp.core.Queue;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class TaskExecHeartbeatConsumer {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(TaskExecHeartbeatConsumer.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AmqpAdmin amqpAdmin;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanTaskService planTaskService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init(){
|
||||||
|
// 初始化队列并绑定到交换机
|
||||||
|
Queue queue = new Queue(RabbitConstant.TASK_EXEC_HEARTBEAT_QUEUE);
|
||||||
|
amqpAdmin.declareQueue(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计划任务心跳
|
||||||
|
* @param message 对象消息
|
||||||
|
*/
|
||||||
|
@RabbitListener(queues = RabbitConstant.TASK_EXEC_HEARTBEAT_QUEUE)
|
||||||
|
public void receive(Message message){
|
||||||
|
try {
|
||||||
|
logger.info("计划任务心跳 ==> " + new String(message.getBody()));
|
||||||
|
Map<String, String> taskMap = JSONUtil.toBean(new String(message.getBody()), Map.class);
|
||||||
|
taskMap.forEach(this::handleTaskHeartbeat);
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.error("任务生成失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTaskHeartbeat(String taskId, String taskType){
|
||||||
|
AtuPlanTask planTask = new AtuPlanTask();
|
||||||
|
planTask.setId(taskId);
|
||||||
|
planTask.setLastHeartbeatTime(new Date());
|
||||||
|
if (PlanConstant.SCRIPT_TYPE_SCENE.equals(taskType)){
|
||||||
|
// 场景任务
|
||||||
|
AtuPlanSceneCaseTask sceneCaseTask = planSceneCaseTaskService.findByPrimaryKey(taskId);
|
||||||
|
sceneCaseTask.setLastHeartbeatTime(new Date());
|
||||||
|
planSceneCaseTaskService.updateByPrimaryKey(sceneCaseTask);
|
||||||
|
// 更新场景节点对应的任务心跳
|
||||||
|
planTask.setId(sceneCaseTask.getTaskId());
|
||||||
|
}
|
||||||
|
// 普通任务
|
||||||
|
planTaskService.updateByPrimaryKey(planTask);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,58 +1,29 @@
|
||||||
package net.northking.cctp.executePlan.consumer;
|
package net.northking.cctp.executePlan.consumer;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import net.northking.cctp.common.dto.AssociatedFilesDto;
|
|
||||||
import net.northking.cctp.common.enums.FileBusinessTypeEnum;
|
|
||||||
import net.northking.cctp.common.http.ResultWrapper;
|
|
||||||
import net.northking.cctp.common.s3.FileDownloadException;
|
|
||||||
import net.northking.cctp.common.s3.NKFile;
|
|
||||||
import net.northking.cctp.common.s3.SimpleStorageService;
|
|
||||||
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
|
||||||
import net.northking.cctp.executePlan.api.service.AtuPlanBatchApiService;
|
import net.northking.cctp.executePlan.api.service.AtuPlanBatchApiService;
|
||||||
import net.northking.cctp.executePlan.api.service.AtuPlanTaskApiService;
|
import net.northking.cctp.executePlan.api.service.AtuPlanSceneCaseTaskApiService;
|
||||||
import net.northking.cctp.executePlan.constants.PlanConstant;
|
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||||
import net.northking.cctp.executePlan.constants.RabbitConstant;
|
import net.northking.cctp.executePlan.constants.RabbitConstant;
|
||||||
import net.northking.cctp.executePlan.constants.RedisConstant;
|
import net.northking.cctp.executePlan.constants.RedisConstant;
|
||||||
import net.northking.cctp.executePlan.db.entity.AtuPlanInfo;
|
|
||||||
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
|
||||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
import net.northking.cctp.executePlan.db.service.AtuPlanAppLinkService;
|
|
||||||
import net.northking.cctp.executePlan.db.service.AtuPlanInfoService;
|
import net.northking.cctp.executePlan.db.service.AtuPlanInfoService;
|
||||||
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
|
||||||
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
||||||
import net.northking.cctp.executePlan.dto.planBatch.AppPerInfo;
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
|
||||||
import net.northking.cctp.executePlan.dto.planBatch.DevicePerInfo;
|
|
||||||
import net.northking.cctp.executePlan.dto.planBatch.MobileTaskPerformanceDto;
|
|
||||||
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNextNodeDto;
|
|
||||||
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNodeExecDto;
|
|
||||||
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNodeInfoDto;
|
|
||||||
import net.northking.cctp.executePlan.dto.planTask.*;
|
|
||||||
import net.northking.cctp.executePlan.feign.AttachmentFeignClient;
|
|
||||||
import net.northking.cctp.executePlan.feign.PublicFeignClient;
|
|
||||||
import net.northking.cctp.executePlan.utils.MinioPathUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.amqp.core.*;
|
import org.springframework.amqp.core.*;
|
||||||
import org.springframework.amqp.core.Queue;
|
|
||||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.io.*;
|
import java.util.Date;
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Title: TaskExecResultConsumer</p>
|
* <p>Title: TaskExecResultConsumer</p>
|
||||||
|
@ -71,37 +42,20 @@ public class TaskExecResultConsumer {
|
||||||
@Autowired
|
@Autowired
|
||||||
private AtuPlanTaskService planTaskService;
|
private AtuPlanTaskService planTaskService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AtuPlanTaskApiService atuPlanTaskApiService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AtuPlanInfoService atuPlanInfoService;
|
private AtuPlanInfoService atuPlanInfoService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PublicFeignClient publicFeignClient;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AmqpAdmin amqpAdmin;
|
private AmqpAdmin amqpAdmin;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AtuPlanAppLinkService atuPlanAppLinkService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AtuPlanBatchApiService planBatchApiService;
|
private AtuPlanBatchApiService planBatchApiService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SimpleStorageService simpleStorageService;
|
private AtuPlanSceneCaseTaskApiService sceneCaseTaskApiService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AtuPlanTaskService atuPlanTaskService;
|
|
||||||
@Autowired
|
|
||||||
private AttachmentFeignClient attachmentFeignClient;
|
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init(){
|
public void init(){
|
||||||
|
@ -113,12 +67,6 @@ public class TaskExecResultConsumer {
|
||||||
Binding binding = BindingBuilder.bind(queue).to(exchange)
|
Binding binding = BindingBuilder.bind(queue).to(exchange)
|
||||||
.with(RabbitConstant.TASK_EXEC_RESULT).noargs();
|
.with(RabbitConstant.TASK_EXEC_RESULT).noargs();
|
||||||
amqpAdmin.declareBinding(binding);
|
amqpAdmin.declareBinding(binding);
|
||||||
|
|
||||||
Queue taskInputQueue = new Queue(RabbitConstant.TASK_INPUT_PARAM_UPDATE);
|
|
||||||
amqpAdmin.declareQueue(taskInputQueue);
|
|
||||||
Binding inputBinding = BindingBuilder.bind(taskInputQueue).to(exchange)
|
|
||||||
.with(RabbitConstant.TASK_INPUT_PARAM_UPDATE).noargs();
|
|
||||||
amqpAdmin.declareBinding(inputBinding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,7 +91,7 @@ public class TaskExecResultConsumer {
|
||||||
}
|
}
|
||||||
// 判断是否场景用例
|
// 判断是否场景用例
|
||||||
if (ObjectUtil.equal(PlanConstant.SCRIPT_TYPE_SCENE, taskExecResult.getCaseType())){
|
if (ObjectUtil.equal(PlanConstant.SCRIPT_TYPE_SCENE, taskExecResult.getCaseType())){
|
||||||
sceneCaseHandle(taskExecResult, clusterKeyPrefix);
|
sceneCaseTaskApiService.sceneCaseHandle(taskExecResult, clusterKeyPrefix);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 查询任务信息
|
// 查询任务信息
|
||||||
|
@ -164,6 +112,7 @@ public class TaskExecResultConsumer {
|
||||||
planTask.setDeviceId(taskExecResult.getDeviceId());
|
planTask.setDeviceId(taskExecResult.getDeviceId());
|
||||||
planTask.setAppId(taskExecResult.getAppId());
|
planTask.setAppId(taskExecResult.getAppId());
|
||||||
planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||||
|
planTask.setLastHeartbeatTime(new Date());
|
||||||
|
|
||||||
if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
|
if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
|
||||||
clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
|
clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
|
||||||
|
@ -182,7 +131,7 @@ public class TaskExecResultConsumer {
|
||||||
if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planTask.getCaseType())
|
if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planTask.getCaseType())
|
||||||
|| PlanConstant.SCRIPT_TYPE_IOS.equals(planTask.getCaseType())) {
|
|| PlanConstant.SCRIPT_TYPE_IOS.equals(planTask.getCaseType())) {
|
||||||
logger.debug("处理移动端性能文件数据");
|
logger.debug("处理移动端性能文件数据");
|
||||||
planTask.setPerDataPath(parseMobilePerformanceFile(taskExecResult));
|
planTask.setPerDataPath(sceneCaseTaskApiService.parseMobilePerformanceFile(taskExecResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终态休眠一会,避免集群同时更新执行中状态造成数据异常
|
// 终态休眠一会,避免集群同时更新执行中状态造成数据异常
|
||||||
|
@ -199,395 +148,10 @@ public class TaskExecResultConsumer {
|
||||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||||
|
|
||||||
// 关联截图文件
|
// 关联截图文件
|
||||||
CompletableFuture.runAsync(() -> associatedActualImgAndVideo(taskExecResult));
|
CompletableFuture.runAsync(() -> sceneCaseTaskApiService.associatedActualImgAndVideo(taskExecResult));
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
logger.error("任务结果更新失败", e);
|
logger.error("任务结果更新失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void associatedActualImgAndVideo(AtuTaskExecResultDto taskExecResult) {
|
|
||||||
try {
|
|
||||||
if (StringUtils.hasText(taskExecResult.getFilePath())) {
|
|
||||||
InputStream inputStream = simpleStorageService.downloadAsStream(NKSecurityContext.getTenantId(), taskExecResult.getFilePath());
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
|
||||||
List<String> fileUriList = new ArrayList<>();
|
|
||||||
String line = null;
|
|
||||||
while ((line = bufferedReader.readLine()) != null) {
|
|
||||||
StepExecuteResult stepExecuteResult = JSONUtil.toBean(line, StepExecuteResult.class);
|
|
||||||
String actualImgUri = stepExecuteResult.getActualImgUri();
|
|
||||||
if (StringUtils.hasText(actualImgUri)) {
|
|
||||||
fileUriList.add(actualImgUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fileUriList.add(taskExecResult.getFilePath());
|
|
||||||
if (StringUtils.hasText(taskExecResult.getVideoPath())) {
|
|
||||||
fileUriList.add(taskExecResult.getVideoPath());
|
|
||||||
}
|
|
||||||
attachmentFeignClient.associatedFiles(fileUriList, taskExecResult.getTaskId());
|
|
||||||
}
|
|
||||||
} catch (FileDownloadException e) {
|
|
||||||
logger.error("文件下载异常", e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("文件读取异常", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseMobilePerformanceFile(AtuTaskExecResultDto taskExecResult){
|
|
||||||
String path = "";
|
|
||||||
DevicePerInfo devicePerInfo = taskExecResult.getDevicePerInfo();
|
|
||||||
logger.debug("设备性能数据结果 => {}", JSONUtil.toJsonStr(devicePerInfo) );
|
|
||||||
if (devicePerInfo == null){
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
AtuPlanInfo planInfo = atuPlanInfoService.findByBatchId(taskExecResult.getBatchId());
|
|
||||||
MobileTaskPerformanceDto taskPerInfo = new MobileTaskPerformanceDto();
|
|
||||||
taskPerInfo.setDeviceId(taskExecResult.getDeviceId());
|
|
||||||
taskPerInfo.setAppId(taskExecResult.getAppId());
|
|
||||||
String devicePerInfoAddress = devicePerInfo.getDevicePerInfoAddress();
|
|
||||||
logger.info("任务ID:{},设备性能数据地址:{}", taskExecResult.getTaskId(), devicePerInfoAddress);
|
|
||||||
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
|
||||||
DevicePerInfoDataDto deviceData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
|
||||||
planInfo.getTenantId(), devicePerInfoAddress, DevicePerInfoDataDto.class);
|
|
||||||
logger.debug("设备性能数据 => {}", JSONUtil.toJsonStr(deviceData));
|
|
||||||
if (deviceData != null){
|
|
||||||
taskPerInfo.setDeviceCpuList(filterData(deviceData.getCpuList()));
|
|
||||||
taskPerInfo.setDeviceMemoList(filterData(deviceData.getMemoList()));
|
|
||||||
taskPerInfo.setDeviceFlowList(filterData(deviceData.getFlowList()));
|
|
||||||
taskPerInfo.setDeviceTemperatureList(filterData(deviceData.getTemperatureList()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppPerInfo appPerInfo = devicePerInfo.getAppPerInfo();
|
|
||||||
if (appPerInfo != null) {
|
|
||||||
// 安装时间
|
|
||||||
taskPerInfo.setAppInstallTime(appPerInfo.getAppInstallTime());
|
|
||||||
// 启动时间
|
|
||||||
taskPerInfo.setAppActiveTime(appPerInfo.getAppActiveTime());
|
|
||||||
|
|
||||||
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
|
||||||
logger.info("任务ID:{},app性能数据地址:{}", taskExecResult.getTaskId(), appPerInfoAddress);
|
|
||||||
if (StrUtil.isNotBlank(appPerInfoAddress)) {
|
|
||||||
AppPerInfoDataDto appData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
|
||||||
planInfo.getTenantId(), appPerInfoAddress, AppPerInfoDataDto.class);
|
|
||||||
logger.debug("app性能数据 => {}", JSONUtil.toJsonStr(appData) );
|
|
||||||
if (appData != null) {
|
|
||||||
taskPerInfo.setAppCpuList(filterData(appData.getCpuList()));
|
|
||||||
taskPerInfo.setAppMemoList(filterData(appData.getMemoList()));
|
|
||||||
taskPerInfo.setAppFlowList(filterData(appData.getFlowList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("上传新的性能数据");
|
|
||||||
logger.debug("MobileTaskPerformanceDto => {}", JSONUtil.toJsonStr(taskPerInfo) );
|
|
||||||
File file = MinioPathUtils.objectToJsonFile(taskPerInfo);
|
|
||||||
try {
|
|
||||||
NKFile nkFile = simpleStorageService.upload(planInfo.getTenantId(), file, true, taskExecResult.getTaskId(), FileBusinessTypeEnum.MOBILE_TASK_PERFORMANCE_SUMMARY);
|
|
||||||
path = nkFile.getId();
|
|
||||||
logger.debug("保存任务性能数据存放地址 => {}", path);
|
|
||||||
}catch (Exception e){
|
|
||||||
logger.error("文件上传异常", e);
|
|
||||||
}
|
|
||||||
// 删除临时性能文件
|
|
||||||
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
|
||||||
String[] pathUrl = MinioPathUtils.idToPath(devicePerInfoAddress);
|
|
||||||
try {
|
|
||||||
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("文件删除删除失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
|
||||||
if (StrUtil.isNotBlank(appPerInfoAddress)){
|
|
||||||
String[] pathUrl = MinioPathUtils.idToPath(appPerInfoAddress);
|
|
||||||
try {
|
|
||||||
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("文件删除删除失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 过滤小于等于0的数据
|
|
||||||
* @param data 需过滤的数据
|
|
||||||
* @return 结果集
|
|
||||||
*/
|
|
||||||
private List<Double> filterData(List<Double> data){
|
|
||||||
if (data == null || data.size() <= 0){
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
return data.stream().filter(num -> num > 0).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 场景用例处理
|
|
||||||
* @param taskExecResult 任务执行结果
|
|
||||||
*/
|
|
||||||
private void sceneCaseHandle(AtuTaskExecResultDto taskExecResult, String clusterKeyPrefix){
|
|
||||||
AtuPlanSceneCaseTask planSceneCaseTask = planSceneCaseTaskService.findByPrimaryKey(taskExecResult.getTaskId());
|
|
||||||
if (ObjectUtil.isNull(planSceneCaseTask)){
|
|
||||||
logger.error("场景节点任务[" + taskExecResult.getTaskId() + "]信息不存在");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//开始执行同步更新计划任务表信息
|
|
||||||
AtuPlanTask planTask = planTaskService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
|
||||||
if (ObjectUtil.isNull(planTask)) {
|
|
||||||
logger.error("任务[" + planSceneCaseTask.getTaskId() + "]信息不存在");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 判断是否开始执行
|
|
||||||
if (ObjectUtil.equal(PlanConstant.TASK_START_EXECUTE_STATUS, taskExecResult.getStatus())){
|
|
||||||
planSceneCaseTask.setStartTime(taskExecResult.getCurrentTime());
|
|
||||||
planSceneCaseTask.setEngineId(taskExecResult.getEngineId());
|
|
||||||
planSceneCaseTask.setDeviceId(taskExecResult.getDeviceId());
|
|
||||||
planSceneCaseTask.setAppId(taskExecResult.getAppId());
|
|
||||||
planSceneCaseTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
|
||||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
|
||||||
// 判断是否首节点
|
|
||||||
AtuPlanSceneCaseTask countParams = new AtuPlanSceneCaseTask();
|
|
||||||
countParams.setTaskId(planSceneCaseTask.getTaskId());
|
|
||||||
long count = planSceneCaseTaskService.count(countParams);
|
|
||||||
if (count == 1) {
|
|
||||||
|
|
||||||
planTask.setStartTime(taskExecResult.getCurrentTime());
|
|
||||||
planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
|
||||||
planTaskService.updateByPrimaryKey(planTask);
|
|
||||||
|
|
||||||
if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
|
|
||||||
clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
|
|
||||||
logger.debug("批次[" + planTask.getBatchId() + "]开始执行,更新计划的状态");
|
|
||||||
atuPlanInfoService.updatePlanByLastBatchId(planTask.getBatchId(), PlanConstant.PLAN_EXECUTING_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新批次统计数据
|
|
||||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
}else {
|
|
||||||
// 获取下一节点信息
|
|
||||||
queryNextNodeInfo(taskExecResult, planSceneCaseTask, planTask.getScriptId(), planTask.getEnvId(),planTask.getScriptJson());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询下一节点信息
|
|
||||||
* @param taskExecResult 任务执行结果
|
|
||||||
* @param planSceneCaseTask 场景用例节点任务
|
|
||||||
* @param scriptUrl 执行脚本地址
|
|
||||||
*/
|
|
||||||
private void queryNextNodeInfo(AtuTaskExecResultDto taskExecResult,
|
|
||||||
AtuPlanSceneCaseTask planSceneCaseTask,
|
|
||||||
String sceneId,
|
|
||||||
String envId,
|
|
||||||
String scriptUrl){
|
|
||||||
|
|
||||||
planSceneCaseTask.setStatus(taskExecResult.getStatus());
|
|
||||||
planSceneCaseTask.setEndTime(taskExecResult.getCurrentTime());
|
|
||||||
planSceneCaseTask.setErrorMsg(taskExecResult.getMessage());
|
|
||||||
planSceneCaseTask.setVideoUrl(taskExecResult.getVideoPath());
|
|
||||||
planSceneCaseTask.setExecResultFile(taskExecResult.getFilePath());
|
|
||||||
logger.debug("更新当前节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
|
||||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
|
||||||
|
|
||||||
AtuSceneNextNodeDto sceneNextNodeDto = new AtuSceneNextNodeDto();
|
|
||||||
//传递计划任务表id
|
|
||||||
sceneNextNodeDto.setTaskId(planSceneCaseTask.getTaskId());
|
|
||||||
sceneNextNodeDto.setCaseId(taskExecResult.getCaseId());
|
|
||||||
sceneNextNodeDto.setNodeId(planSceneCaseTask.getNodeId());
|
|
||||||
sceneNextNodeDto.setSuccess(ObjectUtil.equal(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS, taskExecResult.getStatus()));
|
|
||||||
sceneNextNodeDto.setOutputArgs(taskExecResult.getOutputs());
|
|
||||||
sceneNextNodeDto.setScriptId(sceneId);
|
|
||||||
sceneNextNodeDto.setSceneScriptUrl(scriptUrl);
|
|
||||||
ResultWrapper<AtuSceneNodeExecDto> nextNodeResult;
|
|
||||||
try {
|
|
||||||
logger.debug("查询场景下一节点信息,参数为 => " + JSONUtil.toJsonStr(sceneNextNodeDto));
|
|
||||||
//捕获异常信息
|
|
||||||
nextNodeResult = publicFeignClient.getNextNode(sceneNextNodeDto);
|
|
||||||
if (!nextNodeResult.isSuccess()){
|
|
||||||
logger.error("获取下一节点信息结果异常," + nextNodeResult.getMessage());
|
|
||||||
sceneTaskExceptionEnd(planSceneCaseTask.getTaskId(), nextNodeResult.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("获取下一节点信息异常:",e);
|
|
||||||
sceneTaskExceptionEnd(planSceneCaseTask.getTaskId(), "获取下一节点信息异常");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("查询场景下一节点信息,返回结果为 => " + JSONUtil.toJsonStr(nextNodeResult));
|
|
||||||
AtuSceneNodeExecDto sceneNodeExecDto = nextNodeResult.getData();
|
|
||||||
|
|
||||||
//更新url到表里
|
|
||||||
if (sceneNodeExecDto != null && sceneNodeExecDto.getSceneScriptUrl() != null) {
|
|
||||||
AtuPlanTask byPrimaryKey = atuPlanTaskService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
|
||||||
AtuPlanTask planTask = new AtuPlanTask();
|
|
||||||
planTask.setId(planSceneCaseTask.getTaskId());
|
|
||||||
planTask.setScriptJson(sceneNodeExecDto.getSceneScriptUrl());
|
|
||||||
int rows = atuPlanTaskService.updateByPrimaryKey(planTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
AtuSceneNodeInfoDto nodeInfo = sceneNodeExecDto.getNodeInfo();
|
|
||||||
// 节点不管成功或者失败,若没有连接到结束节点,默认最后节点为结束,节点信息不包含lineId
|
|
||||||
planSceneCaseTask.setNextNodeId(nodeInfo.getNodeId());
|
|
||||||
planSceneCaseTask.setLineId(nodeInfo.getLineId());
|
|
||||||
|
|
||||||
// 记录性能数据
|
|
||||||
if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planSceneCaseTask.getNodeType())
|
|
||||||
|| PlanConstant.SCRIPT_TYPE_IOS.equals(planSceneCaseTask.getNodeType())) {
|
|
||||||
logger.debug("处理场景移动端性能文件数据");
|
|
||||||
planSceneCaseTask.setPerDataPath(parseMobilePerformanceFile(taskExecResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("更新当前节点中下一节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
|
||||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
|
||||||
// 关联文件,使用 CompletableFuture 复用线程
|
|
||||||
CompletableFuture.runAsync(() -> associatedActualImgAndVideo(taskExecResult));
|
|
||||||
// future.get();
|
|
||||||
// 判断是否结束节点
|
|
||||||
if (sceneNodeExecDto.getEnd()){
|
|
||||||
logger.info("场景任务执行结束....");
|
|
||||||
// 更新任务表信息
|
|
||||||
AtuPlanTask planTask = planTaskService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
|
||||||
if (ObjectUtil.isNull(planTask)){
|
|
||||||
logger.error("任务[" + planSceneCaseTask.getTaskId() + "]信息不存在");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//判断任务状态是否未非引擎返回的状态 取消/超时
|
|
||||||
if(ObjectUtil.equal(PlanConstant.TASK_CANCEL_STATUS, planTask.getStatus())
|
|
||||||
||ObjectUtil.equal(PlanConstant.TASK_TIMEOUT_STATUS, planTask.getStatus())){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
planTask.setEndTime(planSceneCaseTask.getEndTime());
|
|
||||||
if (StrUtil.isEmpty(nodeInfo.getLineId())) {
|
|
||||||
planTask.setStatus(planSceneCaseTask.getStatus());
|
|
||||||
planTask.setErrorMsg(planSceneCaseTask.getErrorMsg());
|
|
||||||
}else{
|
|
||||||
// 节点执行结束且连接了结束节点,则该场景任务设置成功执行
|
|
||||||
planTask.setStatus(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS);
|
|
||||||
}
|
|
||||||
logger.debug("更新场景任务为结束");
|
|
||||||
planTaskService.updateByPrimaryKey(planTask);
|
|
||||||
|
|
||||||
// 更新批次统计数据
|
|
||||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
|
||||||
// 删除节点更新时的场景任务文件
|
|
||||||
String noDeleteFileId = "/" + MinioPathUtils.idToPath(sceneNodeExecDto.getSceneScriptUrl())[1];
|
|
||||||
attachmentFeignClient.clearSceneTaskAttachments(sceneId, FileBusinessTypeEnum.SCENE_TASK_REFRESH_FILE.getCode(), noDeleteFileId);
|
|
||||||
}else {
|
|
||||||
// 新增下一节点任务信息
|
|
||||||
try {
|
|
||||||
createNextNodeTask(planSceneCaseTask, nodeInfo, taskExecResult, envId);
|
|
||||||
}catch (Exception e){
|
|
||||||
logger.error("创建下一节点任务信息失败", e);
|
|
||||||
sceneTaskExceptionEnd(planSceneCaseTask.getTaskId(), "创建下一节点任务信息失败" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建下一节点任务
|
|
||||||
* @param planSceneCaseTask 场景用例节点任务
|
|
||||||
* @param nodeInfo 节点信息
|
|
||||||
* @param taskExecResult 任务执行结果
|
|
||||||
*/
|
|
||||||
private void createNextNodeTask(AtuPlanSceneCaseTask planSceneCaseTask, AtuSceneNodeInfoDto nodeInfo,
|
|
||||||
AtuTaskExecResultDto taskExecResult, String envId){
|
|
||||||
logger.debug("保存下一节点信息");
|
|
||||||
logger.debug("节点信息:" + JSONUtil.toJsonStr(nodeInfo));
|
|
||||||
AtuPlanSceneCaseTask nextNodeTask = new AtuPlanSceneCaseTask();
|
|
||||||
nextNodeTask.setId(IdUtil.simpleUUID());
|
|
||||||
nextNodeTask.setTaskId(planSceneCaseTask.getTaskId());
|
|
||||||
nextNodeTask.setScriptId(nodeInfo.getScriptId());
|
|
||||||
nextNodeTask.setVersionId(nodeInfo.getVersionId());
|
|
||||||
nextNodeTask.setVersionName(nodeInfo.getVersionName());
|
|
||||||
nextNodeTask.setScriptName(nodeInfo.getScriptName());
|
|
||||||
nextNodeTask.setScriptJson(nodeInfo.getScriptPath());
|
|
||||||
nextNodeTask.setNodeId(nodeInfo.getNodeId());
|
|
||||||
nextNodeTask.setNodeType(nodeInfo.getNodeType());
|
|
||||||
nextNodeTask.setStatus(PlanConstant.TASK_WAIT_EXECUTE_STATUS);
|
|
||||||
if (CollUtil.isNotEmpty(nodeInfo.getCaseParam())) {
|
|
||||||
nextNodeTask.setNodeParams(JSONUtil.toJsonStr(nodeInfo.getCaseParam()));
|
|
||||||
}
|
|
||||||
nextNodeTask.setPrevNodeId(planSceneCaseTask.getNodeId());
|
|
||||||
nextNodeTask.setCreatedTime(new Date());
|
|
||||||
planSceneCaseTaskService.insert(nextNodeTask);
|
|
||||||
|
|
||||||
// 查询计划信息
|
|
||||||
AtuPlanInfo planInfo = atuPlanInfoService.findByPrimaryKey(taskExecResult.getPlanId());
|
|
||||||
if (ObjectUtil.isNull(planInfo)){
|
|
||||||
logger.error("计划[" + taskExecResult.getPlanId() + "]信息为空");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("生成任务并推送至队列");
|
|
||||||
AtuTaskExecDto atuTaskExecDto = new AtuTaskExecDto();
|
|
||||||
atuTaskExecDto.setTenantId(planInfo.getTenantId());
|
|
||||||
atuTaskExecDto.setProjectId(planInfo.getProjectId());
|
|
||||||
atuTaskExecDto.setEnvId(envId);
|
|
||||||
atuTaskExecDto.setPlanId(planInfo.getId());
|
|
||||||
atuTaskExecDto.setBatchId(taskExecResult.getBatchId());
|
|
||||||
atuTaskExecDto.setTaskId(nextNodeTask.getId());
|
|
||||||
atuTaskExecDto.setCaseId(taskExecResult.getCaseId());
|
|
||||||
atuTaskExecDto.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
|
|
||||||
atuTaskExecDto.setScriptPath(nodeInfo.getScriptPath());
|
|
||||||
atuTaskExecDto.setCaseParams(nodeInfo.getCaseParam());
|
|
||||||
atuTaskExecDto.setAppSet(planInfo.getAppSet());
|
|
||||||
atuTaskExecDto.setScreenRecordSet(planInfo.getScreenRecordSet());
|
|
||||||
atuTaskExecDto.setScreenshotSet(planInfo.getScreenshotSet());
|
|
||||||
atuTaskExecDto.setFailRetryNum(planInfo.getFailRetryCount());
|
|
||||||
|
|
||||||
atuTaskExecDto.setAppId(atuPlanTaskApiService.queryAppId(planInfo.getId(), nodeInfo.getPlatformType(),
|
|
||||||
nodeInfo.getAppPackage(), nodeInfo.getNodeType()));
|
|
||||||
|
|
||||||
// 发送任务执行信息至消息队列
|
|
||||||
atuPlanTaskApiService.sendToQueue(atuTaskExecDto.getBatchId(), atuTaskExecDto, nodeInfo.getNodeType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 场景任务异常结束
|
|
||||||
* @param taskId 任务ID
|
|
||||||
* @param errorMsg 错误信息
|
|
||||||
*/
|
|
||||||
private void sceneTaskExceptionEnd(String taskId, String errorMsg){
|
|
||||||
AtuPlanTask planTask = planTaskService.findByPrimaryKey(taskId);
|
|
||||||
planTask.setStatus(PlanConstant.TASK_EXECUTE_FAIL_STATUS);
|
|
||||||
planTask.setEndTime(System.currentTimeMillis());
|
|
||||||
planTask.setErrorMsg(errorMsg);
|
|
||||||
planTaskService.updateByPrimaryKey(planTask);
|
|
||||||
// 更新批次统计数据
|
|
||||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 接收任务输入参数并更新入库
|
|
||||||
*
|
|
||||||
* @param message 对象消息
|
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
@RabbitListener(queues = RabbitConstant.TASK_INPUT_PARAM_UPDATE)
|
|
||||||
public void receiveTaskInputParam(Message message) {
|
|
||||||
logger.info("接收信息 ==> " + new String(message.getBody()));
|
|
||||||
try {
|
|
||||||
JSONObject event = JSONUtil.parseObj(new String(message.getBody()));
|
|
||||||
AtuTaskExecDto receiveTask = JSONUtil.toBean(event, AtuTaskExecDto.class);
|
|
||||||
if (PlanConstant.SCRIPT_TYPE_SCENE.equals(receiveTask.getCaseType())) {
|
|
||||||
// 更新到场景任务表
|
|
||||||
AtuPlanSceneCaseTask sceneTask = new AtuPlanSceneCaseTask();
|
|
||||||
sceneTask.setId(receiveTask.getTaskId());
|
|
||||||
sceneTask.setNodeParams(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
|
||||||
planSceneCaseTaskService.updateByPrimaryKey(sceneTask);
|
|
||||||
} else {
|
|
||||||
// 更新到任务表
|
|
||||||
AtuPlanTask updateTask = new AtuPlanTask();
|
|
||||||
updateTask.setId(receiveTask.getTaskId());
|
|
||||||
updateTask.setCaseParam(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
|
||||||
planTaskService.updateByPrimaryKey(updateTask);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("更新任务的输入参数失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package net.northking.cctp.executePlan.consumer;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||||
|
import net.northking.cctp.executePlan.constants.RabbitConstant;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecDto;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.core.*;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
public class TaskInputParamConsumer {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(TaskInputParamConsumer.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanTaskService planTaskService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AmqpAdmin amqpAdmin;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init(){
|
||||||
|
// 初始化队列并绑定到交换机
|
||||||
|
Exchange exchange = new DirectExchange(RabbitConstant.PROJECT_EXCHANGE_KEY);
|
||||||
|
amqpAdmin.declareExchange(exchange);
|
||||||
|
Queue queue = new Queue(RabbitConstant.TASK_INPUT_PARAM_UPDATE);
|
||||||
|
amqpAdmin.declareQueue(queue);
|
||||||
|
Binding binding = BindingBuilder.bind(queue).to(exchange)
|
||||||
|
.with(RabbitConstant.TASK_INPUT_PARAM_UPDATE).noargs();
|
||||||
|
amqpAdmin.declareBinding(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收任务输入参数并更新入库
|
||||||
|
*
|
||||||
|
* @param message 对象消息
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@RabbitListener(queues = RabbitConstant.TASK_INPUT_PARAM_UPDATE)
|
||||||
|
public void receiveTaskInputParam(Message message) {
|
||||||
|
logger.info("接收信息 ==> " + new String(message.getBody()));
|
||||||
|
try {
|
||||||
|
JSONObject event = JSONUtil.parseObj(new String(message.getBody()));
|
||||||
|
AtuTaskExecDto receiveTask = JSONUtil.toBean(event, AtuTaskExecDto.class);
|
||||||
|
if (PlanConstant.SCRIPT_TYPE_SCENE.equals(receiveTask.getCaseType())) {
|
||||||
|
// 更新到场景任务表
|
||||||
|
AtuPlanSceneCaseTask sceneTask = new AtuPlanSceneCaseTask();
|
||||||
|
sceneTask.setId(receiveTask.getTaskId());
|
||||||
|
sceneTask.setNodeParams(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
||||||
|
planSceneCaseTaskService.updateByPrimaryKey(sceneTask);
|
||||||
|
} else {
|
||||||
|
// 更新到任务表
|
||||||
|
AtuPlanTask updateTask = new AtuPlanTask();
|
||||||
|
updateTask.setId(receiveTask.getTaskId());
|
||||||
|
updateTask.setCaseParam(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
||||||
|
planTaskService.updateByPrimaryKey(updateTask);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新任务的输入参数失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,4 +79,10 @@ public interface AtuPlanSceneCaseTaskDao extends AtuPlanSceneCaseTaskMapper
|
||||||
|
|
||||||
List<AtuPlanSceneCaseTask> querySceneCaseTasksByTaskIds(@Param("taskIds") List<String> taskIds);
|
List<AtuPlanSceneCaseTask> querySceneCaseTasksByTaskIds(@Param("taskIds") List<String> taskIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询执行超时N秒任务
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return 任务集合
|
||||||
|
*/
|
||||||
|
List<AtuPlanSceneCaseTask> queryExecTimeoutTask(int timeout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package net.northking.cctp.executePlan.db.dao;
|
||||||
import net.northking.cctp.common.http.QueryByPage;
|
import net.northking.cctp.common.http.QueryByPage;
|
||||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
import net.northking.cctp.executePlan.db.mapper.AtuPlanTaskMapper;
|
import net.northking.cctp.executePlan.db.mapper.AtuPlanTaskMapper;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskPageDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskPageDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto;
|
||||||
|
@ -109,4 +110,17 @@ public interface AtuPlanTaskDao extends AtuPlanTaskMapper
|
||||||
|
|
||||||
List<AtuPlanTask> queryTasksByBatchIds(@Param("batchIds") List<String> batchIds);
|
List<AtuPlanTask> queryTasksByBatchIds(@Param("batchIds") List<String> batchIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询执行超时N秒任务
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return 任务集合
|
||||||
|
*/
|
||||||
|
List<AtuPlanTask> queryExecTimeoutTask(int timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询任务扩展信息
|
||||||
|
* @param id 任务id
|
||||||
|
* @return 任务扩展信息
|
||||||
|
*/
|
||||||
|
AtuPlanTaskExtendDto queryTaskExtendById(String id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 京北方信息技术股份有限公司 Corporation 2023 . All rights reserved.
|
* Copyright (c) 京北方信息技术股份有限公司 Corporation 2024 . All rights reserved.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.northking.cctp.executePlan.db.entity;
|
package net.northking.cctp.executePlan.db.entity;
|
||||||
|
@ -17,7 +17,7 @@ import java.util.Date;
|
||||||
* 计划场景用例节点任务表
|
* 计划场景用例节点任务表
|
||||||
*
|
*
|
||||||
* <p>文件内容由代码生成器产生,请不要手动修改! <br>
|
* <p>文件内容由代码生成器产生,请不要手动修改! <br>
|
||||||
* createdate: 2023-11-13 14:35:51 <br>
|
* createdate: 2024-07-03 18:59:03 <br>
|
||||||
* @author: maven-cctp-plugin <br>
|
* @author: maven-cctp-plugin <br>
|
||||||
* @since: 1.0 <br>
|
* @since: 1.0 <br>
|
||||||
*/
|
*/
|
||||||
|
@ -48,6 +48,7 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable
|
||||||
public static final String KEY_execResultFile = "execResultFile";
|
public static final String KEY_execResultFile = "execResultFile";
|
||||||
public static final String KEY_perDataPath = "perDataPath";
|
public static final String KEY_perDataPath = "perDataPath";
|
||||||
public static final String KEY_createdTime = "createdTime";
|
public static final String KEY_createdTime = "createdTime";
|
||||||
|
public static final String KEY_lastHeartbeatTime = "lastHeartbeatTime";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键<br>
|
* 主键<br>
|
||||||
|
@ -189,6 +190,11 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable
|
||||||
*/
|
*/
|
||||||
@ApiModelProperty("创建时间")
|
@ApiModelProperty("创建时间")
|
||||||
private Date createdTime;
|
private Date createdTime;
|
||||||
|
/**
|
||||||
|
* 执行心跳时间<br>
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("执行心跳时间")
|
||||||
|
private Date lastHeartbeatTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键<br>
|
* 主键<br>
|
||||||
|
@ -605,6 +611,30 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable
|
||||||
this.createdTime = (Date)createdTime.clone();
|
this.createdTime = (Date)createdTime.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 执行心跳时间<br>
|
||||||
|
*/
|
||||||
|
public Date getLastHeartbeatTime()
|
||||||
|
{
|
||||||
|
if(lastHeartbeatTime != null)
|
||||||
|
{
|
||||||
|
return (Date)lastHeartbeatTime.clone();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行心跳时间<br>
|
||||||
|
*
|
||||||
|
* @param lastHeartbeatTime 执行心跳时间
|
||||||
|
*/
|
||||||
|
public void setLastHeartbeatTime(Date lastHeartbeatTime)
|
||||||
|
{
|
||||||
|
if(lastHeartbeatTime != null)
|
||||||
|
{
|
||||||
|
this.lastHeartbeatTime = (Date)lastHeartbeatTime.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AtuPlanSceneCaseTask()
|
public AtuPlanSceneCaseTask()
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,6 +49,7 @@ public class AtuPlanTask extends Partition implements Serializable
|
||||||
public static final String KEY_bugId = "bugId";
|
public static final String KEY_bugId = "bugId";
|
||||||
public static final String KEY_perDataPath = "perDataPath";
|
public static final String KEY_perDataPath = "perDataPath";
|
||||||
public static final String KEY_createdTime = "createdTime";
|
public static final String KEY_createdTime = "createdTime";
|
||||||
|
public static final String KEY_lastHeartbeatTime = "lastHeartbeatTime";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键<br>
|
* 主键<br>
|
||||||
|
@ -196,6 +197,11 @@ public class AtuPlanTask extends Partition implements Serializable
|
||||||
*/
|
*/
|
||||||
@ApiModelProperty("创建时间")
|
@ApiModelProperty("创建时间")
|
||||||
private Date createdTime;
|
private Date createdTime;
|
||||||
|
/**
|
||||||
|
* 执行心跳时间<br>
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("执行心跳时间")
|
||||||
|
private Date lastHeartbeatTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键<br>
|
* 主键<br>
|
||||||
|
@ -629,6 +635,30 @@ public class AtuPlanTask extends Partition implements Serializable
|
||||||
this.createdTime = (Date)createdTime.clone();
|
this.createdTime = (Date)createdTime.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 执行心跳时间<br>
|
||||||
|
*/
|
||||||
|
public Date getLastHeartbeatTime()
|
||||||
|
{
|
||||||
|
if(lastHeartbeatTime != null)
|
||||||
|
{
|
||||||
|
return (Date)lastHeartbeatTime.clone();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行心跳时间<br>
|
||||||
|
*
|
||||||
|
* @param lastHeartbeatTime 执行心跳时间
|
||||||
|
*/
|
||||||
|
public void setLastHeartbeatTime(Date lastHeartbeatTime)
|
||||||
|
{
|
||||||
|
if(lastHeartbeatTime != null)
|
||||||
|
{
|
||||||
|
this.lastHeartbeatTime = (Date)lastHeartbeatTime.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AtuPlanTask()
|
public AtuPlanTask()
|
||||||
{
|
{
|
||||||
|
|
|
@ -93,6 +93,11 @@ private static final Logger logger = LoggerFactory.getLogger(AtuPlanSceneCaseTas
|
||||||
return this.atuPlanSceneCaseTaskDao.querySceneCaseTasksByTaskIds(taskIds);
|
return this.atuPlanSceneCaseTaskDao.querySceneCaseTasksByTaskIds(taskIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AtuPlanSceneCaseTask> queryExecTimeoutTask(int timeout) {
|
||||||
|
return this.atuPlanSceneCaseTaskDao.queryExecTimeoutTask(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---- The End by Generator ----//
|
// ---- The End by Generator ----//
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import net.northking.cctp.common.http.QueryByPage;
|
||||||
import net.northking.cctp.executePlan.db.dao.AtuPlanTaskDao;
|
import net.northking.cctp.executePlan.db.dao.AtuPlanTaskDao;
|
||||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskPageDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskPageDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto;
|
||||||
|
@ -156,6 +157,16 @@ private static final Logger logger = LoggerFactory.getLogger(AtuPlanTaskServiceI
|
||||||
return this.atuPlanTaskDao.queryTasksByBatchIds(batchIds);
|
return this.atuPlanTaskDao.queryTasksByBatchIds(batchIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AtuPlanTask> queryExecTimeoutTask(int timeout) {
|
||||||
|
return this.atuPlanTaskDao.queryExecTimeoutTask(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AtuPlanTaskExtendDto queryTaskExtendById(String taskId) {
|
||||||
|
return this.atuPlanTaskDao.queryTaskExtendById(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- The End by Generator ----//
|
// ---- The End by Generator ----//
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,5 +42,6 @@ public interface AtuPlanSceneCaseTaskMapper extends BasicDao<AtuPlanSceneCaseTas
|
||||||
String COLUMN_execResultFile = "exec_result_file";
|
String COLUMN_execResultFile = "exec_result_file";
|
||||||
String COLUMN_perDataPath = "per_data_path";
|
String COLUMN_perDataPath = "per_data_path";
|
||||||
String COLUMN_createdTime = "created_time";
|
String COLUMN_createdTime = "created_time";
|
||||||
|
String COLUMN_lastHeartbeatTime = "last_heartbeat_time";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,5 +43,6 @@ public interface AtuPlanTaskMapper extends BasicDao<AtuPlanTask>
|
||||||
String COLUMN_bugId = "bug_id";
|
String COLUMN_bugId = "bug_id";
|
||||||
String COLUMN_perDataPath = "per_data_path";
|
String COLUMN_perDataPath = "per_data_path";
|
||||||
String COLUMN_createdTime = "created_time";
|
String COLUMN_createdTime = "created_time";
|
||||||
|
String COLUMN_lastHeartbeatTime = "last_heartbeat_time";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,4 +79,10 @@ public interface AtuPlanSceneCaseTaskService extends BasicService<AtuPlanSceneCa
|
||||||
*/
|
*/
|
||||||
List<AtuPlanSceneCaseTask> querySceneCaseTasksByTaskIds(List<String> taskIds);
|
List<AtuPlanSceneCaseTask> querySceneCaseTasksByTaskIds(List<String> taskIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询执行超时N秒任务
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return 任务集合
|
||||||
|
*/
|
||||||
|
List<AtuPlanSceneCaseTask> queryExecTimeoutTask(int timeout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package net.northking.cctp.executePlan.db.service;
|
||||||
import net.northking.cctp.common.db.BasicService;
|
import net.northking.cctp.common.db.BasicService;
|
||||||
import net.northking.cctp.common.http.QueryByPage;
|
import net.northking.cctp.common.http.QueryByPage;
|
||||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskPageDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskPageDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto;
|
||||||
import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto;
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto;
|
||||||
|
@ -113,4 +114,17 @@ public interface AtuPlanTaskService extends BasicService<AtuPlanTask>
|
||||||
*/
|
*/
|
||||||
List<AtuPlanTask> queryTasksByBatchIds(List<String> batchIds);
|
List<AtuPlanTask> queryTasksByBatchIds(List<String> batchIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询执行超时N秒任务
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return 任务集合
|
||||||
|
*/
|
||||||
|
List<AtuPlanTask> queryExecTimeoutTask(int timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询任务扩展信息
|
||||||
|
* @param taskId 任务id
|
||||||
|
* @return 任务扩展信息
|
||||||
|
*/
|
||||||
|
AtuPlanTaskExtendDto queryTaskExtendById(String taskId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package net.northking.cctp.executePlan.dto.planTask;
|
||||||
|
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
|
||||||
|
public class AtuPlanTaskExtendDto extends AtuPlanTask {
|
||||||
|
|
||||||
|
private String planId;
|
||||||
|
|
||||||
|
private String planName;
|
||||||
|
|
||||||
|
public String getPlanId() {
|
||||||
|
return planId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlanId(String planId) {
|
||||||
|
this.planId = planId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlanName() {
|
||||||
|
return planName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlanName(String planName) {
|
||||||
|
this.planName = planName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package net.northking.cctp.executePlan.job;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import net.northking.cctp.executePlan.api.service.AtuPlanSceneCaseTaskApiService;
|
||||||
|
import net.northking.cctp.executePlan.api.service.AtuPlanTaskApiService;
|
||||||
|
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||||
|
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||||
|
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto;
|
||||||
|
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@EnableScheduling
|
||||||
|
@Configuration
|
||||||
|
public class TaskExecTimeoutJob {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(TaskExecTimeoutJob.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanTaskApiService planTaskApiService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanSceneCaseTaskService sceneCaseTaskService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AtuPlanSceneCaseTaskApiService sceneCaseTaskApiService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时时间,默认3分钟,单位秒
|
||||||
|
*/
|
||||||
|
public static final int TIMEOUT = 3 * 60;
|
||||||
|
|
||||||
|
public static final String ERROR_MSG = "引擎执行任务超时";
|
||||||
|
|
||||||
|
@Scheduled(initialDelay = 10 * 1000,fixedDelay = 60 * 1000)
|
||||||
|
private void handleTaskExecTimeout(){
|
||||||
|
// 查询正在执行中且心跳时间超过n分钟的任务
|
||||||
|
List<AtuPlanTask> taskList = planTaskApiService.queryExecTimeoutTask(TIMEOUT);
|
||||||
|
if (CollUtil.isNotEmpty(taskList)){
|
||||||
|
logger.debug("超时任务数量:{}", taskList.size());
|
||||||
|
for (AtuPlanTask planTask : taskList) {
|
||||||
|
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_TIMEOUT_STATUS, ERROR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AtuPlanSceneCaseTask> sceneCaseTaskList = sceneCaseTaskService.queryExecTimeoutTask(TIMEOUT);
|
||||||
|
if (CollUtil.isNotEmpty(sceneCaseTaskList)){
|
||||||
|
logger.debug("超时场景节点任务数量:{}", sceneCaseTaskList.size());
|
||||||
|
AtuTaskExecResultDto taskExecResult = new AtuTaskExecResultDto();
|
||||||
|
taskExecResult.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
|
||||||
|
taskExecResult.setStatus(PlanConstant.TASK_TIMEOUT_STATUS);
|
||||||
|
taskExecResult.setMessage(ERROR_MSG);
|
||||||
|
taskExecResult.setCurrentTime(System.currentTimeMillis());
|
||||||
|
|
||||||
|
for (AtuPlanSceneCaseTask sceneCaseTask : sceneCaseTaskList) {
|
||||||
|
AtuPlanTaskExtendDto taskExtendDto = planTaskApiService.queryTaskExtendById(sceneCaseTask.getTaskId());
|
||||||
|
if (taskExtendDto == null){
|
||||||
|
logger.error("任务[{}]信息不存在", sceneCaseTask.getTaskId());
|
||||||
|
logger.error("删除任务[{}]的节点任务信息", sceneCaseTask.getTaskId());
|
||||||
|
AtuPlanSceneCaseTask delParams = new AtuPlanSceneCaseTask();
|
||||||
|
delParams.setTaskId(sceneCaseTask.getTaskId());
|
||||||
|
sceneCaseTaskService.deleteByExample(delParams);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
taskExecResult.setPlanId(taskExtendDto.getPlanId());
|
||||||
|
taskExecResult.setBatchId(taskExtendDto.getBatchId());
|
||||||
|
taskExecResult.setCaseId(taskExtendDto.getCaseId());
|
||||||
|
taskExecResult.setTaskId(sceneCaseTask.getId());
|
||||||
|
AtuPlanTask planTask = new AtuPlanTask();
|
||||||
|
BeanUtils.copyProperties(taskExtendDto, planTask);
|
||||||
|
// 场景节点执行失败,执行下一节点
|
||||||
|
sceneCaseTaskApiService.queryNextNodeInfo(taskExecResult, sceneCaseTask, planTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
<resultMap id="BaseResultMap" type="net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask">
|
<resultMap id="BaseResultMap" type="net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask">
|
||||||
<!-- 主键 -->
|
<!-- 主键 -->
|
||||||
<id column="id" jdbcType="VARCHAR" property="id"/>
|
<id column="id" jdbcType="VARCHAR" property="id"/>
|
||||||
<!-- 任务主键 -->
|
<!-- 任务主键 -->
|
||||||
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
|
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
|
||||||
<!-- 脚本主键 -->
|
<!-- 脚本主键 -->
|
||||||
<result column="script_id" jdbcType="VARCHAR" property="scriptId"/>
|
<result column="script_id" jdbcType="VARCHAR" property="scriptId"/>
|
||||||
|
@ -55,6 +55,8 @@
|
||||||
<result column="per_data_path" jdbcType="VARCHAR" property="perDataPath"/>
|
<result column="per_data_path" jdbcType="VARCHAR" property="perDataPath"/>
|
||||||
<!-- 创建时间 -->
|
<!-- 创建时间 -->
|
||||||
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
||||||
|
<!-- 执行心跳时间 -->
|
||||||
|
<result column="last_heartbeat_time" jdbcType="TIMESTAMP" property="lastHeartbeatTime"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
|
@ -82,6 +84,7 @@
|
||||||
,exec_result_file
|
,exec_result_file
|
||||||
,per_data_path
|
,per_data_path
|
||||||
,created_time
|
,created_time
|
||||||
|
,last_heartbeat_time
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="Table_Name">
|
<sql id="Table_Name">
|
||||||
|
@ -202,6 +205,9 @@
|
||||||
</if>
|
</if>
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||||
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||||
</if>
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</delete>
|
</delete>
|
||||||
|
@ -282,6 +288,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
created_time,
|
created_time,
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
last_heartbeat_time,
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="id != null">
|
<if test="id != null">
|
||||||
|
@ -356,6 +365,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
#{createdTime, jdbcType=TIMESTAMP},
|
#{createdTime, jdbcType=TIMESTAMP},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
#{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
@ -387,6 +399,7 @@
|
||||||
exec_result_file,
|
exec_result_file,
|
||||||
per_data_path,
|
per_data_path,
|
||||||
created_time,
|
created_time,
|
||||||
|
last_heartbeat_time,
|
||||||
</trim>
|
</trim>
|
||||||
values
|
values
|
||||||
<foreach collection="list" item="item" index="index" separator=",">
|
<foreach collection="list" item="item" index="index" separator=",">
|
||||||
|
@ -415,6 +428,7 @@
|
||||||
#{item.execResultFile, jdbcType=VARCHAR},
|
#{item.execResultFile, jdbcType=VARCHAR},
|
||||||
#{item.perDataPath, jdbcType=VARCHAR},
|
#{item.perDataPath, jdbcType=VARCHAR},
|
||||||
#{item.createdTime, jdbcType=TIMESTAMP},
|
#{item.createdTime, jdbcType=TIMESTAMP},
|
||||||
|
#{item.lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||||
</trim>
|
</trim>
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
@ -492,6 +506,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
created_time = #{createdTime, jdbcType=TIMESTAMP},
|
created_time = #{createdTime, jdbcType=TIMESTAMP},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
last_heartbeat_time = #{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||||
|
</if>
|
||||||
</set>
|
</set>
|
||||||
where id = #{id,jdbcType=VARCHAR}
|
where id = #{id,jdbcType=VARCHAR}
|
||||||
</update>
|
</update>
|
||||||
|
@ -574,6 +591,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
AND created_time = #{createdTime,jdbcType=TIMESTAMP}
|
AND created_time = #{createdTime,jdbcType=TIMESTAMP}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
AND last_heartbeat_time = #{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@ -653,6 +673,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,8 @@
|
||||||
<result column="per_data_path" jdbcType="VARCHAR" property="perDataPath"/>
|
<result column="per_data_path" jdbcType="VARCHAR" property="perDataPath"/>
|
||||||
<!-- 创建时间 -->
|
<!-- 创建时间 -->
|
||||||
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
||||||
|
<!-- 执行心跳时间 -->
|
||||||
|
<result column="last_heartbeat_time" jdbcType="TIMESTAMP" property="lastHeartbeatTime"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
|
@ -85,6 +87,7 @@
|
||||||
,bug_id
|
,bug_id
|
||||||
,per_data_path
|
,per_data_path
|
||||||
,created_time
|
,created_time
|
||||||
|
,last_heartbeat_time
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<sql id="Table_Name">
|
<sql id="Table_Name">
|
||||||
|
@ -208,6 +211,9 @@
|
||||||
</if>
|
</if>
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||||
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||||
</if>
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</delete>
|
</delete>
|
||||||
|
@ -291,6 +297,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
created_time,
|
created_time,
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
last_heartbeat_time,
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="id != null">
|
<if test="id != null">
|
||||||
|
@ -368,6 +377,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
#{createdTime, jdbcType=TIMESTAMP},
|
#{createdTime, jdbcType=TIMESTAMP},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
#{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
@ -400,6 +412,7 @@
|
||||||
bug_id,
|
bug_id,
|
||||||
per_data_path,
|
per_data_path,
|
||||||
created_time,
|
created_time,
|
||||||
|
last_heartbeat_time,
|
||||||
</trim>
|
</trim>
|
||||||
values
|
values
|
||||||
<foreach collection="list" item="item" index="index" separator=",">
|
<foreach collection="list" item="item" index="index" separator=",">
|
||||||
|
@ -429,6 +442,7 @@
|
||||||
#{item.bugId, jdbcType=VARCHAR},
|
#{item.bugId, jdbcType=VARCHAR},
|
||||||
#{item.perDataPath, jdbcType=VARCHAR},
|
#{item.perDataPath, jdbcType=VARCHAR},
|
||||||
#{item.createdTime, jdbcType=TIMESTAMP},
|
#{item.createdTime, jdbcType=TIMESTAMP},
|
||||||
|
#{item.lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||||
</trim>
|
</trim>
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
@ -509,6 +523,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
created_time = #{createdTime, jdbcType=TIMESTAMP},
|
created_time = #{createdTime, jdbcType=TIMESTAMP},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
last_heartbeat_time = #{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||||
|
</if>
|
||||||
</set>
|
</set>
|
||||||
where id = #{id,jdbcType=VARCHAR}
|
where id = #{id,jdbcType=VARCHAR}
|
||||||
</update>
|
</update>
|
||||||
|
@ -594,6 +611,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
AND created_time = #{createdTime,jdbcType=TIMESTAMP}
|
AND created_time = #{createdTime,jdbcType=TIMESTAMP}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
AND last_heartbeat_time = #{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@ -676,6 +696,9 @@
|
||||||
<if test="createdTime != null">
|
<if test="createdTime != null">
|
||||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="lastHeartbeatTime != null">
|
||||||
|
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
|
@ -220,8 +220,15 @@
|
||||||
#{taskId}
|
#{taskId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="queryExecTimeoutTask" resultMap="BaseResultMap">
|
||||||
|
select
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
|
from
|
||||||
|
<include refid="Table_Name"/>
|
||||||
|
where status = 1 and timestampdiff(second, last_heartbeat_time, NOW()) > #{timeout}
|
||||||
|
</select>
|
||||||
|
|
||||||
<delete id="deleteByPlanIdLimit">
|
<delete id="deleteByPlanIdLimit">
|
||||||
delete from atu_plan_scene_case_task
|
delete from atu_plan_scene_case_task
|
||||||
where task_id in (select id from atu_plan_task
|
where task_id in (select id from atu_plan_task
|
||||||
where batch_id in (select id from atu_plan_batch
|
where batch_id in (select id from atu_plan_batch
|
||||||
|
|
|
@ -53,6 +53,13 @@
|
||||||
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap id="TaskExtendMap" extends="BaseResultMap" type="net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto">
|
||||||
|
<!-- 计划主键 -->
|
||||||
|
<result column="plan_id" jdbcType="VARCHAR" property="planId"/>
|
||||||
|
<!-- 脚本主键 -->
|
||||||
|
<result column="plan_name" jdbcType="VARCHAR" property="planName"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
<select id="queryList" resultMap="DetailMap" parameterType="net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto">
|
<select id="queryList" resultMap="DetailMap" parameterType="net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto">
|
||||||
select
|
select
|
||||||
id
|
id
|
||||||
|
@ -307,9 +314,17 @@
|
||||||
#{batchId}
|
#{batchId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="queryExecTimeoutTask" resultMap="BaseResultMap">
|
||||||
|
select <include refid="Base_Column_List"/> from <include refid="Table_Name"/>
|
||||||
|
where status = 1 and case_type != 5 and timestampdiff(second, last_heartbeat_time, NOW()) > #{timeout}
|
||||||
|
</select>
|
||||||
|
<select id="queryTaskExtendById" resultMap="TaskExtendMap">
|
||||||
|
SELECT t.*, b.plan_id from <include refid="Table_Name"/> t
|
||||||
|
left join atu_plan_batch b on b.id = t.batch_id
|
||||||
|
where t.id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<delete id="deleteByPlanIdLimit">
|
||||||
<delete id="deleteByPlanIdLimit">
|
|
||||||
DELETE FROM <include refid="Table_Name"/>
|
DELETE FROM <include refid="Table_Name"/>
|
||||||
where batch_id in (select id from atu_plan_batch where plan_id = #{planId})
|
where batch_id in (select id from atu_plan_batch where plan_id = #{planId})
|
||||||
LIMIT #{num}
|
LIMIT #{num}
|
||||||
|
|
|
@ -51,4 +51,8 @@ public interface AutomationRequestCmd {
|
||||||
String INPUT_PASSWORD_BY_OCR = "input_password_by_ocr"; //输入密码(ocr)
|
String INPUT_PASSWORD_BY_OCR = "input_password_by_ocr"; //输入密码(ocr)
|
||||||
|
|
||||||
String GET_ELEMENT_MONEY_TEXT = "get_element_money_text"; //识别金额
|
String GET_ELEMENT_MONEY_TEXT = "get_element_money_text"; //识别金额
|
||||||
|
|
||||||
|
String SCREEN_SHOT = "screen_shot"; //屏幕截图
|
||||||
|
|
||||||
|
String GET_ELEMENT_VALUE_BY_PATH_OCR = "get_element_value_by_path_ocr"; //获取控件的值(OCR方式)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,10 +57,11 @@ public class AtuElementInfoRestfulCtrl
|
||||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
|
produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
|
||||||
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
public ResultWrapper<String> add(
|
public ResultWrapper<String> add(
|
||||||
@RequestBody @Validated AtuElementInfoAddDto inputDto)
|
@RequestHeader(AuthDependencyConstants.REQUEST_HEADER_PROJECT_ID) String projectId,
|
||||||
|
@RequestBody @Validated AtuElementInfoAddDto inputDto)
|
||||||
{
|
{
|
||||||
ResultWrapper<String> wrapper = new ResultWrapper<>();
|
ResultWrapper<String> wrapper = new ResultWrapper<>();
|
||||||
String uuid = apiService.add(inputDto);
|
String uuid = apiService.add(inputDto, projectId);
|
||||||
return wrapper.success(uuid);
|
return wrapper.success(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +77,11 @@ public class AtuElementInfoRestfulCtrl
|
||||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
|
produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
|
||||||
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
public ResultWrapper<Integer> update(
|
public ResultWrapper<Integer> update(
|
||||||
@RequestBody @Validated AtuElementInfoUpdateDto inputDto) throws IllegalAccessException
|
@RequestHeader(AuthDependencyConstants.REQUEST_HEADER_PROJECT_ID) String projectId,
|
||||||
|
@RequestBody @Validated AtuElementInfoUpdateDto inputDto) throws IllegalAccessException
|
||||||
{
|
{
|
||||||
ResultWrapper<Integer> wrapper = new ResultWrapper<>();
|
ResultWrapper<Integer> wrapper = new ResultWrapper<>();
|
||||||
Integer count = apiService.updateByPK(inputDto);
|
Integer count = apiService.updateByPK(inputDto, projectId);
|
||||||
return wrapper.success(count);
|
return wrapper.success(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public interface AtuElementInfoApiService extends ExcelService
|
||||||
* @param dto 接口数据传输对象
|
* @param dto 接口数据传输对象
|
||||||
* @return 主键
|
* @return 主键
|
||||||
*/
|
*/
|
||||||
String add(AtuElementInfoAddDto dto);
|
String add(AtuElementInfoAddDto dto, String projectId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新元素信息管理
|
* 更新元素信息管理
|
||||||
|
@ -42,7 +42,7 @@ public interface AtuElementInfoApiService extends ExcelService
|
||||||
* @param dto 接口数据传输对象
|
* @param dto 接口数据传输对象
|
||||||
* @return 修改的记录数量
|
* @return 修改的记录数量
|
||||||
*/
|
*/
|
||||||
Integer updateByPK(AtuElementInfoUpdateDto dto) throws IllegalAccessException;
|
Integer updateByPK(AtuElementInfoUpdateDto dto, String projectId) throws IllegalAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取元素信息管理
|
* 获取元素信息管理
|
||||||
|
|
|
@ -298,13 +298,18 @@ public class AtuElementInfoApiServiceImpl extends AbstractExcelService<AtuElemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
||||||
public String add(AtuElementInfoAddDto dto)
|
public String add(AtuElementInfoAddDto dto, String projectId)
|
||||||
{
|
{
|
||||||
//元素目录校验
|
//元素目录校验
|
||||||
AtuElementGroup elementGroup = atuElementGroupService.findByPrimaryKey(dto.getGroupId());
|
AtuElementGroup elementGroup = atuElementGroupService.findByPrimaryKey(dto.getGroupId());
|
||||||
if (null == elementGroup || ScriptConstant.STR_ZERO.equals(elementGroup.getIsLeaf())) {
|
if (null == elementGroup || ScriptConstant.STR_ZERO.equals(elementGroup.getIsLeaf())) {
|
||||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_DIR_IS_VALID);
|
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_DIR_IS_VALID);
|
||||||
}
|
}
|
||||||
|
//元素名称校验
|
||||||
|
List<AtuElementInfo> atuElementInfos = atuElementInfoService.findByName(dto.getName(), projectId);
|
||||||
|
if (!CollectionUtils.isEmpty(atuElementInfos)) {
|
||||||
|
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_IS_EXISTS);
|
||||||
|
}
|
||||||
// 校验获取方式是否为空
|
// 校验获取方式是否为空
|
||||||
if (CollUtil.isEmpty(dto.getFetchTypeList())){
|
if (CollUtil.isEmpty(dto.getFetchTypeList())){
|
||||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS);
|
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS);
|
||||||
|
@ -374,13 +379,18 @@ public class AtuElementInfoApiServiceImpl extends AbstractExcelService<AtuElemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
||||||
public Integer updateByPK(AtuElementInfoUpdateDto dto) throws IllegalAccessException
|
public Integer updateByPK(AtuElementInfoUpdateDto dto, String projectId) throws IllegalAccessException
|
||||||
{
|
{
|
||||||
//参数校验
|
//参数校验
|
||||||
AtuElementInfo oldElement = atuElementInfoService.findByPrimaryKey(dto.getId());
|
AtuElementInfo oldElement = atuElementInfoService.findByPrimaryKey(dto.getId());
|
||||||
if (oldElement == null || ScriptConstant.STR_ONE.equals(oldElement.getIsDeleted())) {
|
if (oldElement == null || ScriptConstant.STR_ONE.equals(oldElement.getIsDeleted())) {
|
||||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_NO_EXISTS);
|
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_NO_EXISTS);
|
||||||
}
|
}
|
||||||
|
//元素名称校验
|
||||||
|
List<AtuElementInfo> atuElementInfos = atuElementInfoService.findByNameButNotSelf(dto.getName(), dto.getId(), projectId);
|
||||||
|
if (!CollectionUtils.isEmpty(atuElementInfos)) {
|
||||||
|
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_IS_EXISTS);
|
||||||
|
}
|
||||||
// 校验获取方式是否为空
|
// 校验获取方式是否为空
|
||||||
if (CollUtil.isEmpty(dto.getFetchTypeList())){
|
if (CollUtil.isEmpty(dto.getFetchTypeList())){
|
||||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS);
|
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS);
|
||||||
|
|
|
@ -35,6 +35,8 @@ public interface AtuElementInfoDao extends AtuElementInfoMapper
|
||||||
|
|
||||||
List<String> selectUpdaterByProject(@Param("projectId") String projectId);
|
List<String> selectUpdaterByProject(@Param("projectId") String projectId);
|
||||||
|
|
||||||
|
List<AtuElementInfo> findByName(@Param("name") String name, @Param("projectId") String projectId);
|
||||||
|
|
||||||
|
List<AtuElementInfo> findByNameButNotSelf(@Param("name") String name, @Param("id") String id, @Param("projectId") String projectId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,5 +70,25 @@ private static final Logger logger = LoggerFactory.getLogger(AtuElementInfoServi
|
||||||
public List<String> selectUpdaterByProject(String projectId) {
|
public List<String> selectUpdaterByProject(String projectId) {
|
||||||
return atuElementInfoDao.selectUpdaterByProject(projectId);
|
return atuElementInfoDao.selectUpdaterByProject(projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名称查询元素
|
||||||
|
* @param name 元素名称
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<AtuElementInfo> findByName(String name, String projectId) {
|
||||||
|
return atuElementInfoDao.findByName(name, projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名称查询元素
|
||||||
|
* @param name 元素名称
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<AtuElementInfo> findByNameButNotSelf(String name, String id, String projectId) {
|
||||||
|
return atuElementInfoDao.findByNameButNotSelf(name, id, projectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,18 @@ public interface AtuElementInfoService extends BasicService<AtuElementInfo>
|
||||||
|
|
||||||
List<String> selectUpdaterByProject(String projectId);
|
List<String> selectUpdaterByProject(String projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名称查询元素
|
||||||
|
* @param name 元素名称
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<AtuElementInfo> findByName(String name, String projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名称查询元素
|
||||||
|
* @param name 元素名称
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<AtuElementInfo> findByNameButNotSelf(String name, String id, String projectId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,9 @@ public class AtuScriptInfoDetailDto extends AtuScriptInfo implements Serializabl
|
||||||
private List<Object> inputList;
|
private List<Object> inputList;
|
||||||
@ApiModelProperty("脚本输出项参数")
|
@ApiModelProperty("脚本输出项参数")
|
||||||
private List<Object> outputList;
|
private List<Object> outputList;
|
||||||
|
@ApiModelProperty("分组信息")
|
||||||
|
private String namePath;
|
||||||
|
|
||||||
|
|
||||||
public List<Object> getInputList() {
|
public List<Object> getInputList() {
|
||||||
return inputList;
|
return inputList;
|
||||||
|
@ -105,4 +108,12 @@ public class AtuScriptInfoDetailDto extends AtuScriptInfo implements Serializabl
|
||||||
public void setPrincipal(String principal) {
|
public void setPrincipal(String principal) {
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNamePath() {
|
||||||
|
return namePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNamePath(String namePath) {
|
||||||
|
this.namePath = namePath;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -20,6 +20,7 @@ public enum ElementLibraryError implements PlatformError {
|
||||||
ELEMENT_MOVE_NO_LEAF("请选择叶子节点目录移动!"),
|
ELEMENT_MOVE_NO_LEAF("请选择叶子节点目录移动!"),
|
||||||
ELEMENT_VALUE_IS_TOO_LONG("元素属性值超出长度限制!"),
|
ELEMENT_VALUE_IS_TOO_LONG("元素属性值超出长度限制!"),
|
||||||
ELEMENT_NO_EXISTS("当前元素不存在,请刷新列表"),
|
ELEMENT_NO_EXISTS("当前元素不存在,请刷新列表"),
|
||||||
|
ELEMENT_IS_EXISTS("当前元素已存在,元素名称不可重复"),
|
||||||
ELEMENT_FETCH_TYPE_NO_EXISTS("元素获取方式为空,请检查");
|
ELEMENT_FETCH_TYPE_NO_EXISTS("元素获取方式为空,请检查");
|
||||||
private static final int START_CODE = 220000;
|
private static final int START_CODE = 220000;
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,25 @@
|
||||||
WHERE is_deleted = 0 AND project_id = #{projectId}
|
WHERE is_deleted = 0 AND project_id = #{projectId}
|
||||||
GROUP BY updated_by
|
GROUP BY updated_by
|
||||||
</select>
|
</select>
|
||||||
|
<select id="findByName" resultType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
||||||
|
select
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
|
from
|
||||||
|
<include refid="Table_Name"/>
|
||||||
|
where is_deleted = '0'
|
||||||
|
and name = #{name}
|
||||||
|
and project_id = #{projectId}
|
||||||
|
</select>
|
||||||
|
<select id="findByNameButNotSelf" resultType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
||||||
|
select
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
|
from
|
||||||
|
<include refid="Table_Name"/>
|
||||||
|
where is_deleted = '0'
|
||||||
|
and id != #{id}
|
||||||
|
and name = #{name}
|
||||||
|
and project_id = #{projectId}
|
||||||
|
</select>
|
||||||
<update id="deleteElementBatch" parameterType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
<update id="deleteElementBatch" parameterType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
||||||
update
|
update
|
||||||
<include refid="Table_Name"/>
|
<include refid="Table_Name"/>
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
<result column="app_package" jdbcType="VARCHAR" property="appPackage"/>
|
<result column="app_package" jdbcType="VARCHAR" property="appPackage"/>
|
||||||
<!-- 版本名称 -->
|
<!-- 版本名称 -->
|
||||||
<result column="version_name" jdbcType="VARCHAR" property="versionName"/>
|
<result column="version_name" jdbcType="VARCHAR" property="versionName"/>
|
||||||
|
<!-- 分组 -->
|
||||||
|
<result column="name_path" jdbcType="VARCHAR" property="namePath"/>
|
||||||
<!-- 应用平台 -->
|
<!-- 应用平台 -->
|
||||||
<result column="platform" jdbcType="VARCHAR" property="platform"/>
|
<result column="platform" jdbcType="VARCHAR" property="platform"/>
|
||||||
|
|
||||||
|
@ -154,9 +156,12 @@
|
||||||
,b.version_name
|
,b.version_name
|
||||||
,a.platform
|
,a.platform
|
||||||
,a.principal_id
|
,a.principal_id
|
||||||
|
,c.name_path
|
||||||
from ${defaultSchema}.atu_script_info a
|
from ${defaultSchema}.atu_script_info a
|
||||||
left join ${defaultSchema}.atu_script_version b
|
left join ${defaultSchema}.atu_script_version b
|
||||||
on (a.id = b.script_id and a.latest_version_id = b.version_id)
|
on (a.id = b.script_id and a.latest_version_id = b.version_id)
|
||||||
|
left join atu_script_group c
|
||||||
|
on a.group_id = c.id
|
||||||
<trim prefix="WHERE" prefixOverrides="AND" >
|
<trim prefix="WHERE" prefixOverrides="AND" >
|
||||||
<if test="query.projectIds != null and query.projectIds.size > 0">
|
<if test="query.projectIds != null and query.projectIds.size > 0">
|
||||||
AND a.project_id IN
|
AND a.project_id IN
|
||||||
|
|
|
@ -51,4 +51,8 @@ public interface AutomationRequestCmd {
|
||||||
String INPUT_PASSWORD_BY_OCR = "input_password_by_ocr"; //输入密码(ocr)
|
String INPUT_PASSWORD_BY_OCR = "input_password_by_ocr"; //输入密码(ocr)
|
||||||
|
|
||||||
String GET_ELEMENT_MONEY_TEXT = "get_element_money_text"; //识别金额
|
String GET_ELEMENT_MONEY_TEXT = "get_element_money_text"; //识别金额
|
||||||
|
|
||||||
|
String SCREEN_SHOT = "screen_shot"; //屏幕截图
|
||||||
|
|
||||||
|
String GET_ELEMENT_VALUE_BY_PATH_OCR = "get_element_value_by_path_ocr"; //获取控件的值(OCR方式)
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,4 +83,9 @@ public interface UpperParamKey {
|
||||||
* 识别类型
|
* 识别类型
|
||||||
*/
|
*/
|
||||||
String USING_TYPE = "using_type";
|
String USING_TYPE = "using_type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户id
|
||||||
|
*/
|
||||||
|
String TENANT_ID = "tenant_id";
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,10 @@ public abstract class AbstractAutomationHandler implements AutomationMessageHand
|
||||||
inputPasswordByOcr(request);
|
inputPasswordByOcr(request);
|
||||||
} else if (AutomationRequestCmd.GET_ELEMENT_MONEY_TEXT.equals(cmd)) {
|
} else if (AutomationRequestCmd.GET_ELEMENT_MONEY_TEXT.equals(cmd)) {
|
||||||
getElementMoneyText(request);
|
getElementMoneyText(request);
|
||||||
|
} else if (AutomationRequestCmd.SCREEN_SHOT.equals(cmd)) {
|
||||||
|
screenShot(request);
|
||||||
|
} else if (AutomationRequestCmd.GET_ELEMENT_VALUE_BY_PATH_OCR.equals(cmd)) {
|
||||||
|
getElementValueByPathOcr(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,4 +184,14 @@ public class AndroidAutomationHandler extends AbstractAutomationHandler{
|
||||||
public void getElementMoneyText(CmdAutomationRequest request) {
|
public void getElementMoneyText(CmdAutomationRequest request) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void screenShot(CmdAutomationRequest request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getElementValueByPathOcr(CmdAutomationRequest request) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,4 +76,8 @@ public interface AutomationMessageHandler {
|
||||||
|
|
||||||
void getElementMoneyText(CmdAutomationRequest request); //识别金额
|
void getElementMoneyText(CmdAutomationRequest request); //识别金额
|
||||||
|
|
||||||
|
void screenShot(CmdAutomationRequest request); //识别金额
|
||||||
|
|
||||||
|
void getElementValueByPathOcr(CmdAutomationRequest request); ////获取控件的值(OCR方式)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,15 @@ import net.northking.cctp.upperComputer.automation.constants.Command;
|
||||||
import net.northking.cctp.upperComputer.automation.constants.UpperParamKey;
|
import net.northking.cctp.upperComputer.automation.constants.UpperParamKey;
|
||||||
import net.northking.cctp.upperComputer.automation.entity.CmdAutomationRequest;
|
import net.northking.cctp.upperComputer.automation.entity.CmdAutomationRequest;
|
||||||
import net.northking.cctp.upperComputer.automation.entity.CmdAutomationResponse;
|
import net.northking.cctp.upperComputer.automation.entity.CmdAutomationResponse;
|
||||||
|
import net.northking.cctp.upperComputer.config.MobileProperty;
|
||||||
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
|
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
|
||||||
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
|
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
|
||||||
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
|
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
|
||||||
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
|
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
|
||||||
import net.northking.cctp.upperComputer.driver.ios.command.data.*;
|
import net.northking.cctp.upperComputer.driver.ios.command.data.*;
|
||||||
|
import net.northking.cctp.upperComputer.entity.Attachment;
|
||||||
import net.northking.cctp.upperComputer.entity.PhoneEntity;
|
import net.northking.cctp.upperComputer.entity.PhoneEntity;
|
||||||
|
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
|
||||||
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
||||||
import net.northking.cctp.upperComputer.service.IosDebuggerServiceImpl;
|
import net.northking.cctp.upperComputer.service.IosDebuggerServiceImpl;
|
||||||
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
||||||
|
@ -142,7 +145,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) {
|
if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "app安装失败");
|
response = CmdAutomationResponse.builderFailure(request, "app安装失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.info("activate app[{}]", appPackage);
|
logger.info("activate app[{}]", appPackage);
|
||||||
|
@ -195,10 +198,10 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
elapsedTime = currentTime - startTime;
|
elapsedTime = currentTime - startTime;
|
||||||
}
|
}
|
||||||
if (null == uiNodeData) {
|
if (null == uiNodeData) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "元素不存在");
|
response = CmdAutomationResponse.builderFailure(request, "元素不存在");
|
||||||
} else {
|
} else {
|
||||||
if (uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0) {
|
if (uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "元素不存在");
|
response = CmdAutomationResponse.builderFailure(request, "元素不存在");
|
||||||
} else {
|
} else {
|
||||||
TapXYData tapXYData = new TapXYData();
|
TapXYData tapXYData = new TapXYData();
|
||||||
tapXYData.setX(uiNodeData.getX());
|
tapXYData.setX(uiNodeData.getX());
|
||||||
|
@ -233,7 +236,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (null == tapXYData) {
|
if (null == tapXYData) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "查找控件失败").withData(false);
|
response = CmdAutomationResponse.builderFailure(request, "查找控件失败").withData(false);
|
||||||
} else {
|
} else {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "查找控件完成").withData(tapXYData);
|
response = CmdAutomationResponse.builderSuccess(request, "查找控件完成").withData(tapXYData);
|
||||||
}
|
}
|
||||||
|
@ -337,14 +340,14 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
|
|
||||||
if (null != uiNodeData) {
|
if (null != uiNodeData) {
|
||||||
if (uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0) {
|
if (uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "找不到控件");
|
response = CmdAutomationResponse.builderFailure(request, "找不到控件");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(value)) {
|
if (StringUtils.isNotBlank(value)) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "已获取控件的值").withData(value);
|
response = CmdAutomationResponse.builderSuccess(request, "已获取控件的值").withData(value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "找不到控件");
|
response = CmdAutomationResponse.builderFailure(request, "找不到控件");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("根据xpath获取控件的值失败,原因:", e);
|
logger.error("根据xpath获取控件的值失败,原因:", e);
|
||||||
|
@ -1052,22 +1055,26 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
Map<String, Object> ocrParamMap = new HashMap<>();
|
Map<String, Object> ocrParamMap = new HashMap<>();
|
||||||
String ocrText = (String) data.get(UpperParamKey.OCR_TEXT);
|
String ocrText = (String) data.get(UpperParamKey.OCR_TEXT);
|
||||||
Integer x = (Integer) data.get(UpperParamKey.X);
|
Double realityX = 0.0;
|
||||||
Integer y = (Integer) data.get(UpperParamKey.Y);
|
Double realityY = 0.0;
|
||||||
Integer screenWidth = (Integer) data.get(UpperParamKey.SCREEN_WIDTH);
|
if(data.get(UpperParamKey.X) != null) {
|
||||||
Integer screenHeight = (Integer) data.get(UpperParamKey.SCREEN_HEIGHT);
|
Integer x = (Integer) data.get(UpperParamKey.X);
|
||||||
//转换比例
|
Integer y = (Integer) data.get(UpperParamKey.Y);
|
||||||
int width = phoneEntity.getScreenWidth() * phoneEntity.getScale();
|
Integer screenWidth = (Integer) data.get(UpperParamKey.SCREEN_WIDTH);
|
||||||
int height = phoneEntity.getScreenHeight() * phoneEntity.getScale();
|
Integer screenHeight = (Integer) data.get(UpperParamKey.SCREEN_HEIGHT);
|
||||||
int realityWidth = width;
|
//转换比例
|
||||||
int realityHeight = height;
|
int width = phoneEntity.getScreenWidth() * phoneEntity.getScale();
|
||||||
if (width > height) { //宽高反了的情况,调换
|
int height = phoneEntity.getScreenHeight() * phoneEntity.getScale();
|
||||||
realityWidth = height;
|
int realityWidth = width;
|
||||||
realityHeight = width;
|
int realityHeight = height;
|
||||||
}
|
if (width > height) { //宽高反了的情况,调换
|
||||||
Double realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth;
|
realityWidth = height;
|
||||||
Double realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight;
|
realityHeight = width;
|
||||||
|
}
|
||||||
|
realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth;
|
||||||
|
realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight;
|
||||||
|
|
||||||
|
}
|
||||||
ocrParamMap.put("img_base64", base64Str);
|
ocrParamMap.put("img_base64", base64Str);
|
||||||
ocrParamMap.put("targets", ocrText);
|
ocrParamMap.put("targets", ocrText);
|
||||||
HttpEntity<Map<String, Object>> ocrEntity = new HttpEntity<>(ocrParamMap, headers);
|
HttpEntity<Map<String, Object>> ocrEntity = new HttpEntity<>(ocrParamMap, headers);
|
||||||
|
@ -1314,7 +1321,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
if (clickSuccess) {
|
if (clickSuccess) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "点击控件成功").withData(clickSuccess);
|
response = CmdAutomationResponse.builderSuccess(request, "点击控件成功").withData(clickSuccess);
|
||||||
} else {
|
} else {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "点击控件失败").withData(false);
|
response = CmdAutomationResponse.builderFailure(request, "点击控件失败").withData(false);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("根据ocr点击控件失败,原因:", e);
|
logger.error("根据ocr点击控件失败,原因:", e);
|
||||||
|
@ -1379,7 +1386,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
} else if ("1".equalsIgnoreCase(type)) {
|
} else if ("1".equalsIgnoreCase(type)) {
|
||||||
logger.info("启动ios的app:{}", appPackage);
|
logger.info("启动ios的app:{}", appPackage);
|
||||||
if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) {
|
if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "app未安装");
|
response = CmdAutomationResponse.builderFailure(request, "app未安装");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean success = deviceHandleHelper.activateApp(phoneEntity.getUdid(), appPackage);
|
boolean success = deviceHandleHelper.activateApp(phoneEntity.getUdid(), appPackage);
|
||||||
|
@ -1391,7 +1398,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
} else {
|
} else {
|
||||||
logger.info("重启ios的app:{}", appPackage);
|
logger.info("重启ios的app:{}", appPackage);
|
||||||
if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) {
|
if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) {
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "app未安装");
|
response = CmdAutomationResponse.builderFailure(request, "app未安装");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean success = iosService.terminateApp(serial, appPackage);
|
boolean success = iosService.terminateApp(serial, appPackage);
|
||||||
|
@ -1511,11 +1518,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
logger.error("请求ocr接口失败,接口地址:{}",ocrAddress,e);
|
logger.error("请求ocr接口失败,接口地址:{}",ocrAddress,e);
|
||||||
}
|
}
|
||||||
logger.info("识别金额为:{}", value);
|
logger.info("识别金额为:{}", value);
|
||||||
if (StringUtils.isNotBlank(value)) {
|
response = CmdAutomationResponse.builderSuccess(request, "识别金额成功").withData(value);
|
||||||
response = CmdAutomationResponse.builderSuccess(request, "识别金额成功").withData(value);
|
|
||||||
} else {
|
|
||||||
response = CmdAutomationResponse.builderFailure(request, "识别金额失败");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("识别失败,原因:", e);
|
logger.error("识别失败,原因:", e);
|
||||||
|
@ -1525,6 +1528,137 @@ public class IosAutomationHandler extends AbstractAutomationHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void screenShot(CmdAutomationRequest request) {
|
||||||
|
CmdAutomationResponse response = CmdAutomationResponse.builderFailure(request, "屏幕截图失败");
|
||||||
|
logger.debug("开始上传截图,信息:{}", JSON.toJSONString(request));
|
||||||
|
try {
|
||||||
|
String serial = phoneEntity.getUdid();
|
||||||
|
File file = ScreenShotUtils.getIOSMobileScreenShot(serial);
|
||||||
|
Map<String, Object> data = request.getData();
|
||||||
|
String path = null;
|
||||||
|
try {
|
||||||
|
//租户id
|
||||||
|
String tenantId = (String) data.get(UpperParamKey.TENANT_ID);
|
||||||
|
String serverAddr = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
|
||||||
|
String publicUploadAddr = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
||||||
|
Attachment upload = HttpUtils.upload(serverAddr + publicUploadAddr, file.getAbsolutePath(), tenantId, "", FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
|
||||||
|
if (null != upload && org.apache.commons.lang3.StringUtils.isNotBlank(upload.getId())) {
|
||||||
|
logger.debug("文件上传成功,返回id:{}", upload.getId());
|
||||||
|
path = upload.getUrlPath();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("截图失败", e);
|
||||||
|
response = CmdAutomationResponse.builderFailure(request, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (file.exists()) {
|
||||||
|
boolean delete = file.delete();
|
||||||
|
if (!delete) {
|
||||||
|
logger.warn("临时文件【{}】删除失败", file.getAbsolutePath());
|
||||||
|
response = CmdAutomationResponse.builderFailure(request, "临时文件【{}】删除失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(path)) {
|
||||||
|
response = CmdAutomationResponse.builderSuccess(request, "屏幕截图成功").withData(path);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("屏幕截图失败,原因:", e);
|
||||||
|
response = CmdAutomationResponse.builderFailure(request, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
sendResultToEngine(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getElementValueByPathOcr(CmdAutomationRequest request) {
|
||||||
|
CmdAutomationResponse response = CmdAutomationResponse.builderFailure(request, "未查找到控件");
|
||||||
|
try {
|
||||||
|
Map<String, Object> data = request.getData();
|
||||||
|
Integer waitTimeout = (Integer) data.get(UpperParamKey.WAIT_TIMEOUT);
|
||||||
|
NKAgent nkAgent = getNkAgent();
|
||||||
|
if (null == nkAgent) {
|
||||||
|
response = CmdAutomationResponse.builderFailure(request, "设备与上位机连接断开");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String nodeTree = (String) data.get(UpperParamKey.NODE_TREE);
|
||||||
|
logger.debug("拿到的nodeTree:{}", nodeTree);
|
||||||
|
SearchUiNodeData searchUiNodeData = new SearchUiNodeData();
|
||||||
|
searchUiNodeData.setChain(nodeTree);
|
||||||
|
UiNodeData uiNodeData = null;
|
||||||
|
String value = null;
|
||||||
|
long startTime = System.currentTimeMillis(); //当前时间
|
||||||
|
long elapsedTime = 0; // 初始化经过的时间
|
||||||
|
while(elapsedTime < waitTimeout * 1000 -300) {
|
||||||
|
uiNodeData = nkAgent.uiNodeInfo(searchUiNodeData);
|
||||||
|
// logger.info("拿到的uiNodeData:{}", uiNodeData);
|
||||||
|
if (uiNodeData != null && !(uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0)) {
|
||||||
|
value = getValueByOcr(uiNodeData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
elapsedTime = currentTime - startTime - 1000;
|
||||||
|
}
|
||||||
|
response = CmdAutomationResponse.builderSuccess(request, "获取控件的值(OCR方式)成功").withData(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("根据xpath判断元素是否存在失败,原因:", e);
|
||||||
|
response = CmdAutomationResponse.builderFailure(request, e.getMessage());
|
||||||
|
} finally{
|
||||||
|
logger.info("根据nodetree查找元素失败的response:{}", response);
|
||||||
|
sendResultToEngine(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getValueByOcr(UiNodeData uiNodeData) {
|
||||||
|
String value = null;
|
||||||
|
String imgBase64 = getOcrAreaByBodeToBase64(uiNodeData);
|
||||||
|
String ocrAddress = SpringUtils.getProperties("nk.http-request-path.ocrGetAllTextNum");
|
||||||
|
try {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
Map<String, Object> ocrParamMap = new HashMap<>();
|
||||||
|
ocrParamMap.put("img_base64", imgBase64);
|
||||||
|
ocrParamMap.put("targets", "");
|
||||||
|
HttpEntity<Map> ocrEntity = new HttpEntity<>(ocrParamMap, headers);
|
||||||
|
List ocrResultList = null;
|
||||||
|
try {
|
||||||
|
logger.info("传参:{}", JSON.toJSONString(ocrParamMap));
|
||||||
|
ocrResultList = HttpUtils.doPost(ocrAddress, ocrEntity, List.class);
|
||||||
|
logger.info("得到的ocr结果:{}", ocrResultList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("ocr失败", e);
|
||||||
|
}
|
||||||
|
List<String> dataList = new ArrayList<>();
|
||||||
|
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||||
|
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||||
|
for (Object o : ocrResultList) {
|
||||||
|
dataList.add(((Map) o).get("text") + "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = JSON.toJSONString(dataList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("请求ocr接口失败,接口地址:{}",ocrAddress,e);
|
||||||
|
}
|
||||||
|
logger.info("识别文字为:{}", value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOcrAreaByBodeToBase64(UiNodeData uiNodeData) {
|
||||||
|
Integer x = uiNodeData.getX();
|
||||||
|
Integer y = uiNodeData.getY();
|
||||||
|
Integer screenWidth = phoneEntity.getScreenWidth();
|
||||||
|
Integer screenHeight = phoneEntity.getScreenHeight();
|
||||||
|
Integer cutWidth = uiNodeData.getWidth();
|
||||||
|
Integer cutHeight = uiNodeData.getHeight();
|
||||||
|
logger.debug("拿到的截图参数----->>>>>x:{},y:{},screenWidth:{},screenHeight:{},cutWidth:{},cutHeight:{}", x, y, screenWidth, screenHeight, cutWidth, cutHeight);
|
||||||
|
File file = deviceHandleHelper.getScreenShotFile(serial, x, y, cutWidth, cutHeight, screenWidth, screenHeight);
|
||||||
|
logger.debug("ocr截图:{}", file.getAbsolutePath());
|
||||||
|
String base64Str = getFileBase64(file);
|
||||||
|
return base64Str;
|
||||||
|
}
|
||||||
|
|
||||||
private void getKeyBoardInfo(String ocrArea) {
|
private void getKeyBoardInfo(String ocrArea) {
|
||||||
Map<String, JSONObject> itemMap = new HashMap<>();
|
Map<String, JSONObject> itemMap = new HashMap<>();
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
|
|
@ -921,7 +921,7 @@ public class AndroidDebuggerServiceImpl extends AbstractDebuggerService {
|
||||||
if (!tmpPicDir.exists()) {
|
if (!tmpPicDir.exists()) {
|
||||||
tmpPicDir.mkdir();
|
tmpPicDir.mkdir();
|
||||||
}
|
}
|
||||||
String tmpPicPath = tmpPicDir.getAbsolutePath() + "/" + System.currentTimeMillis() + ".png";
|
String tmpPicPath = tmpPicDir.getAbsolutePath() + "/" + deviceId + "_" + System.currentTimeMillis() + ".png";
|
||||||
tmpPicFile = new File(tmpPicPath);
|
tmpPicFile = new File(tmpPicPath);
|
||||||
try (FileOutputStream fos = new FileOutputStream(tmpPicFile)){
|
try (FileOutputStream fos = new FileOutputStream(tmpPicFile)){
|
||||||
fos.write(screenShotData);
|
fos.write(screenShotData);
|
||||||
|
@ -932,7 +932,7 @@ public class AndroidDebuggerServiceImpl extends AbstractDebuggerService {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("设备【"+deviceId+"】截图异常", e);
|
logger.error("设备【"+deviceId+"】截图异常", e);
|
||||||
}finally {
|
}finally {
|
||||||
logger.debug("设备【{}】写到本地的图片大小:{}");
|
logger.debug("设备【{}】写到本地的图片大小:{}", deviceId, tmpPicFile != null ? tmpPicFile.length() : 0);
|
||||||
}
|
}
|
||||||
return tmpPicFile;
|
return tmpPicFile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -577,7 +577,7 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screenResponseThread.startRecordScreen((String) params.get("tenantId"), catchParam.getRealWidth(), catchParam.getRealHeight(), "1");
|
screenResponseThread.startRecordScreen((String) params.get("tenantId"), catchParam.getRealWidth(), catchParam.getRealHeight(), "1", null);
|
||||||
SessionUtils.sendSuccessData(session, request, null, "开启录屏成功");
|
SessionUtils.sendSuccessData(session, request, null, "开启录屏成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,7 +660,7 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
|
||||||
screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
|
screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
|
||||||
if (null == screenResponseThread || screenResponseThread.isInterrupted() || !screenResponseThread.isAlive()) {
|
if (null == screenResponseThread || screenResponseThread.isInterrupted() || !screenResponseThread.isAlive()) {
|
||||||
//读取手机端响应线程
|
//读取手机端响应线程
|
||||||
screenResponseThread = new IosScreenResponseThread(phoneEntity);
|
screenResponseThread = new IosScreenResponseThread(phoneEntity, null);
|
||||||
screenResponseThread.start();
|
screenResponseThread.start();
|
||||||
screenResponseThread.startSendScreenToWeb(session, catchParam);
|
screenResponseThread.startSendScreenToWeb(session, catchParam);
|
||||||
screenResponseThread.setScreenOnRequest(request);
|
screenResponseThread.setScreenOnRequest(request);
|
||||||
|
|
|
@ -16,6 +16,7 @@ import net.northking.cctp.upperComputer.driver.agent.command.*;
|
||||||
import net.northking.cctp.upperComputer.driver.agent.command.data.PackageInfo;
|
import net.northking.cctp.upperComputer.driver.agent.command.data.PackageInfo;
|
||||||
import net.northking.cctp.upperComputer.driver.agent.command.protocol.ProtocolCommand;
|
import net.northking.cctp.upperComputer.driver.agent.command.protocol.ProtocolCommand;
|
||||||
import net.northking.cctp.upperComputer.entity.Attachment;
|
import net.northking.cctp.upperComputer.entity.Attachment;
|
||||||
|
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
|
||||||
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
||||||
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
|
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
|
||||||
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
||||||
|
@ -153,7 +154,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
|
||||||
//上传到服务器
|
//上传到服务器
|
||||||
String serverIp = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
|
String serverIp = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
|
||||||
String fileUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
String fileUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
||||||
String fileId = HttpUtils.doUpload(serverIp + fileUploadPath, tmpFile, tenantId);
|
String fileId = HttpUtils.doUpload(serverIp + fileUploadPath, tmpFile, tenantId,null, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
|
||||||
logger.info("上传文件到服务器完成:{}", fileId);
|
logger.info("上传文件到服务器完成:{}", fileId);
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("fileName", fileName);
|
result.put("fileName", fileName);
|
||||||
|
@ -1151,7 +1152,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
|
||||||
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
||||||
Attachment upload = null;
|
Attachment upload = null;
|
||||||
try {
|
try {
|
||||||
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
|
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId,null,FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.debug("[{}]上传截图失败", adbDevice.getSerial(),e);
|
logger.debug("[{}]上传截图失败", adbDevice.getSerial(),e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import net.northking.cctp.upperComputer.driver.ios.command.data.TypeKeysUiNodeDa
|
||||||
import net.northking.cctp.upperComputer.driver.ios.packet.EmptyCommandData;
|
import net.northking.cctp.upperComputer.driver.ios.packet.EmptyCommandData;
|
||||||
import net.northking.cctp.upperComputer.entity.Attachment;
|
import net.northking.cctp.upperComputer.entity.Attachment;
|
||||||
import net.northking.cctp.upperComputer.entity.PhoneEntity;
|
import net.northking.cctp.upperComputer.entity.PhoneEntity;
|
||||||
|
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
|
||||||
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
||||||
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
|
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
|
||||||
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
||||||
|
@ -688,7 +689,7 @@ public class IosMacMessageHandlerThread extends AbstractIosMessageHandlerThread
|
||||||
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
||||||
Attachment upload = null;
|
Attachment upload = null;
|
||||||
try {
|
try {
|
||||||
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
|
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId, null, FileBusinessTypeEnum.MOBILE_RECORD_SCREENSHOT.getCode());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e);
|
logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,8 @@ public class IosScreenResponseThread extends Thread {
|
||||||
|
|
||||||
private boolean sendDeviceStatus = true;
|
private boolean sendDeviceStatus = true;
|
||||||
|
|
||||||
|
private String currentTaskId;
|
||||||
|
|
||||||
// private IosScreenCompressHandleThread screenCompressHandle;
|
// private IosScreenCompressHandleThread screenCompressHandle;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -225,11 +227,10 @@ public class IosScreenResponseThread extends Thread {
|
||||||
/**
|
/**
|
||||||
* @param phone
|
* @param phone
|
||||||
*/
|
*/
|
||||||
public IosScreenResponseThread(PhoneEntity phone) {
|
public IosScreenResponseThread(PhoneEntity phone, String currentTaskId) {
|
||||||
// screenCompressHandle = new IosScreenCompressHandleThread(phone.getUdid());
|
|
||||||
// screenCompressHandle.start();
|
|
||||||
this.phone = phone;
|
this.phone = phone;
|
||||||
setName(phone.getUdid() + "拉取屏幕");
|
setName(phone.getUdid() + "拉取屏幕");
|
||||||
|
this.currentTaskId = currentTaskId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UsbMuxd.ConnectOperator getDeviceConnectOperator(UsbMuxd usbMuxd) {
|
private UsbMuxd.ConnectOperator getDeviceConnectOperator(UsbMuxd usbMuxd) {
|
||||||
|
@ -307,15 +308,15 @@ public class IosScreenResponseThread extends Thread {
|
||||||
interrupt();
|
interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startRecordScreen(String tenantId, int width, int height, String recordType) {
|
public void startRecordScreen(String tenantId, int width, int height,String recordType, String currentTaskId){
|
||||||
this.recordingType = recordType;
|
this.recordingType = recordType;
|
||||||
if (null != screenRecordThread && screenRecordThread.isAlive()) {
|
if (null != screenRecordThread && screenRecordThread.isAlive()) {
|
||||||
screenRecordThread.killRecord();
|
screenRecordThread.killRecord();
|
||||||
}
|
}
|
||||||
SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
|
SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
|
||||||
String dateFormat = format.format(new Date());
|
String dateFormat = format.format(new Date());
|
||||||
String videoName = this.phone.getUdid() + "_" + dateFormat + ".mp4";
|
String videoName = this.phone.getUdid() + "_" + dateFormat + ".mp4";
|
||||||
screenRecordThread = new IosScreenRecordThread(this.phone.getUdid(), videoName, tenantId, width, height);
|
screenRecordThread = new IosScreenRecordThread(this.phone.getUdid(), videoName, tenantId,width, height, currentTaskId);
|
||||||
screenRecordThread.start();
|
screenRecordThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,12 +332,12 @@ public class IosScreenResponseThread extends Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动化结束录屏
|
//自动化结束录屏
|
||||||
public String stopRecord(String tenantId) {
|
public String stopRecord(String tenantId,boolean isSave) {
|
||||||
this.recordingType = "0";
|
this.recordingType = "0";
|
||||||
if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
|
if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String result = screenRecordThread.endRecord(tenantId);
|
String result = screenRecordThread.endRecord(tenantId,isSave);
|
||||||
JSONObject object = JSONObject.parseObject(result, JSONObject.class);
|
JSONObject object = JSONObject.parseObject(result, JSONObject.class);
|
||||||
screenRecordThread = null;
|
screenRecordThread = null;
|
||||||
String videoUrl = object.getString("videoUrl");
|
String videoUrl = object.getString("videoUrl");
|
||||||
|
|
|
@ -10,6 +10,7 @@ import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread
|
||||||
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
|
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
|
||||||
import net.northking.cctp.upperComputer.entity.Attachment;
|
import net.northking.cctp.upperComputer.entity.Attachment;
|
||||||
import net.northking.cctp.upperComputer.entity.PhoneEntity;
|
import net.northking.cctp.upperComputer.entity.PhoneEntity;
|
||||||
|
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
|
||||||
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
import net.northking.cctp.upperComputer.exception.ExecuteException;
|
||||||
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
|
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
|
||||||
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
import net.northking.cctp.upperComputer.utils.HttpUtils;
|
||||||
|
@ -709,7 +710,7 @@ public class IosWindowsAndLinuxMessageHandlerThread extends AbstractIosMessageHa
|
||||||
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
||||||
Attachment upload = null;
|
Attachment upload = null;
|
||||||
try {
|
try {
|
||||||
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
|
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId, null, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e);
|
logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ spring:
|
||||||
nk:
|
nk:
|
||||||
mobile-computer:
|
mobile-computer:
|
||||||
password: 123456
|
password: 123456
|
||||||
keepScreenOn: true #<EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>true<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>agentʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD>false<EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>Ļ
|
keepScreenOn: true #是否点亮屏幕,如果true,启动agent时不会关闭手机屏幕,false则会关闭手机屏幕
|
||||||
idPath: /home/attect/cctp-mobile
|
idPath: /home/attect/cctp-mobile
|
||||||
ctrlIp: 172.16.77.25
|
ctrlIp: 172.16.77.25
|
||||||
stfPath: /home/yc/soft/cctp-mobile
|
stfPath: /home/yc/soft/cctp-mobile
|
||||||
|
@ -36,26 +36,26 @@ nk:
|
||||||
http-request-path:
|
http-request-path:
|
||||||
useNewOcr: true
|
useNewOcr: true
|
||||||
ocrServer: http://192.168.0.33:5000
|
ocrServer: http://192.168.0.33:5000
|
||||||
ocrFindArea: ${nk.http-request-path.ocrServer}/WebAPI/FindTextCoordinateByOcr #ocr<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD>λ<EFBFBD><EFBFBD>
|
ocrFindArea: ${nk.http-request-path.ocrServer}/WebAPI/FindTextCoordinateByOcr #ocr查找文本位置
|
||||||
imgFindArea: ${nk.http-request-path.ocrServer}/WebAPI/findImageInImage #ͼƬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>
|
imgFindArea: ${nk.http-request-path.ocrServer}/WebAPI/findImageInImage #图片查找位置
|
||||||
ocrGetText: ${nk.http-request-path.ocrServer}/WebAPI/GetTextByOcr #ͼƬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>
|
ocrGetText: ${nk.http-request-path.ocrServer}/WebAPI/GetTextByOcr #图片查找位置
|
||||||
ocrGetVerificationCode: http://158.58.160.183:8000/yzm_v2 #<EFBFBD>°<EFBFBD>ocr<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD>
|
ocrGetVerificationCode: http://158.58.160.183:8000/yzm_v2 #新版ocr获取验证码
|
||||||
ocrGetAllTextNum: http://158.58.160.183:8000/rpa_v2 #<EFBFBD>°<EFBFBD>ocr<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>
|
ocrGetAllTextNum: http://158.58.160.183:8000/rpa_v2 #新版ocr获取金额
|
||||||
getSafeKeyBoardNum: http://158.58.160.183:8000/keyboard_v2 #<EFBFBD>°<EFBFBD>ocr<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ȫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
getSafeKeyBoardNum: http://158.58.160.183:8000/keyboard_v2 #新版ocr获取安全键盘
|
||||||
hzBankOcrAddress: http://197.68.24.38:9283/gateway/spring-ocrcloud-platform/OCRA0001 #<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>OCR<EFBFBD><EFBFBD><EFBFBD><EFBFBD>nginx<EFBFBD><EFBFBD>ַ
|
hzBankOcrAddress: http://197.68.24.38:9283/gateway/spring-ocrcloud-platform/OCRA0001 #杭州银行OCR请求nginx地址
|
||||||
macos:
|
macos:
|
||||||
build-wda:
|
build-wda:
|
||||||
build-wda: true #是否重新构建wda
|
build-wda: true #是否重新构建wda
|
||||||
xcode-username: mac01 #mac电脑的用户名,不能是root
|
xcode-username: mac01 #mac电脑的用户名,不能是root
|
||||||
key-chain-password: testtest #mac系统的密码
|
key-chain-password: testtest #mac系统的密码
|
||||||
wda-project-path: /Users/mac01/Downloads/WebDriverAgent #wda工程代码位置
|
wda-project-path: /Users/mac01/Downloads/WebDriverAgent #wda工程代码位置
|
||||||
xcode14-path: /Applications/Xcode14/Xcode.app #xcode14位置
|
xcode14-path: /Applications/Xcode14/Xcode.app #xcode14位置
|
||||||
xcode15-path: /Applications/Xcode.app #xcode15位置
|
xcode15-path: /Applications/Xcode.app #xcode15位置
|
||||||
wda-for-ios17-below-package-path: /Users/mac01/Downloads/wda14/ #打包后wda包的位置,为ios17以下使用
|
wda-for-ios17-below-package-path: /Users/mac01/Downloads/wda14/ #打包后wda包的位置,为ios17以下使用
|
||||||
wda-for-ios17-above-package-path: /Users/mac01/Downloads/wda15/ #打包后wda包的位置,为ios17及以上使用
|
wda-for-ios17-above-package-path: /Users/mac01/Downloads/wda15/ #打包后wda包的位置,为ios17及以上使用
|
||||||
wda-for-ios17-below-package-default-path: /Users/mac01/Downloads/wda14/Build/Products/WebDriverAgentRunner_iphoneos16.4-arm64.xctestrun #如果不打包wda,指定wda的路径,为ios17及以下使用
|
wda-for-ios17-below-package-default-path: /Users/mac01/Downloads/wda14/Build/Products/WebDriverAgentRunner_iphoneos16.4-arm64.xctestrun #如果不打包wda,指定wda的路径,为ios17及以下使用
|
||||||
wda-for-ios17-above-package-default-path: /Users/mac01/Downloads/wda15/Build/Products/WebDriverAgentRunner_iphoneos17.4-arm64.xctestrun #如果不打包wda,指定wda的路径,为ios17及以上使用
|
wda-for-ios17-above-package-default-path: /Users/mac01/Downloads/wda15/Build/Products/WebDriverAgentRunner_iphoneos17.4-arm64.xctestrun #如果不打包wda,指定wda的路径,为ios17及以上使用
|
||||||
print-wda-output: true #是否在日志中打印wda的输出
|
print-wda-output: true #是否在日志中打印wda的输出
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
net.northking: debug
|
net.northking: debug
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
*.log.gz
|
||||||
|
logs/
|
||||||
|
*/logs/
|
||||||
|
*/*/logs/
|
||||||
|
|
||||||
|
# Temp file
|
||||||
|
*.temp
|
||||||
|
temp
|
||||||
|
*/temp
|
||||||
|
|
||||||
|
# IDEA profile dir
|
||||||
|
.idea/
|
||||||
|
*/.idea/
|
||||||
|
*/*/.idea/
|
||||||
|
|
||||||
|
# IDEA project file
|
||||||
|
*.iml
|
||||||
|
*/*.iml
|
||||||
|
*/*/*.iml
|
||||||
|
*/*/*/*.iml
|
||||||
|
|
||||||
|
target/
|
||||||
|
*/target/
|
||||||
|
*/*/target/
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.northking.cctp.device.api.activity.service;
|
||||||
|
|
||||||
import net.northking.cctp.common.db.Pagination;
|
import net.northking.cctp.common.db.Pagination;
|
||||||
import net.northking.cctp.common.http.QueryByPage;
|
import net.northking.cctp.common.http.QueryByPage;
|
||||||
|
import net.northking.cctp.device.db.entity.CdDeviceToken;
|
||||||
import net.northking.cctp.device.db.entity.CdDeviceUsageLog;
|
import net.northking.cctp.device.db.entity.CdDeviceUsageLog;
|
||||||
import net.northking.cctp.device.dto.device.CdMobileDeviceExitDto;
|
import net.northking.cctp.device.dto.device.CdMobileDeviceExitDto;
|
||||||
import net.northking.cctp.device.dto.device.DeviceLockDto;
|
import net.northking.cctp.device.dto.device.DeviceLockDto;
|
||||||
|
@ -48,4 +49,17 @@ public interface DeviceActivityApiService {
|
||||||
String releaseMobDevice(CdMobileDeviceExitDto dto);
|
String releaseMobDevice(CdMobileDeviceExitDto dto);
|
||||||
|
|
||||||
Map<String,List<String>> queryDeviceType(List<String> deviceIds);
|
Map<String,List<String>> queryDeviceType(List<String> deviceIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放设备
|
||||||
|
* @param token 设备token
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
Boolean deviceRelease(String token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放引擎绑定的所有设备
|
||||||
|
* @param deviceTokens 设备token信息
|
||||||
|
*/
|
||||||
|
void release(List<CdDeviceToken> deviceTokens);
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,6 +633,39 @@ public class DeviceActivityApiServiceImpl implements DeviceActivityApiService {
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean deviceRelease(String token) {
|
||||||
|
CdDeviceToken tokenQuery = new CdDeviceToken();
|
||||||
|
tokenQuery.setToken(token);
|
||||||
|
List<CdDeviceToken> resultList = cdDeviceTokenService.query(tokenQuery);
|
||||||
|
release(resultList);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release(List<CdDeviceToken> deviceTokens){
|
||||||
|
if (deviceTokens != null && deviceTokens.size() > 0) {
|
||||||
|
for (CdDeviceToken deviceToken : deviceTokens) {
|
||||||
|
switch (deviceToken.getDeviceType()) {
|
||||||
|
case DeviceConstants.DEVICE_MOB_TYPE:
|
||||||
|
CdMobileDeviceExitDto mobileDto = new CdMobileDeviceExitDto();
|
||||||
|
mobileDto.setDeviceId(deviceToken.getId());
|
||||||
|
mobileDto.setToken(deviceToken.getToken());
|
||||||
|
this.releaseMobDevice(mobileDto);
|
||||||
|
break;
|
||||||
|
case DeviceConstants.DEVICE_PC_TYPE:
|
||||||
|
CdPcDeviceExitDto pcDto = new CdPcDeviceExitDto();
|
||||||
|
pcDto.setDeviceId(deviceToken.getId());
|
||||||
|
pcDto.setToken(deviceToken.getToken());
|
||||||
|
this.releasePcDevice(pcDto);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String releasePcDevice(CdPcDeviceExitDto dto) {
|
public String releasePcDevice(CdPcDeviceExitDto dto) {
|
||||||
Lock lock = redisLockRegistry.obtain(DeviceConstants.LOCK_PC + dto.getDeviceId());
|
Lock lock = redisLockRegistry.obtain(DeviceConstants.LOCK_PC + dto.getDeviceId());
|
||||||
|
@ -792,9 +825,22 @@ public class DeviceActivityApiServiceImpl implements DeviceActivityApiService {
|
||||||
log.setDeviceId(deviceId);
|
log.setDeviceId(deviceId);
|
||||||
log.setDeviceType(cdDeviceToken.getDeviceType());
|
log.setDeviceType(cdDeviceToken.getDeviceType());
|
||||||
List<CdDeviceUsageLog> query = this.cdDeviceUsageLogService.query(log);
|
List<CdDeviceUsageLog> query = this.cdDeviceUsageLogService.query(log);
|
||||||
if (query.size() > 0) {
|
if (query != null && query.size() > 0) {
|
||||||
query.get(0).setEndTime(new Date());
|
CdDeviceUsageLog cdDeviceUsageLog = query.get(0);
|
||||||
this.cdDeviceUsageLogService.updateByPrimaryKey(query.get(0));
|
Date now = new Date();
|
||||||
|
if (cdDeviceUsageLog.getStartTime() != null) {
|
||||||
|
Date oneSecondAfter = new Date(cdDeviceUsageLog.getStartTime().getTime() + 1000);
|
||||||
|
if (now.getTime() > oneSecondAfter.getTime()) {
|
||||||
|
cdDeviceUsageLog.setEndTime(now);
|
||||||
|
this.cdDeviceUsageLogService.updateByPrimaryKey(cdDeviceUsageLog);
|
||||||
|
} else {
|
||||||
|
// 不记录占用时间小于1秒的记录
|
||||||
|
this.cdDeviceUsageLogService.deleteByPrimaryKey(cdDeviceUsageLog.getId());
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
cdDeviceUsageLog.setEndTime(now);
|
||||||
|
this.cdDeviceUsageLogService.updateByPrimaryKey(cdDeviceUsageLog);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import net.northking.cctp.common.exception.PlatformRuntimeException;
|
||||||
import net.northking.cctp.common.http.QueryByPage;
|
import net.northking.cctp.common.http.QueryByPage;
|
||||||
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
||||||
import net.northking.cctp.common.util.UUIDUtil;
|
import net.northking.cctp.common.util.UUIDUtil;
|
||||||
|
import net.northking.cctp.device.api.activity.service.DeviceActivityApiService;
|
||||||
import net.northking.cctp.device.config.EngineConfig;
|
import net.northking.cctp.device.config.EngineConfig;
|
||||||
import net.northking.cctp.device.constants.DeviceConstants;
|
import net.northking.cctp.device.constants.DeviceConstants;
|
||||||
import net.northking.cctp.device.constants.DeviceError;
|
import net.northking.cctp.device.constants.DeviceError;
|
||||||
|
@ -27,6 +28,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -77,6 +79,12 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService<CdEngineInf
|
||||||
@Autowired
|
@Autowired
|
||||||
private EngineConfig engineConfig;
|
private EngineConfig engineConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeviceActivityApiService activityApiService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate redisTemplate;
|
||||||
|
|
||||||
//= Excel 导入导出相关代码 start ==================//
|
//= Excel 导入导出相关代码 start ==================//
|
||||||
@Override
|
@Override
|
||||||
public BasicService<CdEngineInfo> getService()
|
public BasicService<CdEngineInfo> getService()
|
||||||
|
@ -154,7 +162,14 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService<CdEngineInf
|
||||||
{
|
{
|
||||||
CdEngineInfo entity = new CdEngineInfo();
|
CdEngineInfo entity = new CdEngineInfo();
|
||||||
BeanUtils.copyProperties(dto, entity);
|
BeanUtils.copyProperties(dto, entity);
|
||||||
return this.cdEngineInfoService.updateByPrimaryKey(entity);
|
int rows = this.cdEngineInfoService.updateByPrimaryKey(entity);
|
||||||
|
if (rows > 0){
|
||||||
|
if (dto.getEnabled() != null && !dto.getEnabled()){
|
||||||
|
logger.debug("引擎禁用,删除引擎的线程缓存数据");
|
||||||
|
redisTemplate.opsForZSet().remove(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, dto.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -290,6 +305,15 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService<CdEngineInf
|
||||||
this.cdEngineInfoService.insert(dto.getCdEngineInfo());
|
this.cdEngineInfoService.insert(dto.getCdEngineInfo());
|
||||||
resultDto.setEngineId(engineId);
|
resultDto.setEngineId(engineId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.debug("释放引擎占用的设备");
|
||||||
|
List<CdDeviceToken> deviceTokenList = this.cdDeviceTokenService.queryByEngineIds(Collections.singletonList(resultDto.getEngineId()));
|
||||||
|
activityApiService.release(deviceTokenList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("释放引擎占用的设备异常", e);
|
||||||
|
}
|
||||||
|
|
||||||
return resultDto;
|
return resultDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,25 +124,26 @@ public class HeartMQReceiver {
|
||||||
engineInfo.setRunningRelease(dto.getEngineVersion());
|
engineInfo.setRunningRelease(dto.getEngineVersion());
|
||||||
this.cdEngineInfoService.updateByPrimaryKey(engineInfo);
|
this.cdEngineInfoService.updateByPrimaryKey(engineInfo);
|
||||||
|
|
||||||
// 更新缓存中引擎可执行线程数量
|
if (engineInfo.getEnabled()) {
|
||||||
Map<String, Object> executePoolInfo = dto.getExecutePoolInfo();
|
// 更新缓存中引擎可执行线程数量
|
||||||
if (executePoolInfo != null) {
|
Map<String, Object> executePoolInfo = dto.getExecutePoolInfo();
|
||||||
boolean enableMobile = executePoolInfo.get("enableMobile") != null
|
if (executePoolInfo != null) {
|
||||||
&& (boolean) executePoolInfo.get("enableMobile");
|
boolean enableMobile = executePoolInfo.get("enableMobile") != null
|
||||||
if (enableMobile) {
|
&& (boolean) executePoolInfo.get("enableMobile");
|
||||||
Object mobileThread = executePoolInfo.get("mobileThread");
|
if (enableMobile) {
|
||||||
Object mobileThreadActive = executePoolInfo.get("mobile_thread_active");
|
Object mobileThread = executePoolInfo.get("mobileThread");
|
||||||
if (mobileThread != null && mobileThreadActive != null){
|
Object mobileThreadActive = executePoolInfo.get("mobile_thread_active");
|
||||||
BigDecimal mobileThreadBd = new BigDecimal(String.valueOf(mobileThread));
|
if (mobileThread != null && mobileThreadActive != null) {
|
||||||
BigDecimal mobileThreadActiveBd = new BigDecimal(String.valueOf(mobileThreadActive));
|
BigDecimal mobileThreadBd = new BigDecimal(String.valueOf(mobileThread));
|
||||||
if (mobileThreadBd.compareTo(new BigDecimal("0")) > 0) {
|
BigDecimal mobileThreadActiveBd = new BigDecimal(String.valueOf(mobileThreadActive));
|
||||||
BigDecimal divide = mobileThreadActiveBd.divide(mobileThreadBd, 2, RoundingMode.HALF_UP)
|
if (mobileThreadBd.compareTo(new BigDecimal("0")) > 0) {
|
||||||
.multiply(new BigDecimal("100"));
|
BigDecimal divide = mobileThreadActiveBd.divide(mobileThreadBd, 2, RoundingMode.HALF_UP)
|
||||||
redisTemplate.opsForZSet().add(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, engineInfo.getId(),
|
.multiply(new BigDecimal("100"));
|
||||||
divide.doubleValue());
|
redisTemplate.opsForZSet().add(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, engineInfo.getId(),
|
||||||
|
divide.doubleValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ public interface CdDeviceTokenDao extends CdDeviceTokenMapper
|
||||||
{
|
{
|
||||||
|
|
||||||
List<CdDeviceToken> queryDeviceList(@Param("userId") String userId, @Param("ids") List<String> ids);
|
List<CdDeviceToken> queryDeviceList(@Param("userId") String userId, @Param("ids") List<String> ids);
|
||||||
// ---- The End by Generator ----//
|
|
||||||
|
List<CdDeviceToken> queryByEngineIds(@Param("ids") List<String> engineIds);
|
||||||
|
// ---- The End by Generator ----//
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,11 @@ private static final Logger logger = LoggerFactory.getLogger(CdDeviceTokenServic
|
||||||
return cdDeviceTokenDao.queryDeviceList(userId,ids);
|
return cdDeviceTokenDao.queryDeviceList(userId,ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CdDeviceToken> queryByEngineIds(List<String> engineIds) {
|
||||||
|
return cdDeviceTokenDao.queryByEngineIds(engineIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---- The End by Generator ----//
|
// ---- The End by Generator ----//
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ public interface CdDeviceTokenService extends BasicService<CdDeviceToken>
|
||||||
{
|
{
|
||||||
|
|
||||||
List<CdDeviceToken> queryDeviceList(String userId,List<String> ids);
|
List<CdDeviceToken> queryDeviceList(String userId,List<String> ids);
|
||||||
|
|
||||||
|
List<CdDeviceToken> queryByEngineIds(List<String> engineIds);
|
||||||
// ---- The End by Generator ----//
|
// ---- The End by Generator ----//
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,27 +110,8 @@ public class DevicePubCtrl {
|
||||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
public ResultWrapper<Boolean> deviceRelease(@PathVariable("token") String token) {
|
public ResultWrapper<Boolean> deviceRelease(@PathVariable("token") String token) {
|
||||||
ResultWrapper<Boolean> wrapper = new ResultWrapper<>();
|
ResultWrapper<Boolean> wrapper = new ResultWrapper<>();
|
||||||
CdDeviceToken tokenQuery = new CdDeviceToken();
|
Boolean result = this.apiService.deviceRelease(token);
|
||||||
tokenQuery.setToken(token);
|
return wrapper.success(result);
|
||||||
List<CdDeviceToken> resultList = deviceTokenService.query(tokenQuery);
|
|
||||||
if (resultList != null && resultList.size() > 0) {
|
|
||||||
CdDeviceToken device = resultList.get(0);
|
|
||||||
switch (device.getDeviceType()) {
|
|
||||||
case "1":
|
|
||||||
CdMobileDeviceExitDto mobileDto = new CdMobileDeviceExitDto();
|
|
||||||
mobileDto.setDeviceId(device.getId());
|
|
||||||
mobileDto.setToken(token);
|
|
||||||
apiService.releaseMobDevice(mobileDto);
|
|
||||||
break;
|
|
||||||
case "2":
|
|
||||||
CdPcDeviceExitDto pcDto = new CdPcDeviceExitDto();
|
|
||||||
pcDto.setDeviceId(device.getId());
|
|
||||||
pcDto.setToken(token);
|
|
||||||
apiService.releasePcDevice(pcDto);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wrapper.success(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package net.northking.cctp.device.schedule;
|
package net.northking.cctp.device.schedule;
|
||||||
|
|
||||||
import net.northking.cctp.common.http.ResultWrapper;
|
import net.northking.cctp.common.http.ResultWrapper;
|
||||||
|
import net.northking.cctp.device.api.activity.service.DeviceActivityApiService;
|
||||||
import net.northking.cctp.device.bus.feign.PlatformFeign;
|
import net.northking.cctp.device.bus.feign.PlatformFeign;
|
||||||
import net.northking.cctp.device.bus.feign.dto.UserInfoDto;
|
import net.northking.cctp.device.bus.feign.dto.UserInfoDto;
|
||||||
import net.northking.cctp.device.bus.publisher.DeviceStatusPublisher;
|
import net.northking.cctp.device.bus.publisher.DeviceStatusPublisher;
|
||||||
import net.northking.cctp.device.config.EmailConfig;
|
import net.northking.cctp.device.config.EmailConfig;
|
||||||
|
import net.northking.cctp.device.constants.DeviceConstants;
|
||||||
import net.northking.cctp.device.db.entity.*;
|
import net.northking.cctp.device.db.entity.*;
|
||||||
import net.northking.cctp.device.db.service.*;
|
import net.northking.cctp.device.db.service.*;
|
||||||
import net.northking.cctp.device.util.DeviceStatusUtil;
|
import net.northking.cctp.device.util.DeviceStatusUtil;
|
||||||
|
@ -27,10 +29,7 @@ import javax.mail.MessagingException;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ -90,6 +89,9 @@ public class DeviceSchedule {
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmailConfig emailConfig;
|
private EmailConfig emailConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeviceActivityApiService activityApiService;
|
||||||
|
|
||||||
@Scheduled(cron = "0 5 0 * * *")
|
@Scheduled(cron = "0 5 0 * * *")
|
||||||
public void recycleDevice() {
|
public void recycleDevice() {
|
||||||
Lock lock = lockRegistry.obtain("scheduler:DeviceSchedule.recycleDevice");
|
Lock lock = lockRegistry.obtain("scheduler:DeviceSchedule.recycleDevice");
|
||||||
|
@ -183,6 +185,15 @@ public class DeviceSchedule {
|
||||||
statusUtil.updatePcStatus(status);
|
statusUtil.updatePcStatus(status);
|
||||||
}
|
}
|
||||||
this.cdEngineInfoService.updateEngineStatus(ids);
|
this.cdEngineInfoService.updateEngineStatus(ids);
|
||||||
|
try {
|
||||||
|
logger.debug("释放引擎占用的设备");
|
||||||
|
List<CdDeviceToken> deviceTokenList = this.cdDeviceTokenService.queryByEngineIds(ids);
|
||||||
|
activityApiService.release(deviceTokenList);
|
||||||
|
logger.debug("删除缓存中引擎线程的使用率");
|
||||||
|
redisTemplate.opsForZSet().remove(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, ids.toArray());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("释放引擎占用的设备失败", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logger.info("---------------------------校验设备与引擎过期定时任务:结束-----------------------------------");
|
logger.info("---------------------------校验设备与引擎过期定时任务:结束-----------------------------------");
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,23 @@
|
||||||
FROM cd_device_token
|
FROM cd_device_token
|
||||||
WHERE
|
WHERE
|
||||||
user_id = #{userId, jdbcType=VARCHAR}
|
user_id = #{userId, jdbcType=VARCHAR}
|
||||||
AND id in
|
<if test="ids != null and ids.size > 0">
|
||||||
<foreach collection="ids" item="idItem" open="(" separator="," close=")">
|
AND id in
|
||||||
#{idItem, jdbcType=VARCHAR}
|
<foreach collection="ids" item="idItem" open="(" separator="," close=")">
|
||||||
</foreach>
|
#{idItem, jdbcType=VARCHAR}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="queryByEngineIds" resultMap="BaseResultMap">
|
||||||
|
SELECT
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
|
FROM cd_device_token
|
||||||
|
WHERE
|
||||||
|
user_id in
|
||||||
|
<foreach collection="ids" item="idItem" open="(" separator="," close=")">
|
||||||
|
#{idItem, jdbcType=VARCHAR}
|
||||||
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 请在本文件内添加自定义SQL语句 -->
|
<!-- 请在本文件内添加自定义SQL语句 -->
|
||||||
|
|
|
@ -27,7 +27,9 @@ import javax.websocket.CloseReason;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
@ -39,6 +41,10 @@ public class MobileConnectServiceImpl implements MobileConnectService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ThreadPoolExecutor executorService;
|
private ThreadPoolExecutor executorService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* web端到本服务的会话集合 deviceId-Session
|
||||||
|
*/
|
||||||
|
private ConcurrentMap<String,Session> sessionMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理每一个真机连接指令的任务
|
* 处理每一个真机连接指令的任务
|
||||||
|
@ -50,6 +56,11 @@ public class MobileConnectServiceImpl implements MobileConnectService {
|
||||||
*/
|
*/
|
||||||
private ConcurrentHashMap<String, ProcessMsgThread> mobileConnectionThreadMap = new ConcurrentHashMap<>();
|
private ConcurrentHashMap<String, ProcessMsgThread> mobileConnectionThreadMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存用户token的map
|
||||||
|
*/
|
||||||
|
private static Map<String, String> userTokenMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisTemplate redisTemplate;
|
private RedisTemplate redisTemplate;
|
||||||
|
@ -172,7 +183,7 @@ public class MobileConnectServiceImpl implements MobileConnectService {
|
||||||
logger.info("线程池还能支持手机连接.........................");
|
logger.info("线程池还能支持手机连接.........................");
|
||||||
Future future = mobileConnectionTaskMap.get(deviceId);
|
Future future = mobileConnectionTaskMap.get(deviceId);
|
||||||
if (future == null || future.isCancelled() || future.isDone()) {
|
if (future == null || future.isCancelled() || future.isDone()) {
|
||||||
ProcessMsgThread processMsgThread = new ProcessMsgThread(userToken, sessionId, deviceId, deviceToken, session,mode);
|
ProcessMsgThread processMsgThread = new ProcessMsgThread(userToken, session,sessionId, deviceId, deviceToken, mode);
|
||||||
future = executorService.submit(processMsgThread);
|
future = executorService.submit(processMsgThread);
|
||||||
mobileConnectionThreadMap.put(deviceId, processMsgThread);
|
mobileConnectionThreadMap.put(deviceId, processMsgThread);
|
||||||
mobileConnectionTaskMap.put(deviceId, future);
|
mobileConnectionTaskMap.put(deviceId, future);
|
||||||
|
@ -202,4 +213,19 @@ public class MobileConnectServiceImpl implements MobileConnectService {
|
||||||
logger.warn("出现没有sessionId的请求。。。。。。。。。。");
|
logger.warn("出现没有sessionId的请求。。。。。。。。。。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session getSession(String deviceId) {
|
||||||
|
return sessionMap.get(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUserToken(String sessionId, String token) {
|
||||||
|
userTokenMap.put(sessionId, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUserToken(String id) {
|
||||||
|
userTokenMap.remove(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,4 +24,13 @@ public interface MobileConnectService {
|
||||||
|
|
||||||
void releaseUsingDevice(String deviceId, String deviceToken);
|
void releaseUsingDevice(String deviceId, String deviceToken);
|
||||||
|
|
||||||
|
//拿到session
|
||||||
|
Session getSession(String deviceId);
|
||||||
|
|
||||||
|
//添加用户token
|
||||||
|
void addUserToken(String sessionId, String token);
|
||||||
|
|
||||||
|
//移除用户token
|
||||||
|
void removeUserToken(String id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
package net.northking.cctp.mobile.socket;
|
package net.northking.cctp.mobile.socket;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.hzbank.testteam.autotest.dependencies.authDependency.constants.AuthDependencyConstants;
|
||||||
|
import com.hzbank.testteam.autotest.dependencies.authDependency.dto.TokenValueDTO;
|
||||||
|
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.utils.SpringUtil;
|
||||||
import net.northking.cctp.common.exception.PlatformRuntimeException;
|
import net.northking.cctp.common.exception.PlatformRuntimeException;
|
||||||
import net.northking.cctp.common.security.authentication.SecurityUserCache;
|
|
||||||
import net.northking.cctp.mobile.constants.DeviceConstants;
|
import net.northking.cctp.mobile.constants.DeviceConstants;
|
||||||
import net.northking.cctp.mobile.constants.MobileConnectionConstants;
|
import net.northking.cctp.mobile.constants.MobileConnectionConstants;
|
||||||
import net.northking.cctp.mobile.db.service.MobileConnectService;
|
import net.northking.cctp.mobile.db.service.MobileConnectService;
|
||||||
import net.northking.cctp.mobile.entity.UpperWSResponse;
|
import net.northking.cctp.mobile.entity.UpperWSResponse;
|
||||||
import net.northking.cctp.mobile.util.SpringUtils;
|
import net.northking.cctp.mobile.util.SpringUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.redisson.api.RBucket;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -42,13 +46,6 @@ public class MobileSessionManager {
|
||||||
logger.info("Init web session manager ...");
|
logger.info("Init web session manager ...");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SecurityUserCache securityUserCache;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public void setSecurityUserCache(SecurityUserCache securityUserCache) {
|
|
||||||
MobileSessionManager.securityUserCache = securityUserCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 与界面建立Websocket会话
|
* 与界面建立Websocket会话
|
||||||
*
|
*
|
||||||
|
@ -62,6 +59,7 @@ public class MobileSessionManager {
|
||||||
String userToken = queryParams.get(DeviceConstants.AUTHORIZATION);
|
String userToken = queryParams.get(DeviceConstants.AUTHORIZATION);
|
||||||
String sessionId = queryParams.get(DeviceConstants.KEY_SESSION_ID);
|
String sessionId = queryParams.get(DeviceConstants.KEY_SESSION_ID);
|
||||||
String deviceToken = queryParams.get(DeviceConstants.KEY_DEVICE_TOKEN);
|
String deviceToken = queryParams.get(DeviceConstants.KEY_DEVICE_TOKEN);
|
||||||
|
String reportId = queryParams.get(DeviceConstants.KEY_REPORT_ID);
|
||||||
String mode = queryParams.get(DeviceConstants.KEY_MODE);
|
String mode = queryParams.get(DeviceConstants.KEY_MODE);
|
||||||
if (StringUtils.isNotBlank(mode)) {
|
if (StringUtils.isNotBlank(mode)) {
|
||||||
logger.debug("deviceToken:[{}]直播连接视频",deviceToken);
|
logger.debug("deviceToken:[{}]直播连接视频",deviceToken);
|
||||||
|
@ -120,8 +118,11 @@ public class MobileSessionManager {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean authorize(String sessionId, String token) {
|
private boolean authorize(String sessionId, String token) {
|
||||||
if (securityUserCache.hasToken(token)) {
|
RedissonClient redisson = SpringUtil.getBean(RedissonClient.class);
|
||||||
// SpringUtils.getBean(MobileConnectService.class).addUserToken(sessionId, token);
|
RBucket<TokenValueDTO> tokenValueDTORBucket = redisson.getBucket(AuthDependencyConstants.TOKEN_PREFIX + token);
|
||||||
|
TokenValueDTO tokenValueDTO = tokenValueDTORBucket.get();
|
||||||
|
if (tokenValueDTO != null ) {
|
||||||
|
SpringUtils.getBean(MobileConnectService.class).addUserToken(sessionId, token);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2,12 +2,8 @@ package net.northking.cctp.mobile.socket;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import net.northking.cctp.mobile.api.report.service.DeviceUseReportDetailApiService;
|
|
||||||
import net.northking.cctp.mobile.constants.MobileConnectionConstants;
|
import net.northking.cctp.mobile.constants.MobileConnectionConstants;
|
||||||
import net.northking.cctp.mobile.db.service.DeviceUseReportService;
|
|
||||||
import net.northking.cctp.mobile.dto.report.DeviceLogDto;
|
import net.northking.cctp.mobile.dto.report.DeviceLogDto;
|
||||||
import net.northking.cctp.mobile.dto.report.DeviceUseReportDetailAddDto;
|
|
||||||
import net.northking.cctp.mobile.dto.report.DeviceUseReportQueryDto;
|
|
||||||
import net.northking.cctp.mobile.entity.UpperWSResponse;
|
import net.northking.cctp.mobile.entity.UpperWSResponse;
|
||||||
import net.northking.cctp.mobile.entity.WSResponse;
|
import net.northking.cctp.mobile.entity.WSResponse;
|
||||||
import net.northking.cctp.mobile.util.JsonUtils;
|
import net.northking.cctp.mobile.util.JsonUtils;
|
||||||
|
@ -44,6 +40,7 @@ public class UpperWebSocketClient extends WebSocketClient {
|
||||||
|
|
||||||
private RemoteEndpoint.Basic basicRemote;
|
private RemoteEndpoint.Basic basicRemote;
|
||||||
|
|
||||||
|
|
||||||
public UpperWebSocketClient(URI uri, Session webSession, String deviceId, String sessionId) {
|
public UpperWebSocketClient(URI uri, Session webSession, String deviceId, String sessionId) {
|
||||||
super(uri);
|
super(uri);
|
||||||
this.webSession = webSession;
|
this.webSession = webSession;
|
||||||
|
@ -72,7 +69,7 @@ public class UpperWebSocketClient extends WebSocketClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(String msg) {
|
public void onMessage(String msg) {
|
||||||
// logger.debug("服务端接收到上位机信息,msg:{}", msg);
|
//logger.debug("服务端接收到上位机信息,msg:{}", msg);
|
||||||
try {
|
try {
|
||||||
UpperWSResponse msgResponse = JsonUtils.fromJsonString(msg, UpperWSResponse.class);
|
UpperWSResponse msgResponse = JsonUtils.fromJsonString(msg, UpperWSResponse.class);
|
||||||
String cmd = msgResponse.getCmd();
|
String cmd = msgResponse.getCmd();
|
||||||
|
@ -198,12 +195,9 @@ public class UpperWebSocketClient extends WebSocketClient {
|
||||||
*/
|
*/
|
||||||
private void handleUpperMsgForward(UpperWSResponse msgResponse) {
|
private void handleUpperMsgForward(UpperWSResponse msgResponse) {
|
||||||
WSResponse wsResponse = WSResponse.buildResponse(sessionId, msgResponse);
|
WSResponse wsResponse = WSResponse.buildResponse(sessionId, msgResponse);
|
||||||
WSUtils.sendText(basicRemote, JsonUtils.toJson(wsResponse), deviceId);
|
WSUtils.sendText(webSession, JsonUtils.toJson(wsResponse), deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 主动推送消息
|
|
||||||
*/
|
|
||||||
private void handleUpperMsgActive(UpperWSResponse msgResponse) {
|
private void handleUpperMsgActive(UpperWSResponse msgResponse) {
|
||||||
//根据返回结果决定返回内容
|
//根据返回结果决定返回内容
|
||||||
WSResponse wsResponse = WSResponse.buildResponse(msgResponse.getCmd(), sessionId, msgResponse.getData());
|
WSResponse wsResponse = WSResponse.buildResponse(msgResponse.getCmd(), sessionId, msgResponse.getData());
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package net.northking.cctp.mobile.thread;
|
package net.northking.cctp.mobile.thread;
|
||||||
|
|
||||||
|
import net.northking.cctp.common.http.ResultWrapper;
|
||||||
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
||||||
import net.northking.cctp.mobile.constants.MobileConnectionConstants;
|
import net.northking.cctp.mobile.constants.MobileConnectionConstants;
|
||||||
import net.northking.cctp.mobile.db.service.MobileConnectService;
|
import net.northking.cctp.mobile.db.service.MobileConnectService;
|
||||||
|
@ -16,8 +17,11 @@ import net.northking.cctp.mobile.util.JsonUtils;
|
||||||
import net.northking.cctp.mobile.util.SpringUtils;
|
import net.northking.cctp.mobile.util.SpringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.*;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
@ -134,7 +138,7 @@ public class ProcessMsgThread extends Thread{
|
||||||
|
|
||||||
private void exitMobile(){
|
private void exitMobile(){
|
||||||
if (StringUtils.hasText(deviceToken)) {
|
if (StringUtils.hasText(deviceToken)) {
|
||||||
if (StringUtils.isNotBlank(mode)) {
|
if (StringUtils.hasText(mode)) {
|
||||||
logger.info("设备【{}】是直播退出,不释放手机.......",deviceId);
|
logger.info("设备【{}】是直播退出,不释放手机.......",deviceId);
|
||||||
} else {
|
} else {
|
||||||
SpringUtils.getBean(MobileConnectService.class).releaseUsingDevice(deviceId, deviceToken);
|
SpringUtils.getBean(MobileConnectService.class).releaseUsingDevice(deviceId, deviceToken);
|
||||||
|
@ -152,11 +156,10 @@ public class ProcessMsgThread extends Thread{
|
||||||
logger.info("前端设备连接信息添加进队列");
|
logger.info("前端设备连接信息添加进队列");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void exit() {
|
|
||||||
this.alive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMsg2Web(String deviceId, String msg){
|
private void sendMsg2Web(String deviceId, String msg){
|
||||||
|
|
||||||
if(null == webSession || !webSession.isOpen()){
|
if(null == webSession || !webSession.isOpen()){
|
||||||
logger.warn("前端Session[deviceId:{}]已关闭",deviceId);
|
logger.warn("前端Session[deviceId:{}]已关闭",deviceId);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -32,4 +32,27 @@ public class WSUtils {
|
||||||
logger.warn("web端session已经断开",e);
|
logger.warn("web端session已经断开",e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sendText(Session session, String msg, String deviceId) {
|
||||||
|
synchronized (session) {
|
||||||
|
if (null != session && session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText(msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(String.format("设备【%s】发送屏幕数据到前端失败", deviceId), e);
|
||||||
|
//增加重发机制
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("Session[deviceId:{}]不存在或已关闭...", deviceId);
|
||||||
|
//关闭上位机会话
|
||||||
|
try {
|
||||||
|
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "session失效关闭"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn("session关闭失败", e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
*.log.gz
|
||||||
|
logs/
|
||||||
|
*/logs/
|
||||||
|
*/*/logs/
|
||||||
|
|
||||||
|
# Temp file
|
||||||
|
*.temp
|
||||||
|
temp
|
||||||
|
*/temp
|
||||||
|
|
||||||
|
# IDEA profile dir
|
||||||
|
.idea/
|
||||||
|
*/.idea/
|
||||||
|
*/*/.idea/
|
||||||
|
|
||||||
|
# IDEA project file
|
||||||
|
*.iml
|
||||||
|
*/*.iml
|
||||||
|
*/*/*.iml
|
||||||
|
*/*/*/*.iml
|
||||||
|
|
||||||
|
target/
|
||||||
|
*/target/
|
||||||
|
*/*/target/
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>cctp-test-element-core</artifactId>
|
<artifactId>cctp-test-element-core</artifactId>
|
||||||
<version>1.0.1-RELEASE</version>
|
<version>1.0.2-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -6,5 +6,7 @@ public enum InputType {
|
||||||
//单选
|
//单选
|
||||||
select,
|
select,
|
||||||
//多选
|
//多选
|
||||||
selects;
|
selects,
|
||||||
|
|
||||||
|
element;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>NK.Desktop-1.75</finalName>
|
<finalName>NK.Desktop-1.80</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class DesktopLibrary extends AbstractLibrary {
|
||||||
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(DesktopLibrary.class);
|
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(DesktopLibrary.class);
|
||||||
private static final String NAME = "NK.Desktop";
|
private static final String NAME = "NK.Desktop";
|
||||||
private static final String CH_NAME = "PC端操作组件库";
|
private static final String CH_NAME = "PC端操作组件库";
|
||||||
private static final String VERSION = "1.79";
|
private static final String VERSION = "1.80";
|
||||||
private static final Integer TYPE = LibraryConstant.LIB_TYPE_PCBS;
|
private static final Integer TYPE = LibraryConstant.LIB_TYPE_PCBS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -328,7 +328,7 @@ public class DesktopTools {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 断言元素是否存在
|
* 断言元素存在
|
||||||
* @param id 步骤id
|
* @param id 步骤id
|
||||||
* @param title 步骤主标题
|
* @param title 步骤主标题
|
||||||
* @param targets 元素识别方法
|
* @param targets 元素识别方法
|
||||||
|
@ -336,12 +336,11 @@ public class DesktopTools {
|
||||||
* @param delayBefore 执行前等待
|
* @param delayBefore 执行前等待
|
||||||
* @param delayAfter 执行后等待
|
* @param delayAfter 执行后等待
|
||||||
* @param waitForReady 就绪等待
|
* @param waitForReady 就绪等待
|
||||||
* @param value 组件返回值
|
|
||||||
* @param deviceDriver 与PC机器连接的webSocket客户端
|
* @param deviceDriver 与PC机器连接的webSocket客户端
|
||||||
* @param executeContent 执行上下文
|
* @param executeContent 执行上下文
|
||||||
* @return 执行结果
|
* @return 执行结果
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "断言元素是否存在", attributes = "23")
|
@Keyword(alias = "断言元素存在", attributes = "23")
|
||||||
@Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
|
||||||
public Boolean assertElement(
|
public Boolean assertElement(
|
||||||
@Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
|
@Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
|
||||||
|
@ -351,7 +350,7 @@ public class DesktopTools {
|
||||||
@Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
|
@Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
|
||||||
@Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
|
@Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
|
||||||
@Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
|
@Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
|
||||||
@Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
|
@Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "", required = false) String value,
|
||||||
@Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
|
@Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
|
||||||
@Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
|
||||||
IExecuteContext executeContent
|
IExecuteContext executeContent
|
||||||
|
@ -696,7 +695,7 @@ public class DesktopTools {
|
||||||
@Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
|
||||||
IExecuteContext executeContent
|
IExecuteContext executeContent
|
||||||
){
|
){
|
||||||
if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets) || StrUtil.isBlank(value)){
|
if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
|
||||||
throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
|
throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
|
||||||
}
|
}
|
||||||
if (deviceDriver == null){
|
if (deviceDriver == null){
|
||||||
|
@ -725,7 +724,7 @@ public class DesktopTools {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 断言是否包含文本
|
* 断言包含文本
|
||||||
* @param id 步骤id
|
* @param id 步骤id
|
||||||
* @param title 步骤主标题
|
* @param title 步骤主标题
|
||||||
* @param targets 元素识别方法
|
* @param targets 元素识别方法
|
||||||
|
@ -733,12 +732,11 @@ public class DesktopTools {
|
||||||
* @param delayBefore 执行前等待
|
* @param delayBefore 执行前等待
|
||||||
* @param delayAfter 执行后等待
|
* @param delayAfter 执行后等待
|
||||||
* @param waitForReady 就绪等待
|
* @param waitForReady 就绪等待
|
||||||
* @param value 组件返回值
|
|
||||||
* @param deviceDriver 与PC机器连接的webSocket客户端
|
* @param deviceDriver 与PC机器连接的webSocket客户端
|
||||||
* @param executeContent 执行上下文
|
* @param executeContent 执行上下文
|
||||||
* @return 执行结果
|
* @return 执行结果
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "断言是否包含文本", attributes = "23")
|
@Keyword(alias = "断言包含文本", attributes = "23")
|
||||||
@Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
|
||||||
public Boolean assertText(
|
public Boolean assertText(
|
||||||
@Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
|
@Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
|
||||||
|
@ -749,7 +747,7 @@ public class DesktopTools {
|
||||||
@Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
|
@Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
|
||||||
@Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
|
@Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
|
||||||
@Argument(name = "containText", comment = "断言的文本", type = DataType.STRING,required = false) String containText,
|
@Argument(name = "containText", comment = "断言的文本", type = DataType.STRING,required = false) String containText,
|
||||||
@Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
|
@Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "", required = false) String value,
|
||||||
@Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
|
@Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
|
||||||
@Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
|
||||||
IExecuteContext executeContent
|
IExecuteContext executeContent
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
### 版本 1.80
|
||||||
|
更新于2024-07-06
|
||||||
|
1.修改组件名称为断言包含文本,断言存在元素
|
||||||
|
2.将断言的返回值返回值变为非必填项
|
||||||
|
|
||||||
### 版本 1.78
|
### 版本 1.78
|
||||||
更新于2024-06-19
|
更新于2024-06-19
|
||||||
1.添加杭银OCR识别方式
|
1.添加杭银OCR识别方式
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class MobileIosLibrary extends AbstractLibrary {
|
||||||
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(MobileIosLibrary.class);
|
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(MobileIosLibrary.class);
|
||||||
private static final String NAME = "NK.MobileIos";
|
private static final String NAME = "NK.MobileIos";
|
||||||
private static final String CH_NAME = "移动端ios通用组件库";
|
private static final String CH_NAME = "移动端ios通用组件库";
|
||||||
private static final String VERSION = "1.0.12";
|
private static final String VERSION = "1.0.15";
|
||||||
private static final Integer TYPE = LibraryConstant.LIB_TYPE_NOT;
|
private static final Integer TYPE = LibraryConstant.LIB_TYPE_NOT;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -83,4 +83,9 @@ public interface UpperParamKey {
|
||||||
*/
|
*/
|
||||||
String USING_TYPE = "using_type";
|
String USING_TYPE = "using_type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户id
|
||||||
|
*/
|
||||||
|
String TENANT_ID = "tenant_id";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ import net.northking.cctp.element.core.annotation.Keyword;
|
||||||
import net.northking.cctp.element.core.annotation.Keywords;
|
import net.northking.cctp.element.core.annotation.Keywords;
|
||||||
import net.northking.cctp.element.core.annotation.Return;
|
import net.northking.cctp.element.core.annotation.Return;
|
||||||
import net.northking.cctp.element.core.exception.AssertException;
|
import net.northking.cctp.element.core.exception.AssertException;
|
||||||
|
import net.northking.cctp.element.core.exception.EnvironmentException;
|
||||||
import net.northking.cctp.element.core.exception.ExecuteException;
|
import net.northking.cctp.element.core.exception.ExecuteException;
|
||||||
|
import net.northking.cctp.element.core.exception.ParamMistakeException;
|
||||||
import net.northking.cctp.element.core.type.DataType;
|
import net.northking.cctp.element.core.type.DataType;
|
||||||
import net.northking.cctp.element.core.type.InputType;
|
import net.northking.cctp.element.core.type.InputType;
|
||||||
import net.northking.cctp.element.core.type.ParamScope;
|
import net.northking.cctp.element.core.type.ParamScope;
|
||||||
|
@ -23,7 +25,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ios新版的组件
|
* ios新版的组件
|
||||||
|
@ -46,7 +48,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击控件(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "点击控件(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "点击结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "点击结果", type = DataType.BOOLEAN)
|
||||||
public boolean clickElement(IExecuteContext context,
|
public boolean clickElement(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -97,7 +99,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "是否存在元素(ios新)", category = "0", attributes = "9")
|
@Keyword(alias = "是否存在元素(ios新)", category = "0", attributes = "5")
|
||||||
@Return(name = "result", comment = "是否存在元素的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "是否存在元素的结果", type = DataType.BOOLEAN)
|
||||||
public boolean ifElement(IExecuteContext context,
|
public boolean ifElement(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -143,7 +145,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "标准滑动(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "标准滑动(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "滑动结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "滑动结果", type = DataType.BOOLEAN)
|
||||||
public boolean standardSwipe(
|
public boolean standardSwipe(
|
||||||
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, inputType = InputType.select, defaultValue = "up") String direction,
|
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, inputType = InputType.select, defaultValue = "up") String direction,
|
||||||
|
@ -187,7 +189,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "控件滑动(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "控件滑动(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN)
|
||||||
public boolean elementSwipe(IExecuteContext context,
|
public boolean elementSwipe(IExecuteContext context,
|
||||||
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "控件滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, defaultDisplay = false, inputType = InputType.select, defaultValue = "up") String direction,
|
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "控件滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, defaultDisplay = false, inputType = InputType.select, defaultValue = "up") String direction,
|
||||||
|
@ -240,7 +242,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "控件输入文本(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "控件输入文本(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
||||||
public boolean inputText(IExecuteContext context,
|
public boolean inputText(IExecuteContext context,
|
||||||
@Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text,
|
@Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text,
|
||||||
|
@ -294,7 +296,7 @@ public class IOSNewTools {
|
||||||
* @param value 输出值
|
* @param value 输出值
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "获取控件的值(ios新)", category = "0", attributes = "9")
|
@Keyword(alias = "获取控件的值(ios新)", category = "0", attributes = "5")
|
||||||
@Return(name = "result", comment = "获取控件值的结果", type = DataType.OBJECT)
|
@Return(name = "result", comment = "获取控件值的结果", type = DataType.OBJECT)
|
||||||
public String getElementText(IExecuteContext context,
|
public String getElementText(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -343,7 +345,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "长按控件(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "长按控件(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "长按结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "长按结果", type = DataType.BOOLEAN)
|
||||||
public boolean longPress(IExecuteContext context,
|
public boolean longPress(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -395,7 +397,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "断言元素(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "断言元素(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "断言结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "断言结果", type = DataType.BOOLEAN)
|
||||||
public boolean assertElement(IExecuteContext context,
|
public boolean assertElement(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -445,7 +447,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "识别验证码(ios新)", category = "0", attributes = "9")
|
@Keyword(alias = "识别验证码(ios新)", category = "0", attributes = "5")
|
||||||
@Return(name = "result", comment = "验证码识别", type = DataType.STRING)
|
@Return(name = "result", comment = "验证码识别", type = DataType.STRING)
|
||||||
public String getCodeByOcr(
|
public String getCodeByOcr(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -493,7 +495,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击文本(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "点击文本(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
||||||
public Boolean clickText(
|
public Boolean clickText(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -538,7 +540,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击文本(新)(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "点击文本(新)(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
||||||
public Boolean clickTextNew(
|
public Boolean clickTextNew(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -583,7 +585,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return 返回判断的结果
|
* @return 返回判断的结果
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "是否存在文本(ios新)", category = "0", attributes = "9")
|
@Keyword(alias = "是否存在文本(ios新)", category = "0", attributes = "5")
|
||||||
@Return(name = "result", comment = "文本存在与不存在的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "文本存在与不存在的结果", type = DataType.BOOLEAN)
|
||||||
public Boolean ifText(
|
public Boolean ifText(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -629,7 +631,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "app处理(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "app处理(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "app处理", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "app处理", type = DataType.BOOLEAN)
|
||||||
public boolean handleApp(IExecuteContext context,
|
public boolean handleApp(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -666,7 +668,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击home键(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "点击home键(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "点击home键", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "点击home键", type = DataType.BOOLEAN)
|
||||||
public boolean pressKeyHome(IExecuteContext context,
|
public boolean pressKeyHome(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -696,7 +698,7 @@ public class IOSNewTools {
|
||||||
* @param waitTime 等待时间(单位s)
|
* @param waitTime 等待时间(单位s)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "等待(ios新)", category = "0", attributes = "9")
|
@Keyword(alias = "等待(ios新)", category = "0", attributes = "5")
|
||||||
@Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN)
|
||||||
public boolean wait(
|
public boolean wait(
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -766,7 +768,7 @@ public class IOSNewTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "密码输入(ocr)(ios新)", category = "0", attributes = "9", commonlyUse = true)
|
@Keyword(alias = "密码输入(ocr)(ios新)", category = "0", attributes = "5", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "输入结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入结果", type = DataType.BOOLEAN)
|
||||||
public boolean inputPasswordByOcr(IExecuteContext context,
|
public boolean inputPasswordByOcr(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -812,7 +814,7 @@ public class IOSNewTools {
|
||||||
* @param value 输出值
|
* @param value 输出值
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "识别金额(ios新)", category = "0", attributes = "9")
|
@Keyword(alias = "识别金额(ios新)", category = "0", attributes = "5")
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
||||||
public String getElementMoneyText(IExecuteContext context,
|
public String getElementMoneyText(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -848,4 +850,91 @@ public class IOSNewTools {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>屏幕截图(ios新)</p>
|
||||||
|
*
|
||||||
|
* @param deviceDriver 设备连接驱动
|
||||||
|
* @param waitTimeout 默认超时时间
|
||||||
|
* @param preExecuteWait 执行前等待
|
||||||
|
* @param sufExecuteWait 执行后等待
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Keyword(alias = "屏幕截图(ios新)", category = "0", attributes = "5")
|
||||||
|
@Return(name = "result", comment = "是否截图成功", type = DataType.BOOLEAN)
|
||||||
|
public Boolean screenShot(
|
||||||
|
IExecuteContext context,
|
||||||
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||||
|
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float preExecuteWait,
|
||||||
|
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float sufExecuteWait
|
||||||
|
|
||||||
|
) {
|
||||||
|
CommonUtils.handlePreExecuteWait(preExecuteWait); //执行前等待
|
||||||
|
ElementHandleParam param = ElementHandleParam.builder(deviceDriver)
|
||||||
|
.useContext(context)
|
||||||
|
.waitTimeout(waitTimeout);
|
||||||
|
Boolean result = false;
|
||||||
|
try {
|
||||||
|
result = AutomationHandleUtil.screenShot(param);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
logger.warn("用户取消查询元素");
|
||||||
|
throw new ExecuteException("取消操作");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("出现了其他的问题。。。。", e);
|
||||||
|
throw new ExecuteException(e.getMessage());
|
||||||
|
}
|
||||||
|
CommonUtils.handleSufExecuteWait(sufExecuteWait); //执行后等待
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取控件的值(ios新)(OCR方式)</p>
|
||||||
|
*
|
||||||
|
* @param deviceDriver 设备连接驱动
|
||||||
|
* @param targets 定位控件参数
|
||||||
|
* @param waitTimeout 超时时间
|
||||||
|
* @param waitStatusStr 是否等待屏幕稳定
|
||||||
|
* @param swipe 是否滑屏查找控件0-否,up-上滑,down-下滑,left-左滑,right-右滑
|
||||||
|
* @param swipeCount 滑屏次数
|
||||||
|
* @param preExecuteWait 执行前等待
|
||||||
|
* @param sufExecuteWait 执行后等待
|
||||||
|
* @param value 输出值
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Keyword(alias = "获取控件的值(ios新)(OCR方式)", category = "0", attributes = "5")
|
||||||
|
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
||||||
|
public String getElementValueByOcr(IExecuteContext context,
|
||||||
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@Argument(name = "targets", scope = ParamScope.STEP, comment = "控件位置", type = DataType.LIST, defaultDisplay = false) List<IStepTarget> targets,
|
||||||
|
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||||
|
@Argument(name = "waitStatus", scope = ParamScope.ARGS, comment = "等待界面稳定", type = DataType.ENUM, enumValues = WhetherOrNotEnum.class, inputType = InputType.select, defaultValue = "1", defaultDisplay = false) String waitStatusStr,
|
||||||
|
@Argument(name = "swipe", scope = ParamScope.ARGS, comment = "滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, inputType = InputType.select, defaultValue = "0", required = false, defaultDisplay = false) String swipe,
|
||||||
|
@Argument(name = "swipeCount", scope = ParamScope.ARGS, comment = "滑动次数", type = DataType.INTEGER, required = false, defaultValue = "0", defaultDisplay = false) Integer swipeCount,
|
||||||
|
@Argument(name = "value", scope = ParamScope.OUTPUT, comment = "组件返回值", type = DataType.STRING, defaultValue = "") String value,
|
||||||
|
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float preExecuteWait,
|
||||||
|
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float sufExecuteWait) {
|
||||||
|
CommonUtils.handlePreExecuteWait(preExecuteWait); //执行前等待
|
||||||
|
boolean waitStatus = "1".equals(waitStatusStr); //是否等待界面稳定
|
||||||
|
ElementHandleParam param = ElementHandleParam.builder(deviceDriver)
|
||||||
|
.useContext(context)
|
||||||
|
.withTargets(targets)
|
||||||
|
.waitTimeout(waitTimeout)
|
||||||
|
.searchSwipeDirection("0") //不滑动
|
||||||
|
.withSwipeCount(1)
|
||||||
|
.waitStatus(waitStatus);
|
||||||
|
String result = "";
|
||||||
|
try {
|
||||||
|
result = AutomationHandleUtil.getElementValueByOcr(param);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
logger.warn("用户取消查询元素");
|
||||||
|
throw new ExecuteException("取消操作");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("出现了其他的问题。。。。", e);
|
||||||
|
throw new ExecuteException(e.getMessage());
|
||||||
|
}
|
||||||
|
CommonUtils.handleSufExecuteWait(sufExecuteWait); //执行后等待
|
||||||
|
CommonUtils.setParamValue(context, value, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -991,10 +991,6 @@ public class AutomationHandleUtil {
|
||||||
logger.warn("不支持的获取元素值方式:{}", target.getUsing());
|
logger.warn("不支持的获取元素值方式:{}", target.getUsing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isBlank(result)) {
|
|
||||||
throw new ExecuteException("无法获取当前元素的文本");
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,4 +1030,54 @@ public class AutomationHandleUtil {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Boolean screenShot(ElementHandleParam param) throws Exception {
|
||||||
|
String screenShotUrl = "";
|
||||||
|
String token = UUID.randomUUID().toString();
|
||||||
|
String tenantId = (String) param.getContext().getContextVariable("__tenantId");
|
||||||
|
Map<String, Object> paramMap = new HashMap<>();
|
||||||
|
paramMap.put(UpperParamKey.TENANT_ID, tenantId);
|
||||||
|
logger.info("参数:{}", JSON.toJSONString(paramMap));
|
||||||
|
logger.debug("tenantId:{}",tenantId);
|
||||||
|
CmdAutomationRequest builder = CmdAutomationRequest.builder(AutomationRequestCmd.SCREEN_SHOT, token, paramMap);
|
||||||
|
Object o = sendUpperMessageAndWaitReturn(param.getDeviceDriver(), builder, token, param.getWaitTimeout());
|
||||||
|
logger.debug("屏幕截图的结果:{}",o);
|
||||||
|
if (null != o) {
|
||||||
|
screenShotUrl = (String) o;
|
||||||
|
param.getContext().setVariable("mobileStepSnapshot",screenShotUrl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getElementValueByOcr(ElementHandleParam param) throws Exception{
|
||||||
|
String result = "";
|
||||||
|
List<IStepTarget> targets = param.getTargets();
|
||||||
|
for (IStepTarget target : targets) {
|
||||||
|
result = getElementValueByOcr(param.getDeviceDriver(), target, param.getWaitTimeout(), param.getSwipe(), param.getSwipeCount());
|
||||||
|
if (StringUtils.isNotBlank(result)) {
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getElementValueByOcr(DeviceDriver deviceDriver, IStepTarget target, Integer waitTimeout, String swipe, Integer swipeCount) throws Exception{
|
||||||
|
String result = "";
|
||||||
|
String token = UUID.randomUUID().toString();
|
||||||
|
Map<String, Object> paramMap = new HashMap<>();
|
||||||
|
JSONObject object = JSON.parseObject(target.getValue(), JSONObject.class);
|
||||||
|
String xpath = object.getString(UsingType.Key.SELECTOR_KEY);
|
||||||
|
logger.info("拿到的xpath:{}", xpath);
|
||||||
|
paramMap.put(UpperParamKey.NODE_TREE, xpath);
|
||||||
|
paramMap.put(UpperParamKey.IS_SWIPE, swipe);
|
||||||
|
paramMap.put(UpperParamKey.SWIPE_COUNT, swipeCount);
|
||||||
|
CmdAutomationRequest builder = CmdAutomationRequest.builder(AutomationRequestCmd.GET_ELEMENT_VALUE_BY_PATH_OCR, token, paramMap);
|
||||||
|
Object o = sendUpperMessageAndWaitReturn(deviceDriver, builder, token, waitTimeout);
|
||||||
|
logger.debug("根据nodeTree查找,ocr识别的结果:{}", o);
|
||||||
|
if (null != o) {
|
||||||
|
result = (String) o;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
### 版本:1.0.15
|
||||||
|
更新于2024-07-08
|
||||||
|
|
||||||
|
1.增加屏幕截图组件
|
||||||
|
|
||||||
### 版本:1.0.8
|
### 版本:1.0.8
|
||||||
更新于2024-06-11
|
更新于2024-06-11
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class MobileLibrary extends AbstractLibrary {
|
||||||
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(MobileLibrary.class);
|
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(MobileLibrary.class);
|
||||||
private static final String NAME = "NK.mobile";
|
private static final String NAME = "NK.mobile";
|
||||||
private static final String CH_NAME = "移动端通用组件库";
|
private static final String CH_NAME = "移动端通用组件库";
|
||||||
private static final String VERSION = "2.1.44";
|
private static final String VERSION = "2.1.47";
|
||||||
private static final Integer TYPE = LibraryConstant.LIB_TYPE_NOT;
|
private static final Integer TYPE = LibraryConstant.LIB_TYPE_NOT;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package net.northking.cctp.element.mobile.entity;
|
||||||
|
|
||||||
|
import net.northking.cctp.element.core.IStepTarget;
|
||||||
|
|
||||||
|
public class StepTarget implements IStepTarget {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 元素查找方式
|
||||||
|
*/
|
||||||
|
private String using;
|
||||||
|
/**
|
||||||
|
* 定位表达式
|
||||||
|
*/
|
||||||
|
private String value;
|
||||||
|
/**
|
||||||
|
* 备选Or必选
|
||||||
|
*/
|
||||||
|
private String strategy;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsing() {
|
||||||
|
return using;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsing(String using) {
|
||||||
|
this.using = using;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStrategy() {
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStrategy(String strategy) {
|
||||||
|
this.strategy = strategy;
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ public class AndroidTools {
|
||||||
* @param deviceDriver 设备连接驱动
|
* @param deviceDriver 设备连接驱动
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击返回健", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "点击返回健", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "点击返回健", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "点击返回健", type = DataType.BOOLEAN)
|
||||||
public boolean pressKeyBack(@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver) {
|
public boolean pressKeyBack(@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver) {
|
||||||
PressKeyBackThread pressKeyBackThread = new PressKeyBackThread(deviceDriver);
|
PressKeyBackThread pressKeyBackThread = new PressKeyBackThread(deviceDriver);
|
||||||
|
@ -87,7 +87,7 @@ public class AndroidTools {
|
||||||
* @param deviceDriver 设备连接驱动
|
* @param deviceDriver 设备连接驱动
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击多任务键", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "点击多任务键", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "点击多任务键", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "点击多任务键", type = DataType.BOOLEAN)
|
||||||
public boolean pressKeyMenu(@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver) {
|
public boolean pressKeyMenu(@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver) {
|
||||||
PressKeyMenuThread pressKeyMenuThread = new PressKeyMenuThread(deviceDriver);
|
PressKeyMenuThread pressKeyMenuThread = new PressKeyMenuThread(deviceDriver);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.util.ReUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.appium.java_client.android.nativekey.AndroidKey;
|
||||||
import net.northking.cctp.element.core.DeviceDriver;
|
import net.northking.cctp.element.core.DeviceDriver;
|
||||||
import net.northking.cctp.element.core.IExecuteContext;
|
import net.northking.cctp.element.core.IExecuteContext;
|
||||||
import net.northking.cctp.element.core.IStepTarget;
|
import net.northking.cctp.element.core.IStepTarget;
|
||||||
|
@ -55,7 +56,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击文本", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "点击文本", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
||||||
public Boolean clickText(
|
public Boolean clickText(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -119,7 +120,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击文本(新)", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "点击文本(新)", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN)
|
||||||
public Boolean clickTextNew(
|
public Boolean clickTextNew(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -184,7 +185,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return 返回判断的结果
|
* @return 返回判断的结果
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "是否存在文本", category = "0", attributes = "45")
|
@Keyword(alias = "是否存在文本", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "文本存在与不存在的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "文本存在与不存在的结果", type = DataType.BOOLEAN)
|
||||||
public Boolean ifText(
|
public Boolean ifText(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -251,7 +252,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击控件", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "点击控件", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "点击结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "点击结果", type = DataType.BOOLEAN)
|
||||||
public boolean clickElement(IExecuteContext context,
|
public boolean clickElement(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -321,7 +322,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "是否存在元素", category = "0", attributes = "45")
|
@Keyword(alias = "是否存在元素", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "是否存在元素的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "是否存在元素的结果", type = DataType.BOOLEAN)
|
||||||
public boolean ifElement(IExecuteContext context,
|
public boolean ifElement(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -386,7 +387,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "标准滑动", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "标准滑动", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "滑动结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "滑动结果", type = DataType.BOOLEAN)
|
||||||
public boolean standardSwipe(
|
public boolean standardSwipe(
|
||||||
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, inputType = InputType.select, defaultValue = "up") String direction,
|
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, inputType = InputType.select, defaultValue = "up") String direction,
|
||||||
|
@ -451,7 +452,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "控件滑动", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "控件滑动", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN)
|
||||||
public boolean elementSwipe(IExecuteContext context,
|
public boolean elementSwipe(IExecuteContext context,
|
||||||
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "控件滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, defaultDisplay = false, inputType = InputType.select, defaultValue = "up") String direction,
|
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "控件滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, defaultDisplay = false, inputType = InputType.select, defaultValue = "up") String direction,
|
||||||
|
@ -505,6 +506,71 @@ public class CommonTools {
|
||||||
return aBoolean;
|
return aBoolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>滑动查找控件</p>
|
||||||
|
*
|
||||||
|
* @param direction 滑动方向
|
||||||
|
* @param deviceDriver 设备驱动
|
||||||
|
* @param targets 控件信息
|
||||||
|
* @param waitTimeout 默认超时时间
|
||||||
|
* @param waitStatusStr 是否等待界面稳定
|
||||||
|
* @param preExecuteWait 执行前等待
|
||||||
|
* @param sufExecuteWait 执行后等待
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Keyword(alias = "滑动查找控件", category = "0", attributes = "4", commonlyUse = true)
|
||||||
|
@Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN)
|
||||||
|
public boolean swipeAndFindTargetElement(IExecuteContext context,
|
||||||
|
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "控件滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, defaultDisplay = false, inputType = InputType.select, defaultValue = "up") String direction,
|
||||||
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@Argument(name = "targets", scope = ParamScope.STEP, comment = "目标元素", type = DataType.LIST, defaultDisplay = false,inputType = InputType.element) List<IStepTarget> targets,
|
||||||
|
@Argument(name = "references", scope = ParamScope.ARGS, comment = "滑动元素", type = DataType.LIST, defaultDisplay = false,inputType = InputType.element) String referencesStr,
|
||||||
|
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||||
|
@Argument(name = "index", scope = ParamScope.ARGS, comment = "多个时定位第几个", type = DataType.INTEGER, defaultValue = "1",defaultDisplay = false) Integer index,
|
||||||
|
@Argument(name = "waitStatus", scope = ParamScope.ARGS, comment = "等待界面稳定", type = DataType.ENUM, enumValues = WhetherOrNotEnum.class, inputType = InputType.select, defaultValue = "1", defaultDisplay = false) String waitStatusStr,
|
||||||
|
@Argument(name = "value", scope = ParamScope.OUTPUT, comment = "组件返回值", type = DataType.STRING, defaultValue = "") String value,
|
||||||
|
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float preExecuteWait,
|
||||||
|
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float sufExecuteWait) {
|
||||||
|
logger.debug("拿到的reference为:{}",referencesStr);
|
||||||
|
SwipeAndFindTargetElementThread swipeAndFindTargetElementThread = new SwipeAndFindTargetElementThread(context,deviceDriver,targets,referencesStr,waitTimeout,waitStatusStr,direction,index,preExecuteWait,sufExecuteWait);
|
||||||
|
Future<Boolean> future = executorService.submit(swipeAndFindTargetElementThread);
|
||||||
|
Boolean aBoolean = null;
|
||||||
|
deviceDriver.setFuture(future);
|
||||||
|
try {
|
||||||
|
aBoolean = future.get(waitTimeout, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("该线程被中断了。。。。。。");
|
||||||
|
throw new ExecuteException("执行中断");
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
logger.warn("在执行的时候报错了。。。。");
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause instanceof ExecuteException) {
|
||||||
|
ExecuteException ee = (ExecuteException) cause;
|
||||||
|
throw ee;
|
||||||
|
} else if (cause instanceof ParamMistakeException) {
|
||||||
|
ParamMistakeException pe = (ParamMistakeException) cause;
|
||||||
|
throw pe;
|
||||||
|
} else if (cause instanceof NoSuchSessionException) {
|
||||||
|
NoSuchSessionException ne = (NoSuchSessionException) cause;
|
||||||
|
throw ne;
|
||||||
|
} else if (cause instanceof EnvironmentException) {
|
||||||
|
EnvironmentException ee = (EnvironmentException) cause;
|
||||||
|
throw ee;
|
||||||
|
}else{
|
||||||
|
logger.error("出现了其他的问题。。。。");
|
||||||
|
throw new ExecuteException(cause.getMessage());
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
logger.warn("处理超时了。。。。。。");
|
||||||
|
throw new ExecuteException("查找元素超时");
|
||||||
|
} catch (CancellationException e) {
|
||||||
|
logger.warn("该操作被取消了");
|
||||||
|
throw new ExecuteException("用户取消,执行失败");
|
||||||
|
}
|
||||||
|
setParamValue(context,value,aBoolean.toString());
|
||||||
|
return aBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>在控件中输入文本</p>
|
* <p>在控件中输入文本</p>
|
||||||
*
|
*
|
||||||
|
@ -520,7 +586,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
/*@Keyword(alias = "输入文本", category = "0", attributes = "4", commonlyUse = true)
|
/*@Keyword(alias = "输入文本", category = "0", attributes = "45", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
||||||
public boolean inputText(IExecuteContext context,
|
public boolean inputText(IExecuteContext context,
|
||||||
@Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text,
|
@Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text,
|
||||||
|
@ -590,7 +656,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "控件输入文本", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "控件输入文本", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
||||||
public boolean inputText(IExecuteContext context,
|
public boolean inputText(IExecuteContext context,
|
||||||
@Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text,
|
@Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text,
|
||||||
|
@ -661,7 +727,7 @@ public class CommonTools {
|
||||||
* @param value 输出值
|
* @param value 输出值
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "获取控件的值", category = "0", attributes = "45")
|
@Keyword(alias = "获取控件的值", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
||||||
public String getElementText(IExecuteContext context,
|
public String getElementText(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -729,7 +795,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "长按控件", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "长按控件", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN)
|
||||||
public boolean longPress(IExecuteContext context,
|
public boolean longPress(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -795,7 +861,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "断言元素", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "断言元素", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "断言结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "断言结果", type = DataType.BOOLEAN)
|
||||||
public boolean assertElement(IExecuteContext context,
|
public boolean assertElement(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -861,7 +927,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "app处理", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "app处理", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "app处理", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "app处理", type = DataType.BOOLEAN)
|
||||||
public boolean handleApp(IExecuteContext context,
|
public boolean handleApp(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -919,7 +985,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "点击home键", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "点击home键", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "点击home键", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "点击home键", type = DataType.BOOLEAN)
|
||||||
public boolean pressKeyHome(IExecuteContext context,
|
public boolean pressKeyHome(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -978,7 +1044,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "识别验证码", category = "0", attributes = "45")
|
@Keyword(alias = "识别验证码", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "验证码识别", type = DataType.STRING)
|
@Return(name = "result", comment = "验证码识别", type = DataType.STRING)
|
||||||
public String getCodeByOcr(
|
public String getCodeByOcr(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -1061,7 +1127,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "monkey测试", category = "0", attributes = "45")
|
@Keyword(alias = "monkey测试", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN)
|
||||||
public boolean monkeyTest(
|
public boolean monkeyTest(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -1134,7 +1200,7 @@ public class CommonTools {
|
||||||
* @param waitTime 等待时间(单位s)
|
* @param waitTime 等待时间(单位s)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "等待", category = "0", attributes = "45")
|
@Keyword(alias = "等待", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN)
|
||||||
public boolean wait(
|
public boolean wait(
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -1186,7 +1252,7 @@ public class CommonTools {
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "接口方式获取验证码", category = "0", attributes = "45")
|
@Keyword(alias = "接口方式获取验证码", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "接口方式获取验证码", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "接口方式获取验证码", type = DataType.BOOLEAN)
|
||||||
public String getCodeByHttp(
|
public String getCodeByHttp(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -1222,7 +1288,7 @@ public class CommonTools {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keyword(alias = "模拟键盘输入", category = "0", attributes = "45")
|
@Keyword(alias = "模拟键盘输入", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "模拟键盘输入", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "模拟键盘输入", type = DataType.BOOLEAN)
|
||||||
public Boolean inputFromKeyBoard(
|
public Boolean inputFromKeyBoard(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -1274,7 +1340,7 @@ public class CommonTools {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keyword(alias = "输入密码", category = "0", attributes = "45")
|
@Keyword(alias = "输入密码", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "输入密码", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入密码", type = DataType.BOOLEAN)
|
||||||
public Boolean inputPassword(
|
public Boolean inputPassword(
|
||||||
IExecuteContext context,
|
IExecuteContext context,
|
||||||
|
@ -1325,7 +1391,7 @@ public class CommonTools {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>点击控件</p>
|
* <p>密码输入(ocr)</p>
|
||||||
*
|
*
|
||||||
* @param deviceDriver 设备驱动链接
|
* @param deviceDriver 设备驱动链接
|
||||||
* @param targets 定位控件的信息
|
* @param targets 定位控件的信息
|
||||||
|
@ -1334,7 +1400,7 @@ public class CommonTools {
|
||||||
* @param sufExecuteWait 执行后等待
|
* @param sufExecuteWait 执行后等待
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "密码输入(ocr)", category = "0", attributes = "45", commonlyUse = true)
|
@Keyword(alias = "密码输入(ocr)", category = "0", attributes = "4", commonlyUse = true)
|
||||||
@Return(name = "result", comment = "输入结果", type = DataType.BOOLEAN)
|
@Return(name = "result", comment = "输入结果", type = DataType.BOOLEAN)
|
||||||
public boolean inputPasswordByOcr(IExecuteContext context,
|
public boolean inputPasswordByOcr(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -1396,7 +1462,7 @@ public class CommonTools {
|
||||||
* @param value 输出值
|
* @param value 输出值
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Keyword(alias = "识别金额", category = "0", attributes = "45")
|
@Keyword(alias = "识别金额", category = "0", attributes = "4")
|
||||||
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
||||||
public String getElementMoneyText(IExecuteContext context,
|
public String getElementMoneyText(IExecuteContext context,
|
||||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@ -1447,6 +1513,127 @@ public class CommonTools {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>屏幕截图</p>
|
||||||
|
*
|
||||||
|
* @param deviceDriver 设备连接驱动
|
||||||
|
* @param waitTimeout 默认超时时间
|
||||||
|
* @param preExecuteWait 执行前等待
|
||||||
|
* @param sufExecuteWait 执行后等待
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Keyword(alias = "屏幕截图", category = "0", attributes = "4", commonlyUse = true)
|
||||||
|
@Return(name = "result", comment = "是否截图成功", type = DataType.BOOLEAN)
|
||||||
|
public Boolean screenShot(
|
||||||
|
IExecuteContext context,
|
||||||
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||||
|
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float preExecuteWait,
|
||||||
|
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float sufExecuteWait
|
||||||
|
|
||||||
|
) {
|
||||||
|
ScreenShotThread screenShotThread = new ScreenShotThread(context,deviceDriver,waitTimeout,preExecuteWait,sufExecuteWait);
|
||||||
|
Future<Boolean> future = executorService.submit(screenShotThread);
|
||||||
|
Boolean aBoolean = null;
|
||||||
|
deviceDriver.setFuture(future);
|
||||||
|
try {
|
||||||
|
aBoolean = future.get(waitTimeout, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("该线程被中断了。。。。。。");
|
||||||
|
throw new ExecuteException("执行中断");
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
logger.warn("在执行的时候报错了。。。。");
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause instanceof ExecuteException) {
|
||||||
|
ExecuteException ee = (ExecuteException) cause;
|
||||||
|
throw ee;
|
||||||
|
} else if (cause instanceof ParamMistakeException) {
|
||||||
|
ParamMistakeException pe = (ParamMistakeException) cause;
|
||||||
|
throw pe;
|
||||||
|
} else if (cause instanceof NoSuchSessionException) {
|
||||||
|
NoSuchSessionException ne = (NoSuchSessionException) cause;
|
||||||
|
throw ne;
|
||||||
|
} else if (cause instanceof EnvironmentException) {
|
||||||
|
EnvironmentException ee = (EnvironmentException) cause;
|
||||||
|
throw ee;
|
||||||
|
}else if (cause instanceof StaleElementReferenceException) {
|
||||||
|
throw new ExecuteException("当前操作的元素已经失效");
|
||||||
|
} else {
|
||||||
|
logger.error("出现了其他的问题。。。。", e);
|
||||||
|
throw new ExecuteException(cause.getMessage());
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
logger.warn("处理超时了。。。。。。");
|
||||||
|
throw new ExecuteException("查找文本超时");
|
||||||
|
} catch (CancellationException e) {
|
||||||
|
logger.warn("该操作被取消了");
|
||||||
|
throw new ExecuteException("用户取消,执行失败");
|
||||||
|
}
|
||||||
|
return aBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取控件的值(OCR方式)</p>
|
||||||
|
*
|
||||||
|
* @param deviceDriver 设备驱动链接
|
||||||
|
* @param targets 定位控件的信息
|
||||||
|
* @param waitTimeout 默认超时时间
|
||||||
|
* @param preExecuteWait 执行前等待
|
||||||
|
* @param sufExecuteWait 执行后等待
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Keyword(alias = "获取控件的值(OCR方式)", category = "0", attributes = "4")
|
||||||
|
@Return(name = "result", comment = "控件的值", type = DataType.OBJECT)
|
||||||
|
public String getElementValueByOcr(IExecuteContext context,
|
||||||
|
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||||
|
@Argument(name = "targets", scope = ParamScope.STEP, comment = "控件位置", type = DataType.LIST, defaultDisplay = false) List<IStepTarget> targets,
|
||||||
|
@Argument(name = "value", scope = ParamScope.OUTPUT, comment = "组件返回值", type = DataType.STRING, defaultValue = "") String value,
|
||||||
|
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||||
|
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float preExecuteWait,
|
||||||
|
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float sufExecuteWait
|
||||||
|
) {
|
||||||
|
GetElementValueByOcrThread getElementValueByOcrThread = new GetElementValueByOcrThread(context,deviceDriver,targets,waitTimeout,preExecuteWait,sufExecuteWait);
|
||||||
|
Future<String> future = executorService.submit(getElementValueByOcrThread);
|
||||||
|
String result = null;
|
||||||
|
deviceDriver.setFuture(future);
|
||||||
|
try {
|
||||||
|
result = future.get(waitTimeout, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
logger.warn("该线程被中断了。。。。。。");
|
||||||
|
throw new ExecuteException("执行中断");
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
logger.warn("在执行的时候报错了。。。。");
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause instanceof ExecuteException) {
|
||||||
|
ExecuteException ee = (ExecuteException) cause;
|
||||||
|
throw ee;
|
||||||
|
} else if (cause instanceof ParamMistakeException) {
|
||||||
|
ParamMistakeException pe = (ParamMistakeException) cause;
|
||||||
|
throw pe;
|
||||||
|
} else if (cause instanceof NoSuchSessionException) {
|
||||||
|
NoSuchSessionException ne = (NoSuchSessionException) cause;
|
||||||
|
throw ne;
|
||||||
|
} else if (cause instanceof EnvironmentException) {
|
||||||
|
EnvironmentException ee = (EnvironmentException) cause;
|
||||||
|
throw ee;
|
||||||
|
} else if (cause instanceof StaleElementReferenceException) {
|
||||||
|
throw new ExecuteException("当前操作的元素已经失效");
|
||||||
|
} else {
|
||||||
|
logger.error("出现了其他的问题。。。。", e);
|
||||||
|
throw new ExecuteException(cause.getMessage());
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
logger.warn("处理超时了。。。。。。");
|
||||||
|
throw new ExecuteException("查找元素超时");
|
||||||
|
} catch (CancellationException e) {
|
||||||
|
logger.warn("该操作被取消了");
|
||||||
|
throw new ExecuteException("用户取消,执行失败");
|
||||||
|
}
|
||||||
|
setParamValue(context, value, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private void setParamValue(IExecuteContext context, String variable, String value) {
|
private void setParamValue(IExecuteContext context, String variable, String value) {
|
||||||
try {
|
try {
|
||||||
if (variable.contains("#{") && variable.contains("}")) {
|
if (variable.contains("#{") && variable.contains("}")) {
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
package net.northking.cctp.element.mobile.thread;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.appium.java_client.AppiumDriver;
|
||||||
|
import io.appium.java_client.ios.IOSDriver;
|
||||||
|
import net.northking.cctp.element.core.DeviceDriver;
|
||||||
|
import net.northking.cctp.element.core.IExecuteContext;
|
||||||
|
import net.northking.cctp.element.core.IStepTarget;
|
||||||
|
import net.northking.cctp.element.core.exception.ExecuteException;
|
||||||
|
import net.northking.cctp.element.core.exception.ParamMistakeException;
|
||||||
|
import net.northking.cctp.element.mobile.constants.ConfigPath;
|
||||||
|
import net.northking.cctp.element.mobile.constants.UsingType;
|
||||||
|
import net.northking.cctp.element.mobile.entity.PointMessage;
|
||||||
|
import net.northking.cctp.element.mobile.utils.CommonUtils;
|
||||||
|
import net.northking.cctp.element.mobile.utils.HttpUtils;
|
||||||
|
import net.northking.cctp.element.mobile.utils.phoneUtils.ElementUtil;
|
||||||
|
import net.northking.cctp.element.mobile.utils.phoneUtils.ScreenUtil;
|
||||||
|
import org.openqa.selenium.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class GetElementValueByOcrThread implements Callable<String> {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(GetElementValueByOcrThread.class);
|
||||||
|
|
||||||
|
private IExecuteContext context;
|
||||||
|
private DeviceDriver deviceDriver;
|
||||||
|
private List<IStepTarget> targets;
|
||||||
|
private Integer waitTimeout;
|
||||||
|
private Float preExecuteWait;
|
||||||
|
private Float sufExecuteWait;
|
||||||
|
|
||||||
|
|
||||||
|
public GetElementValueByOcrThread(IExecuteContext context, DeviceDriver deviceDriver, List<IStepTarget> targets, Integer waitTimeout, Float preExecuteWait, Float sufExecuteWait) {
|
||||||
|
this.context = context;
|
||||||
|
this.deviceDriver = deviceDriver;
|
||||||
|
this.targets = targets;
|
||||||
|
this.waitTimeout = waitTimeout;
|
||||||
|
this.preExecuteWait = preExecuteWait;
|
||||||
|
this.sufExecuteWait = sufExecuteWait;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call() throws Exception {
|
||||||
|
CommonUtils.handlePreExecuteWait(preExecuteWait);
|
||||||
|
if (null == waitTimeout || waitTimeout <= 0) {
|
||||||
|
waitTimeout = 30;
|
||||||
|
}
|
||||||
|
String result = "";
|
||||||
|
AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) deviceDriver;
|
||||||
|
WebElement webElement = null;
|
||||||
|
for (IStepTarget target : targets) {
|
||||||
|
webElement = xpathFind(driver, target);
|
||||||
|
if (webElement != null) {
|
||||||
|
result = getElementValueByOcr(context, driver, webElement);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommonUtils.handleSufExecuteWait(sufExecuteWait);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xpath找元素
|
||||||
|
*
|
||||||
|
* @param driver
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
private WebElement xpathFind(AppiumDriver<WebElement> driver, IStepTarget target) {
|
||||||
|
JSONObject object = JSON.parseObject(target.getValue(), JSONObject.class);
|
||||||
|
String xpath = object.getString(UsingType.Key.SELECTOR_KEY);
|
||||||
|
logger.info("拿到的xpath:{}", xpath);
|
||||||
|
try {
|
||||||
|
driver.manage().timeouts().implicitlyWait(waitTimeout, TimeUnit.SECONDS);
|
||||||
|
} catch (NoSuchSessionException e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
WebElement webElement = null;
|
||||||
|
List<WebElement> elements = null;
|
||||||
|
try {
|
||||||
|
elements = driver.findElements(By.xpath(xpath));
|
||||||
|
} catch (WebDriverException e) {
|
||||||
|
logger.error("driver查找控件异常:", e);
|
||||||
|
throw new ExecuteException("设备自动化驱动连接异常,查找控件失败");
|
||||||
|
}
|
||||||
|
logger.info("找到元素{}个", elements.size());
|
||||||
|
if (!CollectionUtils.isEmpty(elements)) { //xpath找到了
|
||||||
|
webElement = elements.get(0);
|
||||||
|
}
|
||||||
|
return webElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementValueByOcr(IExecuteContext context, AppiumDriver<WebElement> driver, WebElement webElement) {
|
||||||
|
String imgBase64 = null;
|
||||||
|
try {
|
||||||
|
imgBase64 = ocrFindCodeArea(webElement, driver, context);
|
||||||
|
} catch (NoSuchSessionException | ExecuteException | ParamMistakeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("查找区域截图失败", e);
|
||||||
|
throw new ExecuteException("查找区域截图失败");
|
||||||
|
}
|
||||||
|
if (null == imgBase64) {
|
||||||
|
throw new ExecuteException("未截取到区域");
|
||||||
|
}
|
||||||
|
String ocrAddress = context.getProperty(ConfigPath.OCR_GET_ALL_TEXT_NUM_KEY);
|
||||||
|
try {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
Map<String, Object> ocrParamMap = new HashMap<>();
|
||||||
|
ocrParamMap.put("img_base64", imgBase64);
|
||||||
|
ocrParamMap.put("targets", "");
|
||||||
|
HttpEntity<Map> ocrEntity = new HttpEntity<>(ocrParamMap, headers);
|
||||||
|
List ocrResultList = null;
|
||||||
|
try {
|
||||||
|
logger.info("传参:{}", JSON.toJSONString(ocrParamMap));
|
||||||
|
ocrResultList = HttpUtils.doPost(ocrAddress, ocrEntity, List.class);
|
||||||
|
logger.info("得到的ocr结果:{}", ocrResultList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("ocr失败", e);
|
||||||
|
}
|
||||||
|
List<String> dataList = new ArrayList<>();
|
||||||
|
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||||
|
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||||
|
for (Object o : ocrResultList) {
|
||||||
|
dataList.add(((Map) o).get("text") + "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.toJSONString(dataList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("请求ocr接口失败,接口地址:{}", ocrAddress, e);
|
||||||
|
throw new ExecuteException("请求ocr识别接口失败,请查看ocr识别服务是否开启!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String ocrFindCodeArea(WebElement webElement, AppiumDriver<WebElement> driver, IExecuteContext context) {
|
||||||
|
String result = null;
|
||||||
|
Rectangle rect = webElement.getRect();
|
||||||
|
int x = rect.getX();
|
||||||
|
int y = rect.getY();
|
||||||
|
int width = rect.getWidth();
|
||||||
|
int height = rect.getHeight();
|
||||||
|
Map<String, Object> deviceInfo = context.getDeviceInfo();
|
||||||
|
String resolution = (String) deviceInfo.get("resolution");
|
||||||
|
String[] split = resolution.split("\\*");
|
||||||
|
int screenLength = Integer.parseInt(split[0]);
|
||||||
|
int screenWidth = Integer.parseInt(split[1]);
|
||||||
|
logger.info("通过xpth找到元素的x:{},y:{}, width:{}, height:{},screenLength:{},screenWidth:{}", x, y, width, height, screenLength,screenWidth);
|
||||||
|
Map<String, Object> paramMap = new HashMap<>();
|
||||||
|
if (driver instanceof IOSDriver) {
|
||||||
|
paramMap.put("platform", "1");
|
||||||
|
} else {
|
||||||
|
paramMap.put("platform", "0");
|
||||||
|
}
|
||||||
|
paramMap.put("deviceId", driver.getCapabilities().getCapability("deviceName"));
|
||||||
|
paramMap.put("length", width);
|
||||||
|
paramMap.put("width", height);
|
||||||
|
paramMap.put("resolution", resolution);
|
||||||
|
paramMap.put("x", x);
|
||||||
|
paramMap.put("y", y);
|
||||||
|
paramMap.put("screenLength", screenLength);
|
||||||
|
paramMap.put("screenWidth", screenWidth);
|
||||||
|
logger.info("参数:{}", JSON.toJSONString(paramMap));
|
||||||
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(paramMap);
|
||||||
|
String host = driver.getRemoteAddress().getHost(); //上位机地址
|
||||||
|
String port = (String) deviceInfo.get("port"); //上位机端口
|
||||||
|
String mobileShotAddress = context.getProperty(ConfigPath.MOBILE_SHOT_ADDRESS_KEY);
|
||||||
|
String shotScreenAddress = String.format(ConfigPath.HTTP_FORMAT, host, port, mobileShotAddress);
|
||||||
|
try {
|
||||||
|
result = HttpUtils.doPost(shotScreenAddress, entity, String.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("截图失败", e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package net.northking.cctp.element.mobile.thread;
|
||||||
|
|
||||||
|
import io.appium.java_client.AppiumDriver;
|
||||||
|
import net.northking.cctp.element.core.DeviceDriver;
|
||||||
|
import net.northking.cctp.element.core.IExecuteContext;
|
||||||
|
import net.northking.cctp.element.core.IStepTarget;
|
||||||
|
import net.northking.cctp.element.core.exception.ExecuteException;
|
||||||
|
import net.northking.cctp.element.mobile.entity.PointMessage;
|
||||||
|
import net.northking.cctp.element.mobile.utils.CommonUtils;
|
||||||
|
import net.northking.cctp.element.mobile.utils.phoneUtils.ElementUtil;
|
||||||
|
import net.northking.cctp.element.mobile.utils.phoneUtils.HandleUtils;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
public class ScreenShotThread implements Callable<Boolean> {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(ScreenShotThread.class);
|
||||||
|
|
||||||
|
private IExecuteContext context;
|
||||||
|
private DeviceDriver deviceDriver;
|
||||||
|
private Integer waitTimeout;
|
||||||
|
private Float preExecuteWait;
|
||||||
|
private Float sufExecuteWait;
|
||||||
|
|
||||||
|
public ScreenShotThread(IExecuteContext context, DeviceDriver deviceDriver, Integer waitTimeout, Float preExecuteWait, Float sufExecuteWait) {
|
||||||
|
this.context = context;
|
||||||
|
this.deviceDriver = deviceDriver;
|
||||||
|
this.waitTimeout = waitTimeout;
|
||||||
|
this.preExecuteWait = preExecuteWait;
|
||||||
|
this.sufExecuteWait = sufExecuteWait;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean call() throws Exception {
|
||||||
|
CommonUtils.handlePreExecuteWait(preExecuteWait);
|
||||||
|
AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) deviceDriver;
|
||||||
|
CommonUtils.screenShot(context,driver);
|
||||||
|
CommonUtils.handleSufExecuteWait(sufExecuteWait);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue