master
parent
822e679f0f
commit
5bcb914139
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
||||
</project>
|
|
@ -0,0 +1,38 @@
|
|||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
*.log.gz
|
||||
logs/
|
||||
*/logs/
|
||||
*/*/logs/
|
||||
|
||||
# Temp file
|
||||
*.temp
|
||||
temp
|
||||
*/temp
|
||||
|
||||
# IDEA profile dir
|
||||
.idea/
|
||||
*/.idea/
|
||||
*/*/.idea/
|
||||
|
||||
# IDEA project file
|
||||
*.iml
|
||||
*/*.iml
|
||||
*/*/*.iml
|
||||
*/*/*/*.iml
|
||||
|
||||
target/
|
||||
*/target/
|
||||
*/*/target/
|
||||
*.zip
|
||||
|
||||
Junit test
|
||||
*/src/test/java/
|
||||
*/src/test/resources/
|
||||
|
||||
dist/
|
||||
*.jar
|
||||
|
|
@ -108,6 +108,7 @@
|
|||
<dependency>
|
||||
<groupId>net.northking.cctp</groupId>
|
||||
<artifactId>cctp-test-element-core</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.northking.cctp</groupId>
|
||||
|
|
|
@ -21,6 +21,7 @@ import net.northking.cctp.se.exec.constant.AtuExecConstant;
|
|||
import net.northking.cctp.se.file.Attachment;
|
||||
import net.northking.cctp.se.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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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";
|
||||
|
||||
}
|
||||
|
|
|
@ -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<String> 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<String,Object> 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<ResultWrapper> result = restTemplate.postForEntity(url, paramMap, ResultWrapper.class);
|
||||
if(HttpStatus.OK.equals(result.getStatusCode())){
|
||||
ResultWrapper deviceResult = result.getBody();
|
||||
if (deviceResult != null && deviceResult.isSuccess()) {
|
||||
List<Map<String,String>> body = (List<Map<String,String>>)deviceResult.getData();
|
||||
if(!CollectionUtils.isEmpty(body)){
|
||||
logger.debug("移动任务请求占用成功移动端设备信息:"+JSON.toJSONString(body));
|
||||
for (int i = 0; i < body.size(); i++) {
|
||||
Map<String, String> deviceMap = body.get(i);
|
||||
String deviceId = deviceMap.get("id");
|
||||
String deviceToken = deviceMap.get("token");
|
||||
//获取设备对应的一条计划信息
|
||||
boolean isUsed = getTaskFromQueue(deviceId, deviceToken,TYPE_MOBILE);
|
||||
if(!isUsed){
|
||||
//无任务 释放设备
|
||||
logger.debug("无任务释放移动端设备:"+deviceId);
|
||||
releaseDevice(deviceToken);
|
||||
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<String,Object> paramMap = new HashMap<>();
|
||||
paramMap.put("id",info.getEngineId());
|
||||
List<String> tryAcqurieDeviceIdList = Collections.singletonList(taskDeviceId);
|
||||
paramMap.put("deviceList",tryAcqurieDeviceIdList);
|
||||
paramMap.put("type","1");
|
||||
paramMap.put("userBy","engine");
|
||||
String url = atuServerConfig.getServerUrl() + atuServerConfig.getTryAcqurieDeviceUrl();
|
||||
try {
|
||||
ResponseEntity<ResultWrapper> result = restTemplate.postForEntity(url, paramMap, ResultWrapper.class);
|
||||
if(HttpStatus.OK.equals(result.getStatusCode())){
|
||||
ResultWrapper deviceResult = result.getBody();
|
||||
if (deviceResult != null && deviceResult.isSuccess()) {
|
||||
List<Map<String,String>> body = (List<Map<String,String>>)deviceResult.getData();
|
||||
if(!CollectionUtils.isEmpty(body)){
|
||||
logger.debug("移动任务请求占用成功移动端设备信息:"+JSON.toJSONString(body));
|
||||
for (int i = 0; i < body.size(); i++) {
|
||||
Map<String, String> deviceMap = body.get(i);
|
||||
String deviceId = deviceMap.get("id");
|
||||
String deviceToken = deviceMap.get("token");
|
||||
//获取设备对应的一条计划信息
|
||||
boolean isUsed = getTaskFromQueue(deviceId, deviceToken,TYPE_MOBILE);
|
||||
if(!isUsed){
|
||||
//无任务 释放设备
|
||||
logger.debug("无任务释放移动端设备:"+deviceId);
|
||||
releaseDevice(deviceToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}catch (Exception e){
|
||||
logger.error("请求占用设备失败,原因:",e);
|
||||
//释放所有设备
|
||||
releaseDevices(e, info.getEngineId(), tryAcqurieDeviceIdList);
|
||||
}
|
||||
}catch (Exception e){
|
||||
logger.error("请求占用设备失败,原因:",e);
|
||||
//释放所有设备
|
||||
releaseDevices(e, info.getEngineId(), list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,12 +343,16 @@ public class AtuPlanTaskSchedule {
|
|||
task.setAppDownloadUrl(atuServerConfig.getServerUrl() + atuServerConfig.getAppDownloadUrl());
|
||||
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;
|
||||
});
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package net.northking.cctp.se.plan;
|
||||
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import net.northking.cctp.se.lifecycle.constant.MQConstant;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
public class AtuTaskExecHeartbeatSchedule {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AtuTaskExecHeartbeatSchedule.class);
|
||||
|
||||
@Autowired
|
||||
private RabbitAdmin rabbitAdmin;
|
||||
|
||||
@Autowired
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
/**
|
||||
* 记录正在执行中的任务,key:任务id,value:任务类型
|
||||
*/
|
||||
public static ConcurrentHashMap<String, String> execTaskMap = new ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
Queue queue = new Queue(MQConstant.TASK_EXEC_HEARTBEAT_QUEUE);
|
||||
rabbitAdmin.declareQueue(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送任务执行心跳
|
||||
*/
|
||||
@Scheduled(initialDelay = 10 * 1000,fixedDelay = 60 * 1000)
|
||||
public void sendTaskExecHeartbeat(){
|
||||
if (execTaskMap.size() <= 0){
|
||||
logger.debug("无正在执行的任务...");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
rabbitTemplate.convertAndSend(MQConstant.TASK_EXEC_HEARTBEAT_QUEUE, JSONUtil.toJsonStr(execTaskMap));
|
||||
} catch (Exception e) {
|
||||
logger.error("发送任务执行心跳异常");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,6 +54,11 @@ public class EnginePlanInfo {
|
|||
*/
|
||||
private Date createdTime;
|
||||
|
||||
/**
|
||||
* 委托排序
|
||||
*/
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,4 +46,9 @@ public interface EnginePlanInfoService {
|
|||
*/
|
||||
void uploadFinishedFile();
|
||||
|
||||
/**
|
||||
* 更新计划顺序
|
||||
* @param id 计划id
|
||||
*/
|
||||
void updatePlanInfoSort(String id);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||
@Repository
|
||||
public interface EnginePlanInfoRepository extends JpaRepository<EnginePlanInfo,String> {
|
||||
|
||||
@Query(value = "select * from engine_plan_info where device_id = :deviceId and type = :type order by priority desc ,created_time asc"
|
||||
@Query(value = "select * from engine_plan_info where device_id = :deviceId and type = :type order by priority desc , sort asc , created_time asc"
|
||||
,nativeQuery = true)
|
||||
List<EnginePlanInfo> getOneInfoByDeviceId(@Param("deviceId") String deviceId,@Param("type") String type);
|
||||
|
||||
|
@ -34,5 +34,9 @@ public interface EnginePlanInfoRepository extends JpaRepository<EnginePlanInfo,S
|
|||
@Query(value = "select * from engine_plan_info where 1 = 1"
|
||||
,nativeQuery = true)
|
||||
List<EnginePlanInfo> selectAllPlanInfo();
|
||||
|
||||
|
||||
@Transactional
|
||||
@Modifying
|
||||
@Query(value = "update engine_plan_info set sort = sort + 1 where id = :id",nativeQuery = true)
|
||||
void updatePlanInfoSort(@Param("id") String id);
|
||||
}
|
||||
|
|
|
@ -1514,16 +1514,25 @@ public class ScriptRuntimeExecutor implements ScriptExecutor {
|
|||
* @param stepRet 步骤结果
|
||||
*/
|
||||
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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -677,7 +677,7 @@ public class AtuPlanInfoApiServiceImpl extends AbstractExcelService<AtuPlanInfo>
|
|||
public Map<String, Object> checkDeviceOffline(String planId, String batchId) {
|
||||
// allOffline 0-在线,1-部分离线,2-全部离线
|
||||
Map<String, Object> 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<AtuPlanInfo>
|
|||
// 不是手动执行时,校验设备是否离线
|
||||
if (!PlanConstant.TRIGGER_TYPE_MANUAL.equals(atuPlanRunDto.getTriggerType())){
|
||||
Map<String,Object> 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<AtuPlanInfo>
|
|||
redisTemplate.delete(RedisConstant.PLAN_BATCH_OFFLINE_DEVICE + batchId);
|
||||
}
|
||||
}
|
||||
logger.debug("离线设备id集合: {}", JSONUtil.toJsonStr(offlineDeviceIdList));
|
||||
|
||||
List<AtuPlanBatchDeviceLink> batchDeviceList = new ArrayList<>();
|
||||
// 1.1.获取PC与移动设备集合
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package net.northking.cctp.executePlan.api.service;
|
||||
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
|
||||
|
||||
public interface AtuPlanSceneCaseTaskApiService {
|
||||
|
||||
void sceneCaseHandle(AtuTaskExecResultDto taskExecResult, String clusterKeyPrefix);
|
||||
|
||||
void queryNextNodeInfo(AtuTaskExecResultDto taskExecResult, AtuPlanSceneCaseTask planSceneCaseTask, AtuPlanTask planTask);
|
||||
|
||||
void associatedActualImgAndVideo(AtuTaskExecResultDto taskExecResult);
|
||||
|
||||
String parseMobilePerformanceFile(AtuTaskExecResultDto taskExecResult);
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
package net.northking.cctp.executePlan.api.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import net.northking.cctp.common.enums.FileBusinessTypeEnum;
|
||||
import net.northking.cctp.common.http.ResultWrapper;
|
||||
import net.northking.cctp.common.s3.FileDownloadException;
|
||||
import net.northking.cctp.common.s3.NKFile;
|
||||
import net.northking.cctp.common.s3.SimpleStorageService;
|
||||
import net.northking.cctp.common.security.authentication.NKSecurityContext;
|
||||
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||
import net.northking.cctp.executePlan.constants.RedisConstant;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanInfo;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanInfoService;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||
import net.northking.cctp.executePlan.dto.planBatch.AppPerInfo;
|
||||
import net.northking.cctp.executePlan.dto.planBatch.DevicePerInfo;
|
||||
import net.northking.cctp.executePlan.dto.planBatch.MobileTaskPerformanceDto;
|
||||
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNextNodeDto;
|
||||
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNodeExecDto;
|
||||
import net.northking.cctp.executePlan.dto.planSceneCase.AtuSceneNodeInfoDto;
|
||||
import net.northking.cctp.executePlan.dto.planTask.*;
|
||||
import net.northking.cctp.executePlan.feign.AttachmentFeignClient;
|
||||
import net.northking.cctp.executePlan.feign.PublicFeignClient;
|
||||
import net.northking.cctp.executePlan.utils.MinioPathUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AtuPlanSceneCaseTaskApiServiceImpl implements AtuPlanSceneCaseTaskApiService{
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AtuPlanSceneCaseTaskApiServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private AtuPlanInfoService planInfoService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanBatchApiService planBatchApiService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanTaskApiService planTaskApiService;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private PublicFeignClient publicFeignClient;
|
||||
|
||||
@Autowired
|
||||
private AttachmentFeignClient attachmentFeignClient;
|
||||
|
||||
@Autowired
|
||||
private SimpleStorageService simpleStorageService;
|
||||
|
||||
/**
|
||||
* 场景用例处理
|
||||
* @param taskExecResult 任务执行结果
|
||||
*/
|
||||
@Override
|
||||
public void sceneCaseHandle(AtuTaskExecResultDto taskExecResult, String clusterKeyPrefix){
|
||||
AtuPlanSceneCaseTask planSceneCaseTask = planSceneCaseTaskService.findByPrimaryKey(taskExecResult.getTaskId());
|
||||
if (ObjectUtil.isNull(planSceneCaseTask)){
|
||||
logger.error("场景节点任务[" + taskExecResult.getTaskId() + "]信息不存在");
|
||||
// 根据批次和用例id查询任务信息
|
||||
AtuPlanTask query = new AtuPlanTask();
|
||||
query.setBatchId(taskExecResult.getBatchId());
|
||||
query.setCaseId(taskExecResult.getCaseId());
|
||||
List<AtuPlanTask> taskList = planTaskApiService.query(query);
|
||||
if (CollUtil.isEmpty(taskList)){
|
||||
logger.error("根据批次id[{}]与用例id[{}]无法查询到该任务", query.getBatchId(), query.getCaseId());
|
||||
return;
|
||||
}
|
||||
planTaskApiService.taskExecFailUpdate(taskList.get(0), PlanConstant.TASK_EXECUTE_FAIL_STATUS, "场景节点任务信息不存在");
|
||||
return;
|
||||
}
|
||||
//开始执行同步更新计划任务表信息
|
||||
AtuPlanTask planTask = planTaskApiService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
||||
if (ObjectUtil.isNull(planTask)) {
|
||||
logger.error("任务[" + planSceneCaseTask.getTaskId() + "]信息不存在");
|
||||
return;
|
||||
}
|
||||
// 判断是否开始执行
|
||||
if (ObjectUtil.equal(PlanConstant.TASK_START_EXECUTE_STATUS, taskExecResult.getStatus())){
|
||||
planSceneCaseTask.setStartTime(taskExecResult.getCurrentTime());
|
||||
planSceneCaseTask.setEngineId(taskExecResult.getEngineId());
|
||||
planSceneCaseTask.setDeviceId(taskExecResult.getDeviceId());
|
||||
planSceneCaseTask.setAppId(taskExecResult.getAppId());
|
||||
planSceneCaseTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||
Date now = new Date();
|
||||
planSceneCaseTask.setLastHeartbeatTime(now);
|
||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||
// 判断是否首节点
|
||||
AtuPlanSceneCaseTask countParams = new AtuPlanSceneCaseTask();
|
||||
countParams.setTaskId(planSceneCaseTask.getTaskId());
|
||||
long count = planSceneCaseTaskService.count(countParams);
|
||||
if (count == 1) {
|
||||
|
||||
planTask.setStartTime(taskExecResult.getCurrentTime());
|
||||
planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||
planTask.setLastHeartbeatTime(now);
|
||||
planTaskApiService.updateByPrimaryKey(planTask);
|
||||
|
||||
if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
|
||||
clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
|
||||
logger.debug("批次[" + planTask.getBatchId() + "]开始执行,更新计划的状态");
|
||||
planInfoService.updatePlanByLastBatchId(planTask.getBatchId(), PlanConstant.PLAN_EXECUTING_STATUS);
|
||||
}
|
||||
|
||||
// 更新批次统计数据
|
||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||
}
|
||||
|
||||
}else {
|
||||
// 获取下一节点信息
|
||||
queryNextNodeInfo(taskExecResult, planSceneCaseTask, planTask);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询下一节点信息
|
||||
* @param taskExecResult 任务执行结果
|
||||
* @param planSceneCaseTask 场景用例节点任务
|
||||
* @param planTask 任务信息
|
||||
*/
|
||||
@Override
|
||||
public void queryNextNodeInfo(AtuTaskExecResultDto taskExecResult, AtuPlanSceneCaseTask planSceneCaseTask,
|
||||
AtuPlanTask planTask){
|
||||
|
||||
planSceneCaseTask.setStatus(taskExecResult.getStatus());
|
||||
planSceneCaseTask.setEndTime(taskExecResult.getCurrentTime());
|
||||
planSceneCaseTask.setErrorMsg(taskExecResult.getMessage());
|
||||
planSceneCaseTask.setVideoUrl(taskExecResult.getVideoPath());
|
||||
planSceneCaseTask.setExecResultFile(taskExecResult.getFilePath());
|
||||
logger.debug("更新当前节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||
|
||||
AtuSceneNextNodeDto sceneNextNodeDto = new AtuSceneNextNodeDto();
|
||||
//传递计划任务表id
|
||||
sceneNextNodeDto.setTaskId(planSceneCaseTask.getTaskId());
|
||||
sceneNextNodeDto.setCaseId(taskExecResult.getCaseId());
|
||||
sceneNextNodeDto.setNodeId(planSceneCaseTask.getNodeId());
|
||||
sceneNextNodeDto.setSuccess(ObjectUtil.equal(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS, taskExecResult.getStatus()));
|
||||
sceneNextNodeDto.setOutputArgs(taskExecResult.getOutputs());
|
||||
sceneNextNodeDto.setScriptId(planTask.getScriptId());
|
||||
sceneNextNodeDto.setSceneScriptUrl(planTask.getScriptJson());
|
||||
ResultWrapper<AtuSceneNodeExecDto> nextNodeResult;
|
||||
try {
|
||||
logger.debug("查询场景下一节点信息,参数为 => " + JSONUtil.toJsonStr(sceneNextNodeDto));
|
||||
//捕获异常信息
|
||||
nextNodeResult = publicFeignClient.getNextNode(sceneNextNodeDto);
|
||||
if (!nextNodeResult.isSuccess()){
|
||||
logger.error("获取下一节点信息结果异常," + nextNodeResult.getMessage());
|
||||
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_EXECUTE_FAIL_STATUS,
|
||||
nextNodeResult.getMessage());
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("获取下一节点信息异常:",e);
|
||||
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_EXECUTE_FAIL_STATUS,
|
||||
"获取下一节点信息异常" + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("查询场景下一节点信息,返回结果为 => " + JSONUtil.toJsonStr(nextNodeResult));
|
||||
AtuSceneNodeExecDto sceneNodeExecDto = nextNodeResult.getData();
|
||||
|
||||
//更新url到表里
|
||||
if (sceneNodeExecDto != null && sceneNodeExecDto.getSceneScriptUrl() != null) {
|
||||
planTask.setScriptJson(sceneNodeExecDto.getSceneScriptUrl());
|
||||
planTaskApiService.updateByPrimaryKey(planTask);
|
||||
}
|
||||
|
||||
AtuSceneNodeInfoDto nodeInfo = sceneNodeExecDto.getNodeInfo();
|
||||
// 节点不管成功或者失败,若没有连接到结束节点,默认最后节点为结束,节点信息不包含lineId
|
||||
planSceneCaseTask.setNextNodeId(nodeInfo.getNodeId());
|
||||
planSceneCaseTask.setLineId(nodeInfo.getLineId());
|
||||
|
||||
// 记录性能数据
|
||||
if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planSceneCaseTask.getNodeType())
|
||||
|| PlanConstant.SCRIPT_TYPE_IOS.equals(planSceneCaseTask.getNodeType())) {
|
||||
logger.debug("处理场景移动端性能文件数据");
|
||||
planSceneCaseTask.setPerDataPath(parseMobilePerformanceFile(taskExecResult));
|
||||
}
|
||||
|
||||
logger.debug("更新当前节点中下一节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||
// 关联文件,使用 CompletableFuture 复用线程
|
||||
CompletableFuture.runAsync(() -> associatedActualImgAndVideo(taskExecResult));
|
||||
// future.get();
|
||||
// 判断是否结束节点
|
||||
if (sceneNodeExecDto.getEnd()){
|
||||
logger.info("场景任务执行结束....");
|
||||
//判断任务状态是否未非引擎返回的状态 取消/超时
|
||||
if(ObjectUtil.equal(PlanConstant.TASK_CANCEL_STATUS, planTask.getStatus())
|
||||
||ObjectUtil.equal(PlanConstant.TASK_TIMEOUT_STATUS, planTask.getStatus())){
|
||||
return;
|
||||
}
|
||||
planTask.setEndTime(planSceneCaseTask.getEndTime());
|
||||
if (StrUtil.isEmpty(nodeInfo.getLineId())) {
|
||||
planTask.setStatus(planSceneCaseTask.getStatus());
|
||||
planTask.setErrorMsg(planSceneCaseTask.getErrorMsg());
|
||||
}else{
|
||||
// 节点执行结束且连接了结束节点,则该场景任务设置成功执行
|
||||
planTask.setStatus(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS);
|
||||
}
|
||||
logger.debug("更新场景任务为结束");
|
||||
planTaskApiService.updateByPrimaryKey(planTask);
|
||||
|
||||
// 更新批次统计数据
|
||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||
// 删除节点更新时的场景任务文件
|
||||
String noDeleteFileId = "/" + MinioPathUtils.idToPath(sceneNodeExecDto.getSceneScriptUrl())[1];
|
||||
attachmentFeignClient.clearSceneTaskAttachments(planTask.getScriptId(), FileBusinessTypeEnum.SCENE_TASK_REFRESH_FILE.getCode(), noDeleteFileId);
|
||||
}else {
|
||||
// 新增下一节点任务信息
|
||||
try {
|
||||
createNextNodeTask(planSceneCaseTask, nodeInfo, taskExecResult, planTask.getEnvId());
|
||||
}catch (Exception e){
|
||||
logger.error("创建下一节点任务信息失败", e);
|
||||
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_EXECUTE_FAIL_STATUS,
|
||||
"创建下一节点任务信息失败" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建下一节点任务
|
||||
* @param planSceneCaseTask 场景用例节点任务
|
||||
* @param nodeInfo 节点信息
|
||||
* @param taskExecResult 任务执行结果
|
||||
*/
|
||||
private void createNextNodeTask(AtuPlanSceneCaseTask planSceneCaseTask, AtuSceneNodeInfoDto nodeInfo,
|
||||
AtuTaskExecResultDto taskExecResult, String envId){
|
||||
logger.debug("保存下一节点信息");
|
||||
logger.debug("节点信息:" + JSONUtil.toJsonStr(nodeInfo));
|
||||
AtuPlanSceneCaseTask nextNodeTask = new AtuPlanSceneCaseTask();
|
||||
nextNodeTask.setId(IdUtil.simpleUUID());
|
||||
nextNodeTask.setTaskId(planSceneCaseTask.getTaskId());
|
||||
nextNodeTask.setScriptId(nodeInfo.getScriptId());
|
||||
nextNodeTask.setVersionId(nodeInfo.getVersionId());
|
||||
nextNodeTask.setVersionName(nodeInfo.getVersionName());
|
||||
nextNodeTask.setScriptName(nodeInfo.getScriptName());
|
||||
nextNodeTask.setScriptJson(nodeInfo.getScriptPath());
|
||||
nextNodeTask.setNodeId(nodeInfo.getNodeId());
|
||||
nextNodeTask.setNodeType(nodeInfo.getNodeType());
|
||||
nextNodeTask.setStatus(PlanConstant.TASK_WAIT_EXECUTE_STATUS);
|
||||
if (CollUtil.isNotEmpty(nodeInfo.getCaseParam())) {
|
||||
nextNodeTask.setNodeParams(JSONUtil.toJsonStr(nodeInfo.getCaseParam()));
|
||||
}
|
||||
nextNodeTask.setPrevNodeId(planSceneCaseTask.getNodeId());
|
||||
nextNodeTask.setCreatedTime(new Date());
|
||||
planSceneCaseTaskService.insert(nextNodeTask);
|
||||
|
||||
// 查询计划信息
|
||||
AtuPlanInfo planInfo = planInfoService.findByPrimaryKey(taskExecResult.getPlanId());
|
||||
if (ObjectUtil.isNull(planInfo)){
|
||||
logger.error("计划[" + taskExecResult.getPlanId() + "]信息为空");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("生成任务并推送至队列");
|
||||
AtuTaskExecDto atuTaskExecDto = new AtuTaskExecDto();
|
||||
atuTaskExecDto.setTenantId(planInfo.getTenantId());
|
||||
atuTaskExecDto.setProjectId(planInfo.getProjectId());
|
||||
atuTaskExecDto.setEnvId(envId);
|
||||
atuTaskExecDto.setPlanId(planInfo.getId());
|
||||
atuTaskExecDto.setBatchId(taskExecResult.getBatchId());
|
||||
atuTaskExecDto.setTaskId(nextNodeTask.getId());
|
||||
atuTaskExecDto.setCaseId(taskExecResult.getCaseId());
|
||||
atuTaskExecDto.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
|
||||
atuTaskExecDto.setScriptPath(nodeInfo.getScriptPath());
|
||||
atuTaskExecDto.setCaseParams(nodeInfo.getCaseParam());
|
||||
atuTaskExecDto.setAppSet(planInfo.getAppSet());
|
||||
atuTaskExecDto.setScreenRecordSet(planInfo.getScreenRecordSet());
|
||||
atuTaskExecDto.setScreenshotSet(planInfo.getScreenshotSet());
|
||||
atuTaskExecDto.setFailRetryNum(planInfo.getFailRetryCount());
|
||||
|
||||
atuTaskExecDto.setAppId(planTaskApiService.queryAppId(planInfo.getId(), nodeInfo.getPlatformType(),
|
||||
nodeInfo.getAppPackage(), nodeInfo.getNodeType()));
|
||||
|
||||
// 发送任务执行信息至消息队列
|
||||
planTaskApiService.sendToQueue(atuTaskExecDto.getBatchId(), atuTaskExecDto, nodeInfo.getNodeType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void associatedActualImgAndVideo(AtuTaskExecResultDto taskExecResult) {
|
||||
try {
|
||||
if (StringUtils.hasText(taskExecResult.getFilePath())) {
|
||||
InputStream inputStream = simpleStorageService.downloadAsStream(NKSecurityContext.getTenantId(), taskExecResult.getFilePath());
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
List<String> fileUriList = new ArrayList<>();
|
||||
String line = null;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
StepExecuteResult stepExecuteResult = JSONUtil.toBean(line, StepExecuteResult.class);
|
||||
String actualImgUri = stepExecuteResult.getActualImgUri();
|
||||
if (StringUtils.hasText(actualImgUri)) {
|
||||
fileUriList.add(actualImgUri);
|
||||
}
|
||||
}
|
||||
fileUriList.add(taskExecResult.getFilePath());
|
||||
if (StringUtils.hasText(taskExecResult.getVideoPath())) {
|
||||
fileUriList.add(taskExecResult.getVideoPath());
|
||||
}
|
||||
attachmentFeignClient.associatedFiles(fileUriList, taskExecResult.getTaskId());
|
||||
}
|
||||
} catch (FileDownloadException e) {
|
||||
logger.error("文件下载异常", e);
|
||||
} catch (Exception e) {
|
||||
logger.error("文件读取异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseMobilePerformanceFile(AtuTaskExecResultDto taskExecResult){
|
||||
String path = "";
|
||||
DevicePerInfo devicePerInfo = taskExecResult.getDevicePerInfo();
|
||||
logger.debug("设备性能数据结果 => {}", JSONUtil.toJsonStr(devicePerInfo) );
|
||||
if (devicePerInfo == null){
|
||||
return path;
|
||||
}
|
||||
AtuPlanInfo planInfo = planInfoService.findByBatchId(taskExecResult.getBatchId());
|
||||
MobileTaskPerformanceDto taskPerInfo = new MobileTaskPerformanceDto();
|
||||
taskPerInfo.setDeviceId(taskExecResult.getDeviceId());
|
||||
taskPerInfo.setAppId(taskExecResult.getAppId());
|
||||
String devicePerInfoAddress = devicePerInfo.getDevicePerInfoAddress();
|
||||
logger.info("任务ID:{},设备性能数据地址:{}", taskExecResult.getTaskId(), devicePerInfoAddress);
|
||||
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
||||
DevicePerInfoDataDto deviceData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
||||
planInfo.getTenantId(), devicePerInfoAddress, DevicePerInfoDataDto.class);
|
||||
logger.debug("设备性能数据 => {}", JSONUtil.toJsonStr(deviceData));
|
||||
if (deviceData != null){
|
||||
taskPerInfo.setDeviceCpuList(filterData(deviceData.getCpuList()));
|
||||
taskPerInfo.setDeviceMemoList(filterData(deviceData.getMemoList()));
|
||||
taskPerInfo.setDeviceFlowList(filterData(deviceData.getFlowList()));
|
||||
taskPerInfo.setDeviceTemperatureList(filterData(deviceData.getTemperatureList()));
|
||||
}
|
||||
}
|
||||
AppPerInfo appPerInfo = devicePerInfo.getAppPerInfo();
|
||||
if (appPerInfo != null) {
|
||||
// 安装时间
|
||||
taskPerInfo.setAppInstallTime(appPerInfo.getAppInstallTime());
|
||||
// 启动时间
|
||||
taskPerInfo.setAppActiveTime(appPerInfo.getAppActiveTime());
|
||||
|
||||
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
||||
logger.info("任务ID:{},app性能数据地址:{}", taskExecResult.getTaskId(), appPerInfoAddress);
|
||||
if (StrUtil.isNotBlank(appPerInfoAddress)) {
|
||||
AppPerInfoDataDto appData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
||||
planInfo.getTenantId(), appPerInfoAddress, AppPerInfoDataDto.class);
|
||||
logger.debug("app性能数据 => {}", JSONUtil.toJsonStr(appData) );
|
||||
if (appData != null) {
|
||||
taskPerInfo.setAppCpuList(filterData(appData.getCpuList()));
|
||||
taskPerInfo.setAppMemoList(filterData(appData.getMemoList()));
|
||||
taskPerInfo.setAppFlowList(filterData(appData.getFlowList()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("上传新的性能数据");
|
||||
logger.debug("MobileTaskPerformanceDto => {}", JSONUtil.toJsonStr(taskPerInfo) );
|
||||
File file = MinioPathUtils.objectToJsonFile(taskPerInfo);
|
||||
try {
|
||||
NKFile nkFile = simpleStorageService.upload(planInfo.getTenantId(), file, true, taskExecResult.getTaskId(), FileBusinessTypeEnum.MOBILE_TASK_PERFORMANCE_SUMMARY);
|
||||
path = nkFile.getId();
|
||||
logger.debug("保存任务性能数据存放地址 => {}", path);
|
||||
}catch (Exception e){
|
||||
logger.error("文件上传异常", e);
|
||||
}
|
||||
// 删除临时性能文件
|
||||
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
||||
String[] pathUrl = MinioPathUtils.idToPath(devicePerInfoAddress);
|
||||
try {
|
||||
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
||||
} catch (Exception e) {
|
||||
logger.error("文件删除删除失败", e);
|
||||
}
|
||||
}
|
||||
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
||||
if (StrUtil.isNotBlank(appPerInfoAddress)){
|
||||
String[] pathUrl = MinioPathUtils.idToPath(appPerInfoAddress);
|
||||
try {
|
||||
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
||||
} catch (Exception e) {
|
||||
logger.error("文件删除删除失败", e);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤小于等于0的数据
|
||||
* @param data 需过滤的数据
|
||||
* @return 结果集
|
||||
*/
|
||||
private List<Double> filterData(List<Double> data){
|
||||
if (data == null || data.size() <= 0){
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return data.stream().filter(num -> num > 0).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
|
@ -57,6 +57,13 @@ public interface AtuPlanTaskApiService extends ExcelService
|
|||
*/
|
||||
String insert(AtuPlanTask task);
|
||||
|
||||
/**
|
||||
* 根据id更新任务信息
|
||||
* @param task 任务信息
|
||||
* @return 结果
|
||||
*/
|
||||
int updateByPrimaryKey(AtuPlanTask task);
|
||||
|
||||
/**
|
||||
* 计划批次停止
|
||||
* @param batchId 批次ID
|
||||
|
@ -186,4 +193,17 @@ public interface AtuPlanTaskApiService extends ExcelService
|
|||
List<String> getTaskIdsByScriptIdsAndPlanId(List<String> oldScriptIds, String planId);
|
||||
|
||||
void getActualImagePath(List<String> deletedFilePath, String execResultFile);
|
||||
/**
|
||||
* 查询执行超时N秒任务
|
||||
* @param timeout 超时时间
|
||||
* @return 任务集合
|
||||
*/
|
||||
List<AtuPlanTask> queryExecTimeoutTask(int timeout);
|
||||
|
||||
/**
|
||||
* 根据id查询任务扩展信息
|
||||
* @param taskId 任务id
|
||||
* @return 任务扩展信息
|
||||
*/
|
||||
AtuPlanTaskExtendDto queryTaskExtendById(String taskId);
|
||||
}
|
||||
|
|
|
@ -348,6 +348,11 @@ public class AtuPlanTaskApiServiceImpl extends AbstractExcelService<AtuPlanTask>
|
|||
return task.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByPrimaryKey(AtuPlanTask task) {
|
||||
return this.atuPlanTaskService.updateByPrimaryKey(task);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计划批次停止
|
||||
|
@ -1669,6 +1674,16 @@ public class AtuPlanTaskApiServiceImpl extends AbstractExcelService<AtuPlanTask>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AtuPlanTask> queryExecTimeoutTask(int timeout) {
|
||||
return atuPlanTaskService.queryExecTimeoutTask(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtuPlanTaskExtendDto queryTaskExtendById(String taskId) {
|
||||
return atuPlanTaskService.queryTaskExtendById(taskId);
|
||||
}
|
||||
|
||||
private Map<String, String> getRetryTaskInputInfo(Map<String, List<AtuPlanTask>> envScriptTaskMap,
|
||||
Map<String, List<String>> envScriptMap) {
|
||||
Map<String, String> taskParamsMap = new HashMap<>();
|
||||
|
|
|
@ -76,4 +76,9 @@ public class RabbitConstant {
|
|||
* 批次统计数据更新队列
|
||||
*/
|
||||
public static final String BATCH_SUM_UPDATE_QUEUE = "execute.plan.batch.sum.update";
|
||||
|
||||
/**
|
||||
* 任务执行心跳
|
||||
*/
|
||||
public static final String TASK_EXEC_HEARTBEAT_QUEUE = "plan.task.exec.heartbeat";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package net.northking.cctp.executePlan.consumer;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||
import net.northking.cctp.executePlan.constants.RabbitConstant;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
public class TaskExecHeartbeatConsumer {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(TaskExecHeartbeatConsumer.class);
|
||||
|
||||
@Autowired
|
||||
private AmqpAdmin amqpAdmin;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanTaskService planTaskService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
// 初始化队列并绑定到交换机
|
||||
Queue queue = new Queue(RabbitConstant.TASK_EXEC_HEARTBEAT_QUEUE);
|
||||
amqpAdmin.declareQueue(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计划任务心跳
|
||||
* @param message 对象消息
|
||||
*/
|
||||
@RabbitListener(queues = RabbitConstant.TASK_EXEC_HEARTBEAT_QUEUE)
|
||||
public void receive(Message message){
|
||||
try {
|
||||
logger.info("计划任务心跳 ==> " + new String(message.getBody()));
|
||||
Map<String, String> taskMap = JSONUtil.toBean(new String(message.getBody()), Map.class);
|
||||
taskMap.forEach(this::handleTaskHeartbeat);
|
||||
}catch (Exception e){
|
||||
logger.error("任务生成失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTaskHeartbeat(String taskId, String taskType){
|
||||
AtuPlanTask planTask = new AtuPlanTask();
|
||||
planTask.setId(taskId);
|
||||
planTask.setLastHeartbeatTime(new Date());
|
||||
if (PlanConstant.SCRIPT_TYPE_SCENE.equals(taskType)){
|
||||
// 场景任务
|
||||
AtuPlanSceneCaseTask sceneCaseTask = planSceneCaseTaskService.findByPrimaryKey(taskId);
|
||||
sceneCaseTask.setLastHeartbeatTime(new Date());
|
||||
planSceneCaseTaskService.updateByPrimaryKey(sceneCaseTask);
|
||||
// 更新场景节点对应的任务心跳
|
||||
planTask.setId(sceneCaseTask.getTaskId());
|
||||
}
|
||||
// 普通任务
|
||||
planTaskService.updateByPrimaryKey(planTask);
|
||||
}
|
||||
}
|
|
@ -1,58 +1,29 @@
|
|||
package net.northking.cctp.executePlan.consumer;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>Title: TaskExecResultConsumer</p>
|
||||
|
@ -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<String, Object> 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<String> fileUriList = new ArrayList<>();
|
||||
String line = null;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
StepExecuteResult stepExecuteResult = JSONUtil.toBean(line, StepExecuteResult.class);
|
||||
String actualImgUri = stepExecuteResult.getActualImgUri();
|
||||
if (StringUtils.hasText(actualImgUri)) {
|
||||
fileUriList.add(actualImgUri);
|
||||
}
|
||||
}
|
||||
fileUriList.add(taskExecResult.getFilePath());
|
||||
if (StringUtils.hasText(taskExecResult.getVideoPath())) {
|
||||
fileUriList.add(taskExecResult.getVideoPath());
|
||||
}
|
||||
attachmentFeignClient.associatedFiles(fileUriList, taskExecResult.getTaskId());
|
||||
}
|
||||
} catch (FileDownloadException e) {
|
||||
logger.error("文件下载异常", e);
|
||||
} catch (Exception e) {
|
||||
logger.error("文件读取异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String parseMobilePerformanceFile(AtuTaskExecResultDto taskExecResult){
|
||||
String path = "";
|
||||
DevicePerInfo devicePerInfo = taskExecResult.getDevicePerInfo();
|
||||
logger.debug("设备性能数据结果 => {}", JSONUtil.toJsonStr(devicePerInfo) );
|
||||
if (devicePerInfo == null){
|
||||
return path;
|
||||
}
|
||||
AtuPlanInfo planInfo = atuPlanInfoService.findByBatchId(taskExecResult.getBatchId());
|
||||
MobileTaskPerformanceDto taskPerInfo = new MobileTaskPerformanceDto();
|
||||
taskPerInfo.setDeviceId(taskExecResult.getDeviceId());
|
||||
taskPerInfo.setAppId(taskExecResult.getAppId());
|
||||
String devicePerInfoAddress = devicePerInfo.getDevicePerInfoAddress();
|
||||
logger.info("任务ID:{},设备性能数据地址:{}", taskExecResult.getTaskId(), devicePerInfoAddress);
|
||||
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
||||
DevicePerInfoDataDto deviceData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
||||
planInfo.getTenantId(), devicePerInfoAddress, DevicePerInfoDataDto.class);
|
||||
logger.debug("设备性能数据 => {}", JSONUtil.toJsonStr(deviceData));
|
||||
if (deviceData != null){
|
||||
taskPerInfo.setDeviceCpuList(filterData(deviceData.getCpuList()));
|
||||
taskPerInfo.setDeviceMemoList(filterData(deviceData.getMemoList()));
|
||||
taskPerInfo.setDeviceFlowList(filterData(deviceData.getFlowList()));
|
||||
taskPerInfo.setDeviceTemperatureList(filterData(deviceData.getTemperatureList()));
|
||||
}
|
||||
}
|
||||
AppPerInfo appPerInfo = devicePerInfo.getAppPerInfo();
|
||||
if (appPerInfo != null) {
|
||||
// 安装时间
|
||||
taskPerInfo.setAppInstallTime(appPerInfo.getAppInstallTime());
|
||||
// 启动时间
|
||||
taskPerInfo.setAppActiveTime(appPerInfo.getAppActiveTime());
|
||||
|
||||
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
||||
logger.info("任务ID:{},app性能数据地址:{}", taskExecResult.getTaskId(), appPerInfoAddress);
|
||||
if (StrUtil.isNotBlank(appPerInfoAddress)) {
|
||||
AppPerInfoDataDto appData = MinioPathUtils.downloadFileCoverObj(simpleStorageService,
|
||||
planInfo.getTenantId(), appPerInfoAddress, AppPerInfoDataDto.class);
|
||||
logger.debug("app性能数据 => {}", JSONUtil.toJsonStr(appData) );
|
||||
if (appData != null) {
|
||||
taskPerInfo.setAppCpuList(filterData(appData.getCpuList()));
|
||||
taskPerInfo.setAppMemoList(filterData(appData.getMemoList()));
|
||||
taskPerInfo.setAppFlowList(filterData(appData.getFlowList()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("上传新的性能数据");
|
||||
logger.debug("MobileTaskPerformanceDto => {}", JSONUtil.toJsonStr(taskPerInfo) );
|
||||
File file = MinioPathUtils.objectToJsonFile(taskPerInfo);
|
||||
try {
|
||||
NKFile nkFile = simpleStorageService.upload(planInfo.getTenantId(), file, true, taskExecResult.getTaskId(), FileBusinessTypeEnum.MOBILE_TASK_PERFORMANCE_SUMMARY);
|
||||
path = nkFile.getId();
|
||||
logger.debug("保存任务性能数据存放地址 => {}", path);
|
||||
}catch (Exception e){
|
||||
logger.error("文件上传异常", e);
|
||||
}
|
||||
// 删除临时性能文件
|
||||
if (StrUtil.isNotBlank(devicePerInfoAddress)){
|
||||
String[] pathUrl = MinioPathUtils.idToPath(devicePerInfoAddress);
|
||||
try {
|
||||
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
||||
} catch (Exception e) {
|
||||
logger.error("文件删除删除失败", e);
|
||||
}
|
||||
}
|
||||
String appPerInfoAddress = appPerInfo.getAppPerInfoAddress();
|
||||
if (StrUtil.isNotBlank(appPerInfoAddress)){
|
||||
String[] pathUrl = MinioPathUtils.idToPath(appPerInfoAddress);
|
||||
try {
|
||||
simpleStorageService.delete(pathUrl[0], "/" + pathUrl[1]);
|
||||
} catch (Exception e) {
|
||||
logger.error("文件删除删除失败", e);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤小于等于0的数据
|
||||
* @param data 需过滤的数据
|
||||
* @return 结果集
|
||||
*/
|
||||
private List<Double> filterData(List<Double> data){
|
||||
if (data == null || data.size() <= 0){
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return data.stream().filter(num -> num > 0).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景用例处理
|
||||
* @param taskExecResult 任务执行结果
|
||||
*/
|
||||
private void sceneCaseHandle(AtuTaskExecResultDto taskExecResult, String clusterKeyPrefix){
|
||||
AtuPlanSceneCaseTask planSceneCaseTask = planSceneCaseTaskService.findByPrimaryKey(taskExecResult.getTaskId());
|
||||
if (ObjectUtil.isNull(planSceneCaseTask)){
|
||||
logger.error("场景节点任务[" + taskExecResult.getTaskId() + "]信息不存在");
|
||||
return;
|
||||
}
|
||||
//开始执行同步更新计划任务表信息
|
||||
AtuPlanTask planTask = planTaskService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
||||
if (ObjectUtil.isNull(planTask)) {
|
||||
logger.error("任务[" + planSceneCaseTask.getTaskId() + "]信息不存在");
|
||||
return;
|
||||
}
|
||||
// 判断是否开始执行
|
||||
if (ObjectUtil.equal(PlanConstant.TASK_START_EXECUTE_STATUS, taskExecResult.getStatus())){
|
||||
planSceneCaseTask.setStartTime(taskExecResult.getCurrentTime());
|
||||
planSceneCaseTask.setEngineId(taskExecResult.getEngineId());
|
||||
planSceneCaseTask.setDeviceId(taskExecResult.getDeviceId());
|
||||
planSceneCaseTask.setAppId(taskExecResult.getAppId());
|
||||
planSceneCaseTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||
// 判断是否首节点
|
||||
AtuPlanSceneCaseTask countParams = new AtuPlanSceneCaseTask();
|
||||
countParams.setTaskId(planSceneCaseTask.getTaskId());
|
||||
long count = planSceneCaseTaskService.count(countParams);
|
||||
if (count == 1) {
|
||||
|
||||
planTask.setStartTime(taskExecResult.getCurrentTime());
|
||||
planTask.setStatus(PlanConstant.TASK_START_EXECUTE_STATUS);
|
||||
planTaskService.updateByPrimaryKey(planTask);
|
||||
|
||||
if (!redisTemplate.opsForHash().hasKey(clusterKeyPrefix + RedisConstant.BATCH_SCRIPT_SUM_KEY + planTask.getBatchId(),
|
||||
clusterKeyPrefix + PlanConstant.BATCH_START_TIME)) {
|
||||
logger.debug("批次[" + planTask.getBatchId() + "]开始执行,更新计划的状态");
|
||||
atuPlanInfoService.updatePlanByLastBatchId(planTask.getBatchId(), PlanConstant.PLAN_EXECUTING_STATUS);
|
||||
}
|
||||
|
||||
// 更新批次统计数据
|
||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||
}
|
||||
|
||||
}else {
|
||||
// 获取下一节点信息
|
||||
queryNextNodeInfo(taskExecResult, planSceneCaseTask, planTask.getScriptId(), planTask.getEnvId(),planTask.getScriptJson());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询下一节点信息
|
||||
* @param taskExecResult 任务执行结果
|
||||
* @param planSceneCaseTask 场景用例节点任务
|
||||
* @param scriptUrl 执行脚本地址
|
||||
*/
|
||||
private void queryNextNodeInfo(AtuTaskExecResultDto taskExecResult,
|
||||
AtuPlanSceneCaseTask planSceneCaseTask,
|
||||
String sceneId,
|
||||
String envId,
|
||||
String scriptUrl){
|
||||
|
||||
planSceneCaseTask.setStatus(taskExecResult.getStatus());
|
||||
planSceneCaseTask.setEndTime(taskExecResult.getCurrentTime());
|
||||
planSceneCaseTask.setErrorMsg(taskExecResult.getMessage());
|
||||
planSceneCaseTask.setVideoUrl(taskExecResult.getVideoPath());
|
||||
planSceneCaseTask.setExecResultFile(taskExecResult.getFilePath());
|
||||
logger.debug("更新当前节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||
|
||||
AtuSceneNextNodeDto sceneNextNodeDto = new AtuSceneNextNodeDto();
|
||||
//传递计划任务表id
|
||||
sceneNextNodeDto.setTaskId(planSceneCaseTask.getTaskId());
|
||||
sceneNextNodeDto.setCaseId(taskExecResult.getCaseId());
|
||||
sceneNextNodeDto.setNodeId(planSceneCaseTask.getNodeId());
|
||||
sceneNextNodeDto.setSuccess(ObjectUtil.equal(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS, taskExecResult.getStatus()));
|
||||
sceneNextNodeDto.setOutputArgs(taskExecResult.getOutputs());
|
||||
sceneNextNodeDto.setScriptId(sceneId);
|
||||
sceneNextNodeDto.setSceneScriptUrl(scriptUrl);
|
||||
ResultWrapper<AtuSceneNodeExecDto> nextNodeResult;
|
||||
try {
|
||||
logger.debug("查询场景下一节点信息,参数为 => " + JSONUtil.toJsonStr(sceneNextNodeDto));
|
||||
//捕获异常信息
|
||||
nextNodeResult = publicFeignClient.getNextNode(sceneNextNodeDto);
|
||||
if (!nextNodeResult.isSuccess()){
|
||||
logger.error("获取下一节点信息结果异常," + nextNodeResult.getMessage());
|
||||
sceneTaskExceptionEnd(planSceneCaseTask.getTaskId(), nextNodeResult.getMessage());
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("获取下一节点信息异常:",e);
|
||||
sceneTaskExceptionEnd(planSceneCaseTask.getTaskId(), "获取下一节点信息异常");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("查询场景下一节点信息,返回结果为 => " + JSONUtil.toJsonStr(nextNodeResult));
|
||||
AtuSceneNodeExecDto sceneNodeExecDto = nextNodeResult.getData();
|
||||
|
||||
//更新url到表里
|
||||
if (sceneNodeExecDto != null && sceneNodeExecDto.getSceneScriptUrl() != null) {
|
||||
AtuPlanTask byPrimaryKey = atuPlanTaskService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
||||
AtuPlanTask planTask = new AtuPlanTask();
|
||||
planTask.setId(planSceneCaseTask.getTaskId());
|
||||
planTask.setScriptJson(sceneNodeExecDto.getSceneScriptUrl());
|
||||
int rows = atuPlanTaskService.updateByPrimaryKey(planTask);
|
||||
}
|
||||
|
||||
AtuSceneNodeInfoDto nodeInfo = sceneNodeExecDto.getNodeInfo();
|
||||
// 节点不管成功或者失败,若没有连接到结束节点,默认最后节点为结束,节点信息不包含lineId
|
||||
planSceneCaseTask.setNextNodeId(nodeInfo.getNodeId());
|
||||
planSceneCaseTask.setLineId(nodeInfo.getLineId());
|
||||
|
||||
// 记录性能数据
|
||||
if (PlanConstant.SCRIPT_TYPE_ANDROID.equals(planSceneCaseTask.getNodeType())
|
||||
|| PlanConstant.SCRIPT_TYPE_IOS.equals(planSceneCaseTask.getNodeType())) {
|
||||
logger.debug("处理场景移动端性能文件数据");
|
||||
planSceneCaseTask.setPerDataPath(parseMobilePerformanceFile(taskExecResult));
|
||||
}
|
||||
|
||||
logger.debug("更新当前节点中下一节点信息 => " + JSONUtil.toJsonStr(planSceneCaseTask));
|
||||
planSceneCaseTaskService.updateByPrimaryKey(planSceneCaseTask);
|
||||
// 关联文件,使用 CompletableFuture 复用线程
|
||||
CompletableFuture.runAsync(() -> associatedActualImgAndVideo(taskExecResult));
|
||||
// future.get();
|
||||
// 判断是否结束节点
|
||||
if (sceneNodeExecDto.getEnd()){
|
||||
logger.info("场景任务执行结束....");
|
||||
// 更新任务表信息
|
||||
AtuPlanTask planTask = planTaskService.findByPrimaryKey(planSceneCaseTask.getTaskId());
|
||||
if (ObjectUtil.isNull(planTask)){
|
||||
logger.error("任务[" + planSceneCaseTask.getTaskId() + "]信息不存在");
|
||||
return;
|
||||
}
|
||||
//判断任务状态是否未非引擎返回的状态 取消/超时
|
||||
if(ObjectUtil.equal(PlanConstant.TASK_CANCEL_STATUS, planTask.getStatus())
|
||||
||ObjectUtil.equal(PlanConstant.TASK_TIMEOUT_STATUS, planTask.getStatus())){
|
||||
return;
|
||||
}
|
||||
planTask.setEndTime(planSceneCaseTask.getEndTime());
|
||||
if (StrUtil.isEmpty(nodeInfo.getLineId())) {
|
||||
planTask.setStatus(planSceneCaseTask.getStatus());
|
||||
planTask.setErrorMsg(planSceneCaseTask.getErrorMsg());
|
||||
}else{
|
||||
// 节点执行结束且连接了结束节点,则该场景任务设置成功执行
|
||||
planTask.setStatus(PlanConstant.TASK_EXECUTE_SUCCESS_STATUS);
|
||||
}
|
||||
logger.debug("更新场景任务为结束");
|
||||
planTaskService.updateByPrimaryKey(planTask);
|
||||
|
||||
// 更新批次统计数据
|
||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||
// 删除节点更新时的场景任务文件
|
||||
String noDeleteFileId = "/" + MinioPathUtils.idToPath(sceneNodeExecDto.getSceneScriptUrl())[1];
|
||||
attachmentFeignClient.clearSceneTaskAttachments(sceneId, FileBusinessTypeEnum.SCENE_TASK_REFRESH_FILE.getCode(), noDeleteFileId);
|
||||
}else {
|
||||
// 新增下一节点任务信息
|
||||
try {
|
||||
createNextNodeTask(planSceneCaseTask, nodeInfo, taskExecResult, envId);
|
||||
}catch (Exception e){
|
||||
logger.error("创建下一节点任务信息失败", e);
|
||||
sceneTaskExceptionEnd(planSceneCaseTask.getTaskId(), "创建下一节点任务信息失败" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建下一节点任务
|
||||
* @param planSceneCaseTask 场景用例节点任务
|
||||
* @param nodeInfo 节点信息
|
||||
* @param taskExecResult 任务执行结果
|
||||
*/
|
||||
private void createNextNodeTask(AtuPlanSceneCaseTask planSceneCaseTask, AtuSceneNodeInfoDto nodeInfo,
|
||||
AtuTaskExecResultDto taskExecResult, String envId){
|
||||
logger.debug("保存下一节点信息");
|
||||
logger.debug("节点信息:" + JSONUtil.toJsonStr(nodeInfo));
|
||||
AtuPlanSceneCaseTask nextNodeTask = new AtuPlanSceneCaseTask();
|
||||
nextNodeTask.setId(IdUtil.simpleUUID());
|
||||
nextNodeTask.setTaskId(planSceneCaseTask.getTaskId());
|
||||
nextNodeTask.setScriptId(nodeInfo.getScriptId());
|
||||
nextNodeTask.setVersionId(nodeInfo.getVersionId());
|
||||
nextNodeTask.setVersionName(nodeInfo.getVersionName());
|
||||
nextNodeTask.setScriptName(nodeInfo.getScriptName());
|
||||
nextNodeTask.setScriptJson(nodeInfo.getScriptPath());
|
||||
nextNodeTask.setNodeId(nodeInfo.getNodeId());
|
||||
nextNodeTask.setNodeType(nodeInfo.getNodeType());
|
||||
nextNodeTask.setStatus(PlanConstant.TASK_WAIT_EXECUTE_STATUS);
|
||||
if (CollUtil.isNotEmpty(nodeInfo.getCaseParam())) {
|
||||
nextNodeTask.setNodeParams(JSONUtil.toJsonStr(nodeInfo.getCaseParam()));
|
||||
}
|
||||
nextNodeTask.setPrevNodeId(planSceneCaseTask.getNodeId());
|
||||
nextNodeTask.setCreatedTime(new Date());
|
||||
planSceneCaseTaskService.insert(nextNodeTask);
|
||||
|
||||
// 查询计划信息
|
||||
AtuPlanInfo planInfo = atuPlanInfoService.findByPrimaryKey(taskExecResult.getPlanId());
|
||||
if (ObjectUtil.isNull(planInfo)){
|
||||
logger.error("计划[" + taskExecResult.getPlanId() + "]信息为空");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("生成任务并推送至队列");
|
||||
AtuTaskExecDto atuTaskExecDto = new AtuTaskExecDto();
|
||||
atuTaskExecDto.setTenantId(planInfo.getTenantId());
|
||||
atuTaskExecDto.setProjectId(planInfo.getProjectId());
|
||||
atuTaskExecDto.setEnvId(envId);
|
||||
atuTaskExecDto.setPlanId(planInfo.getId());
|
||||
atuTaskExecDto.setBatchId(taskExecResult.getBatchId());
|
||||
atuTaskExecDto.setTaskId(nextNodeTask.getId());
|
||||
atuTaskExecDto.setCaseId(taskExecResult.getCaseId());
|
||||
atuTaskExecDto.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
|
||||
atuTaskExecDto.setScriptPath(nodeInfo.getScriptPath());
|
||||
atuTaskExecDto.setCaseParams(nodeInfo.getCaseParam());
|
||||
atuTaskExecDto.setAppSet(planInfo.getAppSet());
|
||||
atuTaskExecDto.setScreenRecordSet(planInfo.getScreenRecordSet());
|
||||
atuTaskExecDto.setScreenshotSet(planInfo.getScreenshotSet());
|
||||
atuTaskExecDto.setFailRetryNum(planInfo.getFailRetryCount());
|
||||
|
||||
atuTaskExecDto.setAppId(atuPlanTaskApiService.queryAppId(planInfo.getId(), nodeInfo.getPlatformType(),
|
||||
nodeInfo.getAppPackage(), nodeInfo.getNodeType()));
|
||||
|
||||
// 发送任务执行信息至消息队列
|
||||
atuPlanTaskApiService.sendToQueue(atuTaskExecDto.getBatchId(), atuTaskExecDto, nodeInfo.getNodeType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景任务异常结束
|
||||
* @param taskId 任务ID
|
||||
* @param errorMsg 错误信息
|
||||
*/
|
||||
private void sceneTaskExceptionEnd(String taskId, String errorMsg){
|
||||
AtuPlanTask planTask = planTaskService.findByPrimaryKey(taskId);
|
||||
planTask.setStatus(PlanConstant.TASK_EXECUTE_FAIL_STATUS);
|
||||
planTask.setEndTime(System.currentTimeMillis());
|
||||
planTask.setErrorMsg(errorMsg);
|
||||
planTaskService.updateByPrimaryKey(planTask);
|
||||
// 更新批次统计数据
|
||||
planBatchApiService.updateCacheBatchSumData(planTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收任务输入参数并更新入库
|
||||
*
|
||||
* @param message 对象消息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@RabbitListener(queues = RabbitConstant.TASK_INPUT_PARAM_UPDATE)
|
||||
public void receiveTaskInputParam(Message message) {
|
||||
logger.info("接收信息 ==> " + new String(message.getBody()));
|
||||
try {
|
||||
JSONObject event = JSONUtil.parseObj(new String(message.getBody()));
|
||||
AtuTaskExecDto receiveTask = JSONUtil.toBean(event, AtuTaskExecDto.class);
|
||||
if (PlanConstant.SCRIPT_TYPE_SCENE.equals(receiveTask.getCaseType())) {
|
||||
// 更新到场景任务表
|
||||
AtuPlanSceneCaseTask sceneTask = new AtuPlanSceneCaseTask();
|
||||
sceneTask.setId(receiveTask.getTaskId());
|
||||
sceneTask.setNodeParams(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
||||
planSceneCaseTaskService.updateByPrimaryKey(sceneTask);
|
||||
} else {
|
||||
// 更新到任务表
|
||||
AtuPlanTask updateTask = new AtuPlanTask();
|
||||
updateTask.setId(receiveTask.getTaskId());
|
||||
updateTask.setCaseParam(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
||||
planTaskService.updateByPrimaryKey(updateTask);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务的输入参数失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package net.northking.cctp.executePlan.consumer;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||
import net.northking.cctp.executePlan.constants.RabbitConstant;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
|
||||
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecDto;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
public class TaskInputParamConsumer {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(TaskInputParamConsumer.class);
|
||||
|
||||
@Autowired
|
||||
private AtuPlanSceneCaseTaskService planSceneCaseTaskService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanTaskService planTaskService;
|
||||
|
||||
@Autowired
|
||||
private AmqpAdmin amqpAdmin;
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
// 初始化队列并绑定到交换机
|
||||
Exchange exchange = new DirectExchange(RabbitConstant.PROJECT_EXCHANGE_KEY);
|
||||
amqpAdmin.declareExchange(exchange);
|
||||
Queue queue = new Queue(RabbitConstant.TASK_INPUT_PARAM_UPDATE);
|
||||
amqpAdmin.declareQueue(queue);
|
||||
Binding binding = BindingBuilder.bind(queue).to(exchange)
|
||||
.with(RabbitConstant.TASK_INPUT_PARAM_UPDATE).noargs();
|
||||
amqpAdmin.declareBinding(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收任务输入参数并更新入库
|
||||
*
|
||||
* @param message 对象消息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@RabbitListener(queues = RabbitConstant.TASK_INPUT_PARAM_UPDATE)
|
||||
public void receiveTaskInputParam(Message message) {
|
||||
logger.info("接收信息 ==> " + new String(message.getBody()));
|
||||
try {
|
||||
JSONObject event = JSONUtil.parseObj(new String(message.getBody()));
|
||||
AtuTaskExecDto receiveTask = JSONUtil.toBean(event, AtuTaskExecDto.class);
|
||||
if (PlanConstant.SCRIPT_TYPE_SCENE.equals(receiveTask.getCaseType())) {
|
||||
// 更新到场景任务表
|
||||
AtuPlanSceneCaseTask sceneTask = new AtuPlanSceneCaseTask();
|
||||
sceneTask.setId(receiveTask.getTaskId());
|
||||
sceneTask.setNodeParams(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
||||
planSceneCaseTaskService.updateByPrimaryKey(sceneTask);
|
||||
} else {
|
||||
// 更新到任务表
|
||||
AtuPlanTask updateTask = new AtuPlanTask();
|
||||
updateTask.setId(receiveTask.getTaskId());
|
||||
updateTask.setCaseParam(JSONUtil.toJsonStr(receiveTask.getCaseParams()));
|
||||
planTaskService.updateByPrimaryKey(updateTask);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务的输入参数失败", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -79,4 +79,10 @@ public interface AtuPlanSceneCaseTaskDao extends AtuPlanSceneCaseTaskMapper
|
|||
|
||||
List<AtuPlanSceneCaseTask> querySceneCaseTasksByTaskIds(@Param("taskIds") List<String> taskIds);
|
||||
|
||||
/**
|
||||
* 查询执行超时N秒任务
|
||||
* @param timeout 超时时间
|
||||
* @return 任务集合
|
||||
*/
|
||||
List<AtuPlanSceneCaseTask> queryExecTimeoutTask(int timeout);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package net.northking.cctp.executePlan.db.dao;
|
|||
import net.northking.cctp.common.http.QueryByPage;
|
||||
import net.northking.cctp.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<AtuPlanTask> queryTasksByBatchIds(@Param("batchIds") List<String> batchIds);
|
||||
|
||||
/**
|
||||
* 查询执行超时N秒任务
|
||||
* @param timeout 超时时间
|
||||
* @return 任务集合
|
||||
*/
|
||||
List<AtuPlanTask> queryExecTimeoutTask(int timeout);
|
||||
|
||||
/**
|
||||
* 根据id查询任务扩展信息
|
||||
* @param id 任务id
|
||||
* @return 任务扩展信息
|
||||
*/
|
||||
AtuPlanTaskExtendDto queryTaskExtendById(String id);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 京北方信息技术股份有限公司 Corporation 2023 . All rights reserved.
|
||||
* Copyright (c) 京北方信息技术股份有限公司 Corporation 2024 . All rights reserved.
|
||||
*
|
||||
*/
|
||||
package net.northking.cctp.executePlan.db.entity;
|
||||
|
@ -17,7 +17,7 @@ import java.util.Date;
|
|||
* 计划场景用例节点任务表
|
||||
*
|
||||
* <p>文件内容由代码生成器产生,请不要手动修改! <br>
|
||||
* createdate: 2023-11-13 14:35:51 <br>
|
||||
* createdate: 2024-07-03 18:59:03 <br>
|
||||
* @author: maven-cctp-plugin <br>
|
||||
* @since: 1.0 <br>
|
||||
*/
|
||||
|
@ -48,6 +48,7 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable
|
|||
public static final String KEY_execResultFile = "execResultFile";
|
||||
public static final String KEY_perDataPath = "perDataPath";
|
||||
public static final String KEY_createdTime = "createdTime";
|
||||
public static final String KEY_lastHeartbeatTime = "lastHeartbeatTime";
|
||||
|
||||
/**
|
||||
* 主键<br>
|
||||
|
@ -189,6 +190,11 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable
|
|||
*/
|
||||
@ApiModelProperty("创建时间")
|
||||
private Date createdTime;
|
||||
/**
|
||||
* 执行心跳时间<br>
|
||||
*/
|
||||
@ApiModelProperty("执行心跳时间")
|
||||
private Date lastHeartbeatTime;
|
||||
|
||||
/**
|
||||
* 主键<br>
|
||||
|
@ -605,6 +611,30 @@ public class AtuPlanSceneCaseTask extends Partition implements Serializable
|
|||
this.createdTime = (Date)createdTime.clone();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 执行心跳时间<br>
|
||||
*/
|
||||
public Date getLastHeartbeatTime()
|
||||
{
|
||||
if(lastHeartbeatTime != null)
|
||||
{
|
||||
return (Date)lastHeartbeatTime.clone();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行心跳时间<br>
|
||||
*
|
||||
* @param lastHeartbeatTime 执行心跳时间
|
||||
*/
|
||||
public void setLastHeartbeatTime(Date lastHeartbeatTime)
|
||||
{
|
||||
if(lastHeartbeatTime != null)
|
||||
{
|
||||
this.lastHeartbeatTime = (Date)lastHeartbeatTime.clone();
|
||||
}
|
||||
}
|
||||
|
||||
public AtuPlanSceneCaseTask()
|
||||
{
|
||||
|
|
|
@ -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";
|
||||
|
||||
/**
|
||||
* 主键<br>
|
||||
|
@ -196,6 +197,11 @@ public class AtuPlanTask extends Partition implements Serializable
|
|||
*/
|
||||
@ApiModelProperty("创建时间")
|
||||
private Date createdTime;
|
||||
/**
|
||||
* 执行心跳时间<br>
|
||||
*/
|
||||
@ApiModelProperty("执行心跳时间")
|
||||
private Date lastHeartbeatTime;
|
||||
|
||||
/**
|
||||
* 主键<br>
|
||||
|
@ -629,6 +635,30 @@ public class AtuPlanTask extends Partition implements Serializable
|
|||
this.createdTime = (Date)createdTime.clone();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 执行心跳时间<br>
|
||||
*/
|
||||
public Date getLastHeartbeatTime()
|
||||
{
|
||||
if(lastHeartbeatTime != null)
|
||||
{
|
||||
return (Date)lastHeartbeatTime.clone();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行心跳时间<br>
|
||||
*
|
||||
* @param lastHeartbeatTime 执行心跳时间
|
||||
*/
|
||||
public void setLastHeartbeatTime(Date lastHeartbeatTime)
|
||||
{
|
||||
if(lastHeartbeatTime != null)
|
||||
{
|
||||
this.lastHeartbeatTime = (Date)lastHeartbeatTime.clone();
|
||||
}
|
||||
}
|
||||
|
||||
public AtuPlanTask()
|
||||
{
|
||||
|
|
|
@ -93,6 +93,11 @@ private static final Logger logger = LoggerFactory.getLogger(AtuPlanSceneCaseTas
|
|||
return this.atuPlanSceneCaseTaskDao.querySceneCaseTasksByTaskIds(taskIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AtuPlanSceneCaseTask> queryExecTimeoutTask(int timeout) {
|
||||
return this.atuPlanSceneCaseTaskDao.queryExecTimeoutTask(timeout);
|
||||
}
|
||||
|
||||
|
||||
// ---- The End by Generator ----//
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import net.northking.cctp.common.http.QueryByPage;
|
|||
import net.northking.cctp.executePlan.db.dao.AtuPlanTaskDao;
|
||||
import net.northking.cctp.executePlan.db.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<AtuPlanTask> queryExecTimeoutTask(int timeout) {
|
||||
return this.atuPlanTaskDao.queryExecTimeoutTask(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtuPlanTaskExtendDto queryTaskExtendById(String taskId) {
|
||||
return this.atuPlanTaskDao.queryTaskExtendById(taskId);
|
||||
}
|
||||
|
||||
// ---- The End by Generator ----//
|
||||
|
||||
|
||||
|
|
|
@ -42,5 +42,6 @@ public interface AtuPlanSceneCaseTaskMapper extends BasicDao<AtuPlanSceneCaseTas
|
|||
String COLUMN_execResultFile = "exec_result_file";
|
||||
String COLUMN_perDataPath = "per_data_path";
|
||||
String COLUMN_createdTime = "created_time";
|
||||
String COLUMN_lastHeartbeatTime = "last_heartbeat_time";
|
||||
|
||||
}
|
||||
|
|
|
@ -43,5 +43,6 @@ public interface AtuPlanTaskMapper extends BasicDao<AtuPlanTask>
|
|||
String COLUMN_bugId = "bug_id";
|
||||
String COLUMN_perDataPath = "per_data_path";
|
||||
String COLUMN_createdTime = "created_time";
|
||||
String COLUMN_lastHeartbeatTime = "last_heartbeat_time";
|
||||
|
||||
}
|
||||
|
|
|
@ -79,4 +79,10 @@ public interface AtuPlanSceneCaseTaskService extends BasicService<AtuPlanSceneCa
|
|||
*/
|
||||
List<AtuPlanSceneCaseTask> querySceneCaseTasksByTaskIds(List<String> taskIds);
|
||||
|
||||
/**
|
||||
* 查询执行超时N秒任务
|
||||
* @param timeout 超时时间
|
||||
* @return 任务集合
|
||||
*/
|
||||
List<AtuPlanSceneCaseTask> queryExecTimeoutTask(int timeout);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package net.northking.cctp.executePlan.db.service;
|
|||
import net.northking.cctp.common.db.BasicService;
|
||||
import net.northking.cctp.common.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<AtuPlanTask>
|
|||
*/
|
||||
List<AtuPlanTask> queryTasksByBatchIds(List<String> batchIds);
|
||||
|
||||
/**
|
||||
* 查询执行超时N秒任务
|
||||
* @param timeout 超时时间
|
||||
* @return 任务集合
|
||||
*/
|
||||
List<AtuPlanTask> queryExecTimeoutTask(int timeout);
|
||||
|
||||
/**
|
||||
* 根据id查询任务扩展信息
|
||||
* @param taskId 任务id
|
||||
* @return 任务扩展信息
|
||||
*/
|
||||
AtuPlanTaskExtendDto queryTaskExtendById(String taskId);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package net.northking.cctp.executePlan.dto.planTask;
|
||||
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||
|
||||
public class AtuPlanTaskExtendDto extends AtuPlanTask {
|
||||
|
||||
private String planId;
|
||||
|
||||
private String planName;
|
||||
|
||||
public String getPlanId() {
|
||||
return planId;
|
||||
}
|
||||
|
||||
public void setPlanId(String planId) {
|
||||
this.planId = planId;
|
||||
}
|
||||
|
||||
public String getPlanName() {
|
||||
return planName;
|
||||
}
|
||||
|
||||
public void setPlanName(String planName) {
|
||||
this.planName = planName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package net.northking.cctp.executePlan.job;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import net.northking.cctp.executePlan.api.service.AtuPlanSceneCaseTaskApiService;
|
||||
import net.northking.cctp.executePlan.api.service.AtuPlanTaskApiService;
|
||||
import net.northking.cctp.executePlan.constants.PlanConstant;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask;
|
||||
import net.northking.cctp.executePlan.db.entity.AtuPlanTask;
|
||||
import net.northking.cctp.executePlan.db.service.AtuPlanSceneCaseTaskService;
|
||||
import net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto;
|
||||
import net.northking.cctp.executePlan.dto.planTask.AtuTaskExecResultDto;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EnableScheduling
|
||||
@Configuration
|
||||
public class TaskExecTimeoutJob {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(TaskExecTimeoutJob.class);
|
||||
|
||||
@Autowired
|
||||
private AtuPlanTaskApiService planTaskApiService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanSceneCaseTaskService sceneCaseTaskService;
|
||||
|
||||
@Autowired
|
||||
private AtuPlanSceneCaseTaskApiService sceneCaseTaskApiService;
|
||||
|
||||
/**
|
||||
* 超时时间,默认3分钟,单位秒
|
||||
*/
|
||||
public static final int TIMEOUT = 3 * 60;
|
||||
|
||||
public static final String ERROR_MSG = "引擎执行任务超时";
|
||||
|
||||
@Scheduled(initialDelay = 10 * 1000,fixedDelay = 60 * 1000)
|
||||
private void handleTaskExecTimeout(){
|
||||
// 查询正在执行中且心跳时间超过n分钟的任务
|
||||
List<AtuPlanTask> taskList = planTaskApiService.queryExecTimeoutTask(TIMEOUT);
|
||||
if (CollUtil.isNotEmpty(taskList)){
|
||||
logger.debug("超时任务数量:{}", taskList.size());
|
||||
for (AtuPlanTask planTask : taskList) {
|
||||
planTaskApiService.taskExecFailUpdate(planTask, PlanConstant.TASK_TIMEOUT_STATUS, ERROR_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
List<AtuPlanSceneCaseTask> sceneCaseTaskList = sceneCaseTaskService.queryExecTimeoutTask(TIMEOUT);
|
||||
if (CollUtil.isNotEmpty(sceneCaseTaskList)){
|
||||
logger.debug("超时场景节点任务数量:{}", sceneCaseTaskList.size());
|
||||
AtuTaskExecResultDto taskExecResult = new AtuTaskExecResultDto();
|
||||
taskExecResult.setCaseType(PlanConstant.SCRIPT_TYPE_SCENE);
|
||||
taskExecResult.setStatus(PlanConstant.TASK_TIMEOUT_STATUS);
|
||||
taskExecResult.setMessage(ERROR_MSG);
|
||||
taskExecResult.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
for (AtuPlanSceneCaseTask sceneCaseTask : sceneCaseTaskList) {
|
||||
AtuPlanTaskExtendDto taskExtendDto = planTaskApiService.queryTaskExtendById(sceneCaseTask.getTaskId());
|
||||
if (taskExtendDto == null){
|
||||
logger.error("任务[{}]信息不存在", sceneCaseTask.getTaskId());
|
||||
logger.error("删除任务[{}]的节点任务信息", sceneCaseTask.getTaskId());
|
||||
AtuPlanSceneCaseTask delParams = new AtuPlanSceneCaseTask();
|
||||
delParams.setTaskId(sceneCaseTask.getTaskId());
|
||||
sceneCaseTaskService.deleteByExample(delParams);
|
||||
continue;
|
||||
}
|
||||
taskExecResult.setPlanId(taskExtendDto.getPlanId());
|
||||
taskExecResult.setBatchId(taskExtendDto.getBatchId());
|
||||
taskExecResult.setCaseId(taskExtendDto.getCaseId());
|
||||
taskExecResult.setTaskId(sceneCaseTask.getId());
|
||||
AtuPlanTask planTask = new AtuPlanTask();
|
||||
BeanUtils.copyProperties(taskExtendDto, planTask);
|
||||
// 场景节点执行失败,执行下一节点
|
||||
sceneCaseTaskApiService.queryNextNodeInfo(taskExecResult, sceneCaseTask, planTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
<resultMap id="BaseResultMap" type="net.northking.cctp.executePlan.db.entity.AtuPlanSceneCaseTask">
|
||||
<!-- 主键 -->
|
||||
<id column="id" jdbcType="VARCHAR" property="id"/>
|
||||
<!-- 任务主键 -->
|
||||
<!-- 任务主键 -->
|
||||
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
|
||||
<!-- 脚本主键 -->
|
||||
<result column="script_id" jdbcType="VARCHAR" property="scriptId"/>
|
||||
|
@ -55,6 +55,8 @@
|
|||
<result column="per_data_path" jdbcType="VARCHAR" property="perDataPath"/>
|
||||
<!-- 创建时间 -->
|
||||
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
||||
<!-- 执行心跳时间 -->
|
||||
<result column="last_heartbeat_time" jdbcType="TIMESTAMP" property="lastHeartbeatTime"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
|
@ -82,6 +84,7 @@
|
|||
,exec_result_file
|
||||
,per_data_path
|
||||
,created_time
|
||||
,last_heartbeat_time
|
||||
</sql>
|
||||
|
||||
<sql id="Table_Name">
|
||||
|
@ -202,6 +205,9 @@
|
|||
</if>
|
||||
<if test="createdTime != null">
|
||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
</trim>
|
||||
</delete>
|
||||
|
@ -282,6 +288,9 @@
|
|||
<if test="createdTime != null">
|
||||
created_time,
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
last_heartbeat_time,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -356,6 +365,9 @@
|
|||
<if test="createdTime != null">
|
||||
#{createdTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
#{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
|
@ -387,6 +399,7 @@
|
|||
exec_result_file,
|
||||
per_data_path,
|
||||
created_time,
|
||||
last_heartbeat_time,
|
||||
</trim>
|
||||
values
|
||||
<foreach collection="list" item="item" index="index" separator=",">
|
||||
|
@ -415,6 +428,7 @@
|
|||
#{item.execResultFile, jdbcType=VARCHAR},
|
||||
#{item.perDataPath, jdbcType=VARCHAR},
|
||||
#{item.createdTime, jdbcType=TIMESTAMP},
|
||||
#{item.lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||
</trim>
|
||||
</foreach>
|
||||
</insert>
|
||||
|
@ -492,6 +506,9 @@
|
|||
<if test="createdTime != null">
|
||||
created_time = #{createdTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
last_heartbeat_time = #{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
@ -574,6 +591,9 @@
|
|||
<if test="createdTime != null">
|
||||
AND created_time = #{createdTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
AND last_heartbeat_time = #{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
</trim>
|
||||
</select>
|
||||
|
||||
|
@ -653,6 +673,9 @@
|
|||
<if test="createdTime != null">
|
||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
</trim>
|
||||
</select>
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
<result column="per_data_path" jdbcType="VARCHAR" property="perDataPath"/>
|
||||
<!-- 创建时间 -->
|
||||
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
||||
<!-- 执行心跳时间 -->
|
||||
<result column="last_heartbeat_time" jdbcType="TIMESTAMP" property="lastHeartbeatTime"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
|
@ -85,6 +87,7 @@
|
|||
,bug_id
|
||||
,per_data_path
|
||||
,created_time
|
||||
,last_heartbeat_time
|
||||
</sql>
|
||||
|
||||
<sql id="Table_Name">
|
||||
|
@ -208,6 +211,9 @@
|
|||
</if>
|
||||
<if test="createdTime != null">
|
||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
</trim>
|
||||
</delete>
|
||||
|
@ -291,6 +297,9 @@
|
|||
<if test="createdTime != null">
|
||||
created_time,
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
last_heartbeat_time,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -368,6 +377,9 @@
|
|||
<if test="createdTime != null">
|
||||
#{createdTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
#{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
|
@ -400,6 +412,7 @@
|
|||
bug_id,
|
||||
per_data_path,
|
||||
created_time,
|
||||
last_heartbeat_time,
|
||||
</trim>
|
||||
values
|
||||
<foreach collection="list" item="item" index="index" separator=",">
|
||||
|
@ -429,6 +442,7 @@
|
|||
#{item.bugId, jdbcType=VARCHAR},
|
||||
#{item.perDataPath, jdbcType=VARCHAR},
|
||||
#{item.createdTime, jdbcType=TIMESTAMP},
|
||||
#{item.lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||
</trim>
|
||||
</foreach>
|
||||
</insert>
|
||||
|
@ -509,6 +523,9 @@
|
|||
<if test="createdTime != null">
|
||||
created_time = #{createdTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
last_heartbeat_time = #{lastHeartbeatTime, jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
@ -594,6 +611,9 @@
|
|||
<if test="createdTime != null">
|
||||
AND created_time = #{createdTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
AND last_heartbeat_time = #{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
</trim>
|
||||
</select>
|
||||
|
||||
|
@ -676,6 +696,9 @@
|
|||
<if test="createdTime != null">
|
||||
AND created_time=#{createdTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
<if test="lastHeartbeatTime != null">
|
||||
AND last_heartbeat_time=#{lastHeartbeatTime,jdbcType=TIMESTAMP}
|
||||
</if>
|
||||
</trim>
|
||||
</select>
|
||||
|
||||
|
|
|
@ -220,8 +220,15 @@
|
|||
#{taskId}
|
||||
</foreach>
|
||||
</select>
|
||||
<select id="queryExecTimeoutTask" resultMap="BaseResultMap">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
from
|
||||
<include refid="Table_Name"/>
|
||||
where status = 1 and timestampdiff(second, last_heartbeat_time, NOW()) > #{timeout}
|
||||
</select>
|
||||
|
||||
<delete id="deleteByPlanIdLimit">
|
||||
<delete id="deleteByPlanIdLimit">
|
||||
delete from atu_plan_scene_case_task
|
||||
where task_id in (select id from atu_plan_task
|
||||
where batch_id in (select id from atu_plan_batch
|
||||
|
|
|
@ -53,6 +53,13 @@
|
|||
<result column="created_time" jdbcType="TIMESTAMP" property="createdTime"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="TaskExtendMap" extends="BaseResultMap" type="net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskExtendDto">
|
||||
<!-- 计划主键 -->
|
||||
<result column="plan_id" jdbcType="VARCHAR" property="planId"/>
|
||||
<!-- 脚本主键 -->
|
||||
<result column="plan_name" jdbcType="VARCHAR" property="planName"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="queryList" resultMap="DetailMap" parameterType="net.northking.cctp.executePlan.dto.planTask.AtuPlanTaskQueryDto">
|
||||
select
|
||||
id
|
||||
|
@ -307,9 +314,17 @@
|
|||
#{batchId}
|
||||
</foreach>
|
||||
</select>
|
||||
<select id="queryExecTimeoutTask" resultMap="BaseResultMap">
|
||||
select <include refid="Base_Column_List"/> from <include refid="Table_Name"/>
|
||||
where status = 1 and case_type != 5 and timestampdiff(second, last_heartbeat_time, NOW()) > #{timeout}
|
||||
</select>
|
||||
<select id="queryTaskExtendById" resultMap="TaskExtendMap">
|
||||
SELECT t.*, b.plan_id from <include refid="Table_Name"/> t
|
||||
left join atu_plan_batch b on b.id = t.batch_id
|
||||
where t.id = #{id}
|
||||
</select>
|
||||
|
||||
|
||||
<delete id="deleteByPlanIdLimit">
|
||||
<delete id="deleteByPlanIdLimit">
|
||||
DELETE FROM <include refid="Table_Name"/>
|
||||
where batch_id in (select id from atu_plan_batch where plan_id = #{planId})
|
||||
LIMIT #{num}
|
||||
|
|
|
@ -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方式)
|
||||
}
|
||||
|
|
|
@ -57,10 +57,11 @@ public class AtuElementInfoRestfulCtrl
|
|||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
|
||||
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public ResultWrapper<String> add(
|
||||
@RequestBody @Validated AtuElementInfoAddDto inputDto)
|
||||
@RequestHeader(AuthDependencyConstants.REQUEST_HEADER_PROJECT_ID) String projectId,
|
||||
@RequestBody @Validated AtuElementInfoAddDto inputDto)
|
||||
{
|
||||
ResultWrapper<String> wrapper = new ResultWrapper<>();
|
||||
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<Integer> update(
|
||||
@RequestBody @Validated AtuElementInfoUpdateDto inputDto) throws IllegalAccessException
|
||||
@RequestHeader(AuthDependencyConstants.REQUEST_HEADER_PROJECT_ID) String projectId,
|
||||
@RequestBody @Validated AtuElementInfoUpdateDto inputDto) throws IllegalAccessException
|
||||
{
|
||||
ResultWrapper<Integer> wrapper = new ResultWrapper<>();
|
||||
Integer count = apiService.updateByPK(inputDto);
|
||||
Integer count = apiService.updateByPK(inputDto, projectId);
|
||||
return wrapper.success(count);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* 获取元素信息管理
|
||||
|
|
|
@ -298,13 +298,18 @@ public class AtuElementInfoApiServiceImpl extends AbstractExcelService<AtuElemen
|
|||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
||||
public String add(AtuElementInfoAddDto dto)
|
||||
public String add(AtuElementInfoAddDto dto, String projectId)
|
||||
{
|
||||
//元素目录校验
|
||||
AtuElementGroup elementGroup = atuElementGroupService.findByPrimaryKey(dto.getGroupId());
|
||||
if (null == elementGroup || ScriptConstant.STR_ZERO.equals(elementGroup.getIsLeaf())) {
|
||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_DIR_IS_VALID);
|
||||
}
|
||||
//元素名称校验
|
||||
List<AtuElementInfo> atuElementInfos = atuElementInfoService.findByName(dto.getName(), projectId);
|
||||
if (!CollectionUtils.isEmpty(atuElementInfos)) {
|
||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_IS_EXISTS);
|
||||
}
|
||||
// 校验获取方式是否为空
|
||||
if (CollUtil.isEmpty(dto.getFetchTypeList())){
|
||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS);
|
||||
|
@ -374,13 +379,18 @@ public class AtuElementInfoApiServiceImpl extends AbstractExcelService<AtuElemen
|
|||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
||||
public Integer updateByPK(AtuElementInfoUpdateDto dto) throws IllegalAccessException
|
||||
public Integer updateByPK(AtuElementInfoUpdateDto dto, String projectId) throws IllegalAccessException
|
||||
{
|
||||
//参数校验
|
||||
AtuElementInfo oldElement = atuElementInfoService.findByPrimaryKey(dto.getId());
|
||||
if (oldElement == null || ScriptConstant.STR_ONE.equals(oldElement.getIsDeleted())) {
|
||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_NO_EXISTS);
|
||||
}
|
||||
//元素名称校验
|
||||
List<AtuElementInfo> atuElementInfos = atuElementInfoService.findByNameButNotSelf(dto.getName(), dto.getId(), projectId);
|
||||
if (!CollectionUtils.isEmpty(atuElementInfos)) {
|
||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_IS_EXISTS);
|
||||
}
|
||||
// 校验获取方式是否为空
|
||||
if (CollUtil.isEmpty(dto.getFetchTypeList())){
|
||||
throw new PlatformRuntimeException(ElementLibraryError.ELEMENT_FETCH_TYPE_NO_EXISTS);
|
||||
|
|
|
@ -35,6 +35,8 @@ public interface AtuElementInfoDao extends AtuElementInfoMapper
|
|||
|
||||
List<String> selectUpdaterByProject(@Param("projectId") String projectId);
|
||||
|
||||
List<AtuElementInfo> findByName(@Param("name") String name, @Param("projectId") String projectId);
|
||||
|
||||
List<AtuElementInfo> findByNameButNotSelf(@Param("name") String name, @Param("id") String id, @Param("projectId") String projectId);
|
||||
|
||||
}
|
||||
|
|
|
@ -70,5 +70,25 @@ private static final Logger logger = LoggerFactory.getLogger(AtuElementInfoServi
|
|||
public List<String> selectUpdaterByProject(String projectId) {
|
||||
return atuElementInfoDao.selectUpdaterByProject(projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查询元素
|
||||
* @param name 元素名称
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<AtuElementInfo> findByName(String name, String projectId) {
|
||||
return atuElementInfoDao.findByName(name, projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查询元素
|
||||
* @param name 元素名称
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<AtuElementInfo> findByNameButNotSelf(String name, String id, String projectId) {
|
||||
return atuElementInfoDao.findByNameButNotSelf(name, id, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,18 @@ public interface AtuElementInfoService extends BasicService<AtuElementInfo>
|
|||
|
||||
List<String> selectUpdaterByProject(String projectId);
|
||||
|
||||
/**
|
||||
* 根据名称查询元素
|
||||
* @param name 元素名称
|
||||
* @return
|
||||
*/
|
||||
List<AtuElementInfo> findByName(String name, String projectId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据名称查询元素
|
||||
* @param name 元素名称
|
||||
* @return
|
||||
*/
|
||||
List<AtuElementInfo> findByNameButNotSelf(String name, String id, String projectId);
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ public class AtuScriptInfoDetailDto extends AtuScriptInfo implements Serializabl
|
|||
private List<Object> inputList;
|
||||
@ApiModelProperty("脚本输出项参数")
|
||||
private List<Object> outputList;
|
||||
@ApiModelProperty("分组信息")
|
||||
private String namePath;
|
||||
|
||||
|
||||
public List<Object> 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -203,6 +203,25 @@
|
|||
WHERE is_deleted = 0 AND project_id = #{projectId}
|
||||
GROUP BY updated_by
|
||||
</select>
|
||||
<select id="findByName" resultType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
from
|
||||
<include refid="Table_Name"/>
|
||||
where is_deleted = '0'
|
||||
and name = #{name}
|
||||
and project_id = #{projectId}
|
||||
</select>
|
||||
<select id="findByNameButNotSelf" resultType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
from
|
||||
<include refid="Table_Name"/>
|
||||
where is_deleted = '0'
|
||||
and id != #{id}
|
||||
and name = #{name}
|
||||
and project_id = #{projectId}
|
||||
</select>
|
||||
<update id="deleteElementBatch" parameterType="net.northking.cctp.scriptcase.db.entity.AtuElementInfo">
|
||||
update
|
||||
<include refid="Table_Name"/>
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
<result column="app_package" jdbcType="VARCHAR" property="appPackage"/>
|
||||
<!-- 版本名称 -->
|
||||
<result column="version_name" jdbcType="VARCHAR" property="versionName"/>
|
||||
<!-- 分组 -->
|
||||
<result column="name_path" jdbcType="VARCHAR" property="namePath"/>
|
||||
<!-- 应用平台 -->
|
||||
<result column="platform" jdbcType="VARCHAR" property="platform"/>
|
||||
|
||||
|
@ -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
|
||||
<trim prefix="WHERE" prefixOverrides="AND" >
|
||||
<if test="query.projectIds != null and query.projectIds.size > 0">
|
||||
AND a.project_id IN
|
||||
|
|
|
@ -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方式)
|
||||
}
|
||||
|
|
|
@ -83,4 +83,9 @@ public interface UpperParamKey {
|
|||
* 识别类型
|
||||
*/
|
||||
String USING_TYPE = "using_type";
|
||||
|
||||
/**
|
||||
* 租户id
|
||||
*/
|
||||
String TENANT_ID = "tenant_id";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,4 +76,8 @@ public interface AutomationMessageHandler {
|
|||
|
||||
void getElementMoneyText(CmdAutomationRequest request); //识别金额
|
||||
|
||||
void screenShot(CmdAutomationRequest request); //识别金额
|
||||
|
||||
void getElementValueByPathOcr(CmdAutomationRequest request); ////获取控件的值(OCR方式)
|
||||
|
||||
}
|
||||
|
|
|
@ -9,12 +9,15 @@ import net.northking.cctp.upperComputer.automation.constants.Command;
|
|||
import net.northking.cctp.upperComputer.automation.constants.UpperParamKey;
|
||||
import net.northking.cctp.upperComputer.automation.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<String, Object> 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<Map<String, Object>> 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<String, Object> data = request.getData();
|
||||
String path = null;
|
||||
try {
|
||||
//租户id
|
||||
String tenantId = (String) data.get(UpperParamKey.TENANT_ID);
|
||||
String serverAddr = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
|
||||
String publicUploadAddr = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
|
||||
Attachment upload = HttpUtils.upload(serverAddr + publicUploadAddr, file.getAbsolutePath(), tenantId, "", FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
|
||||
if (null != upload && org.apache.commons.lang3.StringUtils.isNotBlank(upload.getId())) {
|
||||
logger.debug("文件上传成功,返回id:{}", upload.getId());
|
||||
path = upload.getUrlPath();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("截图失败", e);
|
||||
response = CmdAutomationResponse.builderFailure(request, e.getMessage());
|
||||
} finally {
|
||||
if (file.exists()) {
|
||||
boolean delete = file.delete();
|
||||
if (!delete) {
|
||||
logger.warn("临时文件【{}】删除失败", file.getAbsolutePath());
|
||||
response = CmdAutomationResponse.builderFailure(request, "临时文件【{}】删除失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(path)) {
|
||||
response = CmdAutomationResponse.builderSuccess(request, "屏幕截图成功").withData(path);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("屏幕截图失败,原因:", e);
|
||||
response = CmdAutomationResponse.builderFailure(request, e.getMessage());
|
||||
} finally {
|
||||
sendResultToEngine(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getElementValueByPathOcr(CmdAutomationRequest request) {
|
||||
CmdAutomationResponse response = CmdAutomationResponse.builderFailure(request, "未查找到控件");
|
||||
try {
|
||||
Map<String, Object> data = request.getData();
|
||||
Integer waitTimeout = (Integer) data.get(UpperParamKey.WAIT_TIMEOUT);
|
||||
NKAgent nkAgent = getNkAgent();
|
||||
if (null == nkAgent) {
|
||||
response = CmdAutomationResponse.builderFailure(request, "设备与上位机连接断开");
|
||||
return;
|
||||
}
|
||||
String nodeTree = (String) data.get(UpperParamKey.NODE_TREE);
|
||||
logger.debug("拿到的nodeTree:{}", nodeTree);
|
||||
SearchUiNodeData searchUiNodeData = new SearchUiNodeData();
|
||||
searchUiNodeData.setChain(nodeTree);
|
||||
UiNodeData uiNodeData = null;
|
||||
String value = null;
|
||||
long startTime = System.currentTimeMillis(); //当前时间
|
||||
long elapsedTime = 0; // 初始化经过的时间
|
||||
while(elapsedTime < waitTimeout * 1000 -300) {
|
||||
uiNodeData = nkAgent.uiNodeInfo(searchUiNodeData);
|
||||
// logger.info("拿到的uiNodeData:{}", uiNodeData);
|
||||
if (uiNodeData != null && !(uiNodeData.getX() <= 0 && uiNodeData.getY() <= 0)) {
|
||||
value = getValueByOcr(uiNodeData);
|
||||
break;
|
||||
}
|
||||
long currentTime = System.currentTimeMillis();
|
||||
elapsedTime = currentTime - startTime - 1000;
|
||||
}
|
||||
response = CmdAutomationResponse.builderSuccess(request, "获取控件的值(OCR方式)成功").withData(value);
|
||||
} catch (Exception e) {
|
||||
logger.error("根据xpath判断元素是否存在失败,原因:", e);
|
||||
response = CmdAutomationResponse.builderFailure(request, e.getMessage());
|
||||
} finally{
|
||||
logger.info("根据nodetree查找元素失败的response:{}", response);
|
||||
sendResultToEngine(response);
|
||||
}
|
||||
}
|
||||
|
||||
private String getValueByOcr(UiNodeData uiNodeData) {
|
||||
String value = null;
|
||||
String imgBase64 = getOcrAreaByBodeToBase64(uiNodeData);
|
||||
String ocrAddress = SpringUtils.getProperties("nk.http-request-path.ocrGetAllTextNum");
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
Map<String, Object> ocrParamMap = new HashMap<>();
|
||||
ocrParamMap.put("img_base64", imgBase64);
|
||||
ocrParamMap.put("targets", "");
|
||||
HttpEntity<Map> ocrEntity = new HttpEntity<>(ocrParamMap, headers);
|
||||
List ocrResultList = null;
|
||||
try {
|
||||
logger.info("传参:{}", JSON.toJSONString(ocrParamMap));
|
||||
ocrResultList = HttpUtils.doPost(ocrAddress, ocrEntity, List.class);
|
||||
logger.info("得到的ocr结果:{}", ocrResultList);
|
||||
} catch (Exception e) {
|
||||
logger.error("ocr失败", e);
|
||||
}
|
||||
List<String> dataList = new ArrayList<>();
|
||||
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||
for (Object o : ocrResultList) {
|
||||
dataList.add(((Map) o).get("text") + "");
|
||||
}
|
||||
}
|
||||
}
|
||||
value = JSON.toJSONString(dataList);
|
||||
} catch (Exception e) {
|
||||
logger.error("请求ocr接口失败,接口地址:{}",ocrAddress,e);
|
||||
}
|
||||
logger.info("识别文字为:{}", value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getOcrAreaByBodeToBase64(UiNodeData uiNodeData) {
|
||||
Integer x = uiNodeData.getX();
|
||||
Integer y = uiNodeData.getY();
|
||||
Integer screenWidth = phoneEntity.getScreenWidth();
|
||||
Integer screenHeight = phoneEntity.getScreenHeight();
|
||||
Integer cutWidth = uiNodeData.getWidth();
|
||||
Integer cutHeight = uiNodeData.getHeight();
|
||||
logger.debug("拿到的截图参数----->>>>>x:{},y:{},screenWidth:{},screenHeight:{},cutWidth:{},cutHeight:{}", x, y, screenWidth, screenHeight, cutWidth, cutHeight);
|
||||
File file = deviceHandleHelper.getScreenShotFile(serial, x, y, cutWidth, cutHeight, screenWidth, screenHeight);
|
||||
logger.debug("ocr截图:{}", file.getAbsolutePath());
|
||||
String base64Str = getFileBase64(file);
|
||||
return base64Str;
|
||||
}
|
||||
|
||||
private void getKeyBoardInfo(String ocrArea) {
|
||||
Map<String, JSONObject> itemMap = new HashMap<>();
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<String, Object> 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ spring:
|
|||
nk:
|
||||
mobile-computer:
|
||||
password: 123456
|
||||
keepScreenOn: true #<EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>true<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>agentʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD>false<EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>Ļ
|
||||
keepScreenOn: true #是否点亮屏幕,如果true,启动agent时不会关闭手机屏幕,false则会关闭手机屏幕
|
||||
idPath: /home/attect/cctp-mobile
|
||||
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<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD>λ<EFBFBD><EFBFBD>
|
||||
imgFindArea: ${nk.http-request-path.ocrServer}/WebAPI/findImageInImage #ͼƬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>
|
||||
ocrGetText: ${nk.http-request-path.ocrServer}/WebAPI/GetTextByOcr #ͼƬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>
|
||||
ocrGetVerificationCode: http://158.58.160.183:8000/yzm_v2 #<EFBFBD>°<EFBFBD>ocr<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD>
|
||||
ocrGetAllTextNum: http://158.58.160.183:8000/rpa_v2 #<EFBFBD>°<EFBFBD>ocr<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>
|
||||
getSafeKeyBoardNum: http://158.58.160.183:8000/keyboard_v2 #<EFBFBD>°<EFBFBD>ocr<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ȫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
hzBankOcrAddress: http://197.68.24.38:9283/gateway/spring-ocrcloud-platform/OCRA0001 #<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>OCR<EFBFBD><EFBFBD><EFBFBD><EFBFBD>nginx<EFBFBD><EFBFBD>ַ
|
||||
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地址
|
||||
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
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
*.log.gz
|
||||
logs/
|
||||
*/logs/
|
||||
*/*/logs/
|
||||
|
||||
# Temp file
|
||||
*.temp
|
||||
temp
|
||||
*/temp
|
||||
|
||||
# IDEA profile dir
|
||||
.idea/
|
||||
*/.idea/
|
||||
*/*/.idea/
|
||||
|
||||
# IDEA project file
|
||||
*.iml
|
||||
*/*.iml
|
||||
*/*/*.iml
|
||||
*/*/*/*.iml
|
||||
|
||||
target/
|
||||
*/target/
|
||||
*/*/target/
|
||||
|
|
@ -2,6 +2,7 @@ package net.northking.cctp.device.api.activity.service;
|
|||
|
||||
import net.northking.cctp.common.db.Pagination;
|
||||
import net.northking.cctp.common.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<String,List<String>> queryDeviceType(List<String> deviceIds);
|
||||
|
||||
/**
|
||||
* 释放设备
|
||||
* @param token 设备token
|
||||
* @return true or false
|
||||
*/
|
||||
Boolean deviceRelease(String token);
|
||||
|
||||
/**
|
||||
* 释放引擎绑定的所有设备
|
||||
* @param deviceTokens 设备token信息
|
||||
*/
|
||||
void release(List<CdDeviceToken> deviceTokens);
|
||||
}
|
||||
|
|
|
@ -633,6 +633,39 @@ public class DeviceActivityApiServiceImpl implements DeviceActivityApiService {
|
|||
return resultMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deviceRelease(String token) {
|
||||
CdDeviceToken tokenQuery = new CdDeviceToken();
|
||||
tokenQuery.setToken(token);
|
||||
List<CdDeviceToken> resultList = cdDeviceTokenService.query(tokenQuery);
|
||||
release(resultList);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(List<CdDeviceToken> deviceTokens){
|
||||
if (deviceTokens != null && deviceTokens.size() > 0) {
|
||||
for (CdDeviceToken deviceToken : deviceTokens) {
|
||||
switch (deviceToken.getDeviceType()) {
|
||||
case DeviceConstants.DEVICE_MOB_TYPE:
|
||||
CdMobileDeviceExitDto mobileDto = new CdMobileDeviceExitDto();
|
||||
mobileDto.setDeviceId(deviceToken.getId());
|
||||
mobileDto.setToken(deviceToken.getToken());
|
||||
this.releaseMobDevice(mobileDto);
|
||||
break;
|
||||
case DeviceConstants.DEVICE_PC_TYPE:
|
||||
CdPcDeviceExitDto pcDto = new CdPcDeviceExitDto();
|
||||
pcDto.setDeviceId(deviceToken.getId());
|
||||
pcDto.setToken(deviceToken.getToken());
|
||||
this.releasePcDevice(pcDto);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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<CdDeviceUsageLog> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CdEngineInf
|
|||
@Autowired
|
||||
private EngineConfig engineConfig;
|
||||
|
||||
@Autowired
|
||||
private DeviceActivityApiService activityApiService;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
//= Excel 导入导出相关代码 start ==================//
|
||||
@Override
|
||||
public BasicService<CdEngineInfo> getService()
|
||||
|
@ -154,7 +162,14 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService<CdEngineInf
|
|||
{
|
||||
CdEngineInfo entity = new CdEngineInfo();
|
||||
BeanUtils.copyProperties(dto, entity);
|
||||
return this.cdEngineInfoService.updateByPrimaryKey(entity);
|
||||
int rows = this.cdEngineInfoService.updateByPrimaryKey(entity);
|
||||
if (rows > 0){
|
||||
if (dto.getEnabled() != null && !dto.getEnabled()){
|
||||
logger.debug("引擎禁用,删除引擎的线程缓存数据");
|
||||
redisTemplate.opsForZSet().remove(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, dto.getId());
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -290,6 +305,15 @@ public class CdEngineInfoApiServiceImpl extends AbstractExcelService<CdEngineInf
|
|||
this.cdEngineInfoService.insert(dto.getCdEngineInfo());
|
||||
resultDto.setEngineId(engineId);
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("释放引擎占用的设备");
|
||||
List<CdDeviceToken> deviceTokenList = this.cdDeviceTokenService.queryByEngineIds(Collections.singletonList(resultDto.getEngineId()));
|
||||
activityApiService.release(deviceTokenList);
|
||||
} catch (Exception e) {
|
||||
logger.error("释放引擎占用的设备异常", e);
|
||||
}
|
||||
|
||||
return resultDto;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,25 +124,26 @@ public class HeartMQReceiver {
|
|||
engineInfo.setRunningRelease(dto.getEngineVersion());
|
||||
this.cdEngineInfoService.updateByPrimaryKey(engineInfo);
|
||||
|
||||
// 更新缓存中引擎可执行线程数量
|
||||
Map<String, Object> 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<String, Object> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ public interface CdDeviceTokenDao extends CdDeviceTokenMapper
|
|||
{
|
||||
|
||||
List<CdDeviceToken> queryDeviceList(@Param("userId") String userId, @Param("ids") List<String> ids);
|
||||
// ---- The End by Generator ----//
|
||||
|
||||
List<CdDeviceToken> queryByEngineIds(@Param("ids") List<String> engineIds);
|
||||
// ---- The End by Generator ----//
|
||||
|
||||
}
|
||||
|
|
|
@ -56,6 +56,11 @@ private static final Logger logger = LoggerFactory.getLogger(CdDeviceTokenServic
|
|||
return cdDeviceTokenDao.queryDeviceList(userId,ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CdDeviceToken> queryByEngineIds(List<String> engineIds) {
|
||||
return cdDeviceTokenDao.queryByEngineIds(engineIds);
|
||||
}
|
||||
|
||||
|
||||
// ---- The End by Generator ----//
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ public interface CdDeviceTokenService extends BasicService<CdDeviceToken>
|
|||
{
|
||||
|
||||
List<CdDeviceToken> queryDeviceList(String userId,List<String> ids);
|
||||
|
||||
List<CdDeviceToken> queryByEngineIds(List<String> engineIds);
|
||||
// ---- The End by Generator ----//
|
||||
|
||||
}
|
||||
|
|
|
@ -110,27 +110,8 @@ public class DevicePubCtrl {
|
|||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public ResultWrapper<Boolean> deviceRelease(@PathVariable("token") String token) {
|
||||
ResultWrapper<Boolean> wrapper = new ResultWrapper<>();
|
||||
CdDeviceToken tokenQuery = new CdDeviceToken();
|
||||
tokenQuery.setToken(token);
|
||||
List<CdDeviceToken> resultList = deviceTokenService.query(tokenQuery);
|
||||
if (resultList != null && resultList.size() > 0) {
|
||||
CdDeviceToken device = resultList.get(0);
|
||||
switch (device.getDeviceType()) {
|
||||
case "1":
|
||||
CdMobileDeviceExitDto mobileDto = new CdMobileDeviceExitDto();
|
||||
mobileDto.setDeviceId(device.getId());
|
||||
mobileDto.setToken(token);
|
||||
apiService.releaseMobDevice(mobileDto);
|
||||
break;
|
||||
case "2":
|
||||
CdPcDeviceExitDto pcDto = new CdPcDeviceExitDto();
|
||||
pcDto.setDeviceId(device.getId());
|
||||
pcDto.setToken(token);
|
||||
apiService.releasePcDevice(pcDto);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wrapper.success(true);
|
||||
Boolean result = this.apiService.deviceRelease(token);
|
||||
return wrapper.success(result);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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<CdDeviceToken> deviceTokenList = this.cdDeviceTokenService.queryByEngineIds(ids);
|
||||
activityApiService.release(deviceTokenList);
|
||||
logger.debug("删除缓存中引擎线程的使用率");
|
||||
redisTemplate.opsForZSet().remove(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, ids.toArray());
|
||||
} catch (Exception e) {
|
||||
logger.error("释放引擎占用的设备失败", e);
|
||||
}
|
||||
}
|
||||
logger.info("---------------------------校验设备与引擎过期定时任务:结束-----------------------------------");
|
||||
|
||||
|
|
|
@ -8,10 +8,23 @@
|
|||
FROM cd_device_token
|
||||
WHERE
|
||||
user_id = #{userId, jdbcType=VARCHAR}
|
||||
AND id in
|
||||
<foreach collection="ids" item="idItem" open="(" separator="," close=")">
|
||||
#{idItem, jdbcType=VARCHAR}
|
||||
</foreach>
|
||||
<if test="ids != null and ids.size > 0">
|
||||
AND id in
|
||||
<foreach collection="ids" item="idItem" open="(" separator="," close=")">
|
||||
#{idItem, jdbcType=VARCHAR}
|
||||
</foreach>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="queryByEngineIds" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM cd_device_token
|
||||
WHERE
|
||||
user_id in
|
||||
<foreach collection="ids" item="idItem" open="(" separator="," close=")">
|
||||
#{idItem, jdbcType=VARCHAR}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 请在本文件内添加自定义SQL语句 -->
|
||||
|
|
|
@ -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<String,Session> sessionMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 处理每一个真机连接指令的任务
|
||||
|
@ -50,6 +56,11 @@ public class MobileConnectServiceImpl implements MobileConnectService {
|
|||
*/
|
||||
private ConcurrentHashMap<String, ProcessMsgThread> mobileConnectionThreadMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 缓存用户token的map
|
||||
*/
|
||||
private static Map<String, String> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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<TokenValueDTO> tokenValueDTORBucket = redisson.getBucket(AuthDependencyConstants.TOKEN_PREFIX + token);
|
||||
TokenValueDTO tokenValueDTO = tokenValueDTORBucket.get();
|
||||
if (tokenValueDTO != null ) {
|
||||
SpringUtils.getBean(MobileConnectService.class).addUserToken(sessionId, token);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
*.log.gz
|
||||
logs/
|
||||
*/logs/
|
||||
*/*/logs/
|
||||
|
||||
# Temp file
|
||||
*.temp
|
||||
temp
|
||||
*/temp
|
||||
|
||||
# IDEA profile dir
|
||||
.idea/
|
||||
*/.idea/
|
||||
*/*/.idea/
|
||||
|
||||
# IDEA project file
|
||||
*.iml
|
||||
*/*.iml
|
||||
*/*/*.iml
|
||||
*/*/*/*.iml
|
||||
|
||||
target/
|
||||
*/target/
|
||||
*/*/target/
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cctp-test-element-core</artifactId>
|
||||
<version>1.0.1-RELEASE</version>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
@ -6,5 +6,7 @@ public enum InputType {
|
|||
//单选
|
||||
select,
|
||||
//多选
|
||||
selects;
|
||||
selects,
|
||||
|
||||
element;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>NK.Desktop-1.75</finalName>
|
||||
<finalName>NK.Desktop-1.80</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
|
|
@ -23,7 +23,7 @@ public class DesktopLibrary extends AbstractLibrary {
|
|||
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(DesktopLibrary.class);
|
||||
private static final 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
### 版本 1.80
|
||||
更新于2024-07-06
|
||||
1.修改组件名称为断言包含文本,断言存在元素
|
||||
2.将断言的返回值返回值变为非必填项
|
||||
|
||||
### 版本 1.78
|
||||
更新于2024-06-19
|
||||
1.添加杭银OCR识别方式
|
||||
|
|
|
@ -17,7 +17,7 @@ public class MobileIosLibrary extends AbstractLibrary {
|
|||
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(MobileIosLibrary.class);
|
||||
private static final 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
|
||||
|
|
|
@ -83,4 +83,9 @@ public interface UpperParamKey {
|
|||
*/
|
||||
String USING_TYPE = "using_type";
|
||||
|
||||
/**
|
||||
* 租户id
|
||||
*/
|
||||
String TENANT_ID = "tenant_id";
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ import net.northking.cctp.element.core.annotation.Keyword;
|
|||
import net.northking.cctp.element.core.annotation.Keywords;
|
||||
import net.northking.cctp.element.core.annotation.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>屏幕截图(ios新)</p>
|
||||
*
|
||||
* @param deviceDriver 设备连接驱动
|
||||
* @param waitTimeout 默认超时时间
|
||||
* @param preExecuteWait 执行前等待
|
||||
* @param sufExecuteWait 执行后等待
|
||||
* @return
|
||||
*/
|
||||
@Keyword(alias = "屏幕截图(ios新)", category = "0", attributes = "5")
|
||||
@Return(name = "result", comment = "是否截图成功", type = DataType.BOOLEAN)
|
||||
public Boolean screenShot(
|
||||
IExecuteContext context,
|
||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float preExecuteWait,
|
||||
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float sufExecuteWait
|
||||
|
||||
) {
|
||||
CommonUtils.handlePreExecuteWait(preExecuteWait); //执行前等待
|
||||
ElementHandleParam param = ElementHandleParam.builder(deviceDriver)
|
||||
.useContext(context)
|
||||
.waitTimeout(waitTimeout);
|
||||
Boolean result = false;
|
||||
try {
|
||||
result = AutomationHandleUtil.screenShot(param);
|
||||
} catch (InterruptedException ie) {
|
||||
logger.warn("用户取消查询元素");
|
||||
throw new ExecuteException("取消操作");
|
||||
} catch (Exception e) {
|
||||
logger.error("出现了其他的问题。。。。", e);
|
||||
throw new ExecuteException(e.getMessage());
|
||||
}
|
||||
CommonUtils.handleSufExecuteWait(sufExecuteWait); //执行后等待
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>获取控件的值(ios新)(OCR方式)</p>
|
||||
*
|
||||
* @param deviceDriver 设备连接驱动
|
||||
* @param targets 定位控件参数
|
||||
* @param waitTimeout 超时时间
|
||||
* @param waitStatusStr 是否等待屏幕稳定
|
||||
* @param swipe 是否滑屏查找控件0-否,up-上滑,down-下滑,left-左滑,right-右滑
|
||||
* @param swipeCount 滑屏次数
|
||||
* @param preExecuteWait 执行前等待
|
||||
* @param sufExecuteWait 执行后等待
|
||||
* @param value 输出值
|
||||
* @return
|
||||
*/
|
||||
@Keyword(alias = "获取控件的值(ios新)(OCR方式)", category = "0", attributes = "5")
|
||||
@Return(name = "result", comment = "输入的结果", type = DataType.OBJECT)
|
||||
public String getElementValueByOcr(IExecuteContext context,
|
||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||
@Argument(name = "targets", scope = ParamScope.STEP, comment = "控件位置", type = DataType.LIST, defaultDisplay = false) List<IStepTarget> targets,
|
||||
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||
@Argument(name = "waitStatus", scope = ParamScope.ARGS, comment = "等待界面稳定", type = DataType.ENUM, enumValues = WhetherOrNotEnum.class, inputType = InputType.select, defaultValue = "1", defaultDisplay = false) String waitStatusStr,
|
||||
@Argument(name = "swipe", scope = ParamScope.ARGS, comment = "滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, inputType = InputType.select, defaultValue = "0", required = false, defaultDisplay = false) String swipe,
|
||||
@Argument(name = "swipeCount", scope = ParamScope.ARGS, comment = "滑动次数", type = DataType.INTEGER, required = false, defaultValue = "0", defaultDisplay = false) Integer swipeCount,
|
||||
@Argument(name = "value", scope = ParamScope.OUTPUT, comment = "组件返回值", type = DataType.STRING, defaultValue = "") String value,
|
||||
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float preExecuteWait,
|
||||
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float sufExecuteWait) {
|
||||
CommonUtils.handlePreExecuteWait(preExecuteWait); //执行前等待
|
||||
boolean waitStatus = "1".equals(waitStatusStr); //是否等待界面稳定
|
||||
ElementHandleParam param = ElementHandleParam.builder(deviceDriver)
|
||||
.useContext(context)
|
||||
.withTargets(targets)
|
||||
.waitTimeout(waitTimeout)
|
||||
.searchSwipeDirection("0") //不滑动
|
||||
.withSwipeCount(1)
|
||||
.waitStatus(waitStatus);
|
||||
String result = "";
|
||||
try {
|
||||
result = AutomationHandleUtil.getElementValueByOcr(param);
|
||||
} catch (InterruptedException ie) {
|
||||
logger.warn("用户取消查询元素");
|
||||
throw new ExecuteException("取消操作");
|
||||
} catch (Exception e) {
|
||||
logger.error("出现了其他的问题。。。。", e);
|
||||
throw new ExecuteException(e.getMessage());
|
||||
}
|
||||
CommonUtils.handleSufExecuteWait(sufExecuteWait); //执行后等待
|
||||
CommonUtils.setParamValue(context, value, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -991,10 +991,6 @@ public class AutomationHandleUtil {
|
|||
logger.warn("不支持的获取元素值方式:{}", target.getUsing());
|
||||
}
|
||||
}
|
||||
|
||||
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<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put(UpperParamKey.TENANT_ID, tenantId);
|
||||
logger.info("参数:{}", JSON.toJSONString(paramMap));
|
||||
logger.debug("tenantId:{}",tenantId);
|
||||
CmdAutomationRequest builder = CmdAutomationRequest.builder(AutomationRequestCmd.SCREEN_SHOT, token, paramMap);
|
||||
Object o = sendUpperMessageAndWaitReturn(param.getDeviceDriver(), builder, token, param.getWaitTimeout());
|
||||
logger.debug("屏幕截图的结果:{}",o);
|
||||
if (null != o) {
|
||||
screenShotUrl = (String) o;
|
||||
param.getContext().setVariable("mobileStepSnapshot",screenShotUrl);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getElementValueByOcr(ElementHandleParam param) throws Exception{
|
||||
String result = "";
|
||||
List<IStepTarget> targets = param.getTargets();
|
||||
for (IStepTarget target : targets) {
|
||||
result = getElementValueByOcr(param.getDeviceDriver(), target, param.getWaitTimeout(), param.getSwipe(), param.getSwipeCount());
|
||||
if (StringUtils.isNotBlank(result)) {
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String getElementValueByOcr(DeviceDriver deviceDriver, IStepTarget target, Integer waitTimeout, String swipe, Integer swipeCount) throws Exception{
|
||||
String result = "";
|
||||
String token = UUID.randomUUID().toString();
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
JSONObject object = JSON.parseObject(target.getValue(), JSONObject.class);
|
||||
String xpath = object.getString(UsingType.Key.SELECTOR_KEY);
|
||||
logger.info("拿到的xpath:{}", xpath);
|
||||
paramMap.put(UpperParamKey.NODE_TREE, xpath);
|
||||
paramMap.put(UpperParamKey.IS_SWIPE, swipe);
|
||||
paramMap.put(UpperParamKey.SWIPE_COUNT, swipeCount);
|
||||
CmdAutomationRequest builder = CmdAutomationRequest.builder(AutomationRequestCmd.GET_ELEMENT_VALUE_BY_PATH_OCR, token, paramMap);
|
||||
Object o = sendUpperMessageAndWaitReturn(deviceDriver, builder, token, waitTimeout);
|
||||
logger.debug("根据nodeTree查找,ocr识别的结果:{}", o);
|
||||
if (null != o) {
|
||||
result = (String) o;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
### 版本:1.0.15
|
||||
更新于2024-07-08
|
||||
|
||||
1.增加屏幕截图组件
|
||||
|
||||
### 版本:1.0.8
|
||||
更新于2024-06-11
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public class MobileLibrary extends AbstractLibrary {
|
|||
private static final Javadoc2Libdoc JAVA_DOC_2_LIB_DOC = new Javadoc2Libdoc(MobileLibrary.class);
|
||||
private static final 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
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package net.northking.cctp.element.mobile.entity;
|
||||
|
||||
import net.northking.cctp.element.core.IStepTarget;
|
||||
|
||||
public class StepTarget implements IStepTarget {
|
||||
|
||||
/**
|
||||
* 元素查找方式
|
||||
*/
|
||||
private String using;
|
||||
/**
|
||||
* 定位表达式
|
||||
*/
|
||||
private String value;
|
||||
/**
|
||||
* 备选Or必选
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
@Override
|
||||
public String getUsing() {
|
||||
return using;
|
||||
}
|
||||
|
||||
public void setUsing(String using) {
|
||||
this.using = using;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
public void setStrategy(String strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ public class AndroidTools {
|
|||
* @param deviceDriver 设备连接驱动
|
||||
* @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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>滑动查找控件</p>
|
||||
*
|
||||
* @param direction 滑动方向
|
||||
* @param deviceDriver 设备驱动
|
||||
* @param targets 控件信息
|
||||
* @param waitTimeout 默认超时时间
|
||||
* @param waitStatusStr 是否等待界面稳定
|
||||
* @param preExecuteWait 执行前等待
|
||||
* @param sufExecuteWait 执行后等待
|
||||
* @return
|
||||
*/
|
||||
@Keyword(alias = "滑动查找控件", category = "0", attributes = "4", commonlyUse = true)
|
||||
@Return(name = "result", comment = "滑动的结果", type = DataType.BOOLEAN)
|
||||
public boolean swipeAndFindTargetElement(IExecuteContext context,
|
||||
@Argument(name = "direction", scope = ParamScope.ARGS, comment = "控件滑动方向", type = DataType.ENUM, enumValues = SwipeDirection.class, defaultDisplay = false, inputType = InputType.select, defaultValue = "up") String direction,
|
||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||
@Argument(name = "targets", scope = ParamScope.STEP, comment = "目标元素", type = DataType.LIST, defaultDisplay = false,inputType = InputType.element) List<IStepTarget> targets,
|
||||
@Argument(name = "references", scope = ParamScope.ARGS, comment = "滑动元素", type = DataType.LIST, defaultDisplay = false,inputType = InputType.element) String referencesStr,
|
||||
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||
@Argument(name = "index", scope = ParamScope.ARGS, comment = "多个时定位第几个", type = DataType.INTEGER, defaultValue = "1",defaultDisplay = false) Integer index,
|
||||
@Argument(name = "waitStatus", scope = ParamScope.ARGS, comment = "等待界面稳定", type = DataType.ENUM, enumValues = WhetherOrNotEnum.class, inputType = InputType.select, defaultValue = "1", defaultDisplay = false) String waitStatusStr,
|
||||
@Argument(name = "value", scope = ParamScope.OUTPUT, comment = "组件返回值", type = DataType.STRING, defaultValue = "") String value,
|
||||
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float preExecuteWait,
|
||||
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "1.0", defaultDisplay = false) Float sufExecuteWait) {
|
||||
logger.debug("拿到的reference为:{}",referencesStr);
|
||||
SwipeAndFindTargetElementThread swipeAndFindTargetElementThread = new SwipeAndFindTargetElementThread(context,deviceDriver,targets,referencesStr,waitTimeout,waitStatusStr,direction,index,preExecuteWait,sufExecuteWait);
|
||||
Future<Boolean> future = executorService.submit(swipeAndFindTargetElementThread);
|
||||
Boolean aBoolean = null;
|
||||
deviceDriver.setFuture(future);
|
||||
try {
|
||||
aBoolean = future.get(waitTimeout, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn("该线程被中断了。。。。。。");
|
||||
throw new ExecuteException("执行中断");
|
||||
} catch (ExecutionException e) {
|
||||
logger.warn("在执行的时候报错了。。。。");
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof ExecuteException) {
|
||||
ExecuteException ee = (ExecuteException) cause;
|
||||
throw ee;
|
||||
} else if (cause instanceof ParamMistakeException) {
|
||||
ParamMistakeException pe = (ParamMistakeException) cause;
|
||||
throw pe;
|
||||
} else if (cause instanceof NoSuchSessionException) {
|
||||
NoSuchSessionException ne = (NoSuchSessionException) cause;
|
||||
throw ne;
|
||||
} else if (cause instanceof EnvironmentException) {
|
||||
EnvironmentException ee = (EnvironmentException) cause;
|
||||
throw ee;
|
||||
}else{
|
||||
logger.error("出现了其他的问题。。。。");
|
||||
throw new ExecuteException(cause.getMessage());
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
logger.warn("处理超时了。。。。。。");
|
||||
throw new ExecuteException("查找元素超时");
|
||||
} catch (CancellationException e) {
|
||||
logger.warn("该操作被取消了");
|
||||
throw new ExecuteException("用户取消,执行失败");
|
||||
}
|
||||
setParamValue(context,value,aBoolean.toString());
|
||||
return aBoolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>在控件中输入文本</p>
|
||||
*
|
||||
|
@ -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 {
|
|||
}
|
||||
|
||||
/**
|
||||
* <p>点击控件</p>
|
||||
* <p>密码输入(ocr)</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>屏幕截图</p>
|
||||
*
|
||||
* @param deviceDriver 设备连接驱动
|
||||
* @param waitTimeout 默认超时时间
|
||||
* @param preExecuteWait 执行前等待
|
||||
* @param sufExecuteWait 执行后等待
|
||||
* @return
|
||||
*/
|
||||
@Keyword(alias = "屏幕截图", category = "0", attributes = "4", commonlyUse = true)
|
||||
@Return(name = "result", comment = "是否截图成功", type = DataType.BOOLEAN)
|
||||
public Boolean screenShot(
|
||||
IExecuteContext context,
|
||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float preExecuteWait,
|
||||
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float sufExecuteWait
|
||||
|
||||
) {
|
||||
ScreenShotThread screenShotThread = new ScreenShotThread(context,deviceDriver,waitTimeout,preExecuteWait,sufExecuteWait);
|
||||
Future<Boolean> future = executorService.submit(screenShotThread);
|
||||
Boolean aBoolean = null;
|
||||
deviceDriver.setFuture(future);
|
||||
try {
|
||||
aBoolean = future.get(waitTimeout, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn("该线程被中断了。。。。。。");
|
||||
throw new ExecuteException("执行中断");
|
||||
} catch (ExecutionException e) {
|
||||
logger.warn("在执行的时候报错了。。。。");
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof ExecuteException) {
|
||||
ExecuteException ee = (ExecuteException) cause;
|
||||
throw ee;
|
||||
} else if (cause instanceof ParamMistakeException) {
|
||||
ParamMistakeException pe = (ParamMistakeException) cause;
|
||||
throw pe;
|
||||
} else if (cause instanceof NoSuchSessionException) {
|
||||
NoSuchSessionException ne = (NoSuchSessionException) cause;
|
||||
throw ne;
|
||||
} else if (cause instanceof EnvironmentException) {
|
||||
EnvironmentException ee = (EnvironmentException) cause;
|
||||
throw ee;
|
||||
}else if (cause instanceof StaleElementReferenceException) {
|
||||
throw new ExecuteException("当前操作的元素已经失效");
|
||||
} else {
|
||||
logger.error("出现了其他的问题。。。。", e);
|
||||
throw new ExecuteException(cause.getMessage());
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
logger.warn("处理超时了。。。。。。");
|
||||
throw new ExecuteException("查找文本超时");
|
||||
} catch (CancellationException e) {
|
||||
logger.warn("该操作被取消了");
|
||||
throw new ExecuteException("用户取消,执行失败");
|
||||
}
|
||||
return aBoolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>获取控件的值(OCR方式)</p>
|
||||
*
|
||||
* @param deviceDriver 设备驱动链接
|
||||
* @param targets 定位控件的信息
|
||||
* @param waitTimeout 默认超时时间
|
||||
* @param preExecuteWait 执行前等待
|
||||
* @param sufExecuteWait 执行后等待
|
||||
* @return
|
||||
*/
|
||||
@Keyword(alias = "获取控件的值(OCR方式)", category = "0", attributes = "4")
|
||||
@Return(name = "result", comment = "控件的值", type = DataType.OBJECT)
|
||||
public String getElementValueByOcr(IExecuteContext context,
|
||||
@Argument(name = "__deviceDriver", scope = ParamScope.CONTEXT, comment = "设备连接", type = DataType.OBJECT) DeviceDriver deviceDriver,
|
||||
@Argument(name = "targets", scope = ParamScope.STEP, comment = "控件位置", type = DataType.LIST, defaultDisplay = false) List<IStepTarget> targets,
|
||||
@Argument(name = "value", scope = ParamScope.OUTPUT, comment = "组件返回值", type = DataType.STRING, defaultValue = "") String value,
|
||||
@Argument(name = "timeout", scope = ParamScope.ARGS, comment = "默认超时时间", type = DataType.INTEGER, required = false, defaultValue = "30", defaultDisplay = false) Integer waitTimeout,
|
||||
@Argument(name = "preExecuteWait", scope = ParamScope.ARGS, comment = "执行前等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float preExecuteWait,
|
||||
@Argument(name = "sufExecuteWait", scope = ParamScope.ARGS, comment = "执行后等待(单位:s)", type = DataType.INTEGER, required = false, defaultValue = "0.0", defaultDisplay = false) Float sufExecuteWait
|
||||
) {
|
||||
GetElementValueByOcrThread getElementValueByOcrThread = new GetElementValueByOcrThread(context,deviceDriver,targets,waitTimeout,preExecuteWait,sufExecuteWait);
|
||||
Future<String> future = executorService.submit(getElementValueByOcrThread);
|
||||
String result = null;
|
||||
deviceDriver.setFuture(future);
|
||||
try {
|
||||
result = future.get(waitTimeout, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.warn("该线程被中断了。。。。。。");
|
||||
throw new ExecuteException("执行中断");
|
||||
} catch (ExecutionException e) {
|
||||
logger.warn("在执行的时候报错了。。。。");
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof ExecuteException) {
|
||||
ExecuteException ee = (ExecuteException) cause;
|
||||
throw ee;
|
||||
} else if (cause instanceof ParamMistakeException) {
|
||||
ParamMistakeException pe = (ParamMistakeException) cause;
|
||||
throw pe;
|
||||
} else if (cause instanceof NoSuchSessionException) {
|
||||
NoSuchSessionException ne = (NoSuchSessionException) cause;
|
||||
throw ne;
|
||||
} else if (cause instanceof EnvironmentException) {
|
||||
EnvironmentException ee = (EnvironmentException) cause;
|
||||
throw ee;
|
||||
} else if (cause instanceof StaleElementReferenceException) {
|
||||
throw new ExecuteException("当前操作的元素已经失效");
|
||||
} else {
|
||||
logger.error("出现了其他的问题。。。。", e);
|
||||
throw new ExecuteException(cause.getMessage());
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
logger.warn("处理超时了。。。。。。");
|
||||
throw new ExecuteException("查找元素超时");
|
||||
} catch (CancellationException e) {
|
||||
logger.warn("该操作被取消了");
|
||||
throw new ExecuteException("用户取消,执行失败");
|
||||
}
|
||||
setParamValue(context, value, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setParamValue(IExecuteContext context, String variable, String value) {
|
||||
try {
|
||||
if (variable.contains("#{") && variable.contains("}")) {
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
package net.northking.cctp.element.mobile.thread;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.appium.java_client.AppiumDriver;
|
||||
import io.appium.java_client.ios.IOSDriver;
|
||||
import net.northking.cctp.element.core.DeviceDriver;
|
||||
import net.northking.cctp.element.core.IExecuteContext;
|
||||
import net.northking.cctp.element.core.IStepTarget;
|
||||
import net.northking.cctp.element.core.exception.ExecuteException;
|
||||
import net.northking.cctp.element.core.exception.ParamMistakeException;
|
||||
import net.northking.cctp.element.mobile.constants.ConfigPath;
|
||||
import net.northking.cctp.element.mobile.constants.UsingType;
|
||||
import net.northking.cctp.element.mobile.entity.PointMessage;
|
||||
import net.northking.cctp.element.mobile.utils.CommonUtils;
|
||||
import net.northking.cctp.element.mobile.utils.HttpUtils;
|
||||
import net.northking.cctp.element.mobile.utils.phoneUtils.ElementUtil;
|
||||
import net.northking.cctp.element.mobile.utils.phoneUtils.ScreenUtil;
|
||||
import org.openqa.selenium.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class GetElementValueByOcrThread implements Callable<String> {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(GetElementValueByOcrThread.class);
|
||||
|
||||
private IExecuteContext context;
|
||||
private DeviceDriver deviceDriver;
|
||||
private List<IStepTarget> targets;
|
||||
private Integer waitTimeout;
|
||||
private Float preExecuteWait;
|
||||
private Float sufExecuteWait;
|
||||
|
||||
|
||||
public GetElementValueByOcrThread(IExecuteContext context, DeviceDriver deviceDriver, List<IStepTarget> targets, Integer waitTimeout, Float preExecuteWait, Float sufExecuteWait) {
|
||||
this.context = context;
|
||||
this.deviceDriver = deviceDriver;
|
||||
this.targets = targets;
|
||||
this.waitTimeout = waitTimeout;
|
||||
this.preExecuteWait = preExecuteWait;
|
||||
this.sufExecuteWait = sufExecuteWait;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
CommonUtils.handlePreExecuteWait(preExecuteWait);
|
||||
if (null == waitTimeout || waitTimeout <= 0) {
|
||||
waitTimeout = 30;
|
||||
}
|
||||
String result = "";
|
||||
AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) deviceDriver;
|
||||
WebElement webElement = null;
|
||||
for (IStepTarget target : targets) {
|
||||
webElement = xpathFind(driver, target);
|
||||
if (webElement != null) {
|
||||
result = getElementValueByOcr(context, driver, webElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
CommonUtils.handleSufExecuteWait(sufExecuteWait);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* xpath找元素
|
||||
*
|
||||
* @param driver
|
||||
* @param target
|
||||
*/
|
||||
private WebElement xpathFind(AppiumDriver<WebElement> driver, IStepTarget target) {
|
||||
JSONObject object = JSON.parseObject(target.getValue(), JSONObject.class);
|
||||
String xpath = object.getString(UsingType.Key.SELECTOR_KEY);
|
||||
logger.info("拿到的xpath:{}", xpath);
|
||||
try {
|
||||
driver.manage().timeouts().implicitlyWait(waitTimeout, TimeUnit.SECONDS);
|
||||
} catch (NoSuchSessionException e) {
|
||||
throw e;
|
||||
}
|
||||
WebElement webElement = null;
|
||||
List<WebElement> elements = null;
|
||||
try {
|
||||
elements = driver.findElements(By.xpath(xpath));
|
||||
} catch (WebDriverException e) {
|
||||
logger.error("driver查找控件异常:", e);
|
||||
throw new ExecuteException("设备自动化驱动连接异常,查找控件失败");
|
||||
}
|
||||
logger.info("找到元素{}个", elements.size());
|
||||
if (!CollectionUtils.isEmpty(elements)) { //xpath找到了
|
||||
webElement = elements.get(0);
|
||||
}
|
||||
return webElement;
|
||||
}
|
||||
|
||||
public String getElementValueByOcr(IExecuteContext context, AppiumDriver<WebElement> driver, WebElement webElement) {
|
||||
String imgBase64 = null;
|
||||
try {
|
||||
imgBase64 = ocrFindCodeArea(webElement, driver, context);
|
||||
} catch (NoSuchSessionException | ExecuteException | ParamMistakeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
logger.error("查找区域截图失败", e);
|
||||
throw new ExecuteException("查找区域截图失败");
|
||||
}
|
||||
if (null == imgBase64) {
|
||||
throw new ExecuteException("未截取到区域");
|
||||
}
|
||||
String ocrAddress = context.getProperty(ConfigPath.OCR_GET_ALL_TEXT_NUM_KEY);
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
Map<String, Object> ocrParamMap = new HashMap<>();
|
||||
ocrParamMap.put("img_base64", imgBase64);
|
||||
ocrParamMap.put("targets", "");
|
||||
HttpEntity<Map> ocrEntity = new HttpEntity<>(ocrParamMap, headers);
|
||||
List ocrResultList = null;
|
||||
try {
|
||||
logger.info("传参:{}", JSON.toJSONString(ocrParamMap));
|
||||
ocrResultList = HttpUtils.doPost(ocrAddress, ocrEntity, List.class);
|
||||
logger.info("得到的ocr结果:{}", ocrResultList);
|
||||
} catch (Exception e) {
|
||||
logger.error("ocr失败", e);
|
||||
}
|
||||
List<String> dataList = new ArrayList<>();
|
||||
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||
if (!CollectionUtils.isEmpty(ocrResultList)) {
|
||||
for (Object o : ocrResultList) {
|
||||
dataList.add(((Map) o).get("text") + "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return JSON.toJSONString(dataList);
|
||||
} catch (Exception e) {
|
||||
logger.error("请求ocr接口失败,接口地址:{}", ocrAddress, e);
|
||||
throw new ExecuteException("请求ocr识别接口失败,请查看ocr识别服务是否开启!");
|
||||
}
|
||||
}
|
||||
|
||||
public String ocrFindCodeArea(WebElement webElement, AppiumDriver<WebElement> driver, IExecuteContext context) {
|
||||
String result = null;
|
||||
Rectangle rect = webElement.getRect();
|
||||
int x = rect.getX();
|
||||
int y = rect.getY();
|
||||
int width = rect.getWidth();
|
||||
int height = rect.getHeight();
|
||||
Map<String, Object> deviceInfo = context.getDeviceInfo();
|
||||
String resolution = (String) deviceInfo.get("resolution");
|
||||
String[] split = resolution.split("\\*");
|
||||
int screenLength = Integer.parseInt(split[0]);
|
||||
int screenWidth = Integer.parseInt(split[1]);
|
||||
logger.info("通过xpth找到元素的x:{},y:{}, width:{}, height:{},screenLength:{},screenWidth:{}", x, y, width, height, screenLength,screenWidth);
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
if (driver instanceof IOSDriver) {
|
||||
paramMap.put("platform", "1");
|
||||
} else {
|
||||
paramMap.put("platform", "0");
|
||||
}
|
||||
paramMap.put("deviceId", driver.getCapabilities().getCapability("deviceName"));
|
||||
paramMap.put("length", width);
|
||||
paramMap.put("width", height);
|
||||
paramMap.put("resolution", resolution);
|
||||
paramMap.put("x", x);
|
||||
paramMap.put("y", y);
|
||||
paramMap.put("screenLength", screenLength);
|
||||
paramMap.put("screenWidth", screenWidth);
|
||||
logger.info("参数:{}", JSON.toJSONString(paramMap));
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(paramMap);
|
||||
String host = driver.getRemoteAddress().getHost(); //上位机地址
|
||||
String port = (String) deviceInfo.get("port"); //上位机端口
|
||||
String mobileShotAddress = context.getProperty(ConfigPath.MOBILE_SHOT_ADDRESS_KEY);
|
||||
String shotScreenAddress = String.format(ConfigPath.HTTP_FORMAT, host, port, mobileShotAddress);
|
||||
try {
|
||||
result = HttpUtils.doPost(shotScreenAddress, entity, String.class);
|
||||
} catch (Exception e) {
|
||||
logger.error("截图失败", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package net.northking.cctp.element.mobile.thread;
|
||||
|
||||
import io.appium.java_client.AppiumDriver;
|
||||
import net.northking.cctp.element.core.DeviceDriver;
|
||||
import net.northking.cctp.element.core.IExecuteContext;
|
||||
import net.northking.cctp.element.core.IStepTarget;
|
||||
import net.northking.cctp.element.core.exception.ExecuteException;
|
||||
import net.northking.cctp.element.mobile.entity.PointMessage;
|
||||
import net.northking.cctp.element.mobile.utils.CommonUtils;
|
||||
import net.northking.cctp.element.mobile.utils.phoneUtils.ElementUtil;
|
||||
import net.northking.cctp.element.mobile.utils.phoneUtils.HandleUtils;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ScreenShotThread implements Callable<Boolean> {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(ScreenShotThread.class);
|
||||
|
||||
private IExecuteContext context;
|
||||
private DeviceDriver deviceDriver;
|
||||
private Integer waitTimeout;
|
||||
private Float preExecuteWait;
|
||||
private Float sufExecuteWait;
|
||||
|
||||
public ScreenShotThread(IExecuteContext context, DeviceDriver deviceDriver, Integer waitTimeout, Float preExecuteWait, Float sufExecuteWait) {
|
||||
this.context = context;
|
||||
this.deviceDriver = deviceDriver;
|
||||
this.waitTimeout = waitTimeout;
|
||||
this.preExecuteWait = preExecuteWait;
|
||||
this.sufExecuteWait = sufExecuteWait;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
CommonUtils.handlePreExecuteWait(preExecuteWait);
|
||||
AppiumDriver<WebElement> driver = (AppiumDriver<WebElement>) deviceDriver;
|
||||
CommonUtils.screenShot(context,driver);
|
||||
CommonUtils.handleSufExecuteWait(sufExecuteWait);
|
||||
return true;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue