From 5bcb91413907ef07006049b70559714c001dec3a Mon Sep 17 00:00:00 2001 From: "1342486035@qq.com" <1342486035@qq.com> Date: Wed, 10 Jul 2024 14:18:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + .idea/misc.xml | 4 + cctp-atu/.gitignore | 38 ++ cctp-atu/atu-engine/atu-script-engine/pom.xml | 1 + .../cctp/se/exec/DefaultExecThread.java | 6 + .../se/lifecycle/EngineRegisterService.java | 27 +- .../cctp/se/lifecycle/LifecycleSchedule.java | 10 - .../se/lifecycle/constant/MQConstant.java | 4 + .../cctp/se/plan/AtuPlanTaskSchedule.java | 85 ++-- .../se/plan/AtuTaskExecHeartbeatSchedule.java | 55 +++ .../cctp/se/plan/bean/EnginePlanInfo.java | 13 + .../plan/servcie/EnginePlanInfoService.java | 5 + .../servcie/EnginePlanInfoServiceImpl.java | 6 + .../repository/EnginePlanInfoRepository.java | 8 +- .../script/service/ScriptRuntimeExecutor.java | 13 +- .../net/northking/cctp/se/util/HttpUtils.java | 4 +- .../service/AtuPlanInfoApiServiceImpl.java | 5 +- .../AtuPlanSceneCaseTaskApiService.java | 16 + .../AtuPlanSceneCaseTaskApiServiceImpl.java | 426 +++++++++++++++++ .../api/service/AtuPlanTaskApiService.java | 20 + .../service/AtuPlanTaskApiServiceImpl.java | 15 + .../executePlan/constants/RabbitConstant.java | 5 + .../consumer/TaskExecHeartbeatConsumer.java | 76 +++ .../consumer/TaskExecResultConsumer.java | 452 +----------------- .../consumer/TaskInputParamConsumer.java | 75 +++ .../db/dao/AtuPlanSceneCaseTaskDao.java | 6 + .../executePlan/db/dao/AtuPlanTaskDao.java | 14 + .../db/entity/AtuPlanSceneCaseTask.java | 34 +- .../executePlan/db/entity/AtuPlanTask.java | 30 ++ .../impl/AtuPlanSceneCaseTaskServiceImpl.java | 5 + .../db/impl/AtuPlanTaskServiceImpl.java | 11 + .../db/mapper/AtuPlanSceneCaseTaskMapper.java | 1 + .../db/mapper/AtuPlanTaskMapper.java | 1 + .../service/AtuPlanSceneCaseTaskService.java | 6 + .../db/service/AtuPlanTaskService.java | 14 + .../dto/planTask/AtuPlanTaskExtendDto.java | 26 + .../executePlan/job/TaskExecTimeoutJob.java | 85 ++++ .../base/AtuPlanSceneCaseTask.Mapper.xml | 25 +- .../mybatis/base/AtuPlanTask.Mapper.xml | 23 + .../mybatis/ext/AtuPlanSceneCaseTask.Dao.xml | 9 +- .../resources/mybatis/ext/AtuPlanTask.Dao.xml | 19 +- .../driver/constans/AutomationRequestCmd.java | 4 + .../AtuElementInfoRestfulCtrl.java | 10 +- .../service/AtuElementInfoApiService.java | 4 +- .../service/AtuElementInfoApiServiceImpl.java | 14 +- .../scriptcase/db/dao/AtuElementInfoDao.java | 2 + .../db/impl/AtuElementInfoServiceImpl.java | 20 + .../db/service/AtuElementInfoService.java | 13 +- .../atuScriptInfo/AtuScriptInfoDetailDto.java | 11 + .../scriptcase/enums/ElementLibraryError.java | 1 + .../mybatis/ext/AtuElementInfo.Dao.xml | 19 + .../mybatis/ext/AtuScriptInfo.Dao.xml | 5 + .../constants/AutomationRequestCmd.java | 4 + .../automation/constants/UpperParamKey.java | 5 + .../handler/AbstractAutomationHandler.java | 4 + .../handler/AndroidAutomationHandler.java | 10 + .../handler/AutomationMessageHandler.java | 4 + .../handler/IosAutomationHandler.java | 192 ++++++-- .../service/AndroidDebuggerServiceImpl.java | 4 +- .../AbstractIosMessageHandlerThread.java | 4 +- .../thread/AndroidMessageHandlerThread.java | 5 +- .../thread/IosMacMessageHandlerThread.java | 3 +- .../thread/IosScreenResponseThread.java | 17 +- ...osWindowsAndLinuxMessageHandlerThread.java | 3 +- .../src/main/resources/application.yml | 38 +- cctp-production/.gitignore | 30 ++ .../service/DeviceActivityApiService.java | 14 + .../service/DeviceActivityApiServiceImpl.java | 52 +- .../service/CdEngineInfoApiServiceImpl.java | 26 +- .../device/bus/receiver/HeartMQReceiver.java | 35 +- .../cctp/device/db/dao/CdDeviceTokenDao.java | 4 +- .../db/impl/CdDeviceTokenServiceImpl.java | 5 + .../db/service/CdDeviceTokenService.java | 2 + .../cctp/device/pub/DevicePubCtrl.java | 23 +- .../cctp/device/schedule/DeviceSchedule.java | 19 +- .../mybatis/ext/CdDeviceToken.Dao.xml | 21 +- .../db/impl/MobileConnectServiceImpl.java | 28 +- .../db/service/MobileConnectService.java | 9 + .../mobile/socket/MobileSessionManager.java | 21 +- .../mobile/socket/UpperWebSocketClient.java | 12 +- .../cctp/mobile/thread/ProcessMsgThread.java | 11 +- .../northking/cctp/mobile/util/WSUtils.java | 23 + cctp-test-element/.gitignore | 30 ++ .../cctp-test-element-core/pom.xml | 2 +- .../cctp/element/core/type/InputType.java | 4 +- .../cctp-test-element-desktop/pom.xml | 2 +- .../cctp/element/desktop/DesktopLibrary.java | 2 +- .../desktop/keywords/DesktopTools.java | 16 +- .../src/main/resources/changelog.md | 5 + .../element/mobile/ios/MobileIosLibrary.java | 2 +- .../mobile/ios/constants/UpperParamKey.java | 5 + .../mobile/ios/keywords/IOSNewTools.java | 125 ++++- .../ios/utils/AutomationHandleUtil.java | 54 ++- .../src/main/resources/changelog.md | 5 + .../cctp/element/mobile/MobileLibrary.java | 2 +- .../element/mobile/entity/StepTarget.java | 45 ++ .../element/mobile/keywords/AndroidTools.java | 4 +- .../element/mobile/keywords/CommonTools.java | 233 ++++++++- .../thread/GetElementValueByOcrThread.java | 189 ++++++++ .../mobile/thread/ScreenShotThread.java | 45 ++ .../SwipeAndFindTargetElementThread.java | 77 +++ .../mobile/utils/phoneUtils/ElementUtil.java | 338 +++++++++++++ .../src/main/resources/changelog.md | 3 + .../cctp-test-element-library/pom.xml | 2 +- cloud-basic/.gitignore | 28 ++ .../maven/analyse/dialects/MySQLDialect.java | 3 - cloud-platform/.gitignore | 35 ++ cloud-platform/cctp-platform/pom.xml | 2 +- .../V2.7.0_cjf_add_atu_plan_20240705.sql | 5 + .../V2.6.0_wjd_clean_atu_plan20240612.sql | 6 + ...6.0_wjd_clean_atu_script_case_20240612.sql | 14 + .../V2.6.0_wjd_clean_driver_mgr_20240612.sql | 1 + .../V2.6.0_wjd_clean_cctp_mobile_20240612.sql | 6 + ...2.6.0_wjd_clean_cctp_platform_20240612.sql | 36 ++ .../V2.6.0_wjd_clean_projects_20240612.sql | 25 + .../V2.6.0_wjd_clean_saas_20240612.sql | 13 + 116 files changed, 3049 insertions(+), 732 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 cctp-atu/.gitignore create mode 100644 cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuTaskExecHeartbeatSchedule.java create mode 100644 cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiService.java create mode 100644 cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiServiceImpl.java create mode 100644 cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecHeartbeatConsumer.java create mode 100644 cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskInputParamConsumer.java create mode 100644 cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/dto/planTask/AtuPlanTaskExtendDto.java create mode 100644 cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/job/TaskExecTimeoutJob.java create mode 100644 cctp-production/.gitignore create mode 100644 cctp-test-element/.gitignore create mode 100644 cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/entity/StepTarget.java create mode 100644 cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/GetElementValueByOcrThread.java create mode 100644 cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/ScreenShotThread.java create mode 100644 cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/SwipeAndFindTargetElementThread.java create mode 100644 cloud-basic/.gitignore create mode 100644 cloud-platform/.gitignore create mode 100644 sql/atu-execute-plan/V2.7.0_cjf_add_atu_plan_20240705.sql create mode 100644 sqlBackend/atu-execute-plan/20240627/V2.6.0_wjd_clean_atu_plan20240612.sql create mode 100644 sqlBackend/atu-script-case/20240627/V2.6.0_wjd_clean_atu_script_case_20240612.sql create mode 100644 sqlBackend/cctp-device-mgr/20240627/V2.6.0_wjd_clean_driver_mgr_20240612.sql create mode 100644 sqlBackend/cctp-mobile/20240627/V2.6.0_wjd_clean_cctp_mobile_20240612.sql create mode 100644 sqlBackend/cctp-platform/20240627/V2.6.0_wjd_clean_cctp_platform_20240612.sql create mode 100644 sqlBackend/cctp-projects/20240627/V2.6.0_wjd_clean_projects_20240612.sql create mode 100644 sqlBackend/cctp-saas/20240627/V2.6.0_wjd_clean_saas_20240612.sql diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -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 diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f5c6d9e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/cctp-atu/.gitignore b/cctp-atu/.gitignore new file mode 100644 index 0000000..05c3ab8 --- /dev/null +++ b/cctp-atu/.gitignore @@ -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 + diff --git a/cctp-atu/atu-engine/atu-script-engine/pom.xml b/cctp-atu/atu-engine/atu-script-engine/pom.xml index b48008a..f930cb0 100644 --- a/cctp-atu/atu-engine/atu-script-engine/pom.xml +++ b/cctp-atu/atu-engine/atu-script-engine/pom.xml @@ -108,6 +108,7 @@ net.northking.cctp cctp-test-element-core + 1.0.2-SNAPSHOT net.northking.cctp diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/exec/DefaultExecThread.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/exec/DefaultExecThread.java index edcc953..97ec4f3 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/exec/DefaultExecThread.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/exec/DefaultExecThread.java @@ -21,6 +21,7 @@ import net.northking.cctp.se.exec.constant.AtuExecConstant; import net.northking.cctp.se.file.Attachment; import net.northking.cctp.se.lifecycle.EngineRuntime; 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.QuoteData; import net.northking.cctp.se.plan.bean.TaskExecResult; @@ -444,6 +445,11 @@ public class DefaultExecThread implements AtuExecThread{ try { RabbitTemplate rabbitTemplate = SpringUtil.getBean(RabbitTemplate.class); 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) { log.error("任务状态信息更新失败;", e); } diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/EngineRegisterService.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/EngineRegisterService.java index f4746ac..9e99724 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/EngineRegisterService.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/EngineRegisterService.java @@ -107,20 +107,23 @@ public class EngineRegisterService { private void setPcInfoLinux(CdEngineInfoRegisterDto dto, ScriptEngineInfo engineInfo) throws Exception{ CdPcDevice pcInfo = new CdPcDevice(); - pcInfo.setDeviceName(InetAddress.getLocalHost().getHostName()); - pcInfo.setDomainName("设备域名"); pcInfo.setIpAddr(getLinuxIp()); - pcInfo.setOsArch(System.getProperty("os.arch")); - pcInfo.setOsName(System.getProperty("os.name")); - pcInfo.setRamSize(getMemory()); + if (atuServerConfig.getIsPcExecutor()) { + pcInfo.setDeviceName(InetAddress.getLocalHost().getHostName()); + 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); - 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) { diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/LifecycleSchedule.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/LifecycleSchedule.java index e7bd4b5..cae05e5 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/LifecycleSchedule.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/LifecycleSchedule.java @@ -94,16 +94,6 @@ public class LifecycleSchedule } } - /** - * 运行时管理 - * 初始延迟时间 10 秒,间隔 60秒 - */ - @Scheduled(initialDelay = 15 * 1000L, fixedDelay = 60 * 1000L) - public void manage() - { - - } - private void deployNotice(Object object) { byte[] json; try{ diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/constant/MQConstant.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/constant/MQConstant.java index c5e8de8..a61dfac 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/constant/MQConstant.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/lifecycle/constant/MQConstant.java @@ -9,5 +9,9 @@ public class MQConstant { */ public static final String ENGINE_HEARTBEAT = "engine.heartbeat"; + /** + * 任务执行心跳 + */ + public static final String TASK_EXEC_HEARTBEAT_QUEUE = "plan.task.exec.heartbeat"; } diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuPlanTaskSchedule.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuPlanTaskSchedule.java index fe948d8..71e115c 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuPlanTaskSchedule.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuPlanTaskSchedule.java @@ -79,12 +79,7 @@ public class AtuPlanTaskSchedule { logger.error("未开启移动执行线程"); return; } - logger.debug("移动线程池执行情况: {}", ExecutorPool.getMobilePool().getActiveCount() + "/" + - ExecutorPool.getMobilePool().getCorePoolSize()); - if (!ExecutorPool.isMobileAvailable()){ - logger.error("移动线程池中可执行线程已占满"); - return; - } + List list = new ArrayList<>(); try { list = enginePlanInfoService.queryAllDeviceId(TYPE_MOBILE); @@ -92,43 +87,53 @@ public class AtuPlanTaskSchedule { logger.error("引擎获取执行设备信息失败设备:",e); } if(list.size()>0){ - //获取引擎注册后的设备id - EngineRuntime engineRuntime = EngineRuntime.getRuntime(); - ScriptEngineInfo info = engineRuntime.getEngineInfo(); - Map paramMap = new HashMap<>(); - paramMap.put("id",info.getEngineId()); - paramMap.put("deviceList",list); - paramMap.put("type","1"); - paramMap.put("userBy","engine"); - String url = atuServerConfig.getServerUrl() + atuServerConfig.getTryAcqurieDeviceUrl(); - try { - ResponseEntity result = restTemplate.postForEntity(url, paramMap, ResultWrapper.class); - if(HttpStatus.OK.equals(result.getStatusCode())){ - ResultWrapper deviceResult = result.getBody(); - if (deviceResult != null && deviceResult.isSuccess()) { - List> body = (List>)deviceResult.getData(); - if(!CollectionUtils.isEmpty(body)){ - logger.debug("移动任务请求占用成功移动端设备信息:"+JSON.toJSONString(body)); - for (int i = 0; i < body.size(); i++) { - Map 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); + logger.debug("委托执行的设备数量:{}", list.size()); + for (String taskDeviceId : list) { + logger.debug("移动线程池执行情况: {}", ExecutorPool.getMobilePool().getActiveCount() + "/" + + ExecutorPool.getMobilePool().getCorePoolSize()); + if (!ExecutorPool.isMobileAvailable()){ + logger.error("移动线程池中可执行线程已占满"); + return; + } + //获取引擎注册后的设备id + EngineRuntime engineRuntime = EngineRuntime.getRuntime(); + ScriptEngineInfo info = engineRuntime.getEngineInfo(); + Map paramMap = new HashMap<>(); + paramMap.put("id",info.getEngineId()); + List tryAcqurieDeviceIdList = Collections.singletonList(taskDeviceId); + paramMap.put("deviceList",tryAcqurieDeviceIdList); + paramMap.put("type","1"); + paramMap.put("userBy","engine"); + String url = atuServerConfig.getServerUrl() + atuServerConfig.getTryAcqurieDeviceUrl(); + try { + ResponseEntity result = restTemplate.postForEntity(url, paramMap, ResultWrapper.class); + if(HttpStatus.OK.equals(result.getStatusCode())){ + ResultWrapper deviceResult = result.getBody(); + if (deviceResult != null && deviceResult.isSuccess()) { + List> body = (List>)deviceResult.getData(); + if(!CollectionUtils.isEmpty(body)){ + logger.debug("移动任务请求占用成功移动端设备信息:"+JSON.toJSONString(body)); + for (int i = 0; i < body.size(); i++) { + Map 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()); logger.debug("任务信息:" + JSON.toJSONString(task)); planDeviceService.sendTask2Exec(task, deviceToken); + AtuTaskExecHeartbeatSchedule.execTaskMap.put(task.getTaskId(), task.getCaseType()); return true; } catch (Exception e) { logger.debug("消息处理异常,回滚至队列中,消息为:{}", msg); rabbitTemplate.convertAndSend(queueName, msg); throw new ExecuteException("获取队列任务并推送执行异常, " + e.getMessage()); } + }else{ + logger.debug("队列[{}]中无任务信息,修改该委托顺序", queueName); + enginePlanInfoService.updatePlanInfoSort(planInfo.getId()); } return false; }); diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuTaskExecHeartbeatSchedule.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuTaskExecHeartbeatSchedule.java new file mode 100644 index 0000000..0fb260e --- /dev/null +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/AtuTaskExecHeartbeatSchedule.java @@ -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 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("发送任务执行心跳异常"); + } + } +} diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/bean/EnginePlanInfo.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/bean/EnginePlanInfo.java index 538c218..e5b636a 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/bean/EnginePlanInfo.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/bean/EnginePlanInfo.java @@ -54,6 +54,11 @@ public class EnginePlanInfo { */ private Date createdTime; + /** + * 委托排序 + */ + private Long sort; + public String getId() { return id; } @@ -126,4 +131,12 @@ public class EnginePlanInfo { public void setCreatedTime(Date createdTime) { this.createdTime = createdTime; } + + public Long getSort() { + return sort; + } + + public void setSort(Long sort) { + this.sort = sort; + } } diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoService.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoService.java index 397af39..0f67c48 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoService.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoService.java @@ -46,4 +46,9 @@ public interface EnginePlanInfoService { */ void uploadFinishedFile(); + /** + * 更新计划顺序 + * @param id 计划id + */ + void updatePlanInfoSort(String id); } diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoServiceImpl.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoServiceImpl.java index 18df5c8..a0ff6b9 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoServiceImpl.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/plan/servcie/EnginePlanInfoServiceImpl.java @@ -62,6 +62,7 @@ public class EnginePlanInfoServiceImpl implements EnginePlanInfoService { info.setBrowserType(s.getBrowserType()); } info.setCreatedTime(new Date()); + info.setSort(0l); repository.save(info); count++; } @@ -178,4 +179,9 @@ public class EnginePlanInfoServiceImpl implements EnginePlanInfoService { } logger.debug("检查是否存在未上传的任务结果文件结束。。。。"); } + + @Override + public void updatePlanInfoSort(String id) { + repository.updatePlanInfoSort(id); + } } diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/repository/EnginePlanInfoRepository.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/repository/EnginePlanInfoRepository.java index a06c689..7808e40 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/repository/EnginePlanInfoRepository.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/repository/EnginePlanInfoRepository.java @@ -13,7 +13,7 @@ import java.util.List; @Repository public interface EnginePlanInfoRepository extends JpaRepository { - @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) List getOneInfoByDeviceId(@Param("deviceId") String deviceId,@Param("type") String type); @@ -34,5 +34,9 @@ public interface EnginePlanInfoRepository extends JpaRepository selectAllPlanInfo(); - + + @Transactional + @Modifying + @Query(value = "update engine_plan_info set sort = sort + 1 where id = :id",nativeQuery = true) + void updatePlanInfoSort(@Param("id") String id); } diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/script/service/ScriptRuntimeExecutor.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/script/service/ScriptRuntimeExecutor.java index 1bee89e..dca5b43 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/script/service/ScriptRuntimeExecutor.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/script/service/ScriptRuntimeExecutor.java @@ -1514,16 +1514,25 @@ public class ScriptRuntimeExecutor implements ScriptExecutor { * @param stepRet 步骤结果 */ private void snapshot(StepExecuteResult stepRet, boolean snapshotFlag, String msg) { + String contentSnapshotPath = this.getVariableValue(DebugerConst.ReplyConst.MOBILE_STEP_SNAPSHOT); if (snapshotFlag) { log.info(msg); try { DeviceConnection deviceConnection = (DeviceConnection) this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN); String tenantId = this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString(); String imageUrl = deviceConnection.snapshotAllScreen(tenantId, currentTaskId); - stepRet.setActualImgUri(imageUrl); + if (StringUtils.isNotBlank(contentSnapshotPath)) { + stepRet.setActualImgUri(contentSnapshotPath); + } else { + stepRet.setActualImgUri(imageUrl); + } } catch (Exception 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) { - if (listener != null) { + if (listener != null && this.stepLog != null) { this.stepLog.setLevel(logLevel.name()); this.stepLog.setLog(msg); this.stepLog.setStepId(this.currentStepId); diff --git a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/util/HttpUtils.java b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/util/HttpUtils.java index c0c925e..ec466f2 100644 --- a/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/util/HttpUtils.java +++ b/cctp-atu/atu-engine/atu-script-engine/src/main/java/net/northking/cctp/se/util/HttpUtils.java @@ -36,8 +36,8 @@ public class HttpUtils { static { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); - factory.setConnectTimeout(15000); - factory.setReadTimeout(15000); + factory.setConnectTimeout(30000); + factory.setReadTimeout(30000); restTemplate = new RestTemplate(factory); SimpleClientHttpRequestFactory downloadFactory = new SimpleClientHttpRequestFactory(); diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanInfoApiServiceImpl.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanInfoApiServiceImpl.java index 717b643..79c4b7d 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanInfoApiServiceImpl.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanInfoApiServiceImpl.java @@ -677,7 +677,7 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService public Map checkDeviceOffline(String planId, String batchId) { // allOffline 0-在线,1-部分离线,2-全部离线 Map result = new HashMap<>(); - if (batchId == null) { + if (StrUtil.isEmpty(batchId)) { batchId = IdUtil.simpleUUID(); } result.put(PlanConstant.DEVICE_OFFLINE_KEY, PlanConstant.DEVICE_OFFLINE_PORTION); @@ -2543,11 +2543,13 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService // 不是手动执行时,校验设备是否离线 if (!PlanConstant.TRIGGER_TYPE_MANUAL.equals(atuPlanRunDto.getTriggerType())){ Map offlineMap = checkDeviceOffline(atuPlanRunDto.getPlanId(),atuPlanRunDto.getBatchId()); + logger.debug("校验设备是否离线结果: {}", JSONUtil.toJsonStr(offlineMap)); Object allOfflineObj = offlineMap.get(PlanConstant.DEVICE_OFFLINE_KEY); if (allOfflineObj != null && PlanConstant.DEVICE_OFFLINE_ALL == Integer.parseInt(allOfflineObj.toString())) { logger.error("该计划绑定的设备已全部离线"); return; } + atuPlanRunDto.setBatchId(offlineMap.get(PlanConstant.PLAN_BATCH_ID).toString()); } // 1.查询计划信息 AtuPlanInfo planInfo = this.atuPlanInfoService.findByPrimaryKey(atuPlanRunDto.getPlanId()); @@ -2875,6 +2877,7 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService redisTemplate.delete(RedisConstant.PLAN_BATCH_OFFLINE_DEVICE + batchId); } } + logger.debug("离线设备id集合: {}", JSONUtil.toJsonStr(offlineDeviceIdList)); List batchDeviceList = new ArrayList<>(); // 1.1.获取PC与移动设备集合 diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiService.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiService.java new file mode 100644 index 0000000..e2dba82 --- /dev/null +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiService.java @@ -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); +} diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiServiceImpl.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiServiceImpl.java new file mode 100644 index 0000000..93d035f --- /dev/null +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanSceneCaseTaskApiServiceImpl.java @@ -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 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 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 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 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 filterData(List data){ + if (data == null || data.size() <= 0){ + return new ArrayList<>(); + } + return data.stream().filter(num -> num > 0).collect(Collectors.toList()); + } + +} diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiService.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiService.java index 39ce36a..7782ad1 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiService.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiService.java @@ -57,6 +57,13 @@ public interface AtuPlanTaskApiService extends ExcelService */ String insert(AtuPlanTask task); + /** + * 根据id更新任务信息 + * @param task 任务信息 + * @return 结果 + */ + int updateByPrimaryKey(AtuPlanTask task); + /** * 计划批次停止 * @param batchId 批次ID @@ -186,4 +193,17 @@ public interface AtuPlanTaskApiService extends ExcelService List getTaskIdsByScriptIdsAndPlanId(List oldScriptIds, String planId); void getActualImagePath(List deletedFilePath, String execResultFile); + /** + * 查询执行超时N秒任务 + * @param timeout 超时时间 + * @return 任务集合 + */ + List queryExecTimeoutTask(int timeout); + + /** + * 根据id查询任务扩展信息 + * @param taskId 任务id + * @return 任务扩展信息 + */ + AtuPlanTaskExtendDto queryTaskExtendById(String taskId); } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiServiceImpl.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiServiceImpl.java index 1b99420..76a7a9d 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiServiceImpl.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/api/service/AtuPlanTaskApiServiceImpl.java @@ -348,6 +348,11 @@ public class AtuPlanTaskApiServiceImpl extends AbstractExcelService return task.getId(); } + @Override + public int updateByPrimaryKey(AtuPlanTask task) { + return this.atuPlanTaskService.updateByPrimaryKey(task); + } + /** * 计划批次停止 @@ -1669,6 +1674,16 @@ public class AtuPlanTaskApiServiceImpl extends AbstractExcelService } } + @Override + public List queryExecTimeoutTask(int timeout) { + return atuPlanTaskService.queryExecTimeoutTask(timeout); + } + + @Override + public AtuPlanTaskExtendDto queryTaskExtendById(String taskId) { + return atuPlanTaskService.queryTaskExtendById(taskId); + } + private Map getRetryTaskInputInfo(Map> envScriptTaskMap, Map> envScriptMap) { Map taskParamsMap = new HashMap<>(); diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/constants/RabbitConstant.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/constants/RabbitConstant.java index 81dbe4c..30d334d 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/constants/RabbitConstant.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/constants/RabbitConstant.java @@ -76,4 +76,9 @@ public class RabbitConstant { * 批次统计数据更新队列 */ 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"; } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecHeartbeatConsumer.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecHeartbeatConsumer.java new file mode 100644 index 0000000..a7ba3b1 --- /dev/null +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecHeartbeatConsumer.java @@ -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 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); + } +} diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecResultConsumer.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecResultConsumer.java index c19bd85..17a4197 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecResultConsumer.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskExecResultConsumer.java @@ -1,58 +1,29 @@ 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.StrUtil; import cn.hutool.json.JSONObject; 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.AtuPlanTaskApiService; +import net.northking.cctp.executePlan.api.service.AtuPlanSceneCaseTaskApiService; import net.northking.cctp.executePlan.constants.PlanConstant; import net.northking.cctp.executePlan.constants.RabbitConstant; 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.AtuPlanAppLinkService; 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.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 net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.*; -import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import javax.annotation.PostConstruct; -import java.io.*; -import java.util.*; +import java.util.Date; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; /** *

Title: TaskExecResultConsumer

@@ -71,37 +42,20 @@ public class TaskExecResultConsumer { @Autowired private AtuPlanTaskService planTaskService; - @Autowired - private AtuPlanTaskApiService atuPlanTaskApiService; - - @Autowired - private AtuPlanSceneCaseTaskService planSceneCaseTaskService; - @Autowired private AtuPlanInfoService atuPlanInfoService; - @Autowired - private PublicFeignClient publicFeignClient; - @Autowired private AmqpAdmin amqpAdmin; @Autowired private RedisTemplate redisTemplate; - @Autowired - private AtuPlanAppLinkService atuPlanAppLinkService; - @Autowired private AtuPlanBatchApiService planBatchApiService; @Autowired - private SimpleStorageService simpleStorageService; - - @Autowired - private AtuPlanTaskService atuPlanTaskService; - @Autowired - private AttachmentFeignClient attachmentFeignClient; + private AtuPlanSceneCaseTaskApiService sceneCaseTaskApiService; @PostConstruct public void init(){ @@ -113,12 +67,6 @@ public class TaskExecResultConsumer { Binding binding = BindingBuilder.bind(queue).to(exchange) .with(RabbitConstant.TASK_EXEC_RESULT).noargs(); 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())){ - sceneCaseHandle(taskExecResult, clusterKeyPrefix); + sceneCaseTaskApiService.sceneCaseHandle(taskExecResult, clusterKeyPrefix); return; } // 查询任务信息 @@ -164,6 +112,7 @@ public class TaskExecResultConsumer { planTask.setDeviceId(taskExecResult.getDeviceId()); planTask.setAppId(taskExecResult.getAppId()); planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS); + planTask.setLastHeartbeatTime(new Date()); if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(), clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) { @@ -182,7 +131,7 @@ public class TaskExecResultConsumer { if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planTask.getCaseType()) || PlanConstant.SCRIPT_TYPE_IOS.equals(planTask.getCaseType())) { logger.debug("处理移动端性能文件数据"); - planTask.setPerDataPath(parseMobilePerformanceFile(taskExecResult)); + planTask.setPerDataPath(sceneCaseTaskApiService.parseMobilePerformanceFile(taskExecResult)); } // 终态休眠一会,避免集群同时更新执行中状态造成数据异常 @@ -199,395 +148,10 @@ public class TaskExecResultConsumer { planBatchApiService.updateCacheBatchSumData(planTask); // 关联截图文件 - CompletableFuture.runAsync(() -> associatedActualImgAndVideo(taskExecResult)); + CompletableFuture.runAsync(() -> sceneCaseTaskApiService.associatedActualImgAndVideo(taskExecResult)); }catch (Exception 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 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 filterData(List 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 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); - } - } } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskInputParamConsumer.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskInputParamConsumer.java new file mode 100644 index 0000000..63a9ed4 --- /dev/null +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/consumer/TaskInputParamConsumer.java @@ -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); + } + } +} diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanSceneCaseTaskDao.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanSceneCaseTaskDao.java index 94ec5ac..bcbbf52 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanSceneCaseTaskDao.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanSceneCaseTaskDao.java @@ -79,4 +79,10 @@ public interface AtuPlanSceneCaseTaskDao extends AtuPlanSceneCaseTaskMapper List querySceneCaseTasksByTaskIds(@Param("taskIds") List taskIds); + /** + * 查询执行超时N秒任务 + * @param timeout 超时时间 + * @return 任务集合 + */ + List queryExecTimeoutTask(int timeout); } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanTaskDao.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanTaskDao.java index fdde0dd..b592104 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanTaskDao.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/dao/AtuPlanTaskDao.java @@ -7,6 +7,7 @@ package net.northking.cctp.executePlan.db.dao; import net.northking.cctp.common.http.QueryByPage; import net.northking.cctp.executePlan.db.entity.AtuPlanTask; 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.AtuPlanTaskQueryDto; import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto; @@ -109,4 +110,17 @@ public interface AtuPlanTaskDao extends AtuPlanTaskMapper List queryTasksByBatchIds(@Param("batchIds") List batchIds); + /** + * 查询执行超时N秒任务 + * @param timeout 超时时间 + * @return 任务集合 + */ + List queryExecTimeoutTask(int timeout); + + /** + * 根据id查询任务扩展信息 + * @param id 任务id + * @return 任务扩展信息 + */ + AtuPlanTaskExtendDto queryTaskExtendById(String id); } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanSceneCaseTask.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanSceneCaseTask.java index b800956..65d42ce 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanSceneCaseTask.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanSceneCaseTask.java @@ -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; @@ -17,7 +17,7 @@ import java.util.Date; * 计划场景用例节点任务表 * *

文件内容由代码生成器产生,请不要手动修改!
- * createdate: 2023-11-13 14:35:51
+ * createdate: 2024-07-03 18:59:03
* @author: maven-cctp-plugin
* @since: 1.0
*/ @@ -48,6 +48,7 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable public static final String KEY_execResultFile = "execResultFile"; public static final String KEY_perDataPath = "perDataPath"; public static final String KEY_createdTime = "createdTime"; + public static final String KEY_lastHeartbeatTime = "lastHeartbeatTime"; /** * 主键
@@ -189,6 +190,11 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable */ @ApiModelProperty("创建时间") private Date createdTime; + /** + * 执行心跳时间
+ */ + @ApiModelProperty("执行心跳时间") + private Date lastHeartbeatTime; /** * 主键
@@ -605,6 +611,30 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable this.createdTime = (Date)createdTime.clone(); } } + /** + * 执行心跳时间
+ */ + public Date getLastHeartbeatTime() + { + if(lastHeartbeatTime != null) + { + return (Date)lastHeartbeatTime.clone(); + } + return null; + } + + /** + * 执行心跳时间
+ * + * @param lastHeartbeatTime 执行心跳时间 + */ + public void setLastHeartbeatTime(Date lastHeartbeatTime) + { + if(lastHeartbeatTime != null) + { + this.lastHeartbeatTime = (Date)lastHeartbeatTime.clone(); + } + } public AtuPlanSceneCaseTask() { diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanTask.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanTask.java index 3a2b383..d685d4d 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanTask.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/entity/AtuPlanTask.java @@ -49,6 +49,7 @@ public class AtuPlanTask extends Partition implements Serializable public static final String KEY_bugId = "bugId"; public static final String KEY_perDataPath = "perDataPath"; public static final String KEY_createdTime = "createdTime"; + public static final String KEY_lastHeartbeatTime = "lastHeartbeatTime"; /** * 主键
@@ -196,6 +197,11 @@ public class AtuPlanTask extends Partition implements Serializable */ @ApiModelProperty("创建时间") private Date createdTime; + /** + * 执行心跳时间
+ */ + @ApiModelProperty("执行心跳时间") + private Date lastHeartbeatTime; /** * 主键
@@ -629,6 +635,30 @@ public class AtuPlanTask extends Partition implements Serializable this.createdTime = (Date)createdTime.clone(); } } + /** + * 执行心跳时间
+ */ + public Date getLastHeartbeatTime() + { + if(lastHeartbeatTime != null) + { + return (Date)lastHeartbeatTime.clone(); + } + return null; + } + + /** + * 执行心跳时间
+ * + * @param lastHeartbeatTime 执行心跳时间 + */ + public void setLastHeartbeatTime(Date lastHeartbeatTime) + { + if(lastHeartbeatTime != null) + { + this.lastHeartbeatTime = (Date)lastHeartbeatTime.clone(); + } + } public AtuPlanTask() { diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanSceneCaseTaskServiceImpl.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanSceneCaseTaskServiceImpl.java index dc105b7..2679b44 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanSceneCaseTaskServiceImpl.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanSceneCaseTaskServiceImpl.java @@ -93,6 +93,11 @@ private static final Logger logger = LoggerFactory.getLogger(AtuPlanSceneCaseTas return this.atuPlanSceneCaseTaskDao.querySceneCaseTasksByTaskIds(taskIds); } + @Override + public List queryExecTimeoutTask(int timeout) { + return this.atuPlanSceneCaseTaskDao.queryExecTimeoutTask(timeout); + } + // ---- The End by Generator ----// diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanTaskServiceImpl.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanTaskServiceImpl.java index 69bb40c..dfb2fd4 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanTaskServiceImpl.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/impl/AtuPlanTaskServiceImpl.java @@ -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.entity.AtuPlanTask; 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.AtuPlanTaskQueryDto; 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); } + @Override + public List queryExecTimeoutTask(int timeout) { + return this.atuPlanTaskDao.queryExecTimeoutTask(timeout); + } + + @Override + public AtuPlanTaskExtendDto queryTaskExtendById(String taskId) { + return this.atuPlanTaskDao.queryTaskExtendById(taskId); + } + // ---- The End by Generator ----// diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/mapper/AtuPlanSceneCaseTaskMapper.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/mapper/AtuPlanSceneCaseTaskMapper.java index 5262dac..9d95097 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/mapper/AtuPlanSceneCaseTaskMapper.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/mapper/AtuPlanSceneCaseTaskMapper.java @@ -42,5 +42,6 @@ public interface AtuPlanSceneCaseTaskMapper extends BasicDao String COLUMN_bugId = "bug_id"; String COLUMN_perDataPath = "per_data_path"; String COLUMN_createdTime = "created_time"; + String COLUMN_lastHeartbeatTime = "last_heartbeat_time"; } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanSceneCaseTaskService.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanSceneCaseTaskService.java index 1d58c3c..f02281c 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanSceneCaseTaskService.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanSceneCaseTaskService.java @@ -79,4 +79,10 @@ public interface AtuPlanSceneCaseTaskService extends BasicService querySceneCaseTasksByTaskIds(List taskIds); + /** + * 查询执行超时N秒任务 + * @param timeout 超时时间 + * @return 任务集合 + */ + List queryExecTimeoutTask(int timeout); } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanTaskService.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanTaskService.java index e351481..49fa74b 100644 --- a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanTaskService.java +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/db/service/AtuPlanTaskService.java @@ -7,6 +7,7 @@ package net.northking.cctp.executePlan.db.service; import net.northking.cctp.common.db.BasicService; import net.northking.cctp.common.http.QueryByPage; 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.AtuPlanTaskQueryDto; import net.northking.cctp.executePlan.dto.planTask.AtuTaskSendBugDto; @@ -113,4 +114,17 @@ public interface AtuPlanTaskService extends BasicService */ List queryTasksByBatchIds(List batchIds); + /** + * 查询执行超时N秒任务 + * @param timeout 超时时间 + * @return 任务集合 + */ + List queryExecTimeoutTask(int timeout); + + /** + * 根据id查询任务扩展信息 + * @param taskId 任务id + * @return 任务扩展信息 + */ + AtuPlanTaskExtendDto queryTaskExtendById(String taskId); } diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/dto/planTask/AtuPlanTaskExtendDto.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/dto/planTask/AtuPlanTaskExtendDto.java new file mode 100644 index 0000000..e8ea251 --- /dev/null +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/dto/planTask/AtuPlanTaskExtendDto.java @@ -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; + } +} diff --git a/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/job/TaskExecTimeoutJob.java b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/job/TaskExecTimeoutJob.java new file mode 100644 index 0000000..fcb5366 --- /dev/null +++ b/cctp-atu/atu-execute-plan/src/main/java/net/northking/cctp/executePlan/job/TaskExecTimeoutJob.java @@ -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 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 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); + } + } + } +} diff --git a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanSceneCaseTask.Mapper.xml b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanSceneCaseTask.Mapper.xml index 630830a..24f3af6 100644 --- a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanSceneCaseTask.Mapper.xml +++ b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanSceneCaseTask.Mapper.xml @@ -9,7 +9,7 @@ - + @@ -55,6 +55,8 @@ + + @@ -82,6 +84,7 @@ ,exec_result_file ,per_data_path ,created_time + ,last_heartbeat_time @@ -202,6 +205,9 @@ AND created_time=#{createdTime,jdbcType=TIMESTAMP} + + + AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP} @@ -282,6 +288,9 @@ created_time, + + last_heartbeat_time, + @@ -356,6 +365,9 @@ #{createdTime, jdbcType=TIMESTAMP}, + + #{lastHeartbeatTime, jdbcType=TIMESTAMP}, + @@ -387,6 +399,7 @@ exec_result_file, per_data_path, created_time, + last_heartbeat_time, values @@ -415,6 +428,7 @@ #{item.execResultFile, jdbcType=VARCHAR}, #{item.perDataPath, jdbcType=VARCHAR}, #{item.createdTime, jdbcType=TIMESTAMP}, + #{item.lastHeartbeatTime, jdbcType=TIMESTAMP}, @@ -492,6 +506,9 @@ created_time = #{createdTime, jdbcType=TIMESTAMP}, + + last_heartbeat_time = #{lastHeartbeatTime, jdbcType=TIMESTAMP}, + where id = #{id,jdbcType=VARCHAR} @@ -574,6 +591,9 @@ AND created_time = #{createdTime,jdbcType=TIMESTAMP} + + AND last_heartbeat_time = #{lastHeartbeatTime,jdbcType=TIMESTAMP} + @@ -653,6 +673,9 @@ AND created_time=#{createdTime,jdbcType=TIMESTAMP} + + AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP} + diff --git a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanTask.Mapper.xml b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanTask.Mapper.xml index a9d2880..e20e175 100644 --- a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanTask.Mapper.xml +++ b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/base/AtuPlanTask.Mapper.xml @@ -57,6 +57,8 @@ + + @@ -85,6 +87,7 @@ ,bug_id ,per_data_path ,created_time + ,last_heartbeat_time @@ -208,6 +211,9 @@ AND created_time=#{createdTime,jdbcType=TIMESTAMP} + + + AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP} @@ -291,6 +297,9 @@ created_time, + + last_heartbeat_time, + @@ -368,6 +377,9 @@ #{createdTime, jdbcType=TIMESTAMP}, + + #{lastHeartbeatTime, jdbcType=TIMESTAMP}, + @@ -400,6 +412,7 @@ bug_id, per_data_path, created_time, + last_heartbeat_time, values @@ -429,6 +442,7 @@ #{item.bugId, jdbcType=VARCHAR}, #{item.perDataPath, jdbcType=VARCHAR}, #{item.createdTime, jdbcType=TIMESTAMP}, + #{item.lastHeartbeatTime, jdbcType=TIMESTAMP}, @@ -509,6 +523,9 @@ created_time = #{createdTime, jdbcType=TIMESTAMP}, + + last_heartbeat_time = #{lastHeartbeatTime, jdbcType=TIMESTAMP}, + where id = #{id,jdbcType=VARCHAR} @@ -594,6 +611,9 @@ AND created_time = #{createdTime,jdbcType=TIMESTAMP} + + AND last_heartbeat_time = #{lastHeartbeatTime,jdbcType=TIMESTAMP} + @@ -676,6 +696,9 @@ AND created_time=#{createdTime,jdbcType=TIMESTAMP} + + AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP} + diff --git a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanSceneCaseTask.Dao.xml b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanSceneCaseTask.Dao.xml index 355b44b..321234d 100644 --- a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanSceneCaseTask.Dao.xml +++ b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanSceneCaseTask.Dao.xml @@ -220,8 +220,15 @@ #{taskId} + - + delete from atu_plan_scene_case_task where task_id in (select id from atu_plan_task where batch_id in (select id from atu_plan_batch diff --git a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanTask.Dao.xml b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanTask.Dao.xml index 4a755f5..59ebe38 100644 --- a/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanTask.Dao.xml +++ b/cctp-atu/atu-execute-plan/src/main/resources/mybatis/ext/AtuPlanTask.Dao.xml @@ -53,6 +53,13 @@ + + + + + + + + + - - + DELETE FROM where batch_id in (select id from atu_plan_batch where plan_id = #{planId}) LIMIT #{num} diff --git a/cctp-atu/atu-mobile-driver/src/main/java/net/northking/cctp/mobile/driver/constans/AutomationRequestCmd.java b/cctp-atu/atu-mobile-driver/src/main/java/net/northking/cctp/mobile/driver/constans/AutomationRequestCmd.java index 92bc700..ddad86b 100644 --- a/cctp-atu/atu-mobile-driver/src/main/java/net/northking/cctp/mobile/driver/constans/AutomationRequestCmd.java +++ b/cctp-atu/atu-mobile-driver/src/main/java/net/northking/cctp/mobile/driver/constans/AutomationRequestCmd.java @@ -51,4 +51,8 @@ public interface AutomationRequestCmd { String INPUT_PASSWORD_BY_OCR = "input_password_by_ocr"; //输入密码(ocr) 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方式) } diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/AtuElementInfoRestfulCtrl.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/AtuElementInfoRestfulCtrl.java index e5af861..414847f 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/AtuElementInfoRestfulCtrl.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/AtuElementInfoRestfulCtrl.java @@ -57,10 +57,11 @@ public class AtuElementInfoRestfulCtrl produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultWrapper add( - @RequestBody @Validated AtuElementInfoAddDto inputDto) + @RequestHeader(AuthDependencyConstants.REQUEST_HEADER_PROJECT_ID) String projectId, + @RequestBody @Validated AtuElementInfoAddDto inputDto) { ResultWrapper wrapper = new ResultWrapper<>(); - String uuid = apiService.add(inputDto); + String uuid = apiService.add(inputDto, projectId); return wrapper.success(uuid); } @@ -76,10 +77,11 @@ public class AtuElementInfoRestfulCtrl produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultWrapper update( - @RequestBody @Validated AtuElementInfoUpdateDto inputDto) throws IllegalAccessException + @RequestHeader(AuthDependencyConstants.REQUEST_HEADER_PROJECT_ID) String projectId, + @RequestBody @Validated AtuElementInfoUpdateDto inputDto) throws IllegalAccessException { ResultWrapper wrapper = new ResultWrapper<>(); - Integer count = apiService.updateByPK(inputDto); + Integer count = apiService.updateByPK(inputDto, projectId); return wrapper.success(count); } diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiService.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiService.java index 301a2d9..49e4f09 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiService.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiService.java @@ -34,7 +34,7 @@ public interface AtuElementInfoApiService extends ExcelService * @param dto 接口数据传输对象 * @return 主键 */ - String add(AtuElementInfoAddDto dto); + String add(AtuElementInfoAddDto dto, String projectId); /** * 更新元素信息管理 @@ -42,7 +42,7 @@ public interface AtuElementInfoApiService extends ExcelService * @param dto 接口数据传输对象 * @return 修改的记录数量 */ - Integer updateByPK(AtuElementInfoUpdateDto dto) throws IllegalAccessException; + Integer updateByPK(AtuElementInfoUpdateDto dto, String projectId) throws IllegalAccessException; /** * 获取元素信息管理 diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiServiceImpl.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiServiceImpl.java index 9c82280..838014d 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiServiceImpl.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/api/elementLibrary/service/AtuElementInfoApiServiceImpl.java @@ -298,13 +298,18 @@ public class AtuElementInfoApiServiceImpl extends AbstractExcelService atuElementInfos = atuElementInfoService.findByName(dto.getName(), projectId); + if (!CollectionUtils.isEmpty(atuElementInfos)) { + throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_IS_EXISTS); + } // 校验获取方式是否为空 if (CollUtil.isEmpty(dto.getFetchTypeList())){ throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS); @@ -374,13 +379,18 @@ public class AtuElementInfoApiServiceImpl extends AbstractExcelService atuElementInfos = atuElementInfoService.findByNameButNotSelf(dto.getName(), dto.getId(), projectId); + if (!CollectionUtils.isEmpty(atuElementInfos)) { + throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_IS_EXISTS); + } // 校验获取方式是否为空 if (CollUtil.isEmpty(dto.getFetchTypeList())){ throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS); diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/dao/AtuElementInfoDao.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/dao/AtuElementInfoDao.java index ea46379..a43ff6a 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/dao/AtuElementInfoDao.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/dao/AtuElementInfoDao.java @@ -35,6 +35,8 @@ public interface AtuElementInfoDao extends AtuElementInfoMapper List selectUpdaterByProject(@Param("projectId") String projectId); + List findByName(@Param("name") String name, @Param("projectId") String projectId); + List findByNameButNotSelf(@Param("name") String name, @Param("id") String id, @Param("projectId") String projectId); } diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/impl/AtuElementInfoServiceImpl.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/impl/AtuElementInfoServiceImpl.java index a8b9ca2..b8ab0d5 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/impl/AtuElementInfoServiceImpl.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/impl/AtuElementInfoServiceImpl.java @@ -70,5 +70,25 @@ private static final Logger logger = LoggerFactory.getLogger(AtuElementInfoServi public List selectUpdaterByProject(String projectId) { return atuElementInfoDao.selectUpdaterByProject(projectId); } + + /** + * 根据名称查询元素 + * @param name 元素名称 + * @return + */ + @Override + public List findByName(String name, String projectId) { + return atuElementInfoDao.findByName(name, projectId); + } + + /** + * 根据名称查询元素 + * @param name 元素名称 + * @return + */ + @Override + public List findByNameButNotSelf(String name, String id, String projectId) { + return atuElementInfoDao.findByNameButNotSelf(name, id, projectId); + } } diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/service/AtuElementInfoService.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/service/AtuElementInfoService.java index 84acdf6..56b259d 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/service/AtuElementInfoService.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/db/service/AtuElementInfoService.java @@ -49,7 +49,18 @@ public interface AtuElementInfoService extends BasicService List selectUpdaterByProject(String projectId); + /** + * 根据名称查询元素 + * @param name 元素名称 + * @return + */ + List findByName(String name, String projectId); - + /** + * 根据名称查询元素 + * @param name 元素名称 + * @return + */ + List findByNameButNotSelf(String name, String id, String projectId); } diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/dto/atuScript/atuScriptInfo/AtuScriptInfoDetailDto.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/dto/atuScript/atuScriptInfo/AtuScriptInfoDetailDto.java index 728817a..c96bfa5 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/dto/atuScript/atuScriptInfo/AtuScriptInfoDetailDto.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/dto/atuScript/atuScriptInfo/AtuScriptInfoDetailDto.java @@ -41,6 +41,9 @@ public class AtuScriptInfoDetailDto extends AtuScriptInfo implements Serializabl private List inputList; @ApiModelProperty("脚本输出项参数") private List outputList; + @ApiModelProperty("分组信息") + private String namePath; + public List getInputList() { return inputList; @@ -105,4 +108,12 @@ public class AtuScriptInfoDetailDto extends AtuScriptInfo implements Serializabl public void setPrincipal(String principal) { this.principal = principal; } + + public String getNamePath() { + return namePath; + } + + public void setNamePath(String namePath) { + this.namePath = namePath; + } } \ No newline at end of file diff --git a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/enums/ElementLibraryError.java b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/enums/ElementLibraryError.java index e8f0a2b..78d895c 100644 --- a/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/enums/ElementLibraryError.java +++ b/cctp-atu/atu-script-case/src/main/java/net/northking/cctp/scriptcase/enums/ElementLibraryError.java @@ -20,6 +20,7 @@ public enum ElementLibraryError implements PlatformError { ELEMENT_MOVE_NO_LEAF("请选择叶子节点目录移动!"), ELEMENT_VALUE_IS_TOO_LONG("元素属性值超出长度限制!"), ELEMENT_NO_EXISTS("当前元素不存在,请刷新列表"), + ELEMENT_IS_EXISTS("当前元素已存在,元素名称不可重复"), ELEMENT_FETCH_TYPE_NO_EXISTS("元素获取方式为空,请检查"); private static final int START_CODE = 220000; diff --git a/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuElementInfo.Dao.xml b/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuElementInfo.Dao.xml index 10c1bc3..7bdb1f2 100644 --- a/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuElementInfo.Dao.xml +++ b/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuElementInfo.Dao.xml @@ -203,6 +203,25 @@ WHERE is_deleted = 0 AND project_id = #{projectId} GROUP BY updated_by + + update diff --git a/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuScriptInfo.Dao.xml b/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuScriptInfo.Dao.xml index 9ae31ad..e31af79 100644 --- a/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuScriptInfo.Dao.xml +++ b/cctp-atu/atu-script-case/src/main/resources/mybatis/ext/AtuScriptInfo.Dao.xml @@ -46,6 +46,8 @@ + + @@ -154,9 +156,12 @@ ,b.version_name ,a.platform ,a.principal_id + ,c.name_path from ${defaultSchema}.atu_script_info a left join ${defaultSchema}.atu_script_version b 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 AND a.project_id IN diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/AutomationRequestCmd.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/AutomationRequestCmd.java index e0b4fe4..9b9e299 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/AutomationRequestCmd.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/AutomationRequestCmd.java @@ -51,4 +51,8 @@ public interface AutomationRequestCmd { String INPUT_PASSWORD_BY_OCR = "input_password_by_ocr"; //输入密码(ocr) 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方式) } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/UpperParamKey.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/UpperParamKey.java index 48746cd..47c9a6d 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/UpperParamKey.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/constants/UpperParamKey.java @@ -83,4 +83,9 @@ public interface UpperParamKey { * 识别类型 */ String USING_TYPE = "using_type"; + + /** + * 租户id + */ + String TENANT_ID = "tenant_id"; } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AbstractAutomationHandler.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AbstractAutomationHandler.java index 71e7b8c..12aead3 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AbstractAutomationHandler.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AbstractAutomationHandler.java @@ -75,6 +75,10 @@ public abstract class AbstractAutomationHandler implements AutomationMessageHand inputPasswordByOcr(request); } else if (AutomationRequestCmd.GET_ELEMENT_MONEY_TEXT.equals(cmd)) { getElementMoneyText(request); + } else if (AutomationRequestCmd.SCREEN_SHOT.equals(cmd)) { + screenShot(request); + } else if (AutomationRequestCmd.GET_ELEMENT_VALUE_BY_PATH_OCR.equals(cmd)) { + getElementValueByPathOcr(request); } } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AndroidAutomationHandler.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AndroidAutomationHandler.java index ba26fd8..481bdc0 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AndroidAutomationHandler.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AndroidAutomationHandler.java @@ -184,4 +184,14 @@ public class AndroidAutomationHandler extends AbstractAutomationHandler{ public void getElementMoneyText(CmdAutomationRequest request) { } + + @Override + public void screenShot(CmdAutomationRequest request) { + + } + + @Override + public void getElementValueByPathOcr(CmdAutomationRequest request) { + + } } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AutomationMessageHandler.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AutomationMessageHandler.java index 17edd02..a383750 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AutomationMessageHandler.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/AutomationMessageHandler.java @@ -76,4 +76,8 @@ public interface AutomationMessageHandler { void getElementMoneyText(CmdAutomationRequest request); //识别金额 + void screenShot(CmdAutomationRequest request); //识别金额 + + void getElementValueByPathOcr(CmdAutomationRequest request); ////获取控件的值(OCR方式) + } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/IosAutomationHandler.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/IosAutomationHandler.java index 7aac567..39e9a60 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/IosAutomationHandler.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/automation/handler/IosAutomationHandler.java @@ -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.entity.CmdAutomationRequest; 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.UpperComputerManager; import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread; import net.northking.cctp.upperComputer.driver.ios.NKAgent; 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.enums.FileBusinessTypeEnum; import net.northking.cctp.upperComputer.exception.ExecuteException; import net.northking.cctp.upperComputer.service.IosDebuggerServiceImpl; import net.northking.cctp.upperComputer.utils.HttpUtils; @@ -142,7 +145,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler { } } if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) { - response = CmdAutomationResponse.builderSuccess(request, "app安装失败"); + response = CmdAutomationResponse.builderFailure(request, "app安装失败"); return; } logger.info("activate app[{}]", appPackage); @@ -195,10 +198,10 @@ public class IosAutomationHandler extends AbstractAutomationHandler { elapsedTime = currentTime - startTime; } if (null == uiNodeData) { - response = CmdAutomationResponse.builderSuccess(request, "元素不存在"); + response = CmdAutomationResponse.builderFailure(request, "元素不存在"); } else { if (uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0) { - response = CmdAutomationResponse.builderSuccess(request, "元素不存在"); + response = CmdAutomationResponse.builderFailure(request, "元素不存在"); } else { TapXYData tapXYData = new TapXYData(); tapXYData.setX(uiNodeData.getX()); @@ -233,7 +236,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler { } } if (null == tapXYData) { - response = CmdAutomationResponse.builderSuccess(request, "查找控件失败").withData(false); + response = CmdAutomationResponse.builderFailure(request, "查找控件失败").withData(false); } else { response = CmdAutomationResponse.builderSuccess(request, "查找控件完成").withData(tapXYData); } @@ -337,14 +340,14 @@ public class IosAutomationHandler extends AbstractAutomationHandler { if (null != uiNodeData) { if (uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0) { - response = CmdAutomationResponse.builderSuccess(request, "找不到控件"); + response = CmdAutomationResponse.builderFailure(request, "找不到控件"); return; } if (StringUtils.isNotBlank(value)) { response = CmdAutomationResponse.builderSuccess(request, "已获取控件的值").withData(value); } } else { - response = CmdAutomationResponse.builderSuccess(request, "找不到控件"); + response = CmdAutomationResponse.builderFailure(request, "找不到控件"); } } catch (Exception e) { logger.error("根据xpath获取控件的值失败,原因:", e); @@ -1052,22 +1055,26 @@ public class IosAutomationHandler extends AbstractAutomationHandler { headers.setContentType(MediaType.APPLICATION_JSON); Map ocrParamMap = new HashMap<>(); String ocrText = (String) data.get(UpperParamKey.OCR_TEXT); - Integer x = (Integer) data.get(UpperParamKey.X); - Integer y = (Integer) data.get(UpperParamKey.Y); - Integer screenWidth = (Integer) data.get(UpperParamKey.SCREEN_WIDTH); - Integer screenHeight = (Integer) data.get(UpperParamKey.SCREEN_HEIGHT); - //转换比例 - int width = phoneEntity.getScreenWidth() * phoneEntity.getScale(); - int height = phoneEntity.getScreenHeight() * phoneEntity.getScale(); - int realityWidth = width; - int realityHeight = height; - if (width > height) { //宽高反了的情况,调换 - realityWidth = height; - realityHeight = width; - } - Double realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth; - Double realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight; + Double realityX = 0.0; + Double realityY = 0.0; + if(data.get(UpperParamKey.X) != null) { + Integer x = (Integer) data.get(UpperParamKey.X); + Integer y = (Integer) data.get(UpperParamKey.Y); + Integer screenWidth = (Integer) data.get(UpperParamKey.SCREEN_WIDTH); + Integer screenHeight = (Integer) data.get(UpperParamKey.SCREEN_HEIGHT); + //转换比例 + int width = phoneEntity.getScreenWidth() * phoneEntity.getScale(); + int height = phoneEntity.getScreenHeight() * phoneEntity.getScale(); + int realityWidth = width; + int realityHeight = height; + if (width > height) { //宽高反了的情况,调换 + realityWidth = height; + realityHeight = width; + } + realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth; + realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight; + } ocrParamMap.put("img_base64", base64Str); ocrParamMap.put("targets", ocrText); HttpEntity> ocrEntity = new HttpEntity<>(ocrParamMap, headers); @@ -1314,7 +1321,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler { if (clickSuccess) { response = CmdAutomationResponse.builderSuccess(request, "点击控件成功").withData(clickSuccess); } else { - response = CmdAutomationResponse.builderSuccess(request, "点击控件失败").withData(false); + response = CmdAutomationResponse.builderFailure(request, "点击控件失败").withData(false); } } catch (Exception e) { logger.error("根据ocr点击控件失败,原因:", e); @@ -1379,7 +1386,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler { } else if ("1".equalsIgnoreCase(type)) { logger.info("启动ios的app:{}", appPackage); if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) { - response = CmdAutomationResponse.builderSuccess(request, "app未安装"); + response = CmdAutomationResponse.builderFailure(request, "app未安装"); return; } boolean success = deviceHandleHelper.activateApp(phoneEntity.getUdid(), appPackage); @@ -1391,7 +1398,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler { } else { logger.info("重启ios的app:{}", appPackage); if (!deviceHandleHelper.isAppInstalled(phoneEntity.getUdid(), appPackage)) { - response = CmdAutomationResponse.builderSuccess(request, "app未安装"); + response = CmdAutomationResponse.builderFailure(request, "app未安装"); return; } boolean success = iosService.terminateApp(serial, appPackage); @@ -1511,11 +1518,7 @@ public class IosAutomationHandler extends AbstractAutomationHandler { logger.error("请求ocr接口失败,接口地址:{}",ocrAddress,e); } logger.info("识别金额为:{}", value); - if (StringUtils.isNotBlank(value)) { - response = CmdAutomationResponse.builderSuccess(request, "识别金额成功").withData(value); - } else { - response = CmdAutomationResponse.builderFailure(request, "识别金额失败"); - } + response = CmdAutomationResponse.builderSuccess(request, "识别金额成功").withData(value); } } catch (Exception 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 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 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 ocrParamMap = new HashMap<>(); + ocrParamMap.put("img_base64", imgBase64); + ocrParamMap.put("targets", ""); + HttpEntity 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 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) { Map itemMap = new HashMap<>(); HttpHeaders httpHeaders = new HttpHeaders(); diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/service/AndroidDebuggerServiceImpl.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/service/AndroidDebuggerServiceImpl.java index 6ae8f6d..97e4250 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/service/AndroidDebuggerServiceImpl.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/service/AndroidDebuggerServiceImpl.java @@ -921,7 +921,7 @@ public class AndroidDebuggerServiceImpl extends AbstractDebuggerService { if (!tmpPicDir.exists()) { tmpPicDir.mkdir(); } - String tmpPicPath = tmpPicDir.getAbsolutePath() + "/" + System.currentTimeMillis() + ".png"; + String tmpPicPath = tmpPicDir.getAbsolutePath() + "/" + deviceId + "_" + System.currentTimeMillis() + ".png"; tmpPicFile = new File(tmpPicPath); try (FileOutputStream fos = new FileOutputStream(tmpPicFile)){ fos.write(screenShotData); @@ -932,7 +932,7 @@ public class AndroidDebuggerServiceImpl extends AbstractDebuggerService { } catch (Exception e) { logger.error("设备【"+deviceId+"】截图异常", e); }finally { - logger.debug("设备【{}】写到本地的图片大小:{}"); + logger.debug("设备【{}】写到本地的图片大小:{}", deviceId, tmpPicFile != null ? tmpPicFile.length() : 0); } return tmpPicFile; } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AbstractIosMessageHandlerThread.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AbstractIosMessageHandlerThread.java index a5802d4..609b441 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AbstractIosMessageHandlerThread.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AbstractIosMessageHandlerThread.java @@ -577,7 +577,7 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan 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, "开启录屏成功"); } @@ -660,7 +660,7 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid()); if (null == screenResponseThread || screenResponseThread.isInterrupted() || !screenResponseThread.isAlive()) { //读取手机端响应线程 - screenResponseThread = new IosScreenResponseThread(phoneEntity); + screenResponseThread = new IosScreenResponseThread(phoneEntity, null); screenResponseThread.start(); screenResponseThread.startSendScreenToWeb(session, catchParam); screenResponseThread.setScreenOnRequest(request); diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AndroidMessageHandlerThread.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AndroidMessageHandlerThread.java index a70b07c..99e7497 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AndroidMessageHandlerThread.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/AndroidMessageHandlerThread.java @@ -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.protocol.ProtocolCommand; 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.ParamMistakeException; 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 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); Map result = new HashMap<>(); result.put("fileName", fileName); @@ -1151,7 +1152,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler { String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr"); Attachment upload = null; try { - upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId); + upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId,null,FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode()); } catch (Exception e) { logger.debug("[{}]上传截图失败", adbDevice.getSerial(),e); } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosMacMessageHandlerThread.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosMacMessageHandlerThread.java index aa9b5fa..781aa48 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosMacMessageHandlerThread.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosMacMessageHandlerThread.java @@ -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.entity.Attachment; 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.ParamMistakeException; import net.northking.cctp.upperComputer.utils.HttpUtils; @@ -688,7 +689,7 @@ public class IosMacMessageHandlerThread extends AbstractIosMessageHandlerThread String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr"); Attachment upload = null; try { - upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId); + upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId, null, FileBusinessTypeEnum.MOBILE_RECORD_SCREENSHOT.getCode()); } catch (Exception e) { logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e); } diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosScreenResponseThread.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosScreenResponseThread.java index bc12dd6..8c217ff 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosScreenResponseThread.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosScreenResponseThread.java @@ -67,6 +67,8 @@ public class IosScreenResponseThread extends Thread { private boolean sendDeviceStatus = true; + private String currentTaskId; + // private IosScreenCompressHandleThread screenCompressHandle; @Override @@ -225,11 +227,10 @@ public class IosScreenResponseThread extends Thread { /** * @param phone */ - public IosScreenResponseThread(PhoneEntity phone) { -// screenCompressHandle = new IosScreenCompressHandleThread(phone.getUdid()); -// screenCompressHandle.start(); + public IosScreenResponseThread(PhoneEntity phone, String currentTaskId) { this.phone = phone; setName(phone.getUdid() + "拉取屏幕"); + this.currentTaskId = currentTaskId; } private UsbMuxd.ConnectOperator getDeviceConnectOperator(UsbMuxd usbMuxd) { @@ -307,15 +308,15 @@ public class IosScreenResponseThread extends Thread { 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; if (null != screenRecordThread && screenRecordThread.isAlive()) { screenRecordThread.killRecord(); } SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); String dateFormat = format.format(new Date()); - String videoName = this.phone.getUdid() + "_" + dateFormat + ".mp4"; - screenRecordThread = new IosScreenRecordThread(this.phone.getUdid(), videoName, tenantId, width, height); + String videoName = this.phone.getUdid() + "_" + dateFormat + ".mp4"; + screenRecordThread = new IosScreenRecordThread(this.phone.getUdid(), videoName, tenantId,width, height, currentTaskId); 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"; if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) { return null; } - String result = screenRecordThread.endRecord(tenantId); + String result = screenRecordThread.endRecord(tenantId,isSave); JSONObject object = JSONObject.parseObject(result, JSONObject.class); screenRecordThread = null; String videoUrl = object.getString("videoUrl"); diff --git a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosWindowsAndLinuxMessageHandlerThread.java b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosWindowsAndLinuxMessageHandlerThread.java index 99cec2f..6e9461d 100644 --- a/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosWindowsAndLinuxMessageHandlerThread.java +++ b/cctp-atu/atu-upper-computer/src/main/java/net/northking/cctp/upperComputer/webSocket/thread/IosWindowsAndLinuxMessageHandlerThread.java @@ -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.entity.Attachment; 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.ParamMistakeException; import net.northking.cctp.upperComputer.utils.HttpUtils; @@ -709,7 +710,7 @@ public class IosWindowsAndLinuxMessageHandlerThread extends AbstractIosMessageHa String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr"); Attachment upload = null; try { - upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId); + upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId, null, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode()); } catch (Exception e) { logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e); } diff --git a/cctp-atu/atu-upper-computer/src/main/resources/application.yml b/cctp-atu/atu-upper-computer/src/main/resources/application.yml index 90b7a83..424cdb3 100644 --- a/cctp-atu/atu-upper-computer/src/main/resources/application.yml +++ b/cctp-atu/atu-upper-computer/src/main/resources/application.yml @@ -17,7 +17,7 @@ spring: nk: mobile-computer: password: 123456 - keepScreenOn: true #�Ƿ������Ļ�����true������agentʱ����ر��ֻ���Ļ��false���ر��ֻ���Ļ + keepScreenOn: true #ǷĻtrueagentʱرֻĻfalseرֻĻ idPath: /home/attect/cctp-mobile ctrlIp: 172.16.77.25 stfPath: /home/yc/soft/cctp-mobile @@ -36,26 +36,26 @@ nk: http-request-path: useNewOcr: true ocrServer: http://192.168.0.33:5000 - ocrFindArea: ${nk.http-request-path.ocrServer}/WebAPI/FindTextCoordinateByOcr #ocr�����ı�λ�� - imgFindArea: ${nk.http-request-path.ocrServer}/WebAPI/findImageInImage #ͼƬ����λ�� - ocrGetText: ${nk.http-request-path.ocrServer}/WebAPI/GetTextByOcr #ͼƬ����λ�� - ocrGetVerificationCode: http://158.58.160.183:8000/yzm_v2 #�°�ocr��ȡ��֤�� - ocrGetAllTextNum: http://158.58.160.183:8000/rpa_v2 #�°�ocr��ȡ��� - getSafeKeyBoardNum: http://158.58.160.183:8000/keyboard_v2 #�°�ocr��ȡ��ȫ���� - hzBankOcrAddress: http://197.68.24.38:9283/gateway/spring-ocrcloud-platform/OCRA0001 #��������OCR����nginx��ַ + ocrFindArea: ${nk.http-request-path.ocrServer}/WebAPI/FindTextCoordinateByOcr #ocrıλ + imgFindArea: ${nk.http-request-path.ocrServer}/WebAPI/findImageInImage #ͼƬλ + ocrGetText: ${nk.http-request-path.ocrServer}/WebAPI/GetTextByOcr #ͼƬλ + ocrGetVerificationCode: http://158.58.160.183:8000/yzm_v2 #°ocrȡ֤ + ocrGetAllTextNum: http://158.58.160.183:8000/rpa_v2 #°ocrȡ + getSafeKeyBoardNum: http://158.58.160.183:8000/keyboard_v2 #°ocrȡȫ + hzBankOcrAddress: http://197.68.24.38:9283/gateway/spring-ocrcloud-platform/OCRA0001 #OCRnginxַ macos: build-wda: - build-wda: true #是否重新构建wda - xcode-username: mac01 #mac电脑的用户名,不能是root - key-chain-password: testtest #mac系统的密码 - wda-project-path: /Users/mac01/Downloads/WebDriverAgent #wda工程代码位置 - xcode14-path: /Applications/Xcode14/Xcode.app #xcode14位置 - xcode15-path: /Applications/Xcode.app #xcode15位置 - 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-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及以上使用 - print-wda-output: true #是否在日志中打印wda的输出 + build-wda: true #Ƿ¹wda + xcode-username: mac01 #macԵûroot + key-chain-password: testtest #macϵͳ + wda-project-path: /Users/mac01/Downloads/WebDriverAgent #wda̴λ + xcode14-path: /Applications/Xcode14/Xcode.app #xcode14λ + xcode15-path: /Applications/Xcode.app #xcode15λ + 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-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ʹ + print-wda-output: true #Ƿ־дӡwda logging: level: net.northking: debug diff --git a/cctp-production/.gitignore b/cctp-production/.gitignore new file mode 100644 index 0000000..95ae9b2 --- /dev/null +++ b/cctp-production/.gitignore @@ -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/ + diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiService.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiService.java index 9329332..0917ee6 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiService.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiService.java @@ -2,6 +2,7 @@ package net.northking.cctp.device.api.activity.service; import net.northking.cctp.common.db.Pagination; 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.dto.device.CdMobileDeviceExitDto; import net.northking.cctp.device.dto.device.DeviceLockDto; @@ -48,4 +49,17 @@ public interface DeviceActivityApiService { String releaseMobDevice(CdMobileDeviceExitDto dto); Map> queryDeviceType(List deviceIds); + + /** + * 释放设备 + * @param token 设备token + * @return true or false + */ + Boolean deviceRelease(String token); + + /** + * 释放引擎绑定的所有设备 + * @param deviceTokens 设备token信息 + */ + void release(List deviceTokens); } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiServiceImpl.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiServiceImpl.java index af1e147..3b5cc81 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiServiceImpl.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/activity/service/DeviceActivityApiServiceImpl.java @@ -633,6 +633,39 @@ public class DeviceActivityApiServiceImpl implements DeviceActivityApiService { return resultMap; } + @Override + public Boolean deviceRelease(String token) { + CdDeviceToken tokenQuery = new CdDeviceToken(); + tokenQuery.setToken(token); + List resultList = cdDeviceTokenService.query(tokenQuery); + release(resultList); + return true; + } + + @Override + public void release(List 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 public String releasePcDevice(CdPcDeviceExitDto dto) { Lock lock = redisLockRegistry.obtain(DeviceConstants.LOCK_PC + dto.getDeviceId()); @@ -792,9 +825,22 @@ public class DeviceActivityApiServiceImpl implements DeviceActivityApiService { log.setDeviceId(deviceId); log.setDeviceType(cdDeviceToken.getDeviceType()); List query = this.cdDeviceUsageLogService.query(log); - if (query.size() > 0) { - query.get(0).setEndTime(new Date()); - this.cdDeviceUsageLogService.updateByPrimaryKey(query.get(0)); + if (query != null && query.size() > 0) { + CdDeviceUsageLog cdDeviceUsageLog = 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); + } } } } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/engineInfo/service/CdEngineInfoApiServiceImpl.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/engineInfo/service/CdEngineInfoApiServiceImpl.java index 3ff8c1c..0a72f5e 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/engineInfo/service/CdEngineInfoApiServiceImpl.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/api/engineInfo/service/CdEngineInfoApiServiceImpl.java @@ -14,6 +14,7 @@ import net.northking.cctp.common.exception.PlatformRuntimeException; import net.northking.cctp.common.http.QueryByPage; import net.northking.cctp.common.security.authentication.NKSecurityContext; 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.constants.DeviceConstants; import net.northking.cctp.device.constants.DeviceError; @@ -27,6 +28,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.*; @@ -77,6 +79,12 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService getService() @@ -154,7 +162,14 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService 0){ + if (dto.getEnabled() != null && !dto.getEnabled()){ + logger.debug("引擎禁用,删除引擎的线程缓存数据"); + redisTemplate.opsForZSet().remove(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, dto.getId()); + } + } + return rows; } @Override @@ -290,6 +305,15 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService deviceTokenList = this.cdDeviceTokenService.queryByEngineIds(Collections.singletonList(resultDto.getEngineId())); + activityApiService.release(deviceTokenList); + } catch (Exception e) { + logger.error("释放引擎占用的设备异常", e); + } + return resultDto; } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/bus/receiver/HeartMQReceiver.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/bus/receiver/HeartMQReceiver.java index ed52a82..a336330 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/bus/receiver/HeartMQReceiver.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/bus/receiver/HeartMQReceiver.java @@ -124,25 +124,26 @@ public class HeartMQReceiver { engineInfo.setRunningRelease(dto.getEngineVersion()); this.cdEngineInfoService.updateByPrimaryKey(engineInfo); - // 更新缓存中引擎可执行线程数量 - Map executePoolInfo = dto.getExecutePoolInfo(); - if (executePoolInfo != null) { - boolean enableMobile = executePoolInfo.get("enableMobile") != null - && (boolean) executePoolInfo.get("enableMobile"); - if (enableMobile) { - Object mobileThread = executePoolInfo.get("mobileThread"); - Object mobileThreadActive = executePoolInfo.get("mobile_thread_active"); - if (mobileThread != null && mobileThreadActive != null){ - BigDecimal mobileThreadBd = new BigDecimal(String.valueOf(mobileThread)); - BigDecimal mobileThreadActiveBd = new BigDecimal(String.valueOf(mobileThreadActive)); - if (mobileThreadBd.compareTo(new BigDecimal("0")) > 0) { - BigDecimal divide = mobileThreadActiveBd.divide(mobileThreadBd, 2, RoundingMode.HALF_UP) - .multiply(new BigDecimal("100")); - redisTemplate.opsForZSet().add(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, engineInfo.getId(), - divide.doubleValue()); + if (engineInfo.getEnabled()) { + // 更新缓存中引擎可执行线程数量 + Map executePoolInfo = dto.getExecutePoolInfo(); + if (executePoolInfo != null) { + boolean enableMobile = executePoolInfo.get("enableMobile") != null + && (boolean) executePoolInfo.get("enableMobile"); + if (enableMobile) { + Object mobileThread = executePoolInfo.get("mobileThread"); + Object mobileThreadActive = executePoolInfo.get("mobile_thread_active"); + if (mobileThread != null && mobileThreadActive != null) { + BigDecimal mobileThreadBd = new BigDecimal(String.valueOf(mobileThread)); + BigDecimal mobileThreadActiveBd = new BigDecimal(String.valueOf(mobileThreadActive)); + if (mobileThreadBd.compareTo(new BigDecimal("0")) > 0) { + BigDecimal divide = mobileThreadActiveBd.divide(mobileThreadBd, 2, RoundingMode.HALF_UP) + .multiply(new BigDecimal("100")); + redisTemplate.opsForZSet().add(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, engineInfo.getId(), + divide.doubleValue()); + } } } - } } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/dao/CdDeviceTokenDao.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/dao/CdDeviceTokenDao.java index 2729063..108717b 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/dao/CdDeviceTokenDao.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/dao/CdDeviceTokenDao.java @@ -24,6 +24,8 @@ public interface CdDeviceTokenDao extends CdDeviceTokenMapper { List queryDeviceList(@Param("userId") String userId, @Param("ids") List ids); - // ---- The End by Generator ----// + + List queryByEngineIds(@Param("ids") List engineIds); + // ---- The End by Generator ----// } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/impl/CdDeviceTokenServiceImpl.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/impl/CdDeviceTokenServiceImpl.java index 2539910..cba9eec 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/impl/CdDeviceTokenServiceImpl.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/impl/CdDeviceTokenServiceImpl.java @@ -56,6 +56,11 @@ private static final Logger logger = LoggerFactory.getLogger(CdDeviceTokenServic return cdDeviceTokenDao.queryDeviceList(userId,ids); } + @Override + public List queryByEngineIds(List engineIds) { + return cdDeviceTokenDao.queryByEngineIds(engineIds); + } + // ---- The End by Generator ----// diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/service/CdDeviceTokenService.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/service/CdDeviceTokenService.java index 2c7c30f..a67d369 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/service/CdDeviceTokenService.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/db/service/CdDeviceTokenService.java @@ -13,6 +13,8 @@ public interface CdDeviceTokenService extends BasicService { List queryDeviceList(String userId,List ids); + + List queryByEngineIds(List engineIds); // ---- The End by Generator ----// } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/pub/DevicePubCtrl.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/pub/DevicePubCtrl.java index dc2c400..baa4793 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/pub/DevicePubCtrl.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/pub/DevicePubCtrl.java @@ -110,27 +110,8 @@ public class DevicePubCtrl { produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultWrapper deviceRelease(@PathVariable("token") String token) { ResultWrapper wrapper = new ResultWrapper<>(); - CdDeviceToken tokenQuery = new CdDeviceToken(); - tokenQuery.setToken(token); - List 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); + Boolean result = this.apiService.deviceRelease(token); + return wrapper.success(result); } diff --git a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/schedule/DeviceSchedule.java b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/schedule/DeviceSchedule.java index aefb5bf..95404f5 100644 --- a/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/schedule/DeviceSchedule.java +++ b/cctp-production/cctp-device-mgr/src/main/java/net/northking/cctp/device/schedule/DeviceSchedule.java @@ -1,10 +1,12 @@ package net.northking.cctp.device.schedule; 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.dto.UserInfoDto; import net.northking.cctp.device.bus.publisher.DeviceStatusPublisher; 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.service.*; import net.northking.cctp.device.util.DeviceStatusUtil; @@ -27,10 +29,7 @@ import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; +import java.util.*; import java.util.concurrent.locks.Lock; @Component @@ -90,6 +89,9 @@ public class DeviceSchedule { @Autowired private EmailConfig emailConfig; + @Autowired + private DeviceActivityApiService activityApiService; + @Scheduled(cron = "0 5 0 * * *") public void recycleDevice() { Lock lock = lockRegistry.obtain("scheduler:DeviceSchedule.recycleDevice"); @@ -183,6 +185,15 @@ public class DeviceSchedule { statusUtil.updatePcStatus(status); } this.cdEngineInfoService.updateEngineStatus(ids); + try { + logger.debug("释放引擎占用的设备"); + List 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("---------------------------校验设备与引擎过期定时任务:结束-----------------------------------"); diff --git a/cctp-production/cctp-device-mgr/src/main/resources/mybatis/ext/CdDeviceToken.Dao.xml b/cctp-production/cctp-device-mgr/src/main/resources/mybatis/ext/CdDeviceToken.Dao.xml index c4effdf..1f6d68d 100644 --- a/cctp-production/cctp-device-mgr/src/main/resources/mybatis/ext/CdDeviceToken.Dao.xml +++ b/cctp-production/cctp-device-mgr/src/main/resources/mybatis/ext/CdDeviceToken.Dao.xml @@ -8,10 +8,23 @@ FROM cd_device_token WHERE user_id = #{userId, jdbcType=VARCHAR} - AND id in - - #{idItem, jdbcType=VARCHAR} - + + AND id in + + #{idItem, jdbcType=VARCHAR} + + + + + diff --git a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/impl/MobileConnectServiceImpl.java b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/impl/MobileConnectServiceImpl.java index b2f525a..8813719 100644 --- a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/impl/MobileConnectServiceImpl.java +++ b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/impl/MobileConnectServiceImpl.java @@ -27,7 +27,9 @@ import javax.websocket.CloseReason; import javax.websocket.Session; import java.io.OutputStream; import java.net.URI; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; @@ -39,6 +41,10 @@ public class MobileConnectServiceImpl implements MobileConnectService { @Autowired private ThreadPoolExecutor executorService; + /** + * web端到本服务的会话集合 deviceId-Session + */ + private ConcurrentMap sessionMap = new ConcurrentHashMap<>(); /** * 处理每一个真机连接指令的任务 @@ -50,6 +56,11 @@ public class MobileConnectServiceImpl implements MobileConnectService { */ private ConcurrentHashMap mobileConnectionThreadMap = new ConcurrentHashMap<>(); + /** + * 缓存用户token的map + */ + private static Map userTokenMap = new ConcurrentHashMap<>(); + @Autowired private RedisTemplate redisTemplate; @@ -172,7 +183,7 @@ public class MobileConnectServiceImpl implements MobileConnectService { logger.info("线程池还能支持手机连接........................."); Future future = mobileConnectionTaskMap.get(deviceId); 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); mobileConnectionThreadMap.put(deviceId, processMsgThread); mobileConnectionTaskMap.put(deviceId, future); @@ -202,4 +213,19 @@ public class MobileConnectServiceImpl implements MobileConnectService { 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); + } } diff --git a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/service/MobileConnectService.java b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/service/MobileConnectService.java index cf75559..b4b0b9c 100644 --- a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/service/MobileConnectService.java +++ b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/db/service/MobileConnectService.java @@ -24,4 +24,13 @@ public interface MobileConnectService { void releaseUsingDevice(String deviceId, String deviceToken); + //拿到session + Session getSession(String deviceId); + + //添加用户token + void addUserToken(String sessionId, String token); + + //移除用户token + void removeUserToken(String id); + } diff --git a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/MobileSessionManager.java b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/MobileSessionManager.java index d79dfae..74c47a7 100644 --- a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/MobileSessionManager.java +++ b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/MobileSessionManager.java @@ -1,14 +1,18 @@ package net.northking.cctp.mobile.socket; 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.security.authentication.SecurityUserCache; import net.northking.cctp.mobile.constants.DeviceConstants; import net.northking.cctp.mobile.constants.MobileConnectionConstants; import net.northking.cctp.mobile.db.service.MobileConnectService; import net.northking.cctp.mobile.entity.UpperWSResponse; import net.northking.cctp.mobile.util.SpringUtils; import org.apache.commons.lang.StringUtils; +import org.redisson.api.RBucket; +import org.redisson.api.RedissonClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -42,13 +46,6 @@ public class MobileSessionManager { logger.info("Init web session manager ..."); } - private static SecurityUserCache securityUserCache; - - @Autowired - public void setSecurityUserCache(SecurityUserCache securityUserCache) { - MobileSessionManager.securityUserCache = securityUserCache; - } - /** * 与界面建立Websocket会话 * @@ -62,6 +59,7 @@ public class MobileSessionManager { String userToken = queryParams.get(DeviceConstants.AUTHORIZATION); String sessionId = queryParams.get(DeviceConstants.KEY_SESSION_ID); String deviceToken = queryParams.get(DeviceConstants.KEY_DEVICE_TOKEN); + String reportId = queryParams.get(DeviceConstants.KEY_REPORT_ID); String mode = queryParams.get(DeviceConstants.KEY_MODE); if (StringUtils.isNotBlank(mode)) { logger.debug("deviceToken:[{}]直播连接视频",deviceToken); @@ -120,8 +118,11 @@ public class MobileSessionManager { * @return */ private boolean authorize(String sessionId, String token) { - if (securityUserCache.hasToken(token)) { -// SpringUtils.getBean(MobileConnectService.class).addUserToken(sessionId, token); + RedissonClient redisson = SpringUtil.getBean(RedissonClient.class); + RBucket tokenValueDTORBucket = redisson.getBucket(AuthDependencyConstants.TOKEN_PREFIX + token); + TokenValueDTO tokenValueDTO = tokenValueDTORBucket.get(); + if (tokenValueDTO != null ) { + SpringUtils.getBean(MobileConnectService.class).addUserToken(sessionId, token); return true; } return false; diff --git a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/UpperWebSocketClient.java b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/UpperWebSocketClient.java index 0548ff1..b54eec0 100644 --- a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/UpperWebSocketClient.java +++ b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/socket/UpperWebSocketClient.java @@ -2,12 +2,8 @@ package net.northking.cctp.mobile.socket; import com.alibaba.fastjson.JSON; 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.db.service.DeviceUseReportService; 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.WSResponse; import net.northking.cctp.mobile.util.JsonUtils; @@ -44,6 +40,7 @@ public class UpperWebSocketClient extends WebSocketClient { private RemoteEndpoint.Basic basicRemote; + public UpperWebSocketClient(URI uri, Session webSession, String deviceId, String sessionId) { super(uri); this.webSession = webSession; @@ -72,7 +69,7 @@ public class UpperWebSocketClient extends WebSocketClient { @Override public void onMessage(String msg) { -// logger.debug("服务端接收到上位机信息,msg:{}", msg); + //logger.debug("服务端接收到上位机信息,msg:{}", msg); try { UpperWSResponse msgResponse = JsonUtils.fromJsonString(msg, UpperWSResponse.class); String cmd = msgResponse.getCmd(); @@ -198,12 +195,9 @@ public class UpperWebSocketClient extends WebSocketClient { */ private void handleUpperMsgForward(UpperWSResponse 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) { //根据返回结果决定返回内容 WSResponse wsResponse = WSResponse.buildResponse(msgResponse.getCmd(), sessionId, msgResponse.getData()); diff --git a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/thread/ProcessMsgThread.java b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/thread/ProcessMsgThread.java index ab0f63d..58cabc2 100644 --- a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/thread/ProcessMsgThread.java +++ b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/thread/ProcessMsgThread.java @@ -1,5 +1,6 @@ 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.mobile.constants.MobileConnectionConstants; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; import javax.websocket.Session; +import java.net.URI; +import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -134,7 +138,7 @@ public class ProcessMsgThread extends Thread{ private void exitMobile(){ if (StringUtils.hasText(deviceToken)) { - if (StringUtils.isNotBlank(mode)) { + if (StringUtils.hasText(mode)) { logger.info("设备【{}】是直播退出,不释放手机.......",deviceId); } else { SpringUtils.getBean(MobileConnectService.class).releaseUsingDevice(deviceId, deviceToken); @@ -152,11 +156,10 @@ public class ProcessMsgThread extends Thread{ logger.info("前端设备连接信息添加进队列"); } - public void exit() { - this.alive = false; - } + private void sendMsg2Web(String deviceId, String msg){ + if(null == webSession || !webSession.isOpen()){ logger.warn("前端Session[deviceId:{}]已关闭",deviceId); return; diff --git a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/util/WSUtils.java b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/util/WSUtils.java index 26ce7c5..c137bc4 100644 --- a/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/util/WSUtils.java +++ b/cctp-production/cctp-mobile/src/main/java/net/northking/cctp/mobile/util/WSUtils.java @@ -32,4 +32,27 @@ public class WSUtils { 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; + } + } + + } } diff --git a/cctp-test-element/.gitignore b/cctp-test-element/.gitignore new file mode 100644 index 0000000..95ae9b2 --- /dev/null +++ b/cctp-test-element/.gitignore @@ -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/ + diff --git a/cctp-test-element/cctp-test-element-core/pom.xml b/cctp-test-element/cctp-test-element-core/pom.xml index 6888b61..6be1251 100644 --- a/cctp-test-element/cctp-test-element-core/pom.xml +++ b/cctp-test-element/cctp-test-element-core/pom.xml @@ -11,7 +11,7 @@ 4.0.0 cctp-test-element-core - 1.0.1-RELEASE + 1.0.2-SNAPSHOT jar diff --git a/cctp-test-element/cctp-test-element-core/src/main/java/net/northking/cctp/element/core/type/InputType.java b/cctp-test-element/cctp-test-element-core/src/main/java/net/northking/cctp/element/core/type/InputType.java index ff7daf7..fa45ce1 100644 --- a/cctp-test-element/cctp-test-element-core/src/main/java/net/northking/cctp/element/core/type/InputType.java +++ b/cctp-test-element/cctp-test-element-core/src/main/java/net/northking/cctp/element/core/type/InputType.java @@ -6,5 +6,7 @@ public enum InputType { //单选 select, //多选 - selects; + selects, + + element; } diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/pom.xml b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/pom.xml index a4ce009..20e5d40 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/pom.xml +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/pom.xml @@ -28,7 +28,7 @@ - NK.Desktop-1.75 + NK.Desktop-1.80 org.apache.maven.plugins diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/DesktopLibrary.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/DesktopLibrary.java index e52d305..feee002 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/DesktopLibrary.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/DesktopLibrary.java @@ -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 String NAME = "NK.Desktop"; 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; /** diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/keywords/DesktopTools.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/keywords/DesktopTools.java index 8ce9023..23fc489 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/keywords/DesktopTools.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/java/net/northking/cctp/element/desktop/keywords/DesktopTools.java @@ -328,7 +328,7 @@ public class DesktopTools { } /** - * 断言元素是否存在 + * 断言元素存在 * @param id 步骤id * @param title 步骤主标题 * @param targets 元素识别方法 @@ -336,12 +336,11 @@ public class DesktopTools { * @param delayBefore 执行前等待 * @param delayAfter 执行后等待 * @param waitForReady 就绪等待 - * @param value 组件返回值 * @param deviceDriver 与PC机器连接的webSocket客户端 * @param executeContent 执行上下文 * @return 执行结果 */ - @Keyword(alias = "断言元素是否存在", attributes = "23") + @Keyword(alias = "断言元素存在", attributes = "23") @Return(name = "result", comment = "返回值", type = DataType.BOOLEAN) public Boolean assertElement( @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 = "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 = "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 = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver, 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, 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); } if (deviceDriver == null){ @@ -725,7 +724,7 @@ public class DesktopTools { } /** - * 断言是否包含文本 + * 断言包含文本 * @param id 步骤id * @param title 步骤主标题 * @param targets 元素识别方法 @@ -733,12 +732,11 @@ public class DesktopTools { * @param delayBefore 执行前等待 * @param delayAfter 执行后等待 * @param waitForReady 就绪等待 - * @param value 组件返回值 * @param deviceDriver 与PC机器连接的webSocket客户端 * @param executeContent 执行上下文 * @return 执行结果 */ - @Keyword(alias = "断言是否包含文本", attributes = "23") + @Keyword(alias = "断言包含文本", attributes = "23") @Return(name = "result", comment = "返回值", type = DataType.BOOLEAN) public Boolean assertText( @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 = "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 = "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 = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver, IExecuteContext executeContent diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/resources/changelog.md b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/resources/changelog.md index e60a6c1..9461589 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/resources/changelog.md +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-desktop/src/main/resources/changelog.md @@ -1,3 +1,8 @@ +### 版本 1.80 + 更新于2024-07-06 + 1.修改组件名称为断言包含文本,断言存在元素 + 2.将断言的返回值返回值变为非必填项 + ### 版本 1.78 更新于2024-06-19 1.添加杭银OCR识别方式 diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/MobileIosLibrary.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/MobileIosLibrary.java index de68a2b..702973b 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/MobileIosLibrary.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/MobileIosLibrary.java @@ -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 String NAME = "NK.MobileIos"; 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; @Override diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/constants/UpperParamKey.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/constants/UpperParamKey.java index fe5a971..502ea53 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/constants/UpperParamKey.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/constants/UpperParamKey.java @@ -83,4 +83,9 @@ public interface UpperParamKey { */ String USING_TYPE = "using_type"; + /** + * 租户id + */ + String TENANT_ID = "tenant_id"; + } diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/keywords/IOSNewTools.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/keywords/IOSNewTools.java index a66ac4d..571b126 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/keywords/IOSNewTools.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/keywords/IOSNewTools.java @@ -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.Return; 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.ParamMistakeException; import net.northking.cctp.element.core.type.DataType; import net.northking.cctp.element.core.type.InputType; import net.northking.cctp.element.core.type.ParamScope; @@ -23,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; -import java.util.concurrent.CancellationException; +import java.util.concurrent.*; /** * ios新版的组件 @@ -46,7 +48,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean clickElement(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -97,7 +99,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "是否存在元素(ios新)", category = "0", attributes = "9") + @Keyword(alias = "是否存在元素(ios新)", category = "0", attributes = "5") @Return(name = "result", comment = "是否存在元素的结果", type = DataType.BOOLEAN) public boolean ifElement(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -143,7 +145,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean standardSwipe( @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 执行后等待 * @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) 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, @@ -240,7 +242,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean inputText(IExecuteContext context, @Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text, @@ -294,7 +296,7 @@ public class IOSNewTools { * @param value 输出值 * @return */ - @Keyword(alias = "获取控件的值(ios新)", category = "0", attributes = "9") + @Keyword(alias = "获取控件的值(ios新)", category = "0", attributes = "5") @Return(name = "result", comment = "获取控件值的结果", type = DataType.OBJECT) public String getElementText(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -343,7 +345,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean longPress(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -395,7 +397,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean assertElement(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -445,7 +447,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "识别验证码(ios新)", category = "0", attributes = "9") + @Keyword(alias = "识别验证码(ios新)", category = "0", attributes = "5") @Return(name = "result", comment = "验证码识别", type = DataType.STRING) public String getCodeByOcr( IExecuteContext context, @@ -493,7 +495,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public Boolean clickText( IExecuteContext context, @@ -538,7 +540,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public Boolean clickTextNew( IExecuteContext context, @@ -583,7 +585,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @return 返回判断的结果 */ - @Keyword(alias = "是否存在文本(ios新)", category = "0", attributes = "9") + @Keyword(alias = "是否存在文本(ios新)", category = "0", attributes = "5") @Return(name = "result", comment = "文本存在与不存在的结果", type = DataType.BOOLEAN) public Boolean ifText( IExecuteContext context, @@ -629,7 +631,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean handleApp(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -666,7 +668,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean pressKeyHome(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -696,7 +698,7 @@ public class IOSNewTools { * @param waitTime 等待时间(单位s) * @return */ - @Keyword(alias = "等待(ios新)", category = "0", attributes = "9") + @Keyword(alias = "等待(ios新)", category = "0", attributes = "5") @Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN) public boolean wait( @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -766,7 +768,7 @@ public class IOSNewTools { * @param sufExecuteWait 执行后等待 * @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) public boolean inputPasswordByOcr(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -812,7 +814,7 @@ public class IOSNewTools { * @param value 输出值 * @return */ - @Keyword(alias = "识别金额(ios新)", category = "0", attributes = "9") + @Keyword(alias = "识别金额(ios新)", category = "0", attributes = "5") @Return(name = "result", comment = "输入的结果", type = DataType.OBJECT) public String getElementMoneyText(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -848,4 +850,91 @@ public class IOSNewTools { return result; } + /** + *

屏幕截图(ios新)

+ * + * @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; + } + + /** + *

获取控件的值(ios新)(OCR方式)

+ * + * @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 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; + } + } diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/utils/AutomationHandleUtil.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/utils/AutomationHandleUtil.java index c75325b..269b73e 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/utils/AutomationHandleUtil.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/java/net/northking/cctp/element/mobile/ios/utils/AutomationHandleUtil.java @@ -991,10 +991,6 @@ public class AutomationHandleUtil { logger.warn("不支持的获取元素值方式:{}", target.getUsing()); } } - - if (StringUtils.isBlank(result)) { - throw new ExecuteException("无法获取当前元素的文本"); - } return result; } @@ -1034,4 +1030,54 @@ public class AutomationHandleUtil { 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 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 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 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; + } } diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/resources/changelog.md b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/resources/changelog.md index 62e414d..b2b4d36 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/resources/changelog.md +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile-ios/src/main/resources/changelog.md @@ -1,3 +1,8 @@ +### 版本:1.0.15 +更新于2024-07-08 + +1.增加屏幕截图组件 + ### 版本:1.0.8 更新于2024-06-11 diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/MobileLibrary.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/MobileLibrary.java index 33e8a05..7ea20c2 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/MobileLibrary.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/MobileLibrary.java @@ -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 String NAME = "NK.mobile"; 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; @Override diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/entity/StepTarget.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/entity/StepTarget.java new file mode 100644 index 0000000..99b5243 --- /dev/null +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/entity/StepTarget.java @@ -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; + } +} diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/AndroidTools.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/AndroidTools.java index bece456..676562b 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/AndroidTools.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/AndroidTools.java @@ -32,7 +32,7 @@ public class AndroidTools { * @param deviceDriver 设备连接驱动 * @return */ - @Keyword(alias = "点击返回健", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "点击返回健", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "点击返回健", type = DataType.BOOLEAN) public boolean pressKeyBack(@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver) { PressKeyBackThread pressKeyBackThread = new PressKeyBackThread(deviceDriver); @@ -87,7 +87,7 @@ public class AndroidTools { * @param deviceDriver 设备连接驱动 * @return */ - @Keyword(alias = "点击多任务键", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "点击多任务键", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "点击多任务键", type = DataType.BOOLEAN) public boolean pressKeyMenu(@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver) { PressKeyMenuThread pressKeyMenuThread = new PressKeyMenuThread(deviceDriver); diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/CommonTools.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/CommonTools.java index c5b63de..ed48d2a 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/CommonTools.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/keywords/CommonTools.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.ReUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; 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.IExecuteContext; import net.northking.cctp.element.core.IStepTarget; @@ -55,7 +56,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "点击文本", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "点击文本", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN) public Boolean clickText( IExecuteContext context, @@ -119,7 +120,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "点击文本(新)", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "点击文本(新)", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "是否点击成功", type = DataType.BOOLEAN) public Boolean clickTextNew( IExecuteContext context, @@ -184,7 +185,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return 返回判断的结果 */ - @Keyword(alias = "是否存在文本", category = "0", attributes = "45") + @Keyword(alias = "是否存在文本", category = "0", attributes = "4") @Return(name = "result", comment = "文本存在与不存在的结果", type = DataType.BOOLEAN) public Boolean ifText( IExecuteContext context, @@ -251,7 +252,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "点击控件", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "点击控件", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "点击结果", type = DataType.BOOLEAN) public boolean clickElement(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -321,7 +322,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "是否存在元素", category = "0", attributes = "45") + @Keyword(alias = "是否存在元素", category = "0", attributes = "4") @Return(name = "result", comment = "是否存在元素的结果", type = DataType.BOOLEAN) public boolean ifElement(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -386,7 +387,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "标准滑动", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "标准滑动", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "滑动结果", type = DataType.BOOLEAN) public boolean standardSwipe( @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 执行后等待 * @return */ - @Keyword(alias = "控件滑动", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "控件滑动", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN) 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, @@ -505,6 +506,71 @@ public class CommonTools { return aBoolean; } + /** + *

滑动查找控件

+ * + * @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 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 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; + } + /** *

在控件中输入文本

* @@ -520,7 +586,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - /*@Keyword(alias = "输入文本", category = "0", attributes = "4", commonlyUse = true) + /*@Keyword(alias = "输入文本", category = "0", attributes = "45", commonlyUse = true) @Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN) public boolean inputText(IExecuteContext context, @Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text, @@ -590,7 +656,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "控件输入文本", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "控件输入文本", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN) public boolean inputText(IExecuteContext context, @Argument(name = "text", scope = ParamScope.VARIABLES, comment = "输入的文本", type = DataType.STRING) String text, @@ -661,7 +727,7 @@ public class CommonTools { * @param value 输出值 * @return */ - @Keyword(alias = "获取控件的值", category = "0", attributes = "45") + @Keyword(alias = "获取控件的值", category = "0", attributes = "4") @Return(name = "result", comment = "输入的结果", type = DataType.OBJECT) public String getElementText(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -729,7 +795,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "长按控件", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "长按控件", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "输入的结果", type = DataType.BOOLEAN) public boolean longPress(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -795,7 +861,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "断言元素", category = "0", attributes = "45", commonlyUse = true) + @Keyword(alias = "断言元素", category = "0", attributes = "4", commonlyUse = true) @Return(name = "result", comment = "断言结果", type = DataType.BOOLEAN) public boolean assertElement(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -861,7 +927,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @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) public boolean handleApp(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -919,7 +985,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @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) public boolean pressKeyHome(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -978,7 +1044,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "识别验证码", category = "0", attributes = "45") + @Keyword(alias = "识别验证码", category = "0", attributes = "4") @Return(name = "result", comment = "验证码识别", type = DataType.STRING) public String getCodeByOcr( IExecuteContext context, @@ -1061,7 +1127,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @return */ - @Keyword(alias = "monkey测试", category = "0", attributes = "45") + @Keyword(alias = "monkey测试", category = "0", attributes = "4") @Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN) public boolean monkeyTest( IExecuteContext context, @@ -1134,7 +1200,7 @@ public class CommonTools { * @param waitTime 等待时间(单位s) * @return */ - @Keyword(alias = "等待", category = "0", attributes = "45") + @Keyword(alias = "等待", category = "0", attributes = "4") @Return(name = "result", comment = "验证码识别", type = DataType.BOOLEAN) public boolean wait( @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -1186,7 +1252,7 @@ public class CommonTools { * * @return */ - @Keyword(alias = "接口方式获取验证码", category = "0", attributes = "45") + @Keyword(alias = "接口方式获取验证码", category = "0", attributes = "4") @Return(name = "result", comment = "接口方式获取验证码", type = DataType.BOOLEAN) public String getCodeByHttp( IExecuteContext context, @@ -1222,7 +1288,7 @@ public class CommonTools { return result; } - @Keyword(alias = "模拟键盘输入", category = "0", attributes = "45") + @Keyword(alias = "模拟键盘输入", category = "0", attributes = "4") @Return(name = "result", comment = "模拟键盘输入", type = DataType.BOOLEAN) public Boolean inputFromKeyBoard( IExecuteContext context, @@ -1274,7 +1340,7 @@ public class CommonTools { return result; } - @Keyword(alias = "输入密码", category = "0", attributes = "45") + @Keyword(alias = "输入密码", category = "0", attributes = "4") @Return(name = "result", comment = "输入密码", type = DataType.BOOLEAN) public Boolean inputPassword( IExecuteContext context, @@ -1325,7 +1391,7 @@ public class CommonTools { } /** - *

点击控件

+ *

密码输入(ocr)

* * @param deviceDriver 设备驱动链接 * @param targets 定位控件的信息 @@ -1334,7 +1400,7 @@ public class CommonTools { * @param sufExecuteWait 执行后等待 * @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) public boolean inputPasswordByOcr(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -1396,7 +1462,7 @@ public class CommonTools { * @param value 输出值 * @return */ - @Keyword(alias = "识别金额", category = "0", attributes = "45") + @Keyword(alias = "识别金额", category = "0", attributes = "4") @Return(name = "result", comment = "输入的结果", type = DataType.OBJECT) public String getElementMoneyText(IExecuteContext context, @Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver, @@ -1447,6 +1513,127 @@ public class CommonTools { return result; } + /** + *

屏幕截图

+ * + * @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 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; + } + + /** + *

获取控件的值(OCR方式)

+ * + * @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 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 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) { try { if (variable.contains("#{") && variable.contains("}")) { diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/GetElementValueByOcrThread.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/GetElementValueByOcrThread.java new file mode 100644 index 0000000..602cc82 --- /dev/null +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/GetElementValueByOcrThread.java @@ -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 { + + private Logger logger = LoggerFactory.getLogger(GetElementValueByOcrThread.class); + + private IExecuteContext context; + private DeviceDriver deviceDriver; + private List targets; + private Integer waitTimeout; + private Float preExecuteWait; + private Float sufExecuteWait; + + + public GetElementValueByOcrThread(IExecuteContext context, DeviceDriver deviceDriver, List 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 driver = (AppiumDriver) 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 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 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 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 ocrParamMap = new HashMap<>(); + ocrParamMap.put("img_base64", imgBase64); + ocrParamMap.put("targets", ""); + HttpEntity 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 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 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 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 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> 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; + } + + +} diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/ScreenShotThread.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/ScreenShotThread.java new file mode 100644 index 0000000..740fa13 --- /dev/null +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/ScreenShotThread.java @@ -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 { + + 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 driver = (AppiumDriver) deviceDriver; + CommonUtils.screenShot(context,driver); + CommonUtils.handleSufExecuteWait(sufExecuteWait); + return true; + } +} diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/SwipeAndFindTargetElementThread.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/SwipeAndFindTargetElementThread.java new file mode 100644 index 0000000..3046832 --- /dev/null +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/thread/SwipeAndFindTargetElementThread.java @@ -0,0 +1,77 @@ +package net.northking.cctp.element.mobile.thread; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +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.entity.StepTarget; +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.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +public class SwipeAndFindTargetElementThread implements Callable { + + private Logger logger = LoggerFactory.getLogger(SwipeAndFindTargetElementThread.class); + + private IExecuteContext context; + private DeviceDriver deviceDriver; + private List targets; + private String referenceStr; + private Integer waitTimeout; + private String waitStatusStr; + private String direction; + private Integer index; + private Float preExecuteWait; + private Float sufExecuteWait; + + public SwipeAndFindTargetElementThread(IExecuteContext context, DeviceDriver deviceDriver, List targets, String referenceStr, Integer waitTimeout, String waitStatusStr, String direction, Integer index, Float preExecuteWait, Float sufExecuteWait) { + this.context = context; + this.deviceDriver = deviceDriver; + this.targets = targets; + this.referenceStr = referenceStr; + this.waitTimeout = waitTimeout; + this.waitStatusStr = waitStatusStr; + this.direction = direction; + this.index = index; + this.preExecuteWait = preExecuteWait; + this.sufExecuteWait = sufExecuteWait; + } + + @Override + public Boolean call() throws Exception { + CommonUtils.handlePreExecuteWait(preExecuteWait); + boolean waitStatus = "1".equals(waitStatusStr); + if (null == waitTimeout || waitTimeout <= 0) { + waitTimeout = 30; + } + logger.debug("执行滑动查找控件的操作,滑动控件:{}", referenceStr); + List references = JSONObject.parseArray(referenceStr, StepTarget.class); + List iStepTargets = new ArrayList<>(); + for (StepTarget reference : references) { + iStepTargets.add(reference); + } + AppiumDriver driver = (AppiumDriver) deviceDriver; + PointMessage point = ElementUtil.findWebElement(context, driver, iStepTargets, waitTimeout, waitStatus, "0", 0); + if (null == point) { + logger.warn("没有找到要滑动的控件位置...................."); + throw new ExecuteException("未找到需要滑动的元素"); + } + logger.debug("找到要滑动的控件位置:{}",JSON.toJSONString(point)); + PointMessage targetPoint = ElementUtil.swipeAndFindTargetElement(point,context, driver, this.targets,waitTimeout,direction,index); + CommonUtils.handleSufExecuteWait(sufExecuteWait); + return targetPoint != null; + } +} diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/utils/phoneUtils/ElementUtil.java b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/utils/phoneUtils/ElementUtil.java index a514812..f610a04 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/utils/phoneUtils/ElementUtil.java +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/java/net/northking/cctp/element/mobile/utils/phoneUtils/ElementUtil.java @@ -1708,6 +1708,344 @@ public class ElementUtil { return exist; } + public static PointMessage swipeAndFindTargetElement(PointMessage swipePoint, IExecuteContext context, AppiumDriver driver, List targets, Integer waitTimeout, String direction, Integer index) { + logger.debug("执行滑动查找控件的操作,目标控件:{}", JSON.toJSONString(targets)); + long startTime = System.currentTimeMillis(); + long endTime = System.currentTimeMillis(); + PointMessage targetPoint = null; + int num = 1; + do { + logger.debug("开始第{}次查找", num); + targetPoint = findWebElementByTargets(context, driver, targets, index); + logger.debug("第{}次查找的结果:{}", num,JSON.toJSONString(targetPoint)); + if (null == targetPoint) { + logger.debug("开始第{}次滑屏",num); + swipeScreenByPoint(swipePoint,driver,direction,context); + } + num++; + endTime = System.currentTimeMillis(); + } while (targetPoint == null && endTime - startTime < waitTimeout * 1000); + logger.debug("在规定的时间内查找的结果:{}", JSON.toJSONString(targetPoint)); + return targetPoint; + } + + private static void swipeScreenByPoint(PointMessage swipePoint, AppiumDriver driver, String direction, IExecuteContext context) { + int x1 = swipePoint.getX(); + int y1 = swipePoint.getY(); + int x2, y2; + if ("up".equalsIgnoreCase(direction)) { + x2 = x1; + y2 = y1 - 500; + } else if ("down".equalsIgnoreCase(direction)) { + x2 = x1; + y2 = y1 + 500; + } else if ("left".equalsIgnoreCase(direction)) { + x2 = x1 - 500; + y2 = y1; + } else if ("right".equalsIgnoreCase(direction)) { + x2 = x1 + 500; + y2 = y1; + } else { + throw new ParamMistakeException("滑动方向仅支持:up、down、left、right"); + } + logger.debug("开始节点x:{} y:{},结束节点x:{},y:{}", x1, y1, x2, y2); + try { + new TouchAction(driver).press(PointOption.point(x1, y1)) + .waitAction(WaitOptions.waitOptions(Duration.ofMillis(2000))) + .moveTo(PointOption.point(x2, y2)) + .release().perform(); + } catch (WebDriverException e) { + if (e instanceof NoSuchSessionException) { + throw e; + } else { + logger.warn("driver 抽风了。。。。。,重新来一次"); + try { + Thread.sleep(1500); + } catch (InterruptedException interruptedException) { + + } + new TouchAction(driver).press(PointOption.point(x1, y1)) + .waitAction(WaitOptions.waitOptions(Duration.ofMillis(2000))) + .moveTo(PointOption.point(x2, y2)) + .release().perform(); + } + } + } + + private static PointMessage findWebElementByTargets(IExecuteContext context, AppiumDriver driver, List targets, Integer index) { + PointMessage targetPoint = null; + for (IStepTarget target : targets) { + switch (target.getUsing()) { + case UsingType.SELECTOR: + targetPoint = findElementByXpath(driver, target, index); + if (null != targetPoint) { + return targetPoint; + } + break; + case UsingType.OCR: + targetPoint = findElementByOcr(target, driver, context); + if (null != targetPoint) { + return targetPoint; + } + break; + case UsingType.POINT: + targetPoint = pointFind(target, driver, context); + if (null != targetPoint) { + return targetPoint; + } + break; + case UsingType.IMAGE: + targetPoint = findElementByImage(context, driver, target); + if (null != targetPoint) { + return targetPoint; + } + break; + default: + logger.warn("不支持的查找元素方式:{}", target.getUsing()); + break; + } + } + return null; + } + + private static PointMessage findElementByImage(IExecuteContext context, AppiumDriver driver, IStepTarget target) { + PointMessage point = null; + String serverAddress = context.getProperty(ConfigPath.SERVER_URL); //图片下载地址 + String picDownloadAddress = context.getProperty(ConfigPath.DOWNLOAD_PIC_ADDRESS); //图片下载地址 + String findPicAddress = context.getProperty(ConfigPath.FIND_PIC_ADDRESS_KEY); //找图片地址 + String screenShotAddress = context.getProperty(ConfigPath.MOBILE_SCREEN_SHOT_ADDRESS_KEY); //截全屏地址 + JSONObject value = JSON.parseObject(target.getValue(), JSONObject.class); +// Float faultRate = value.getFloat("faultRate"); + Float faultRate = 0.8f; + String imageUrl = value.getString("imgUri"); + String fileName = imageUrl.substring(imageUrl.lastIndexOf("_") + 1); + Map deviceInfo = context.getDeviceInfo(); + String host = driver.getRemoteAddress().getHost(); //上位机地址 + String port = (String) deviceInfo.get("port"); //上位机端口 + String resolution = (String) deviceInfo.get("resolution"); //分辨率 + String url = serverAddress + picDownloadAddress + imageUrl; + String shotAllScreenAddress = String.format(ConfigPath.HTTP_FORMAT, host, port, screenShotAddress); + HttpUtils.downloadFileToLocal(url, System.getProperty("user.dir") + "/tempPic", fileName); + File file = new File(System.getProperty("user.dir") + "/tempPic/" + fileName); + String base64Img = Base64.encode(file); + Map area = findAreaByImage(shotAllScreenAddress, findPicAddress, driver, 3, base64Img, faultRate, resolution, "0", 0); + if (null != area) { + try { + byte[] bytes = new BASE64Decoder().decodeBuffer(base64Img); + BufferedImage read = ImageIO.read(new ByteArrayInputStream(bytes)); + int width = read.getWidth(); + int height = read.getHeight(); + point = new PointMessage(area.get("x").intValue() + width / 2, area.get("y").intValue() + height / 2, true); + } catch (IOException e) { + logger.error("转换base64图片失败:{}", e); + } + } + return point; + } + + private static PointMessage findElementByOcr(IStepTarget target, AppiumDriver driver, IExecuteContext context) { + String ocrAddress = context.getProperty(ConfigPath.OCR_ADDRESS_KEY); + String mobileShotAddress = context.getProperty(ConfigPath.MOBILE_SHOT_ADDRESS_KEY); + PointMessage point = null; + JSONObject value = JSON.parseObject(target.getValue(), JSONObject.class); + Integer x = value.getInteger("x"); + Integer y = value.getInteger("y"); + Integer screenWidth = value.getInteger("screenWidth"); + Integer screenHeight = value.getInteger("screenHeight"); + Map deviceInfo = context.getDeviceInfo(); + String resolution = (String) deviceInfo.get("resolution"); + boolean[] rotation = new boolean[1]; //默认是正常手机的宽高 + rotation[0] = true; + if (screenWidth > screenHeight) { + rotation[0] = false; + } + Map 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", value.getInteger("width")); + paramMap.put("width", value.getInteger("height")); + paramMap.put("resolution", resolution); + paramMap.put("x", x); + paramMap.put("y", y); + paramMap.put("screenLength", screenWidth); + paramMap.put("screenWidth", screenHeight); + logger.info("参数:{}", JSON.toJSONString(paramMap)); + HttpEntity> entity = new HttpEntity<>(paramMap); + String host = driver.getRemoteAddress().getHost(); //上位机地址 + String port = (String) deviceInfo.get("port"); //上位机端口 + String shotScreenAddress = String.format(ConfigPath.HTTP_FORMAT, host, port, mobileShotAddress); + String result = null; + try { + result = HttpUtils.doPost(shotScreenAddress, entity, String.class); + } catch (Exception e) { + logger.error("截图失败", e); + } + if (null == result) { + return point; + } + boolean useNewOcr = Boolean.parseBoolean(context.getProperty(ConfigPath.USE_NEW_OCR)); + if (useNewOcr) { + ocrAddress = context.getProperty(ConfigPath.OCR_GET_ALL_TEXT_NUM_KEY); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + String ocrText = value.getString("ocrText"); + Integer index = value.getInteger("index"); + Map ocrParamMap = new HashMap<>(); + ocrParamMap.put("img_base64", result); + if (!StringUtils.hasText(ocrText)) { + throw new ExecuteException("点击的文本不能为空!"); + } + ocrParamMap.put("targets", ocrText); + HttpEntity> 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); + } + if (!CollectionUtils.isEmpty(ocrResultList)) { + String[] split = resolution.split("\\*"); + int width = Integer.parseInt(split[0]); + int height = Integer.parseInt(split[1]); + int realityWidth = width; + int realityHeight = height; + if (rotation[0]) { //正常 + if (width > height) { //宽大于高,调换 + realityWidth = height; + realityHeight = width; + } + } else { //不正常,横屏 + if (width < height) { //宽小于高,调换 + realityWidth = height; + realityHeight = width; + } + } + Double realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth; + Double realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight; + Object all = ocrResultList.get(0); + JSONObject jsonObject = JSONObject.parseObject(JSONArray.toJSONString(all)); + //todo:如果传了;是多个文本一起找,暂时只考虑一个文本 + JSONArray data = jsonObject.getJSONArray(ocrText); //所有坐标 + if (!CollectionUtils.isEmpty(data)) { + if (data.size() == 1) { + JSONObject itemPoint = JSONObject.parseObject(JSONObject.toJSONString(data.get(0))); + point = new PointMessage(itemPoint.getInteger("x") + (itemPoint.getInteger("w") / 2) + realityX.intValue(), + itemPoint.getInteger("y") + (itemPoint.getInteger("h") / 2) + realityY.intValue(), true); + } else { + if (index > 0) { + if (index <= data.size()) { + point = sortPointAndGetIndexPoint(data, index); + } else { + throw new ExecuteException(String.format("选择元素的位置大于找到元素的个数,找到元素%s个", data.size())); + } + } + } + } + } + } else { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + Map ocrParamMap = new HashMap<>(); + ocrParamMap.put("base64Image", result); + ocrParamMap.put("CaseSensitive", (null == value.getBoolean("CaseSensitive")) ? false : value.getBoolean("CaseSensitive")); + if (!StringUtils.hasText(value.getString("ocrText"))) { + throw new ExecuteException("ocr查找的文本不能为空!"); + } + ocrParamMap.put("ocrText", value.getString("ocrText")); + HttpEntity> ocrEntity = new HttpEntity<>(ocrParamMap, headers); + JSONObject ocrResultMap = null; + try { + logger.info("传参:{}", JSON.toJSONString(ocrParamMap)); + ocrResultMap = HttpUtils.doPost(ocrAddress, ocrEntity, JSONObject.class); + logger.info("得到的ocr结果:{}", ocrResultMap); + } catch (Exception e) { + logger.error("ocr失败", e); + } + if (null != ocrResultMap) { + Boolean success = ocrResultMap.getBoolean("success"); + if (success) { + //转换比例 + String[] split = resolution.split("\\*"); + int width = Integer.parseInt(split[0]); + int height = Integer.parseInt(split[1]); + + int realityWidth = width; + int realityHeight = height; + if (width > height) { //宽高反了的情况,调换 + realityWidth = height; + realityHeight = width; + } + Double realityX = x.doubleValue() / screenWidth.doubleValue() * realityWidth; + Double realityY = y.doubleValue() / screenHeight.doubleValue() * realityHeight; + JSONArray data = ocrResultMap.getJSONArray("data"); + Integer index = value.getInteger("index"); + JSONObject pointMap = null; + if (data != null && data.size() == 1) { //只找到一个,直接返回 + pointMap = data.getJSONObject(0); + } else if (data != null && data.size() > 1) { //找到多个,选择用户选择的第几个 + if (index > 0) { + if (index <= data.size()) { + pointMap = data.getJSONObject(index - 1); + } + } + } + if (null != pointMap) { + point = new PointMessage(realityX.intValue() + pointMap.getInteger("x"), realityY.intValue() + pointMap.getInteger("y"), true); + } + } + } + } + return point; + } + + private static PointMessage findElementByXpath(AppiumDriver driver, IStepTarget target, Integer index) { + PointMessage point = null; + 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(3, TimeUnit.SECONDS); + } catch (NoSuchSessionException e) { + throw e; + } + WebElement webElement = null; + + List elements = null; + try { + elements = driver.findElements(By.xpath(xpath)); + } catch (NoSuchSessionException e) { + throw e; + } + logger.info("当前页面找到元素{}个", elements.size()); + if (!CollectionUtils.isEmpty(elements)) { //xpath找到了 + if (elements.size() == 1) { + webElement = elements.get(0); + } else { //找到多个时,暂时还是返回第一个 + logger.info("用xpath在当前页面找到{}元素,拿第{}个元素.......................", elements.size(), index); + if (index <= elements.size()) { + webElement = elements.get(index - 1); + } + } + } else { //xpath没找到 + logger.info("用xpath在当前页面没有找到元素......................."); + } + if (null != webElement) { + Rectangle rect = webElement.getRect(); + int x = rect.getX(); + int y = rect.getY(); + int width = rect.getWidth(); + int height = rect.getHeight(); + point = new PointMessage(x + width / 2, y + height / 2, false); + } + return point; + } + private static boolean xpathFindElementExist(AppiumDriver driver, IStepTarget target, int timeOut, String swipe, Integer swipeCount) { JSONObject object = JSON.parseObject(target.getValue(), JSONObject.class); String xpath = object.getString(UsingType.Key.SELECTOR_KEY); diff --git a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/resources/changelog.md b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/resources/changelog.md index 7f3e22d..24f381d 100644 --- a/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/resources/changelog.md +++ b/cctp-test-element/cctp-test-element-library/cctp-test-element-mobile/src/main/resources/changelog.md @@ -1,3 +1,6 @@ +### 版本:2.1.46 +更新于2024-07-08 +- 增加屏幕截图组件 ### 版本:2.1.32 更新于2024-06-13 - 杭银OCR添加验证码功能 diff --git a/cctp-test-element/cctp-test-element-library/pom.xml b/cctp-test-element/cctp-test-element-library/pom.xml index b6642e9..26bcb5e 100644 --- a/cctp-test-element/cctp-test-element-library/pom.xml +++ b/cctp-test-element/cctp-test-element-library/pom.xml @@ -27,7 +27,7 @@ net.northking.cctp cctp-test-element-core - 1.0.0-RELEASE + 1.0.2-SNAPSHOT provided diff --git a/cloud-basic/.gitignore b/cloud-basic/.gitignore new file mode 100644 index 0000000..d11383e --- /dev/null +++ b/cloud-basic/.gitignore @@ -0,0 +1,28 @@ +# Compiled class file +*.class + +# Log file +*.log +*.log.gz +logs/ +*/logs/ +*/*/logs/ + +# Temp file +*.temp + +# IDEA profile dir +.idea/ +*/.idea/ +*/*/.idea/ + +# IDEA project file +*.iml +*/*.iml +*/*/*.iml +*/*/*/*.iml + +target/ +*/target/ +*/*/target/ + diff --git a/cloud-basic/cctp-maven-plugin/src/main/java/net/northking/cctp/maven/analyse/dialects/MySQLDialect.java b/cloud-basic/cctp-maven-plugin/src/main/java/net/northking/cctp/maven/analyse/dialects/MySQLDialect.java index 08f0e9d..ba6dbf2 100644 --- a/cloud-basic/cctp-maven-plugin/src/main/java/net/northking/cctp/maven/analyse/dialects/MySQLDialect.java +++ b/cloud-basic/cctp-maven-plugin/src/main/java/net/northking/cctp/maven/analyse/dialects/MySQLDialect.java @@ -107,9 +107,6 @@ public final class MySQLDialect extends AbsDialect rs.close(); pstmt.close(); } - if (conn != null){ - conn.close(); - } } } diff --git a/cloud-platform/.gitignore b/cloud-platform/.gitignore new file mode 100644 index 0000000..14745ad --- /dev/null +++ b/cloud-platform/.gitignore @@ -0,0 +1,35 @@ +# 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/ + + +Junit test +*/src/test/java/ +*/src/test/resources/ + diff --git a/cloud-platform/cctp-platform/pom.xml b/cloud-platform/cctp-platform/pom.xml index 195e55a..ba0c280 100644 --- a/cloud-platform/cctp-platform/pom.xml +++ b/cloud-platform/cctp-platform/pom.xml @@ -141,7 +141,7 @@ net.northking.cctp cctp-test-element-core - ${project.version} + 1.0.2-SNAPSHOT diff --git a/sql/atu-execute-plan/V2.7.0_cjf_add_atu_plan_20240705.sql b/sql/atu-execute-plan/V2.7.0_cjf_add_atu_plan_20240705.sql new file mode 100644 index 0000000..a4b0205 --- /dev/null +++ b/sql/atu-execute-plan/V2.7.0_cjf_add_atu_plan_20240705.sql @@ -0,0 +1,5 @@ +ALTER TABLE atu_execute_plan.atu_plan_task +ADD COLUMN `last_heartbeat_time` datetime NULL COMMENT '执行心跳时间' AFTER `created_time`; + +ALTER TABLE atu_execute_plan.atu_plan_scene_case_task +ADD COLUMN `last_heartbeat_time` datetime NULL COMMENT '执行心跳时间' AFTER `created_time`; \ No newline at end of file diff --git a/sqlBackend/atu-execute-plan/20240627/V2.6.0_wjd_clean_atu_plan20240612.sql b/sqlBackend/atu-execute-plan/20240627/V2.6.0_wjd_clean_atu_plan20240612.sql new file mode 100644 index 0000000..cac5229 --- /dev/null +++ b/sqlBackend/atu-execute-plan/20240627/V2.6.0_wjd_clean_atu_plan20240612.sql @@ -0,0 +1,6 @@ +RENAME TABLE atu_execute_plan.atu_execplan_tag to atu_execplan_tag_bak20240429; +RENAME TABLE atu_execute_plan.atu_mail_record to atu_mail_record_bak20240429; +RENAME TABLE atu_execute_plan.atu_plan_case_set_link to atu_plan_case_set_link_bak20240429; +RENAME TABLE atu_execute_plan.atu_plan_script_input_link to atu_plan_script_input_link_bak20240429; +RENAME TABLE atu_execute_plan.atu_plan_script_input_link_md to atu_plan_script_input_link_md_bak20240429; + \ No newline at end of file diff --git a/sqlBackend/atu-script-case/20240627/V2.6.0_wjd_clean_atu_script_case_20240612.sql b/sqlBackend/atu-script-case/20240627/V2.6.0_wjd_clean_atu_script_case_20240612.sql new file mode 100644 index 0000000..2565d93 --- /dev/null +++ b/sqlBackend/atu-script-case/20240627/V2.6.0_wjd_clean_atu_script_case_20240612.sql @@ -0,0 +1,14 @@ +RENAME TABLE atu_script_case. atu_case_info to atu_case_info_bak20240429; +RENAME TABLE atu_script_case. atu_case_script_link to atu_case_script_link_bak20240429; +RENAME TABLE atu_script_case. atu_case_set to atu_case_set_bak20240429; +RENAME TABLE atu_script_case. atu_case_set_case_link to atu_case_set_case_link_bak20240429; +RENAME TABLE atu_script_case. atu_case_set_case_link_md to atu_case_set_case_link_md_bak20240429; +RENAME TABLE atu_script_case. atu_case_tag to atu_case_tag_bak20240429; +RENAME TABLE atu_script_case. atu_case_tree to atu_case_tree_bak20240429; +RENAME TABLE atu_script_case. atu_db_config to atu_db_config_bak20240429; +RENAME TABLE atu_script_case. atu_project_env to atu_project_env_bak20240429; +RENAME TABLE atu_script_case. his_bus_component_info to his_bus_component_info_bak20240429; +RENAME TABLE atu_script_case. his_component_param to his_component_param_bak20240429; +RENAME TABLE atu_script_case. library_com_param to library_com_param_bak20240429; +RENAME TABLE atu_script_case. re_bus_component_info to re_bus_component_info_bak20240429; +RENAME TABLE atu_script_case. re_component_param to re_component_param_bak20240429; \ No newline at end of file diff --git a/sqlBackend/cctp-device-mgr/20240627/V2.6.0_wjd_clean_driver_mgr_20240612.sql b/sqlBackend/cctp-device-mgr/20240627/V2.6.0_wjd_clean_driver_mgr_20240612.sql new file mode 100644 index 0000000..b43a598 --- /dev/null +++ b/sqlBackend/cctp-device-mgr/20240627/V2.6.0_wjd_clean_driver_mgr_20240612.sql @@ -0,0 +1 @@ +RENAME TABLE cctp_device_mgr. cd_engine_log to cd_engine_log_bak20240429; diff --git a/sqlBackend/cctp-mobile/20240627/V2.6.0_wjd_clean_cctp_mobile_20240612.sql b/sqlBackend/cctp-mobile/20240627/V2.6.0_wjd_clean_cctp_mobile_20240612.sql new file mode 100644 index 0000000..2c540dc --- /dev/null +++ b/sqlBackend/cctp-mobile/20240627/V2.6.0_wjd_clean_cctp_mobile_20240612.sql @@ -0,0 +1,6 @@ +RENAME TABLE cctp_mobile_business. app to app_bak20240429; +RENAME TABLE cctp_mobile_business. device_online_record to device_online_record_bak20240429; +RENAME TABLE cctp_mobile_business. device_owner to device_owner_bak20240429; +RENAME TABLE cctp_mobile_business. device_owner_time to device_owner_time_bak20240429; +RENAME TABLE cctp_mobile_business. device_use_report to device_use_report_bak20240429; +RENAME TABLE cctp_mobile_business. device_use_report_detail to device_use_report_detail_bak20240429; \ No newline at end of file diff --git a/sqlBackend/cctp-platform/20240627/V2.6.0_wjd_clean_cctp_platform_20240612.sql b/sqlBackend/cctp-platform/20240627/V2.6.0_wjd_clean_cctp_platform_20240612.sql new file mode 100644 index 0000000..13d07e8 --- /dev/null +++ b/sqlBackend/cctp-platform/20240627/V2.6.0_wjd_clean_cctp_platform_20240612.sql @@ -0,0 +1,36 @@ +RENAME TABLE cctp_platform. clt_dict_catalog to clt_dict_catalog_bak20240429; +RENAME TABLE cctp_platform. clt_dict_item to clt_dict_item_bak20240429; +RENAME TABLE cctp_platform. clt_func to clt_func_bak20240429; +RENAME TABLE cctp_platform. clt_func_resource to clt_func_resource_bak20240429; +RENAME TABLE cctp_platform. clt_org to clt_org_bak20240429; +RENAME TABLE cctp_platform. clt_org_manager to clt_org_manager_bak20240429; +RENAME TABLE cctp_platform. clt_personalize to clt_personalize_bak20240429; +RENAME TABLE cctp_platform. clt_resource to clt_resource_bak20240429; +RENAME TABLE cctp_platform. clt_role to clt_role_bak20240429; +RENAME TABLE cctp_platform. clt_role_func to clt_role_func_bak20240429; +RENAME TABLE cctp_platform. clt_sys_dict_catalog to clt_sys_dict_catalog_bak20240429; +RENAME TABLE cctp_platform. clt_sys_dict_item to clt_sys_dict_item_bak20240429; +RENAME TABLE cctp_platform. clt_test_team to clt_test_team_bak20240429; +RENAME TABLE cctp_platform. clt_test_team_member to clt_test_team_member_bak20240429; +RENAME TABLE cctp_platform. clt_user to clt_user_bak20240429; +RENAME TABLE cctp_platform. clt_user_extend to clt_user_extend_bak20240429; +RENAME TABLE cctp_platform. clt_user_login to clt_user_login_bak20240429; +RENAME TABLE cctp_platform. clt_user_role to clt_user_role_bak20240429; +RENAME TABLE cctp_platform. cp_attach to cp_attach_bak20240429; +RENAME TABLE cctp_platform. cp_env to cp_env_bak20240429; +RENAME TABLE cctp_platform. cp_feedback to cp_feedback_bak20240429; +RENAME TABLE cctp_platform. cp_init_data to cp_init_data_bak20240429; +RENAME TABLE cctp_platform. cp_knowledge_info to cp_knowledge_info_bak20240429; +RENAME TABLE cctp_platform. cp_knowledge_tree to cp_knowledge_tree_bak20240429; +RENAME TABLE cctp_platform. cp_label to cp_label_bak20240429; +RENAME TABLE cctp_platform. cp_manage_template to cp_manage_template_bak20240429; +RENAME TABLE cctp_platform. cp_manage_template_layer to cp_manage_template_layer_bak20240429; +RENAME TABLE cctp_platform. cp_notice to cp_notice_bak20240429; +RENAME TABLE cctp_platform. cp_notice_extend to cp_notice_extend_bak20240429; +RENAME TABLE cctp_platform. cp_rule_collection to cp_rule_collection_bak20240429; +RENAME TABLE cctp_platform. cp_rule_config to cp_rule_config_bak20240429; +RENAME TABLE cctp_platform. cp_template to cp_template_bak20240429; +RENAME TABLE cctp_platform. cp_template_project_type to cp_template_project_type_bak20240429; +RENAME TABLE cctp_platform. cp_template_properties to cp_template_properties_bak20240429; +RENAME TABLE cctp_platform. cp_workbench_resource to cp_workbench_resource_bak20240429; +RENAME TABLE cctp_platform. environment_temp to environment_temp_bak20240429; \ No newline at end of file diff --git a/sqlBackend/cctp-projects/20240627/V2.6.0_wjd_clean_projects_20240612.sql b/sqlBackend/cctp-projects/20240627/V2.6.0_wjd_clean_projects_20240612.sql new file mode 100644 index 0000000..c926c11 --- /dev/null +++ b/sqlBackend/cctp-projects/20240627/V2.6.0_wjd_clean_projects_20240612.sql @@ -0,0 +1,25 @@ +RENAME TABLE cctp_projects. clt_oper_project to clt_oper_project_bak20240429; +RENAME TABLE cctp_projects. cp_demand_manager to cp_demand_manager_bak20240429; +RENAME TABLE cctp_projects. cp_demand_plan to cp_demand_plan_bak20240429; +RENAME TABLE cctp_projects. cp_demand_system to cp_demand_system_bak20240429; +RENAME TABLE cctp_projects. cp_project to cp_project_bak20240429; +RENAME TABLE cctp_projects. cp_project_bug_process to cp_project_bug_process_bak20240429; +RENAME TABLE cctp_projects. cp_project_demand to cp_project_demand_bak20240429; +RENAME TABLE cctp_projects. cp_project_details to cp_project_details_bak20240429; +RENAME TABLE cctp_projects. cp_project_env to cp_project_env_bak20240429; +RENAME TABLE cctp_projects. cp_project_implement_process to cp_project_implement_process_bak20240429; +RENAME TABLE cctp_projects. cp_project_layer to cp_project_layer_bak20240429; +RENAME TABLE cctp_projects. cp_project_manage to cp_project_manage_bak20240429; +RENAME TABLE cctp_projects. cp_project_principal to cp_project_principal_bak20240429; +RENAME TABLE cctp_projects. cp_project_risk to cp_project_risk_bak20240429; +RENAME TABLE cctp_projects. cp_project_rule to cp_project_rule_bak20240429; +RENAME TABLE cctp_projects. cp_project_template to cp_project_template_bak20240429; +RENAME TABLE cctp_projects. cp_team to cp_team_bak20240429; +RENAME TABLE cctp_projects. cp_team_member to cp_team_member_bak20240429; +RENAME TABLE cctp_projects. mt_project_group to mt_project_group_bak20240429; +RENAME TABLE cctp_projects. mt_project_group_user to mt_project_group_user_bak20240429; +RENAME TABLE cctp_projects. mt_project_team to mt_project_team_bak20240429; +RENAME TABLE cctp_projects. mt_project_team_role to mt_project_team_role_bak20240429; +RENAME TABLE cctp_projects. mt_project_template to mt_project_template_bak20240429; +RENAME TABLE cctp_projects. project_overall_gantt_chart to project_overall_gantt_chart_bak20240429; +RENAME TABLE cctp_projects. project_test_report to project_test_report_bak20240429; \ No newline at end of file diff --git a/sqlBackend/cctp-saas/20240627/V2.6.0_wjd_clean_saas_20240612.sql b/sqlBackend/cctp-saas/20240627/V2.6.0_wjd_clean_saas_20240612.sql new file mode 100644 index 0000000..a241ce5 --- /dev/null +++ b/sqlBackend/cctp-saas/20240627/V2.6.0_wjd_clean_saas_20240612.sql @@ -0,0 +1,13 @@ +RENAME TABLE cctp_saas. saas_announcement to saas_announcement_bak20240429; +RENAME TABLE cctp_saas. saas_app to saas_app_bak20240429; +RENAME TABLE cctp_saas. saas_app_data_source to saas_app_data_source_bak20240429; +RENAME TABLE cctp_saas. saas_app_resource to saas_app_resource_bak20240429; +RENAME TABLE cctp_saas. saas_data_source to saas_data_source_bak20240429; +RENAME TABLE cctp_saas. saas_data_source_cluster to saas_data_source_cluster_bak20240429; +RENAME TABLE cctp_saas. saas_func_resource to saas_func_resource_bak20240429; +RENAME TABLE cctp_saas. saas_software to saas_software_bak20240429; +RENAME TABLE cctp_saas. saas_software_app to saas_software_app_bak20240429; +RENAME TABLE cctp_saas. saas_software_func to saas_software_func_bak20240429; +RENAME TABLE cctp_saas. saas_tenant_menu to saas_tenant_menu_bak20240429; +RENAME TABLE cctp_saas. saas_tenant_software to saas_tenant_software_bak20240429; +RENAME TABLE cctp_saas. saas_tenant_software_func to saas_tenant_software_func_bak20240429; \ No newline at end of file