diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobHistoryMapper.java b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobHistoryMapper.java index 300d00b989..c01720a5f3 100644 --- a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobHistoryMapper.java +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobHistoryMapper.java @@ -130,4 +130,26 @@ List selectFailoverJobHistory( @Param("statusList") List statusList, @Param("startTimestamp") Long startTimestamp, @Param("limit") Integer limit); + + List taskDurationTopN( + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("umUser") String username, + @Param("engineType") String engineType); + + List taskDurationTopNWithUserCreator( + @Param("umUser") String username, + @Param("userCreatorKey") String userCreatorKey, + @Param("userCreatorValue") String userCreator, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); + + List taskDurationTopNWithCreatorOnly( + @Param("umUser") String username, + @Param("userCreatorKey") String userCreatorKey, + @Param("creator") String userCreator, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); } diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobStatisticsMapper.java b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobStatisticsMapper.java new file mode 100644 index 0000000000..9c0ee86d9e --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/dao/JobStatisticsMapper.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.linkis.jobhistory.dao; + +import org.apache.linkis.jobhistory.entity.JobStatistics; + +import org.apache.ibatis.annotations.Param; + +import java.util.Date; + +public interface JobStatisticsMapper { + + JobStatistics taskExecutionStatistics( + @Param("umUser") String username, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); + + JobStatistics taskExecutionStatisticsWithUserCreator( + @Param("umUser") String username, + @Param("userCreatorKey") String userCreatorKey, + @Param("userCreatorValue") String userCreator, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); + + JobStatistics taskExecutionStatisticsWithCreatorOnly( + @Param("umUser") String username, + @Param("userCreatorKey") String userCreatorKey, + @Param("creator") String userCreator, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); + + JobStatistics engineExecutionStatisticsWithUserCreator( + @Param("umUser") String username, + @Param("userCreatorValue") String userCreator, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); + + JobStatistics engineExecutionStatistics( + @Param("umUser") String username, + @Param("creator") String userCreator, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("engineType") String engineType); +} diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/entity/JobStatistics.java b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/entity/JobStatistics.java new file mode 100644 index 0000000000..23621708f5 --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/entity/JobStatistics.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.linkis.jobhistory.entity; + +public class JobStatistics { + + private Long id; + + private Integer sumCount; + + private Integer succeedCount; + + private Integer failedCount; + + private Integer cancelledCount; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getSumCount() { + return sumCount; + } + + public void setSumCount(Integer sumCount) { + this.sumCount = sumCount; + } + + public Integer getSucceedCount() { + return succeedCount; + } + + public void setSucceedCount(Integer succeedCount) { + this.succeedCount = succeedCount; + } + + public Integer getFailedCount() { + return failedCount; + } + + public void setFailedCount(Integer failedCount) { + this.failedCount = failedCount; + } + + public Integer getCancelledCount() { + return cancelledCount; + } + + public void setCancelledCount(Integer cancelledCount) { + this.cancelledCount = cancelledCount; + } + + @Override + public String toString() { + return "JobHistory{" + "id=" + id + '}'; + } +} diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/QueryRestfulApi.java b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/QueryRestfulApi.java index a18da3a042..0ae72b84b3 100644 --- a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/QueryRestfulApi.java +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/QueryRestfulApi.java @@ -412,4 +412,93 @@ public Message listundone( return Message.ok().data(JobRequestConstants.TOTAL_PAGE(), total); } + + /** Method list should not contain subjob, which may cause performance problems. */ + @ApiOperation(value = "listDurationTop", notes = "listDurationTop", response = Message.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = "startDate", dataType = "long", example = "1658937600001"), + @ApiImplicitParam(name = "endDate", dataType = "long", example = "1658937600000"), + @ApiImplicitParam(name = "executeApplicationName", dataType = "String"), + @ApiImplicitParam(name = "creator", required = false, dataType = "String", value = "creator"), + @ApiImplicitParam( + name = "proxyUser", + required = false, + dataType = "String", + value = "proxyUser"), + @ApiImplicitParam(name = "pageNow", required = false, dataType = "Integer", value = "page now"), + @ApiImplicitParam(name = "pageSize", dataType = "Integer"), + }) + @RequestMapping(path = "/listDurationTop", method = RequestMethod.GET) + public Message listDurationTop( + HttpServletRequest req, + @RequestParam(value = "startDate", required = false) Long startDate, + @RequestParam(value = "endDate", required = false) Long endDate, + @RequestParam(value = "executeApplicationName", required = false) + String executeApplicationName, + @RequestParam(value = "creator", required = false) String creator, + @RequestParam(value = "proxyUser", required = false) String proxyUser, + @RequestParam(value = "pageNow", required = false) Integer pageNow, + @RequestParam(value = "pageSize", required = false) Integer pageSize) + throws QueryException { + if (StringUtils.isEmpty(pageNow)) { + pageNow = 1; + } + if (StringUtils.isEmpty(pageSize)) { + pageSize = 20; + } + if (StringUtils.isEmpty(proxyUser)) { + proxyUser = null; + } else { + if (!QueryUtils.checkNameValid(proxyUser)) { + return Message.error("Invalid proxyUser : " + proxyUser); + } + } + if (StringUtils.isEmpty(creator)) { + creator = null; + } else { + if (!QueryUtils.checkNameValid(creator)) { + return Message.error("Invalid creator : " + creator); + } + } + if (!StringUtils.isEmpty(executeApplicationName)) { + if (!QueryUtils.checkNameValid(executeApplicationName)) { + return Message.error("Invalid applicationName : " + executeApplicationName); + } + } else { + executeApplicationName = null; + } + + if (endDate == null) { + endDate = System.currentTimeMillis(); + } + if (startDate == null) { + startDate = 0L; + } + + Date sDate = new Date(startDate); + Date eDate = new Date(endDate); + if (sDate.getTime() == eDate.getTime()) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(endDate); + calendar.add(Calendar.DAY_OF_MONTH, 1); + eDate = new Date(calendar.getTime().getTime()); // todo check + } + List queryTasks = null; + PageHelper.startPage(pageNow, pageSize); + try { + queryTasks = + jobHistoryQueryService.taskDurationTopN( + sDate, eDate, proxyUser, creator, executeApplicationName); + } finally { + PageHelper.clearPage(); + } + + List vos = new ArrayList<>(); + for (JobHistory jobHistory : queryTasks) { + QueryUtils.exchangeExecutionCode(jobHistory); + QueryTaskVO taskVO = TaskConversions.jobHistory2TaskVO(jobHistory, null); + vos.add(taskVO); + } + return Message.ok().data(TaskConstant.TASKS, vos); + } } diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/StatisticsRestfulApi.java b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/StatisticsRestfulApi.java new file mode 100644 index 0000000000..23a12c2b94 --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/java/org/apache/linkis/jobhistory/restful/api/StatisticsRestfulApi.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.linkis.jobhistory.restful.api; + +import org.apache.linkis.governance.common.entity.job.QueryException; +import org.apache.linkis.jobhistory.entity.JobStatistics; +import org.apache.linkis.jobhistory.service.JobStatisticsQueryService; +import org.apache.linkis.jobhistory.util.QueryUtils; +import org.apache.linkis.server.Message; + +import org.apache.commons.lang3.time.DateUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Api(tags = "jobstatistics api") +@RestController +@RequestMapping(path = "/jobhistory/jobstatistics") +public class StatisticsRestfulApi { + + private Logger log = LoggerFactory.getLogger(this.getClass()); + + @Autowired private JobStatisticsQueryService jobStatisticsQueryService; + + @ApiOperation(value = "taskCount", notes = "taskCount", response = Message.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = "startDate", dataType = "long"), + @ApiImplicitParam(name = "endDate", required = false, dataType = "long", value = "end date"), + @ApiImplicitParam(name = "executeApplicationName", dataType = "String"), + @ApiImplicitParam(name = "creator", required = false, dataType = "String", value = "creator"), + @ApiImplicitParam( + name = "proxyUser", + required = false, + dataType = "String", + value = "proxyUser"), + }) + @RequestMapping(path = "/taskCount", method = RequestMethod.GET) + public Message taskCount( + HttpServletRequest req, + @RequestParam(value = "startDate", required = false) Long startDate, + @RequestParam(value = "endDate", required = false) Long endDate, + @RequestParam(value = "executeApplicationName", required = false) + String executeApplicationName, + @RequestParam(value = "creator", required = false) String creator, + @RequestParam(value = "proxyUser", required = false) String proxyUser) + throws IOException, QueryException { + if (endDate == null) { + endDate = System.currentTimeMillis(); + } + if (startDate == null) { + startDate = 0L; + } + Date sDate = new Date(startDate); + Date eDate = new Date(endDate); + if (startDate == 0L) { + sDate = DateUtils.addDays(eDate, -1); + } + if (sDate.getTime() == eDate.getTime()) { + Calendar instance = Calendar.getInstance(); + instance.setTimeInMillis(endDate); + instance.add(Calendar.DAY_OF_MONTH, 1); + eDate = new Date(instance.getTime().getTime()); + } + if (StringUtils.isEmpty(proxyUser)) { + proxyUser = null; + } else { + if (!QueryUtils.checkNameValid(proxyUser)) { + return Message.error("Invalid proxyUser : " + proxyUser); + } + } + if (StringUtils.isEmpty(creator)) { + creator = null; + } else { + if (!QueryUtils.checkNameValid(creator)) { + return Message.error("Invalid creator : " + creator); + } + } + if (!StringUtils.isEmpty(executeApplicationName)) { + if (!QueryUtils.checkNameValid(executeApplicationName)) { + return Message.error("Invalid applicationName : " + executeApplicationName); + } + } else { + executeApplicationName = null; + } + JobStatistics jobStatistics = + jobStatisticsQueryService.taskExecutionStatistics( + sDate, eDate, proxyUser, creator, executeApplicationName); + + return Message.ok() + .data("sumCount", jobStatistics.getSumCount()) + .data("succeedCount", jobStatistics.getSucceedCount()) + .data("failedCount", jobStatistics.getFailedCount()) + .data("cancelledCount", jobStatistics.getCancelledCount()); + } + + @ApiOperation(value = "engineCount", notes = "engineCount", response = Message.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = "startDate", dataType = "long"), + @ApiImplicitParam(name = "endDate", required = false, dataType = "long", value = "end date"), + @ApiImplicitParam(name = "executeApplicationName", dataType = "String"), + @ApiImplicitParam(name = "creator", required = false, dataType = "String", value = "creator"), + @ApiImplicitParam( + name = "proxyUser", + required = false, + dataType = "String", + value = "proxyUser"), + }) + @RequestMapping(path = "/engineCount", method = RequestMethod.GET) + public Message engineCount( + HttpServletRequest req, + @RequestParam(value = "startDate", required = false) Long startDate, + @RequestParam(value = "endDate", required = false) Long endDate, + @RequestParam(value = "executeApplicationName", required = false) + String executeApplicationName, + @RequestParam(value = "creator", required = false) String creator, + @RequestParam(value = "proxyUser", required = false) String proxyUser) + throws IOException, QueryException { + if (endDate == null) { + endDate = System.currentTimeMillis(); + } + if (startDate == null) { + startDate = 0L; + } + Date sDate = new Date(startDate); + Date eDate = new Date(endDate); + if (startDate == 0L) { + sDate = DateUtils.addDays(eDate, -1); + } + if (sDate.getTime() == eDate.getTime()) { + Calendar instance = Calendar.getInstance(); + instance.setTimeInMillis(endDate); + instance.add(Calendar.DAY_OF_MONTH, 1); + eDate = new Date(instance.getTime().getTime()); + } + if (StringUtils.isEmpty(proxyUser)) { + proxyUser = null; + } else { + if (!QueryUtils.checkNameValid(proxyUser)) { + return Message.error("Invalid proxyUser : " + proxyUser); + } + } + if (StringUtils.isEmpty(creator)) { + creator = null; + } else { + if (!QueryUtils.checkNameValid(creator)) { + return Message.error("Invalid creator : " + creator); + } + } + if (!StringUtils.isEmpty(executeApplicationName)) { + if (!QueryUtils.checkNameValid(executeApplicationName)) { + return Message.error("Invalid applicationName : " + executeApplicationName); + } + } else { + executeApplicationName = null; + } + JobStatistics jobStatistics = + jobStatisticsQueryService.engineExecutionStatistics( + sDate, eDate, proxyUser, creator, executeApplicationName); + + return Message.ok() + .data("countEngine", jobStatistics.getSumCount()) + .data("countEngineSucceed", jobStatistics.getSucceedCount()) + .data("countEngineFailed", jobStatistics.getFailedCount()) + .data("countEngineShutting", jobStatistics.getCancelledCount()); + } +} diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/common/JobStatisticsMapper.xml b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/common/JobStatisticsMapper.xml new file mode 100644 index 0000000000..809f82e3fc --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/common/JobStatisticsMapper.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobHistoryMapper.xml b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobHistoryMapper.xml index a5b769f2d3..6644f6b54b 100644 --- a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobHistoryMapper.xml +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobHistoryMapper.xml @@ -64,7 +64,7 @@ - SELECT * FROM linkis_ps_job_history_group_history id = #{id} @@ -170,11 +170,11 @@ SELECT a.* FROM linkis_ps_job_history_group_history a WHERE ( - a.instances = '' - OR a.instances IS NULL - OR a.instances NOT IN #{key} - OR EXISTS ( - SELECT 1 FROM - ( - - SELECT #{key} AS instances, #{val} AS registryTime - - ) b - WHERE a.instances = b.instances AND a.created_time FROM_UNIXTIME(b.registryTime/1000) - ) + a.instances = '' + OR a.instances IS NULL + OR a.instances NOT IN #{key} + OR EXISTS ( + SELECT 1 FROM + ( + + SELECT #{key} AS instances, #{val} AS registryTime + + ) b + WHERE a.instances = b.instances AND a.created_time FROM_UNIXTIME(b.registryTime/1000) + ) ) AND status IN #{status} @@ -252,4 +252,42 @@ + + + + + + diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobStatisticsMapper.xml b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobStatisticsMapper.xml new file mode 100644 index 0000000000..d05728faac --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/mysql/JobStatisticsMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobHistoryMapper.xml b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobHistoryMapper.xml index f7e75dea0e..0289141e89 100644 --- a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobHistoryMapper.xml +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobHistoryMapper.xml @@ -172,11 +172,11 @@ SELECT a.* FROM linkis_ps_job_history_group_history a WHERE ( - a.instances = '' - OR a.instances IS NULL - OR a.instances NOT IN #{key} - OR EXISTS ( - SELECT 1 FROM - ( - - SELECT #{key} AS instances, #{val} AS registryTime - - ) b - WHERE a.instances = b.instances AND a.created_time FROM_UNIXTIME(b.registryTime/1000) - ) + a.instances = '' + OR a.instances IS NULL + OR a.instances NOT IN #{key} + OR EXISTS ( + SELECT 1 FROM + ( + + SELECT #{key} AS instances, #{val} AS registryTime + + ) b + WHERE a.instances = b.instances AND a.created_time FROM_UNIXTIME(b.registryTime/1000) + ) ) AND status IN #{status} @@ -251,4 +251,43 @@ LIMIT #{limit} + + + + + + + diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobStatisticsMapper.xml b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobStatisticsMapper.xml new file mode 100644 index 0000000000..f1dd6e0887 --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/resources/mapper/postgresql/JobStatisticsMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobHistoryQueryService.java b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobHistoryQueryService.java index b8731554d4..335ea7cdee 100644 --- a/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobHistoryQueryService.java +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobHistoryQueryService.java @@ -5,16 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.apache.linkis.jobhistory.service; import org.apache.linkis.governance.common.entity.job.JobRequest; @@ -53,4 +53,6 @@ public interface JobHistoryQueryService { void changeObserveInfoById(JobHistory jobHistory); void clearUndoneTasksByEntranceInstance(EntranceInstanceConfRequest request, Sender sender); + + List taskDurationTopN(Date sDate, Date eDate, String username, String creator, String engineType); } diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobStatisticsQueryService.java b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobStatisticsQueryService.java new file mode 100644 index 0000000000..5d11b623ad --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/JobStatisticsQueryService.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.linkis.jobhistory.service; + +import org.apache.linkis.jobhistory.entity.JobStatistics; + +import java.util.Date; + + +public interface JobStatisticsQueryService { + + JobStatistics taskExecutionStatistics(Date startDate, Date endDate, String username, String creator, String engineType); + + JobStatistics engineExecutionStatistics(Date startDate, Date endDate, String username, String creator, String engineType); +} diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobHistoryQueryServiceImpl.scala b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobHistoryQueryServiceImpl.scala index 5c49be89b7..84b73d6a23 100644 --- a/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobHistoryQueryServiceImpl.scala +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobHistoryQueryServiceImpl.scala @@ -486,4 +486,44 @@ class JobHistoryQueryServiceImpl extends JobHistoryQueryService with Logging { } } + override def taskDurationTopN( + sDate: Date, + eDate: Date, + username: String, + creator: String, + engineType: String + ): util.List[JobHistory] = { + val result = if (StringUtils.isBlank(creator)) { + jobHistoryMapper.taskDurationTopN(sDate, eDate, username, engineType) + } else if (StringUtils.isBlank(username)) { + val fakeLabel = new UserCreatorLabel + jobHistoryMapper.taskDurationTopNWithCreatorOnly( + username, + fakeLabel.getLabelKey, + creator, + sDate, + eDate, + engineType + ) + } else { + val fakeLabel = new UserCreatorLabel + fakeLabel.setUser(username) + fakeLabel.setCreator(creator) + val userCreator = fakeLabel.getStringValue + Utils.tryCatch(fakeLabel.valueCheck(userCreator)) { t => + logger.info("input user or creator is not correct", t) + throw t + } + jobHistoryMapper.taskDurationTopNWithUserCreator( + username, + fakeLabel.getLabelKey, + userCreator, + sDate, + eDate, + engineType + ) + } + result + } + } diff --git a/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobStatisticsQueryServiceImpl.scala b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobStatisticsQueryServiceImpl.scala new file mode 100644 index 0000000000..27b8173d05 --- /dev/null +++ b/linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/JobStatisticsQueryServiceImpl.scala @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.linkis.jobhistory.service.impl + +import org.apache.linkis.common.utils.{Logging, Utils} +import org.apache.linkis.jobhistory.dao.JobStatisticsMapper +import org.apache.linkis.jobhistory.entity.JobStatistics +import org.apache.linkis.jobhistory.service.JobStatisticsQueryService +import org.apache.linkis.manager.label.entity.engine.UserCreatorLabel + +import org.apache.commons.lang3.StringUtils + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service + +import java.util.Date + +@Service +class JobStatisticsQueryServiceImpl extends JobStatisticsQueryService with Logging { + + @Autowired + private var jobStatisticsMapper: JobStatisticsMapper = _ + + override def taskExecutionStatistics( + sDate: Date, + eDate: Date, + username: String, + creator: String, + engineType: String + ): JobStatistics = { + val result = if (StringUtils.isBlank(creator)) { + jobStatisticsMapper.taskExecutionStatistics(username, sDate, eDate, engineType) + } else if (StringUtils.isBlank(username)) { + val fakeLabel = new UserCreatorLabel + jobStatisticsMapper.taskExecutionStatisticsWithCreatorOnly( + username, + fakeLabel.getLabelKey, + creator, + sDate, + eDate, + engineType + ) + } else { + val fakeLabel = new UserCreatorLabel + fakeLabel.setUser(username) + fakeLabel.setCreator(creator) + val userCreator = fakeLabel.getStringValue + Utils.tryCatch(fakeLabel.valueCheck(userCreator)) { t => + logger.info("input user or creator is not correct", t) + throw t + } + jobStatisticsMapper.taskExecutionStatisticsWithUserCreator( + username, + fakeLabel.getLabelKey, + userCreator, + sDate, + eDate, + engineType + ) + } + result + } + + override def engineExecutionStatistics( + sDate: Date, + eDate: Date, + username: String, + creator: String, + engineType: String + ): JobStatistics = { + val result = if (StringUtils.isBlank(username) || StringUtils.isBlank(creator)) { + jobStatisticsMapper.engineExecutionStatistics(username, creator, sDate, eDate, engineType) + } else { + val fakeLabel = new UserCreatorLabel + fakeLabel.setUser(username) + fakeLabel.setCreator(creator) + val userCreator = fakeLabel.getStringValue + Utils.tryCatch(fakeLabel.valueCheck(userCreator)) { t => + logger.info("input user or creator is not correct", t) + throw t + } + jobStatisticsMapper.engineExecutionStatisticsWithUserCreator( + username, + userCreator, + sDate, + eDate, + engineType + ) + } + result + } + +} diff --git a/linkis-web/package.json b/linkis-web/package.json index b43a05d183..fbc9566ec7 100644 --- a/linkis-web/package.json +++ b/linkis-web/package.json @@ -51,7 +51,8 @@ "vue-router": "3.4.8", "vuedraggable": "2.24.3", "vuescroll": "4.16.1", - "worker-loader": "3.0.8" + "worker-loader": "3.0.8", + "echarts": "^5.1.1" }, "devDependencies": { "@intlify/vue-i18n-loader": "1.0.0", diff --git a/linkis-web/src/apps/linkis/i18n/common/en.json b/linkis-web/src/apps/linkis/i18n/common/en.json index eaec2aa06f..ce5c0da805 100644 --- a/linkis-web/src/apps/linkis/i18n/common/en.json +++ b/linkis-web/src/apps/linkis/i18n/common/en.json @@ -90,6 +90,9 @@ "tenant": "Tenant", "inputTenant": "Please Input Tenant", "globalSettings": "GlobalSettings", + "taskOverTimeTop": "TaskDuration-TOP", + "taskTotalNum": "TaskTotalNum", + "engineTotalNum":"engineTotalNum", "resultSet": { "prefixText": "Because your result set is large, for a better experience, ", "linkText": "View result set", @@ -107,7 +110,7 @@ "resourceManagement": { "resourceUsage": "Resource usage", "applicationList": "Applications" - }, + }, "time": { "second": "Second", "minute": "Minute", @@ -199,7 +202,8 @@ "EngineConnList": "Engine Conn List", "opsTool": "OPS Tool", "userConfig": "User Configuration", - "configManagement": "Configuration Management" + "configManagement": "Configuration Management", + "statisticsDashboard": "Statistics Dashboard" } } }, diff --git a/linkis-web/src/apps/linkis/i18n/common/zh.json b/linkis-web/src/apps/linkis/i18n/common/zh.json index ba1a8919b8..50f6d2ecf0 100644 --- a/linkis-web/src/apps/linkis/i18n/common/zh.json +++ b/linkis-web/src/apps/linkis/i18n/common/zh.json @@ -90,6 +90,9 @@ "tenant": "租户标签", "inputTenant": "请输入租户标签", "globalSettings": "全局设置", + "taskOverTimeTop": "任务耗时-TOP", + "taskTotalNum": "任务总数", + "engineTotalNum":"引擎总数", "resultSet": { "prefixText": "因为您的结果集较大,为了更好的体验,", "linkText": "点击查看结果集", @@ -199,7 +202,8 @@ "EngineConnList": "引擎列表", "opsTool": "运维工具", "userConfig": "用户配置", - "configManagement": "配置项管理" + "configManagement": "配置项管理", + "statisticsDashboard": "统计看板" } } }, diff --git a/linkis-web/src/apps/linkis/module/statisticsDashboard/index.js b/linkis-web/src/apps/linkis/module/statisticsDashboard/index.js new file mode 100644 index 0000000000..1bec7a05b4 --- /dev/null +++ b/linkis-web/src/apps/linkis/module/statisticsDashboard/index.js @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +export default { + name: 'StatisticsDashboard', + dispatchs: { + Workbench: ['add'], + }, + component: () => import('./index.vue'), +}; diff --git a/linkis-web/src/apps/linkis/module/statisticsDashboard/index.scss b/linkis-web/src/apps/linkis/module/statisticsDashboard/index.scss new file mode 100644 index 0000000000..3a042ec069 --- /dev/null +++ b/linkis-web/src/apps/linkis/module/statisticsDashboard/index.scss @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '@/common/style/variables.scss'; +.progress-wrap { + position: $relative; + height: 20px; + display: flex; + align-items: center; + font-size: 10px; + background: $background-color-base; + border-radius: 10px; + .progress-busy { + background: $success-color; + height: 100%; + border-radius: 10px; + position: $absolute; + z-index: 0; + } + .progress-label { + width: 100%; + z-index: 1; + } +} +.global-history { + position: $relative; + height: 100%; + overflow-x: hidden; + .global-history-searchbar { + position: $relative; + .float-right { + position: $absolute; + right: 10px; + } + } + .ivu-form { + display: flex; + .ivu-form-item { + margin-right: 4px; + margin-bottom: 10px; + .ivu-form-item-content { + display: flex !important; + } + } + .ivu-input-with-suffix { + padding-right: 7px; + } + } + .global-history-table { + display: flex; + justify-content: center; + align-items: center; + margin-top: 16px; + .ivu-table th { + background-color: $table-thead-blue-bg; + color: $body-background; + } + } + .global-history-loading { + animation: ani-demo-spin 1s linear infinite; + } + .divider { + margin-top: 7px; + height: 20px; + } + .global-history-page { + margin-top: 10px; + width: 100%; + text-align: center; + } +} +.datepicker { + .ivu-picker-panel-body { + background-color: $background-color-white; + } + .ivu-icon { + display: none; + } +} +.workbench-log-view { + height: calc(100% - 76px) !important; + .log-tools { + height: 36px; + line-height: 36px; + padding-left: 10px; + background: $background-color-base; + position: $relative; + border-bottom: 2px solid $border-color-base; + overflow: hidden; + margin-bottom: -2px; + .log-tools-control { + display: inline-block; + position: $absolute; + top: 2px; + .log-tabs { + display: inline-block; + position: $absolute; + } + .log-search { + width: 100px; + position: $absolute; + left: 350px; + top: 5px; + font-size: $font-size-small; + } + .err-badge { + background: $error-color !important; + } + .warn-badge { + background: $yellow-color !important; + } + } + } +} + +.render-btn { + padding: 1px 0 2px 0 !important; +} +.pie-content{ + width: 100%; + height: 280px; + display: flex; + justify-content: space-around; + overflow:hidden; + min-width: 1000px; + padding:10px 0; + .pie-chart{ + width: 42%; + min-width: 500px; + max-width: 700px; + height: 100%; + } +} +.global-history-title{ + font-size: 16px; + color: #6379bb; +} +::v-deep .we-table-wrap{ + border: 1px solid #dcdee2; +} +::v-deep .we-table-wrap .we-table{ + border:none +} \ No newline at end of file diff --git a/linkis-web/src/apps/linkis/module/statisticsDashboard/index.vue b/linkis-web/src/apps/linkis/module/statisticsDashboard/index.vue new file mode 100644 index 0000000000..16f2c09a6e --- /dev/null +++ b/linkis-web/src/apps/linkis/module/statisticsDashboard/index.vue @@ -0,0 +1,786 @@ + + + + + diff --git a/linkis-web/src/apps/linkis/module/statisticsDashboard/statisticsDashboard.vue b/linkis-web/src/apps/linkis/module/statisticsDashboard/statisticsDashboard.vue new file mode 100644 index 0000000000..2600cbf868 --- /dev/null +++ b/linkis-web/src/apps/linkis/module/statisticsDashboard/statisticsDashboard.vue @@ -0,0 +1,441 @@ + + + + + + diff --git a/linkis-web/src/apps/linkis/router.js b/linkis-web/src/apps/linkis/router.js index 9aa3b05a32..70ea0b0454 100644 --- a/linkis-web/src/apps/linkis/router.js +++ b/linkis-web/src/apps/linkis/router.js @@ -319,6 +319,26 @@ export default [ publicPage: true, }, }, + { + name: 'statisticsDashboard', + path: 'statisticsDashboard', + component: () => + import('./module/statisticsDashboard/index.vue'), + meta: { + title: 'statisticsDashboard', + publicPage: true, + }, + }, + { + name: 'statisticsDashboardDetail', + path: 'statisticsDashboardDetail', + component: () => + import('./module/statisticsDashboard/statisticsDashboard.vue'), + meta: { + title: 'statisticsDashboardHistory', + publicPage: true, + }, + }, ], }, ] diff --git a/linkis-web/src/apps/linkis/view/linkis/index.vue b/linkis-web/src/apps/linkis/view/linkis/index.vue index 07b792801c..6530358eea 100644 --- a/linkis-web/src/apps/linkis/view/linkis/index.vue +++ b/linkis-web/src/apps/linkis/view/linkis/index.vue @@ -187,6 +187,13 @@ export default { ), showSubMenu: true, }, + { + key: '1-13', + name: this.$t( + 'message.linkis.sideNavList.function.children.statisticsDashboard' + ), + path: '/console/statisticsDashboard', + }, ], }, datasourceNavList: {