Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
kt-keystone
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Matrix
kt-keystone
Commits
aa46a4ee
提交
aa46a4ee
authored
3月 16, 2022
作者:
黄夏豪
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix(base): 修复了一个单词拼错了的问题
上级
00059943
隐藏空白字符变更
内嵌
并排
正在显示
15 个修改的文件
包含
168 行增加
和
58 行删除
+168
-58
CaseActuator.java
.../main/java/org/matrix/actuators/usecase/CaseActuator.java
+8
-9
ExecutionHistory.java
...ain/java/org/matrix/database/entity/ExecutionHistory.java
+1
-1
ExecutionRecord.java
...main/java/org/matrix/database/entity/ExecutionRecord.java
+1
-1
ExecutionHistoryMapper.java
...va/org/matrix/database/mapper/ExecutionHistoryMapper.java
+4
-2
IExecutionRecordService.java
.../org/matrix/database/service/IExecutionRecordService.java
+3
-3
ExecutionRecordImpl.java
...org/matrix/database/service/impl/ExecutionRecordImpl.java
+4
-4
ExecutionSocketHandler.java
...c/main/java/org/matrix/socket/ExecutionSocketHandler.java
+5
-4
ExecutionStatusMonitorSocketPool.java
...a/org/matrix/socket/ExecutionStatusMonitorSocketPool.java
+1
-2
MonitorSocketPool.java
...c/main/java/org/matrix/socket/pool/MonitorSocketPool.java
+20
-19
LogQueueRuntime.java
...rc/main/java/org/matrix/socket/queue/LogQueueRuntime.java
+9
-7
ExecuteMonitorVo.java
.../src/main/java/org/matrix/socket/vo/ExecuteMonitorVo.java
+1
-1
ExecutionStatusMonitorVo.java
...n/java/org/matrix/socket/vo/ExecutionStatusMonitorVo.java
+14
-0
TestExecuteLog.java
...se/src/main/java/org/matrix/socket/vo/TestExecuteLog.java
+1
-1
ExecutionHistoryController.java
...atrix/autotest/controller/ExecutionHistoryController.java
+58
-1
ExecutionRecordController.java
...matrix/autotest/controller/ExecutionRecordController.java
+38
-3
没有找到文件。
kt-base/src/main/java/org/matrix/actuators/usecase/CaseActuator.java
浏览文件 @
aa46a4ee
...
@@ -222,7 +222,7 @@ public class CaseActuator implements Actuator {
...
@@ -222,7 +222,7 @@ public class CaseActuator implements Actuator {
* 外界如果执行测试用例的话请走这个接口,执行TestCase,并控制运行态日志池,进行日志的生成。
* 外界如果执行测试用例的话请走这个接口,执行TestCase,并控制运行态日志池,进行日志的生成。
*/
*/
public
List
<
TestCaseExecuteResult
>
runTestCase
(
WebSocketSession
session
,
CaseExecuteVo
caseExecuteVo
)
{
public
List
<
TestCaseExecuteResult
>
runTestCase
(
WebSocketSession
session
,
CaseExecuteVo
caseExecuteVo
)
{
String
uni
on
Key
=
UUID
.
randomUUID
().
toString
();
String
uni
que
Key
=
UUID
.
randomUUID
().
toString
();
Long
currentThreadId
=
ThreadUtil
.
currentThreadId
();
Long
currentThreadId
=
ThreadUtil
.
currentThreadId
();
try
{
try
{
//将websocketSession 加入到socket池子中
//将websocketSession 加入到socket池子中
...
@@ -235,7 +235,7 @@ public class CaseActuator implements Actuator {
...
@@ -235,7 +235,7 @@ public class CaseActuator implements Actuator {
}
}
}
}
//建立执行历史(ExecutionHistory)
//建立执行历史(ExecutionHistory)
insertExecutionHistory
(
uni
on
Key
,
caseExecuteVo
);
insertExecutionHistory
(
uni
que
Key
,
caseExecuteVo
);
for
(
TestCaseListDataBto
testCaseListDataBto
:
testCaseListDataBtoList
)
{
for
(
TestCaseListDataBto
testCaseListDataBto
:
testCaseListDataBtoList
)
{
LogQueueRuntime
LogQueueRuntime
.
initTestCaseLog
(
.
initTestCaseLog
(
...
@@ -243,7 +243,7 @@ public class CaseActuator implements Actuator {
...
@@ -243,7 +243,7 @@ public class CaseActuator implements Actuator {
caseExecuteVo
.
getUserId
()
caseExecuteVo
.
getUserId
()
,
testCaseListDataBto
.
getTestCase
().
getId
()
,
testCaseListDataBto
.
getTestCase
().
getId
()
,
caseExecuteVo
.
getType
()
,
caseExecuteVo
.
getType
()
,
uni
on
Key
,
uni
que
Key
);
);
//执行测试用例
//执行测试用例
return
executeTestCases
(
testCaseListDataBto
,
caseExecuteVo
.
getEnvId
(),
caseExecuteVo
.
getProjectId
());
return
executeTestCases
(
testCaseListDataBto
,
caseExecuteVo
.
getEnvId
(),
caseExecuteVo
.
getProjectId
());
...
@@ -274,9 +274,8 @@ public class CaseActuator implements Actuator {
...
@@ -274,9 +274,8 @@ public class CaseActuator implements Actuator {
return
null
;
return
null
;
}
}
public
void
insertExecutionHistory
(
String
uni
on
Key
,
CaseExecuteVo
caseExecuteVo
)
{
public
void
insertExecutionHistory
(
String
uni
que
Key
,
CaseExecuteVo
caseExecuteVo
)
{
List
<
TestCaseListDataBto
>
testCaseListDataBtoList
=
caseExecuteVo
.
getTestCaseListDataBtoList
();
List
<
TestCaseListDataBto
>
testCaseListDataBtoList
=
caseExecuteVo
.
getTestCaseListDataBtoList
();
Long
firstExecutionKey
=
getExecutionKey
(
caseExecuteVo
);
Long
firstExecutionKey
=
getExecutionKey
(
caseExecuteVo
);
List
<
ExecutionHistory
>
executionHistories
=
new
ArrayList
<>();
List
<
ExecutionHistory
>
executionHistories
=
new
ArrayList
<>();
for
(
TestCaseListDataBto
testCaseListDataBto
:
testCaseListDataBtoList
)
{
for
(
TestCaseListDataBto
testCaseListDataBto
:
testCaseListDataBtoList
)
{
...
@@ -288,7 +287,7 @@ public class CaseActuator implements Actuator {
...
@@ -288,7 +287,7 @@ public class CaseActuator implements Actuator {
executionHistory
.
setUserId
(
caseExecuteVo
.
getUserId
());
executionHistory
.
setUserId
(
caseExecuteVo
.
getUserId
());
executionHistory
.
setCaseId
(
testCase
.
getId
());
executionHistory
.
setCaseId
(
testCase
.
getId
());
executionHistory
.
setDataId
(
testDataList
.
get
(
i
).
getId
());
executionHistory
.
setDataId
(
testDataList
.
get
(
i
).
getId
());
executionHistory
.
setUni
onKey
(
union
Key
);
executionHistory
.
setUni
queKey
(
unique
Key
);
executionHistory
.
setExecutionKey
(
firstExecutionKey
);
executionHistory
.
setExecutionKey
(
firstExecutionKey
);
if
(
i
==
0
){
if
(
i
==
0
){
executionHistory
.
setStartTime
(
LocalDateTime
.
now
());
executionHistory
.
setStartTime
(
LocalDateTime
.
now
());
...
@@ -298,7 +297,7 @@ public class CaseActuator implements Actuator {
...
@@ -298,7 +297,7 @@ public class CaseActuator implements Actuator {
}
}
Long
lastExecutionKey
=
getExecutionKey
(
caseExecuteVo
);
Long
lastExecutionKey
=
getExecutionKey
(
caseExecuteVo
);
if
(!
lastExecutionKey
.
equals
(
firstExecutionKey
)){
if
(!
lastExecutionKey
.
equals
(
firstExecutionKey
)){
insertExecutionHistory
(
uni
on
Key
,
caseExecuteVo
);
insertExecutionHistory
(
uni
que
Key
,
caseExecuteVo
);
}
}
executionHistoryService
.
saveBatch
(
executionHistories
);
executionHistoryService
.
saveBatch
(
executionHistories
);
}
}
...
@@ -343,7 +342,7 @@ public class CaseActuator implements Actuator {
...
@@ -343,7 +342,7 @@ public class CaseActuator implements Actuator {
.
eq
(
ExecutionHistory:
:
getJobId
,
currentTestExecute
.
getTestJobId
())
.
eq
(
ExecutionHistory:
:
getJobId
,
currentTestExecute
.
getTestJobId
())
.
eq
(
ExecutionHistory:
:
getCaseId
,
currentTestExecute
.
getTestCaseId
())
.
eq
(
ExecutionHistory:
:
getCaseId
,
currentTestExecute
.
getTestCaseId
())
.
eq
(
ExecutionHistory:
:
getDataId
,
currentTestExecute
.
getTestDataId
())
.
eq
(
ExecutionHistory:
:
getDataId
,
currentTestExecute
.
getTestDataId
())
.
eq
(
ExecutionHistory:
:
getUni
onKey
,
currentTestExecute
.
getUnion
Key
())
.
eq
(
ExecutionHistory:
:
getUni
queKey
,
currentTestExecute
.
getUnique
Key
())
.
eq
(
ExecutionHistory:
:
getUserId
,
currentTestExecute
.
getUserId
())
.
eq
(
ExecutionHistory:
:
getUserId
,
currentTestExecute
.
getUserId
())
);
);
...
@@ -363,7 +362,7 @@ public class CaseActuator implements Actuator {
...
@@ -363,7 +362,7 @@ public class CaseActuator implements Actuator {
.
lambdaUpdate
(
ExecutionHistory
.
class
)
.
lambdaUpdate
(
ExecutionHistory
.
class
)
.
eq
(
ExecutionHistory:
:
getJobId
,
currentTestExecute
.
getTestJobId
())
.
eq
(
ExecutionHistory:
:
getJobId
,
currentTestExecute
.
getTestJobId
())
.
eq
(
ExecutionHistory:
:
getCaseId
,
currentTestExecute
.
getTestCaseId
())
.
eq
(
ExecutionHistory:
:
getCaseId
,
currentTestExecute
.
getTestCaseId
())
.
eq
(
ExecutionHistory:
:
getUni
onKey
,
currentTestExecute
.
getUnion
Key
())
.
eq
(
ExecutionHistory:
:
getUni
queKey
,
currentTestExecute
.
getUnique
Key
())
.
eq
(
ExecutionHistory:
:
getUserId
,
currentTestExecute
.
getUserId
())
.
eq
(
ExecutionHistory:
:
getUserId
,
currentTestExecute
.
getUserId
())
.
ne
(
ExecutionHistory:
:
getStatus
,
ExecutionHistoryStatus
.
FINISH
)
.
ne
(
ExecutionHistory:
:
getStatus
,
ExecutionHistoryStatus
.
FINISH
)
);
);
...
...
kt-base/src/main/java/org/matrix/database/entity/ExecutionHistory.java
浏览文件 @
aa46a4ee
...
@@ -28,7 +28,7 @@ import java.time.LocalDateTime;
...
@@ -28,7 +28,7 @@ import java.time.LocalDateTime;
@TableName
(
value
=
"kt_execution_history"
)
@TableName
(
value
=
"kt_execution_history"
)
public
class
ExecutionHistory
extends
BaseEntity
{
public
class
ExecutionHistory
extends
BaseEntity
{
private
String
uni
on
Key
;
private
String
uni
que
Key
;
private
Long
caseId
;
private
Long
caseId
;
...
...
kt-base/src/main/java/org/matrix/database/entity/ExecutionRecord.java
浏览文件 @
aa46a4ee
...
@@ -33,7 +33,7 @@ public class ExecutionRecord extends BaseEntity {
...
@@ -33,7 +33,7 @@ public class ExecutionRecord extends BaseEntity {
private
Long
testCaseId
;
private
Long
testCaseId
;
@ApiModelProperty
(
"key用来记录执行批次"
)
@ApiModelProperty
(
"key用来记录执行批次"
)
private
String
uni
on
Key
;
private
String
uni
que
Key
;
@ApiModelProperty
(
"类型"
)
@ApiModelProperty
(
"类型"
)
private
TestExecuteType
type
;
private
TestExecuteType
type
;
...
...
kt-base/src/main/java/org/matrix/database/mapper/ExecutionHistoryMapper.java
浏览文件 @
aa46a4ee
...
@@ -32,10 +32,11 @@ public interface ExecutionHistoryMapper extends BaseMapper<ExecutionHistory> {
...
@@ -32,10 +32,11 @@ public interface ExecutionHistoryMapper extends BaseMapper<ExecutionHistory> {
@Select
(
"<script>"
+
@Select
(
"<script>"
+
"SELECT\n"
+
"SELECT\n"
+
"\tkeh.id,\n"
+
"\tkeh.id,\n"
+
"\tkeh.union_key uni
on
Key,\n"
+
"\tkeh.union_key uni
que
Key,\n"
+
"\tkeh.case_id caseId,\n"
+
"\tkeh.case_id caseId,\n"
+
"\tkeh.data_id dataId,\n"
+
"\tkeh.data_id dataId,\n"
+
"\tkeh.job_id jobId,\n"
+
"\tkeh.job_id jobId,\n"
+
"\tkeh.execution_key executionKey,\n"
+
"\tIF(MIN( keh.`status` )= 0 or MIN( keh.`status` ) = 1,1,MAX(keh.`status`)) `status`,\n"
+
"\tIF(MIN( keh.`status` )= 0 or MIN( keh.`status` ) = 1,1,MAX(keh.`status`)) `status`,\n"
+
"\tktc.`name` caseName,\n"
+
"\tktc.`name` caseName,\n"
+
"\tktd.`name` dataName,"
+
"\tktd.`name` dataName,"
+
...
@@ -70,10 +71,11 @@ public interface ExecutionHistoryMapper extends BaseMapper<ExecutionHistory> {
...
@@ -70,10 +71,11 @@ public interface ExecutionHistoryMapper extends BaseMapper<ExecutionHistory> {
@Select
(
"<script>"
+
@Select
(
"<script>"
+
"SELECT\n"
+
"SELECT\n"
+
"\tkeh.id,\n"
+
"\tkeh.id,\n"
+
"\tkeh.union_key uni
on
Key,\n"
+
"\tkeh.union_key uni
que
Key,\n"
+
"\tkeh.case_id caseId,\n"
+
"\tkeh.case_id caseId,\n"
+
"\tkeh.data_id dataId,\n"
+
"\tkeh.data_id dataId,\n"
+
"\tkeh.job_id jobId,\n"
+
"\tkeh.job_id jobId,\n"
+
"\tkeh.execution_key executionKey,\n"
+
"IF\n"
+
"IF\n"
+
"\t(\n"
+
"\t(\n"
+
"\tMIN( keh.`status` )= 0 or MIN( keh.`status` ) = 1,\n"
+
"\tMIN( keh.`status` )= 0 or MIN( keh.`status` ) = 1,\n"
+
...
...
kt-base/src/main/java/org/matrix/database/service/IExecutionRecordService.java
浏览文件 @
aa46a4ee
...
@@ -33,9 +33,9 @@ public interface IExecutionRecordService extends IService<ExecutionRecord> {
...
@@ -33,9 +33,9 @@ public interface IExecutionRecordService extends IService<ExecutionRecord> {
void
addExecutionRecord
(
TestExecuteLog
testExecuteLog
,
String
log
);
void
addExecutionRecord
(
TestExecuteLog
testExecuteLog
,
String
log
);
/**
/**
* 根据uni
on
Key将对应的执行记录修改为目标状态
* 根据uni
que
Key将对应的执行记录修改为目标状态
* @param uni
onKey 类似于批次Id的作用,同一批执行拥有相同union
Key
* @param uni
queKey 类似于批次Id的作用,同一批执行拥有相同unique
Key
* @param status 目标状态
* @param status 目标状态
*/
*/
void
setStatusByUni
onKey
(
String
union
Key
,
ExecutionRecType
status
);
void
setStatusByUni
queKey
(
String
unique
Key
,
ExecutionRecType
status
);
}
}
kt-base/src/main/java/org/matrix/database/service/impl/ExecutionRecordImpl.java
浏览文件 @
aa46a4ee
...
@@ -34,15 +34,15 @@ public class ExecutionRecordImpl extends ServiceImpl<ExecutionRecordMapper, Exec
...
@@ -34,15 +34,15 @@ public class ExecutionRecordImpl extends ServiceImpl<ExecutionRecordMapper, Exec
@Transactional
(
rollbackFor
=
Exception
.
class
)
@Transactional
(
rollbackFor
=
Exception
.
class
)
@Override
@Override
public
void
setStatusByUni
onKey
(
String
union
Key
,
ExecutionRecType
status
)
{
public
void
setStatusByUni
queKey
(
String
unique
Key
,
ExecutionRecType
status
)
{
boolean
emptyIfStr
=
StrUtil
.
isEmptyIfStr
(
uni
on
Key
);
boolean
emptyIfStr
=
StrUtil
.
isEmptyIfStr
(
uni
que
Key
);
if
(
emptyIfStr
){
if
(
emptyIfStr
){
throw
new
GlobalException
(
"不能根据空的uni
onKey
修改执行状态"
);
throw
new
GlobalException
(
"不能根据空的uni
que
修改执行状态"
);
}
else
{
}
else
{
ExecutionRecord
executionRecord
=
new
ExecutionRecord
();
ExecutionRecord
executionRecord
=
new
ExecutionRecord
();
update
(
executionRecord
,
Wrappers
update
(
executionRecord
,
Wrappers
.
lambdaUpdate
(
ExecutionRecord
.
class
)
.
lambdaUpdate
(
ExecutionRecord
.
class
)
.
eq
(
ExecutionRecord:
:
getUni
onKey
,
union
Key
)
.
eq
(
ExecutionRecord:
:
getUni
queKey
,
unique
Key
)
);
);
}
}
}
}
...
...
kt-base/src/main/java/org/matrix/socket/ExecutionSocketHandler.java
浏览文件 @
aa46a4ee
...
@@ -5,7 +5,7 @@ import com.alibaba.fastjson.JSONException;
...
@@ -5,7 +5,7 @@ import com.alibaba.fastjson.JSONException;
import
org.matrix.actuators.usecase.CaseActuator
;
import
org.matrix.actuators.usecase.CaseActuator
;
import
org.matrix.database.service.ITestDataService
;
import
org.matrix.database.service.ITestDataService
;
import
org.matrix.socket.enums.SocketType
;
import
org.matrix.socket.enums.SocketType
;
import
org.matrix.socket.pool.
Execute
MonitorSocketPool
;
import
org.matrix.socket.pool.MonitorSocketPool
;
import
org.matrix.socket.vo.CaseExecuteVo
;
import
org.matrix.socket.vo.CaseExecuteVo
;
import
org.matrix.socket.vo.ExecuteMonitorVo
;
import
org.matrix.socket.vo.ExecuteMonitorVo
;
import
org.matrix.socket.vo.SocketVo
;
import
org.matrix.socket.vo.SocketVo
;
...
@@ -23,10 +23,11 @@ import org.springframework.web.socket.handler.TextWebSocketHandler;
...
@@ -23,10 +23,11 @@ import org.springframework.web.socket.handler.TextWebSocketHandler;
@Component
@Component
public
class
ExecutionSocketHandler
extends
TextWebSocketHandler
{
public
class
ExecutionSocketHandler
extends
TextWebSocketHandler
{
public
static
final
MonitorSocketPool
EXECUTE_MONITOR_SOCKET_POOL
=
new
MonitorSocketPool
();
final
CaseActuator
caseActuator
;
final
CaseActuator
caseActuator
;
final
ITestDataService
testDataService
;
final
ITestDataService
testDataService
;
public
ExecutionSocketHandler
(
CaseActuator
caseActuator
,
ITestDataService
testDataService
)
{
public
ExecutionSocketHandler
(
CaseActuator
caseActuator
,
ITestDataService
testDataService
)
{
this
.
caseActuator
=
caseActuator
;
this
.
caseActuator
=
caseActuator
;
this
.
testDataService
=
testDataService
;
this
.
testDataService
=
testDataService
;
...
@@ -45,7 +46,7 @@ public class ExecutionSocketHandler extends TextWebSocketHandler {
...
@@ -45,7 +46,7 @@ public class ExecutionSocketHandler extends TextWebSocketHandler {
caseActuator
.
runTestCase
(
session
,
caseExecuteVo
);
caseActuator
.
runTestCase
(
session
,
caseExecuteVo
);
}
else
if
(
SocketType
.
TEST_CASE_MONITOR
.
equals
(
socketVo
.
getSocketType
()))
{
}
else
if
(
SocketType
.
TEST_CASE_MONITOR
.
equals
(
socketVo
.
getSocketType
()))
{
ExecuteMonitorVo
caseExecuteVo
=
JSON
.
parseObject
(
payload
,
ExecuteMonitorVo
.
class
);
ExecuteMonitorVo
caseExecuteVo
=
JSON
.
parseObject
(
payload
,
ExecuteMonitorVo
.
class
);
E
xecuteMonitorSocketPool
.
add
(
caseExecuteVo
.
getUnion
Key
(),
session
);
E
XECUTE_MONITOR_SOCKET_POOL
.
add
(
caseExecuteVo
.
getUnique
Key
(),
session
);
}
else
{
}
else
{
session
.
sendMessage
(
new
TextMessage
(
"入参不符合规定"
));
session
.
sendMessage
(
new
TextMessage
(
"入参不符合规定"
));
}
}
...
@@ -57,7 +58,7 @@ public class ExecutionSocketHandler extends TextWebSocketHandler {
...
@@ -57,7 +58,7 @@ public class ExecutionSocketHandler extends TextWebSocketHandler {
@Override
@Override
public
void
afterConnectionClosed
(
WebSocketSession
session
,
CloseStatus
status
)
{
public
void
afterConnectionClosed
(
WebSocketSession
session
,
CloseStatus
status
)
{
E
xecuteMonitorSocketPool
.
remove
(
session
);
E
XECUTE_MONITOR_SOCKET_POOL
.
remove
(
session
);
}
}
...
...
kt-base/src/main/java/org/matrix/socket/ExecutionStatusMonitorSocketPool.java
浏览文件 @
aa46a4ee
package
org
.
matrix
.
socket
;
package
org
.
matrix
.
socket
;
import
org.matrix.socket.pool.ExecuteMonitorSocketPool
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.socket.CloseStatus
;
import
org.springframework.web.socket.CloseStatus
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.TextMessage
;
...
@@ -23,6 +22,6 @@ public class ExecutionStatusMonitorSocketPool extends TextWebSocketHandler {
...
@@ -23,6 +22,6 @@ public class ExecutionStatusMonitorSocketPool extends TextWebSocketHandler {
@Override
@Override
public
void
afterConnectionClosed
(
WebSocketSession
session
,
CloseStatus
status
)
{
public
void
afterConnectionClosed
(
WebSocketSession
session
,
CloseStatus
status
)
{
ExecuteMonitorSocketPool
.
remove
(
session
);
}
}
}
}
kt-base/src/main/java/org/matrix/socket/pool/
Execute
MonitorSocketPool.java
→
kt-base/src/main/java/org/matrix/socket/pool/MonitorSocketPool.java
浏览文件 @
aa46a4ee
...
@@ -11,60 +11,60 @@ import java.util.concurrent.ConcurrentHashMap;
...
@@ -11,60 +11,60 @@ import java.util.concurrent.ConcurrentHashMap;
*
*
* @author huangxiahao
* @author huangxiahao
*/
*/
public
class
Execute
MonitorSocketPool
{
public
class
MonitorSocketPool
{
/**
/**
* uni
on
Key -> socket队列
* uni
que
Key -> socket队列
* key为uni
on
Key value 为 socket队列
* key为uni
que
Key value 为 socket队列
*/
*/
private
static
final
ConcurrentHashMap
<
String
,
List
<
WebSocketSession
>>
SOCKET_MAP
=
new
ConcurrentHashMap
<>();
private
ConcurrentHashMap
<
String
,
List
<
WebSocketSession
>>
socketMap
=
new
ConcurrentHashMap
<>();
/**
/**
* socketId -> uni
on
Key队列
* socketId -> uni
que
Key队列
* key为socketId value 为 uni
on
Key队列
* key为socketId value 为 uni
que
Key队列
*/
*/
private
static
final
ConcurrentHashMap
<
String
,
List
<
String
>>
SOCKET_KEY_MAP
=
new
ConcurrentHashMap
<>();
private
ConcurrentHashMap
<
String
,
List
<
String
>>
socketKeyMap
=
new
ConcurrentHashMap
<>();
public
static
void
add
(
String
key
,
WebSocketSession
clientSocket
)
{
public
void
add
(
String
key
,
WebSocketSession
clientSocket
)
{
if
(
clientSocket
!=
null
&
key
!=
null
)
{
if
(
clientSocket
!=
null
&
key
!=
null
)
{
List
<
WebSocketSession
>
webSocketSessions
=
SOCKET_MAP
.
get
(
key
);
List
<
WebSocketSession
>
webSocketSessions
=
socketMap
.
get
(
key
);
if
(
webSocketSessions
!=
null
)
{
if
(
webSocketSessions
!=
null
)
{
webSocketSessions
.
add
(
clientSocket
);
webSocketSessions
.
add
(
clientSocket
);
}
else
{
}
else
{
webSocketSessions
=
new
ArrayList
<>();
webSocketSessions
=
new
ArrayList
<>();
webSocketSessions
.
add
(
clientSocket
);
webSocketSessions
.
add
(
clientSocket
);
SOCKET_MAP
.
put
(
key
,
webSocketSessions
);
socketMap
.
put
(
key
,
webSocketSessions
);
}
}
addKeyMap
(
clientSocket
.
getId
(),
key
);
addKeyMap
(
clientSocket
.
getId
(),
key
);
}
}
}
}
public
static
void
addKeyMap
(
String
socketId
,
String
key
)
{
public
void
addKeyMap
(
String
socketId
,
String
key
)
{
if
(
socketId
!=
null
&
key
!=
null
)
{
if
(
socketId
!=
null
&
key
!=
null
)
{
List
<
String
>
list
=
SOCKET_KEY_MAP
.
get
(
socketId
);
List
<
String
>
list
=
socketKeyMap
.
get
(
socketId
);
if
(
list
!=
null
)
{
if
(
list
!=
null
)
{
list
.
add
(
key
);
list
.
add
(
key
);
}
else
{
}
else
{
list
=
new
ArrayList
<>();
list
=
new
ArrayList
<>();
list
.
add
(
key
);
list
.
add
(
key
);
SOCKET_KEY_MAP
.
put
(
socketId
,
list
);
socketKeyMap
.
put
(
socketId
,
list
);
}
}
}
}
}
}
public
static
void
remove
(
WebSocketSession
clientSocket
)
{
public
void
remove
(
WebSocketSession
clientSocket
)
{
String
socketId
=
clientSocket
.
getId
();
String
socketId
=
clientSocket
.
getId
();
List
<
String
>
list
=
SOCKET_KEY_MAP
.
get
(
socketId
);
List
<
String
>
list
=
socketKeyMap
.
get
(
socketId
);
if
(
list
!=
null
)
{
if
(
list
!=
null
)
{
for
(
String
s
:
list
)
{
for
(
String
s
:
list
)
{
SOCKET_MAP
.
get
(
s
).
remove
(
clientSocket
);
socketMap
.
get
(
s
).
remove
(
clientSocket
);
}
}
}
}
SOCKET_KEY_MAP
.
remove
(
socketId
);
socketKeyMap
.
remove
(
socketId
);
}
}
public
static
List
<
WebSocketSession
>
get
(
String
key
)
{
public
List
<
WebSocketSession
>
get
(
String
key
)
{
return
SOCKET_MAP
.
get
(
key
);
return
socketMap
.
get
(
key
);
}
}
}
}
\ No newline at end of file
kt-base/src/main/java/org/matrix/socket/queue/LogQueueRuntime.java
浏览文件 @
aa46a4ee
...
@@ -3,12 +3,14 @@ package org.matrix.socket.queue;
...
@@ -3,12 +3,14 @@ package org.matrix.socket.queue;
import
cn.hutool.log.LogFactory
;
import
cn.hutool.log.LogFactory
;
import
cn.hutool.log.level.Level
;
import
cn.hutool.log.level.Level
;
import
com.alibaba.fastjson.JSONObject
;
import
com.alibaba.fastjson.JSONObject
;
import
org.matrix.actuators.httpclient.HttpClientActuator
;
import
org.matrix.actuators.util.ThreadUtil
;
import
org.matrix.actuators.util.ThreadUtil
;
import
org.matrix.database.service.IExecutionRecordService
;
import
org.matrix.database.service.IExecutionRecordService
;
import
org.matrix.exception.GlobalException
;
import
org.matrix.exception.GlobalException
;
import
org.matrix.socket.ExecutionSocketHandler
;
import
org.matrix.socket.vo.TestExecuteLog
;
import
org.matrix.socket.vo.TestExecuteLog
;
import
org.matrix.socket.enums.TestExecuteType
;
import
org.matrix.socket.enums.TestExecuteType
;
import
org.matrix.socket.pool.
Execute
MonitorSocketPool
;
import
org.matrix.socket.pool.MonitorSocketPool
;
import
org.matrix.socket.pool.TestCaseExecuteSocketPool
;
import
org.matrix.socket.pool.TestCaseExecuteSocketPool
;
import
org.matrix.util.SpringUtils
;
import
org.matrix.util.SpringUtils
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.TextMessage
;
...
@@ -63,7 +65,7 @@ public class LogQueueRuntime {
...
@@ -63,7 +65,7 @@ public class LogQueueRuntime {
TestExecuteLog
testExecuteLog
=
LOG_MAP
.
get
(
currentThreadId
);
TestExecuteLog
testExecuteLog
=
LOG_MAP
.
get
(
currentThreadId
);
if
(
testExecuteLog
!=
null
){
if
(
testExecuteLog
!=
null
){
testExecuteLog
.
addLog
(
log
);
testExecuteLog
.
addLog
(
log
);
sendMessage
(
currentThreadId
,
testExecuteLog
.
getUni
on
Key
(),
log
);
sendMessage
(
currentThreadId
,
testExecuteLog
.
getUni
que
Key
(),
log
);
addExecutionRecord
(
testExecuteLog
,
log
);
addExecutionRecord
(
testExecuteLog
,
log
);
}
else
{
}
else
{
throw
new
GlobalException
(
"该线程中的TestCaseLog对象未初始化,请先调用put方法初始化对象"
);
throw
new
GlobalException
(
"该线程中的TestCaseLog对象未初始化,请先调用put方法初始化对象"
);
...
@@ -74,7 +76,7 @@ public class LogQueueRuntime {
...
@@ -74,7 +76,7 @@ public class LogQueueRuntime {
LOG_MAP
.
remove
(
threadId
);
LOG_MAP
.
remove
(
threadId
);
}
}
public
static
void
sendMessage
(
Long
threadId
,
String
uni
on
Key
,
Object
log
)
{
public
static
void
sendMessage
(
Long
threadId
,
String
uni
que
Key
,
Object
log
)
{
try
{
try
{
TestExecuteLog
testExecuteLog
=
LOG_MAP
.
get
(
threadId
);
TestExecuteLog
testExecuteLog
=
LOG_MAP
.
get
(
threadId
);
// 测试任务ID.测试用例ID.测试数据ID
// 测试任务ID.测试用例ID.测试数据ID
...
@@ -87,8 +89,8 @@ public class LogQueueRuntime {
...
@@ -87,8 +89,8 @@ public class LogQueueRuntime {
,
testExecuteLog
.
getTestDataId
()
,
testExecuteLog
.
getTestDataId
()
,
message
.
substring
(
1
,
message
.
length
()-
1
)
,
message
.
substring
(
1
,
message
.
length
()-
1
)
);
);
//如果uni
on
Key中存在监听的socket则向该socket发送消息
//如果uni
que
Key中存在监听的socket则向该socket发送消息
List
<
WebSocketSession
>
webSocketSessions
=
Execut
eMonitorSocketPool
.
get
(
union
Key
);
List
<
WebSocketSession
>
webSocketSessions
=
Execut
ionSocketHandler
.
EXECUTE_MONITOR_SOCKET_POOL
.
get
(
unique
Key
);
if
(
webSocketSessions
!=
null
){
if
(
webSocketSessions
!=
null
){
for
(
WebSocketSession
webSocketSession
:
webSocketSessions
)
{
for
(
WebSocketSession
webSocketSession
:
webSocketSessions
)
{
webSocketSession
.
sendMessage
(
new
TextMessage
(
webSocketSession
.
sendMessage
(
new
TextMessage
(
...
@@ -109,13 +111,13 @@ public class LogQueueRuntime {
...
@@ -109,13 +111,13 @@ public class LogQueueRuntime {
}
}
}
}
public
static
void
initTestCaseLog
(
Long
jobId
,
Long
userId
,
Long
caseId
,
TestExecuteType
type
,
String
uni
on
Key
)
{
public
static
void
initTestCaseLog
(
Long
jobId
,
Long
userId
,
Long
caseId
,
TestExecuteType
type
,
String
uni
que
Key
)
{
TestExecuteLog
testExecuteLog
=
new
TestExecuteLog
();
TestExecuteLog
testExecuteLog
=
new
TestExecuteLog
();
testExecuteLog
.
setTestJobId
(
jobId
);
testExecuteLog
.
setTestJobId
(
jobId
);
testExecuteLog
.
setTestCaseId
(
caseId
);
testExecuteLog
.
setTestCaseId
(
caseId
);
testExecuteLog
.
setUserId
(
userId
);
testExecuteLog
.
setUserId
(
userId
);
testExecuteLog
.
setType
(
type
);
testExecuteLog
.
setType
(
type
);
testExecuteLog
.
setUni
onKey
(
union
Key
);
testExecuteLog
.
setUni
queKey
(
unique
Key
);
if
(
checkIsInRun
(
testExecuteLog
)){
if
(
checkIsInRun
(
testExecuteLog
)){
throw
new
GlobalException
(
"当前用例正在执行中"
);
throw
new
GlobalException
(
"当前用例正在执行中"
);
}
}
...
...
kt-base/src/main/java/org/matrix/socket/vo/ExecuteMonitorVo.java
浏览文件 @
aa46a4ee
...
@@ -9,6 +9,6 @@ import lombok.Data;
...
@@ -9,6 +9,6 @@ import lombok.Data;
@Data
@Data
public
class
ExecuteMonitorVo
extends
SocketVo
{
public
class
ExecuteMonitorVo
extends
SocketVo
{
private
String
uni
on
Key
;
private
String
uni
que
Key
;
}
}
kt-base/src/main/java/org/matrix/socket/vo/ExecutionStatusMonitorVo.java
0 → 100644
浏览文件 @
aa46a4ee
package
org
.
matrix
.
socket
.
vo
;
import
lombok.Data
;
/**
* 前端需要监听某一个测试任务或者测试用例调试时需要传入的值
* @author huangxiahao
*/
@Data
public
class
ExecutionStatusMonitorVo
{
}
kt-base/src/main/java/org/matrix/socket/vo/TestExecuteLog.java
浏览文件 @
aa46a4ee
...
@@ -19,7 +19,7 @@ public class TestExecuteLog {
...
@@ -19,7 +19,7 @@ public class TestExecuteLog {
/**
/**
* 用于记录执行记录的唯一KEY,同一批次的执行记录应该具有相同的KEY。
* 用于记录执行记录的唯一KEY,同一批次的执行记录应该具有相同的KEY。
*/
*/
private
String
uni
on
Key
;
private
String
uni
que
Key
;
private
TestExecuteType
type
=
TestExecuteType
.
TEST_CASE
;
private
TestExecuteType
type
=
TestExecuteType
.
TEST_CASE
;
...
...
kt-web/src/main/java/org/matrix/autotest/controller/ExecutionHistoryController.java
浏览文件 @
aa46a4ee
package
org
.
matrix
.
autotest
.
controller
;
package
org
.
matrix
.
autotest
.
controller
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.baomidou.mybatisplus.core.toolkit.Wrappers
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
io.swagger.annotations.*
;
import
io.swagger.annotations.*
;
import
org.matrix.autotest.utils.PageTools
;
import
org.matrix.database.entity.ExecutionHistory
;
import
org.matrix.database.entity.ExecutionRecord
;
import
org.matrix.database.entity.TestData
;
import
org.matrix.database.service.IExecutionHistoryService
;
import
org.matrix.database.service.IExecutionHistoryService
;
import
org.matrix.database.service.ITestDataService
;
import
org.matrix.database.vo.CommonResult
;
import
org.matrix.database.vo.CommonResult
;
import
org.matrix.database.vo.CommonResultObj
;
import
org.matrix.database.vo.CommonResultObj
;
import
org.matrix.database.vo.ExecutionHistoryVo
;
import
org.matrix.database.vo.ExecutionHistoryVo
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.bind.annotation.*
;
import
java.util.Collection
;
import
java.util.List
;
import
java.util.Optional
;
import
java.util.stream.Collectors
;
/**
/**
* @author hxh
* @author hxh
*/
*/
...
@@ -20,11 +35,53 @@ public class ExecutionHistoryController {
...
@@ -20,11 +35,53 @@ public class ExecutionHistoryController {
private
final
IExecutionHistoryService
executionHistoryService
;
private
final
IExecutionHistoryService
executionHistoryService
;
@Autowired
ITestDataService
testDataService
;
public
ExecutionHistoryController
(
IExecutionHistoryService
executionHistoryService
)
{
public
ExecutionHistoryController
(
IExecutionHistoryService
executionHistoryService
)
{
this
.
executionHistoryService
=
executionHistoryService
;
this
.
executionHistoryService
=
executionHistoryService
;
}
}
@ApiOperation
(
value
=
"分页查询执行历史"
,
notes
=
"假如查询测试用例的执行历史,请入参caseId,假如查询测试任务的执行历史,请入参jobId"
)
@ApiOperation
(
value
=
"分页查询执行历史详情"
,
notes
=
"假如查询测试用例的执行历史,请入参caseId,假如查询测试任务的执行历史,请入参jobId"
)
@GetMapping
(
"/executionHistoryDetail"
)
@ApiImplicitParams
({
@ApiImplicitParam
(
name
=
"pageSize"
,
value
=
"页码"
,
required
=
true
,
paramType
=
"query"
),
@ApiImplicitParam
(
name
=
"pageNum"
,
value
=
"当前页显示调试"
,
required
=
true
,
paramType
=
"query"
),
@ApiImplicitParam
(
name
=
"uniqueKey"
,
value
=
"执行历史批次号"
,
paramType
=
"query"
),
@ApiImplicitParam
(
name
=
"jobId"
,
value
=
"测试任务ID"
,
paramType
=
"query"
),
@ApiImplicitParam
(
name
=
"caseId"
,
value
=
"测试用例ID"
,
paramType
=
"query"
)
})
public
ResponseEntity
<
CommonResultObj
<
IPage
<
ExecutionHistoryVo
>>>
findExecutionHistory
(
@RequestParam
(
defaultValue
=
"10"
)
int
pageSize
,
@RequestParam
(
defaultValue
=
"1"
)
int
pageNum
,
@RequestParam
(
defaultValue
=
"-1"
)
Long
jobId
,
Long
caseId
,
String
uniqueKey
){
Page
<
ExecutionHistory
>
results
=
Optional
.
of
(
executionHistoryService
.
page
(
Page
.
of
(
pageNum
,
pageSize
)
,
Wrappers
.
lambdaQuery
(
ExecutionHistory
.
class
)
.
eq
(
jobId
!=
null
,
ExecutionHistory:
:
getJobId
,
jobId
)
.
eq
(
caseId
!=
null
,
ExecutionHistory:
:
getCaseId
,
caseId
)
.
eq
(
StringUtils
.
hasLength
(
uniqueKey
),
ExecutionHistory:
:
getUniqueKey
,
uniqueKey
)
)).
orElse
(
new
Page
<>());
PageTools
.
pageTool
(
pageSize
,
pageNum
,
results
);
List
<
ExecutionHistory
>
records
=
results
.
getRecords
();
List
<
ExecutionHistoryVo
>
collect
=
records
.
stream
().
map
(
executionHistory
->
{
ExecutionHistoryVo
executionHistoryVo
=
new
ExecutionHistoryVo
();
BeanUtils
.
copyProperties
(
executionHistory
,
executionHistoryVo
);
TestData
byId
=
testDataService
.
getById
(
executionHistory
.
getDataId
());
executionHistoryVo
.
setDataName
(
byId
.
getName
());
return
executionHistoryVo
;
}).
collect
(
Collectors
.
toList
());
Page
<
ExecutionHistoryVo
>
resultPage
=
new
Page
<>();
BeanUtils
.
copyProperties
(
results
,
resultPage
);
resultPage
.
setRecords
(
collect
);
return
resultPage
.
getRecords
().
size
()
!=
0
?
CommonResult
.
success
(
resultPage
,
"查询成功"
)
:
CommonResult
.
failed
(
resultPage
,
"查询失败或无数据"
);
}
@ApiOperation
(
value
=
"分页查询执行历史缩略"
,
notes
=
"假如查询测试用例的执行历史,请入参caseId,假如查询测试任务的执行历史,请入参jobId"
)
@GetMapping
(
"/executionHistory"
)
@GetMapping
(
"/executionHistory"
)
@ApiImplicitParams
({
@ApiImplicitParams
({
@ApiImplicitParam
(
name
=
"pageSize"
,
value
=
"页码"
,
required
=
true
,
paramType
=
"query"
),
@ApiImplicitParam
(
name
=
"pageSize"
,
value
=
"页码"
,
required
=
true
,
paramType
=
"query"
),
...
...
kt-web/src/main/java/org/matrix/autotest/controller/ExecutionRecordController.java
浏览文件 @
aa46a4ee
...
@@ -10,11 +10,13 @@ import org.matrix.database.service.IExecutionRecordService;
...
@@ -10,11 +10,13 @@ import org.matrix.database.service.IExecutionRecordService;
import
org.matrix.database.vo.CommonResult
;
import
org.matrix.database.vo.CommonResult
;
import
org.matrix.database.vo.CommonResultObj
;
import
org.matrix.database.vo.CommonResultObj
;
import
org.matrix.enums.ExecutionRecType
;
import
org.matrix.enums.ExecutionRecType
;
import
org.matrix.exception.GlobalException
;
import
org.matrix.socket.enums.TestExecuteType
;
import
org.matrix.socket.enums.TestExecuteType
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.util.StringUtils
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.bind.annotation.*
;
import
java.util.List
;
import
java.util.Optional
;
import
java.util.Optional
;
/**
/**
...
@@ -40,7 +42,7 @@ public class ExecutionRecordController {
...
@@ -40,7 +42,7 @@ public class ExecutionRecordController {
* @param userId 查询条件:用户ID
* @param userId 查询条件:用户ID
* @param testDataId 查询条件:测试数据ID
* @param testDataId 查询条件:测试数据ID
* @param testCaseId 查询条件:测试用例ID
* @param testCaseId 查询条件:测试用例ID
* @param uni
on
Key 查询条件:唯一批次号
* @param uni
que
Key 查询条件:唯一批次号
* @param status 查询条件:运行状态
* @param status 查询条件:运行状态
* @param type 查询条件:执行类型
* @param type 查询条件:执行类型
* @return 分页查询的结果, 执行记录
* @return 分页查询的结果, 执行记录
...
@@ -53,7 +55,7 @@ public class ExecutionRecordController {
...
@@ -53,7 +55,7 @@ public class ExecutionRecordController {
Long
userId
,
Long
userId
,
Long
testDataId
,
Long
testDataId
,
Long
testCaseId
,
Long
testCaseId
,
String
uni
on
Key
,
String
uni
que
Key
,
ExecutionRecType
status
,
ExecutionRecType
status
,
TestExecuteType
type
)
{
TestExecuteType
type
)
{
Page
<
ExecutionRecord
>
results
=
Optional
.
of
(
executionRecordService
.
page
(
Page
.
of
(
pageNum
,
pageSize
)
Page
<
ExecutionRecord
>
results
=
Optional
.
of
(
executionRecordService
.
page
(
Page
.
of
(
pageNum
,
pageSize
)
...
@@ -61,7 +63,7 @@ public class ExecutionRecordController {
...
@@ -61,7 +63,7 @@ public class ExecutionRecordController {
.
eq
(
userId
!=
null
,
ExecutionRecord:
:
getUserId
,
userId
)
.
eq
(
userId
!=
null
,
ExecutionRecord:
:
getUserId
,
userId
)
.
eq
(
testDataId
!=
null
,
ExecutionRecord:
:
getTestDataId
,
testDataId
)
.
eq
(
testDataId
!=
null
,
ExecutionRecord:
:
getTestDataId
,
testDataId
)
.
eq
(
testCaseId
!=
null
,
ExecutionRecord:
:
getTestCaseId
,
testCaseId
)
.
eq
(
testCaseId
!=
null
,
ExecutionRecord:
:
getTestCaseId
,
testCaseId
)
.
eq
(
StringUtils
.
hasLength
(
uni
onKey
),
ExecutionRecord:
:
getUnionKey
,
union
Key
)
.
eq
(
StringUtils
.
hasLength
(
uni
queKey
),
ExecutionRecord:
:
getUniqueKey
,
unique
Key
)
.
eq
(
type
!=
null
,
ExecutionRecord:
:
getType
,
type
)
.
eq
(
type
!=
null
,
ExecutionRecord:
:
getType
,
type
)
)).
orElse
(
new
Page
<>());
)).
orElse
(
new
Page
<>());
PageTools
.
pageTool
(
pageSize
,
pageNum
,
results
);
PageTools
.
pageTool
(
pageSize
,
pageNum
,
results
);
...
@@ -70,6 +72,39 @@ public class ExecutionRecordController {
...
@@ -70,6 +72,39 @@ public class ExecutionRecordController {
:
CommonResult
.
failed
(
results
,
"查询失败或无数据"
);
:
CommonResult
.
failed
(
results
,
"查询失败或无数据"
);
}
}
/**
* 分页查询执行记录
*
* @param userId 查询条件:用户ID
* @param testDataId 查询条件:测试数据ID
* @param testCaseId 查询条件:测试用例ID
* @param uniqueKey 查询条件:唯一批次号
* @param status 查询条件:运行状态
* @param type 查询条件:执行类型
* @return 分页查询的结果, 执行记录
*/
@ApiOperation
(
"分页查询执行记录"
)
@GetMapping
(
"/list"
)
public
ResponseEntity
<
CommonResultObj
<
List
<
ExecutionRecord
>>>
findListExecutionRecord
(
Long
userId
,
Long
testDataId
,
Long
testCaseId
,
String
uniqueKey
,
ExecutionRecType
status
,
TestExecuteType
type
)
{
List
<
ExecutionRecord
>
results
=
Optional
.
of
(
executionRecordService
.
list
(
Wrappers
.
lambdaQuery
(
ExecutionRecord
.
class
)
.
eq
(
userId
!=
null
,
ExecutionRecord:
:
getUserId
,
userId
)
.
eq
(
testDataId
!=
null
,
ExecutionRecord:
:
getTestDataId
,
testDataId
)
.
eq
(
testCaseId
!=
null
,
ExecutionRecord:
:
getTestCaseId
,
testCaseId
)
.
eq
(
StringUtils
.
hasLength
(
uniqueKey
),
ExecutionRecord:
:
getUniqueKey
,
uniqueKey
)
.
eq
(
type
!=
null
,
ExecutionRecord:
:
getType
,
type
)
)).
orElseThrow
(()->
new
GlobalException
(
"xxx"
));
return
results
.
size
()
!=
0
?
CommonResult
.
success
(
results
,
"查询成功"
)
:
CommonResult
.
failed
(
results
,
"查询失败或无数据"
);
}
/**
/**
* 添加执行记录
* 添加执行记录
*
*
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论