feat(web): 增加离线检索页面和任务查看

This commit is contained in:
2024-01-10 16:15:32 +08:00
parent be64de7fe8
commit 8d6c637b37
5 changed files with 216 additions and 1 deletions

View File

@@ -0,0 +1,56 @@
package com.lanyuanxiaoyao.service.web.controller;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.configuration.ExecutorProvider;
import com.lanyuanxiaoyao.service.forest.service.TaskService;
import com.lanyuanxiaoyao.service.web.controller.base.AmisMapResponse;
import com.lanyuanxiaoyao.service.web.controller.base.AmisResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 任务提交
*
* @author lanyuanxiaoyao
* @date 2024-01-10
*/
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RestController
@RequestMapping("task")
public class TaskController {
private static final Logger logger = LoggerFactory.getLogger(TaskController.class);
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping("scan")
public AmisResponse<Object> scan(
@RequestParam("hdfs") String hdfs,
@RequestParam("key") String key,
@RequestParam(value = "mode", defaultValue = "") String mode
) {
if (StrUtil.isBlank(hdfs) || StrUtil.isBlank(key)) {
throw new RuntimeException("Argument cannot be blank");
}
ExecutorProvider.EXECUTORS.submit(() -> {
boolean scanLog = StrUtil.contains(mode, "log");
boolean scanData = StrUtil.contains(mode, "data");
String applicationId = taskService.scan(hdfs, key, scanLog, scanData);
logger.info("Task: {}", applicationId);
});
return AmisResponse.responseSuccess();
}
@GetMapping("results")
public AmisMapResponse results(@RequestParam("task_id") String taskId) {
return AmisResponse.responseMapData()
.setData("text", taskService.results(taskId).makeString("\n"));
}
}

View File

@@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnApplication;
import com.lanyuanxiaoyao.service.configuration.utils.DatetimeUtil;
import java.time.Instant;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* YarnApplication 展示类
@@ -15,12 +17,15 @@ import java.time.Instant;
* @date 2023-04-21
*/
public class YarnApplicationVO {
private static final Pattern TASK_REGEX = Pattern.compile("^Service_Task_(.+)_(.{8})$");
@JsonIgnore
private final YarnApplication yarnApplication;
private final Boolean isHudiApplication;
private final Boolean isSyncApplication;
private final Boolean isCompactionApplication;
private final Boolean isTaskApplication;
private String startTimeFromNow;
private String finishTimeFromNow;
@@ -28,6 +33,9 @@ public class YarnApplicationVO {
private String flinkJobName;
private String alias;
private String taskName;
private String taskId;
private Double compactionCompletionRatio = 0.0;
public YarnApplicationVO(YarnApplication yarnApplication) {
@@ -37,6 +45,13 @@ public class YarnApplicationVO {
isCompactionApplication = NameHelper.isCompactionJob(yarnApplication.getName());
isHudiApplication = isSyncApplication || isCompactionApplication;
Matcher matcher = TASK_REGEX.matcher(yarnApplication.getName());
isTaskApplication = matcher.matches();
if (isTaskApplication && matcher.groupCount() == 2) {
taskName = matcher.group(1);
taskId = matcher.group(2);
}
long now = Instant.now().toEpochMilli();
if (ObjectUtil.isNotNull(yarnApplication.getStartedTime()) && yarnApplication.getStartedTime() != 0) {
startTimeFromNow = DatetimeUtil.fromNow(now, yarnApplication.getStartedTime());
@@ -147,6 +162,10 @@ public class YarnApplicationVO {
return isHudiApplication;
}
public Boolean getTaskApplication() {
return isTaskApplication;
}
public String getStartTimeFromNow() {
return startTimeFromNow;
}
@@ -167,6 +186,14 @@ public class YarnApplicationVO {
return alias;
}
public String getTaskName() {
return taskName;
}
public String getTaskId() {
return taskId;
}
public Double getCompactionCompletionRatio() {
return compactionCompletionRatio;
}

View File

@@ -238,7 +238,7 @@ function yarnCrudColumns() {
label: '名称',
className: 'nowrap',
type: 'tpl',
tpl: "${IF(syncApplication, '<span class=\"rounded-xl label label-primary\">S</span>', IF(compactionApplication, '<span class=\"rounded-xl label label-primary\">C</span>', ''))}${IF(hudiApplication, '<span class=\"mx-2\"/>', '')}${IF(syncApplication, flinkJobName, IF(compactionApplication, alias, name))}",
tpl: "${IF(syncApplication, '<span class=\"rounded-xl label label-primary\">S</span>', IF(compactionApplication, '<span class=\"rounded-xl label label-primary\">C</span>', IF(taskApplication, '<span class=\"rounded-xl label label-primary\">T</span>', '')))}${IF(hudiApplication || taskApplication, '<span class=\"mx-2\"/>', '')}${IF(syncApplication, flinkJobName, IF(compactionApplication, alias, IF(taskApplication, taskName, name)))}",
// tpl: '${name}',
backgroundScale: {
min: 0.001,
@@ -399,6 +399,40 @@ function yarnCrudColumns() {
level: 'link',
tooltip: '${alias}'
},
{
disabledOn: '${finalStatus != \'SUCCEEDED\' || state != \'FINISHED\'}',
disabledTip: '无结果',
visibleOn: 'taskId',
label: '结果',
level: 'link',
type: 'action',
actionType: 'dialog',
dialog: {
title: '结果',
...readOnlyDialogOptions(),
size: 'xl',
body: [
{
type: 'service',
api: {
method: 'get',
url: '${base}/task/results',
data: {
task_id: '${taskId|default:undefined}'
}
},
body: {
type: 'editor',
disabled: true,
name: 'text',
options: {
wordWrap: 'on'
}
}
}
],
},
},
{
label: 'ID',
type: 'action',

View File

@@ -0,0 +1,96 @@
function taskTab() {
return {
title: `离线检索`,
tab: [
{
type: 'form',
title: '检索文件',
actions: [
{
type: 'submit',
label: '提交任务',
actionType: 'ajax',
api: {
method: 'get',
url: '${base}/task/scan',
data: {
hdfs: '${hdfs|default:undefined}',
key: '${key|default:undefined}',
mode: '${scan_mode|default:undefined}',
}
}
},
],
body: [
{
name: 'scan_mode',
type: 'checkboxes',
label: '检索范围',
checkAll: true,
required: true,
value: 'log',
options: [
{label: '日志文件', value: 'log'},
{label: '数据文件', value: 'data'},
]
},
{
type: 'group',
body: [
{
type: 'input-text',
name: 'key',
label: '检索字段',
required: true,
clearable: true,
description: '检索带有该字符的记录',
columnRatio: 4,
},
{
type: 'input-text',
name: 'hdfs',
label: 'HDFS路经',
required: true,
clearable: true,
description: '输入表HDFS路径',
autoComplete: '${base}/table/all_hdfs?key=$term',
columnRatio: 8,
},
]
}
]
},
{
type: 'crud',
api: {
method: 'get',
url: `\${base}/yarn/job_list`,
data: {
clusters: `b12`,
page: '${page|default:undefined}',
count: '${perPage|default:undefined}',
order: '${orderBy|default:undefined}',
direction: '${orderDir|default:undefined}',
filter_state: '${state|default:undefined}',
filter_final_status: '${finalStatus|default:undefined}',
search_id: '${id|default:undefined}',
search_name: 'Service_Task',
precise: false,
}
},
affixHeader: false,
interval: 10000,
syncLocation: false,
silentPolling: true,
resizable: false,
perPage: 10,
headerToolbar: [
"reload",
paginationCommonOptions(),
],
footerToolbar: [],
columns: yarnCrudColumns(),
}
]
}
}

View File

@@ -49,6 +49,7 @@
<script src="components/version-tab.js"></script>
<script src="components/overview-tab.js"></script>
<script src="components/tool-tab.js"></script>
<script src="components/task-tab.js"></script>
<script type="text/javascript">
(function () {
let amis = amisRequire('amis/embed')
@@ -73,6 +74,7 @@
cloudTab(),
yarnClusterTab(),
toolTab(),
taskTab(),
]
}
}