diff --git a/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java b/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java new file mode 100644 index 0000000..fc50289 --- /dev/null +++ b/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java @@ -0,0 +1,96 @@ +package com.usthe.manager.controller; + +import com.usthe.common.entity.dto.Message; +import com.usthe.manager.pojo.entity.Monitor; +import com.usthe.manager.service.MonitorService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Predicate; + +import java.util.HashSet; +import java.util.List; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +/** + * 监控管理批量API + * @author tom + * @date 2021/12/1 20:43 + */ +@Api(tags = "监控列表API") +@RestController +@RequestMapping(path = "/monitors", produces = {APPLICATION_JSON_VALUE}) +public class MonitorsController { + + @Autowired + private MonitorService monitorService; + + @GetMapping + @ApiOperation(value = "查询监控列表", notes = "根据查询过滤项获取监控信息列表") + public ResponseEntity>> getMonitors( + @ApiParam(value = "监控ID", example = "6565463543") @RequestParam(required = false) List ids, + @ApiParam(value = "监控类型", example = "linux") @RequestParam(required = false) String app, + @ApiParam(value = "监控名称,模糊查询", example = "linux-127.0.0.1") @RequestParam(required = false) String name, + @ApiParam(value = "监控Host,模糊查询", example = "127.0.0.1") @RequestParam(required = false) String host, + @ApiParam(value = "排序字段,默认id", example = "name") @RequestParam(defaultValue = "id") String sort, + @ApiParam(value = "排序方式,asc:升序,desc:降序", example = "asc") @RequestParam(defaultValue = "asc") String order, + @ApiParam(value = "列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex, + @ApiParam(value = "列表分页数量", example = "10") @RequestParam(defaultValue = "8") int pageSize) { + + Specification specification = (root, query, criteriaBuilder) -> { + Predicate predicate = criteriaBuilder.conjunction(); + if (ids != null && !ids.isEmpty()) { + CriteriaBuilder.In inPredicate= criteriaBuilder.in(root.get("id")); + for (long id : ids) { + inPredicate.value(id); + } + predicate = criteriaBuilder.and(inPredicate); + } + if (app != null && !"".equals(app)) { + Predicate predicateApp = criteriaBuilder.equal(root.get("app"), app); + predicate = criteriaBuilder.and(predicateApp); + } + if (name != null && !"".equals(name)) { + Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%"); + predicate = criteriaBuilder.and(predicateName); + } + if (host != null && !"".equals(host)) { + Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + host + "%"); + predicate = criteriaBuilder.and(predicateHost); + } + return predicate; + }; + // 分页是必须的 + Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort)); + PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp); + Page monitorPage = monitorService.getMonitors(specification, pageRequest); + Message> message = new Message<>(monitorPage); + return ResponseEntity.ok(message); + } + + @DeleteMapping + @ApiOperation(value = "批量删除监控", notes = "根据监控ID列表批量删除监控项") + public ResponseEntity> deleteMonitors( + @ApiParam(value = "监控IDs", example = "6565463543") @RequestParam(required = false) List ids + ) { + if (ids != null && !ids.isEmpty()) { + monitorService.deleteMonitors(new HashSet<>(ids)); + } + Message message = new Message<>(); + return ResponseEntity.ok(message); + } +} diff --git a/manager/src/main/java/com/usthe/manager/dao/MonitorDao.java b/manager/src/main/java/com/usthe/manager/dao/MonitorDao.java index 25ab71e..8ce222b 100644 --- a/manager/src/main/java/com/usthe/manager/dao/MonitorDao.java +++ b/manager/src/main/java/com/usthe/manager/dao/MonitorDao.java @@ -2,13 +2,30 @@ package com.usthe.manager.dao; import com.usthe.manager.pojo.entity.Monitor; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.List; +import java.util.Set; /** * AuthResources 数据库操作 * @author tomsun28 * @date 2021/11/14 11:24 */ -public interface MonitorDao extends JpaRepository { +public interface MonitorDao extends JpaRepository, JpaSpecificationExecutor { + /** + * 根据监控ID列表删除监控 + * @param monitorIds 监控ID列表 + */ + void deleteAllByIdIn(Set monitorIds); + + /** + * 根据监控ID列表查询监控 + * @param monitorIds 监控ID列表 + * @return 监控列表 + */ + List findMonitorsByIdIn(Set monitorIds); + } diff --git a/manager/src/main/java/com/usthe/manager/service/MonitorService.java b/manager/src/main/java/com/usthe/manager/service/MonitorService.java index 1031f54..9592690 100644 --- a/manager/src/main/java/com/usthe/manager/service/MonitorService.java +++ b/manager/src/main/java/com/usthe/manager/service/MonitorService.java @@ -4,8 +4,12 @@ import com.usthe.manager.pojo.dto.MonitorDto; import com.usthe.manager.pojo.entity.Monitor; import com.usthe.manager.pojo.entity.Param; import com.usthe.manager.support.exception.MonitorDetectException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; import java.util.List; +import java.util.Set; /** * 监控管理服务 @@ -54,6 +58,13 @@ public interface MonitorService { */ void deleteMonitor(long id) throws RuntimeException; + /** + * 批量删除监控 + * @param ids 监控ID + * @throws RuntimeException 删除过程中异常抛出 + */ + void deleteMonitors(Set ids) throws RuntimeException; + /** * 获取监控信息 * @param id 监控ID @@ -61,4 +72,12 @@ public interface MonitorService { * @throws RuntimeException 查询过程中异常抛出 */ MonitorDto getMonitor(long id) throws RuntimeException; + + /** + * 动态条件查询 + * @param specification 查询条件 + * @param pageRequest 分页参数 + * @return 查询结果 + */ + Page getMonitors(Specification specification, PageRequest pageRequest); } diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java index 93d1ec8..7c179a5 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java @@ -21,12 +21,16 @@ import com.usthe.manager.support.exception.MonitorDetectException; import com.usthe.scheduler.JobScheduling; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; /** @@ -166,10 +170,20 @@ public class MonitorServiceImpl implements MonitorService { break; case "password": // 明文密码需加密传输存储 - String value = param.getValue(); - if (!AesUtil.isCiphertext(value)) { - value = AesUtil.aesEncode(value); - param.setValue(value); + String passwordValue = param.getValue(); + if (!AesUtil.isCiphertext(passwordValue)) { + passwordValue = AesUtil.aesEncode(passwordValue); + param.setValue(passwordValue); + } + break; + case "boolean": + // boolean校验 + String booleanValue = param.getValue(); + try { + Boolean.parseBoolean(booleanValue); + } catch (Exception e) { + throw new IllegalArgumentException("Params field " + field + " value " + + booleanValue + " is invalid boolean value."); } break; // todo 更多参数定义与实际值格式校验 @@ -236,6 +250,19 @@ public class MonitorServiceImpl implements MonitorService { } } + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteMonitors(Set ids) throws RuntimeException { + List monitors = monitorDao.findMonitorsByIdIn(ids); + if (monitors != null) { + monitorDao.deleteAll(monitors); + paramDao.deleteParamsByMonitorIdIn(ids); + for (Monitor monitor : monitors) { + jobScheduling.cancelAsyncCollectJob(monitor.getJobId()); + } + } + } + @Override @Transactional(readOnly = true) public MonitorDto getMonitor(long id) throws RuntimeException { @@ -250,4 +277,9 @@ public class MonitorServiceImpl implements MonitorService { return null; } } + + @Override + public Page getMonitors(Specification specification, PageRequest pageRequest) { + return monitorDao.findAll(specification, pageRequest); + } } diff --git a/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java b/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java index cd172a5..777061c 100644 --- a/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java +++ b/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java @@ -129,7 +129,7 @@ public class GlobalExceptionHandler { if (log.isDebugEnabled()) { log.debug("[input argument not valid happen]-{}", errorMessage, e); } - Message message = Message.builder().msg(errorMessage.toString()).build(); + Message message = Message.builder().msg(errorMessage.toString()).code(PARAM_INVALID).build(); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(message); } @@ -146,7 +146,7 @@ public class GlobalExceptionHandler { errorMessage = exception.getMessage(); } log.warn("[scheduler warning]-{}", errorMessage); - Message message = Message.builder().msg(errorMessage).build(); + Message message = Message.builder().msg(errorMessage).code(MONITOR_CONFLICT).build(); return ResponseEntity.status(HttpStatus.CONFLICT).body(message); } @@ -163,7 +163,7 @@ public class GlobalExceptionHandler { errorMessage = exception.getMessage(); } log.warn("[database error happen]-{}", errorMessage, exception); - Message message = Message.builder().msg(errorMessage).build(); + Message message = Message.builder().msg(errorMessage).code(MONITOR_CONFLICT).build(); return ResponseEntity.status(HttpStatus.CONFLICT).body(message); } @@ -197,7 +197,7 @@ public class GlobalExceptionHandler { errorMessage = exception.getMessage(); } log.error("[monitor]-[unknown error happen]-{}", errorMessage, exception); - Message message = Message.builder().msg(errorMessage).build(); + Message message = Message.builder().msg(errorMessage).code(MONITOR_CONFLICT).build(); return ResponseEntity.status(HttpStatus.CONFLICT).body(message); } diff --git a/manager/src/main/resources/define/param/A-example.yml b/manager/src/main/resources/define/param/A-example.yml index 755df76..501ea7e 100644 --- a/manager/src/main/resources/define/param/A-example.yml +++ b/manager/src/main/resources/define/param/A-example.yml @@ -32,4 +32,4 @@ param: required: false # 当type为boolean时,前端用switch展示开关 # 当type为radio单选框,checkbox复选框时,option表示可选项值列表 - option: Yes,No \ No newline at end of file + # option: Yes,No \ No newline at end of file diff --git a/web-app/src/app/core/interceptor/default.interceptor.ts b/web-app/src/app/core/interceptor/default.interceptor.ts index a4fe3f7..2bcfcac 100644 --- a/web-app/src/app/core/interceptor/default.interceptor.ts +++ b/web-app/src/app/core/interceptor/default.interceptor.ts @@ -4,7 +4,7 @@ import { HttpHandler, HttpHeaders, HttpInterceptor, - HttpRequest, + HttpRequest, HttpResponse, HttpResponseBase } from '@angular/common/http'; import { Injectable, Injector } from '@angular/core'; @@ -27,6 +27,7 @@ const CODE_MESSAGE: { [key: number]: string } = { 403: '用户得到授权,但是访问是被禁止的。', 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。', 406: '请求的格式不可得。', + 409: '请求与服务器端目标资源的当前状态相冲突', 410: '请求的资源被永久删除,且不会再得到的。', 422: '当创建一个对象时,发生一个验证错误。', 500: '服务器发生错误,请检查服务器。', @@ -63,9 +64,9 @@ export class DefaultInterceptor implements HttpInterceptor { } private checkStatus(ev: HttpResponseBase): void { - if (ev.status >= 200 && ev.status < 500) { - return; - } + // if (ev.status >= 200 && ev.status < 500) { + // return; + // } const errorText = CODE_MESSAGE[ev.status] || ev.statusText; this.notification.error(`抱歉服务器繁忙 ${ev.status}: ${ev.url}`, errorText); } @@ -158,27 +159,40 @@ export class DefaultInterceptor implements HttpInterceptor { const newReq = req.clone({ url, setHeaders: this.fillHeaders(req.headers) }); return next.handle(newReq).pipe( mergeMap(httpEvent => { + if (httpEvent instanceof HttpResponseBase) { - // 处理token过期自动刷新 - switch (httpEvent.status) { - case 401: - if (this.refreshTokenEnabled) { - return this.tryRefreshToken(httpEvent, req, next); - } - this.toLogin(); - break; - case 403 | 404 | 500: - this.goTo(`/exception/${httpEvent.status}?url=${req.urlWithParams}`); - break; - default: - break; - } + // todo 处理成功状态响应 + return of(httpEvent); } else { return of(httpEvent); } }), catchError((err: HttpErrorResponse) => { + // 处理失败响应,处理token过期自动刷新 + switch (err.status) { + case 401: + if (this.refreshTokenEnabled) { + return this.tryRefreshToken(err, req, next); + } + this.toLogin(); + break; + case 403: + case 404: + case 500: + this.goTo(`/exception/${err.status}?url=${req.urlWithParams}`); + break; + case 400: + let resp = new HttpResponse({ + body: err.error, + headers: err.headers, + status: err.status, + statusText: err.statusText + }); + return of(resp); + default: + break; + } this.checkStatus(err); console.warn(`${err.status} == ${err.message}`) return throwError(err); diff --git a/web-app/src/app/pojo/Message.ts b/web-app/src/app/pojo/Message.ts index 0e42366..8f4a641 100644 --- a/web-app/src/app/pojo/Message.ts +++ b/web-app/src/app/pojo/Message.ts @@ -1,5 +1,5 @@ -export class Message { - data: any; +export class Message { + data!: T; msg!: string; code: number = 0; } diff --git a/web-app/src/app/pojo/Monitor.ts b/web-app/src/app/pojo/Monitor.ts index d58c767..5a56007 100644 --- a/web-app/src/app/pojo/Monitor.ts +++ b/web-app/src/app/pojo/Monitor.ts @@ -3,7 +3,7 @@ export class Monitor { name!: string; app!: string; host!: string; - intervals!: number; + intervals: number = 600; status!: number; description!: string; creator!: string; diff --git a/web-app/src/app/pojo/Page.ts b/web-app/src/app/pojo/Page.ts new file mode 100644 index 0000000..246b7f9 --- /dev/null +++ b/web-app/src/app/pojo/Page.ts @@ -0,0 +1,14 @@ + +export class Page { + content!: T[]; + // 集合总页数 + totalPages!: number; + // 集合总数 + totalElements!: number; + // 查询的pageSize + size!: number; + // 查询的pageIndex,从0开始 + number!: number; + // 当前页的集合数量 + numberOfElements!: number; +} diff --git a/web-app/src/app/pojo/Param.ts b/web-app/src/app/pojo/Param.ts index 91ca677..67d77c9 100644 --- a/web-app/src/app/pojo/Param.ts +++ b/web-app/src/app/pojo/Param.ts @@ -1,6 +1,6 @@ export class Param { id!: number; - field: string | undefined; + field!: string; type: number | undefined; - value: string | undefined; + value: any; } diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html index 419e5e1..57b9243 100644 --- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html @@ -1 +1,141 @@ -

monitor-modify works!

+ + + + + + + + + + + 监控列表 + + + + + 修改 {{monitor.app}} 监控 + + + + + +
+
+ + 监控Host + + + + + + 监控名称 + + + + + + + + + {{paramDefine.name}} + + + + + + {{paramDefine.name}} + + + + + + + + + + + + {{paramDefine.name}} + + + + + + {{paramDefine.name}} + + + + + + + + + + + 采集间隔 + + + + + + + + 启动探测 + + + + + + + 描述备注 + + + + + + + +
+
+ + + +
+
+
+
+
+ diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts index 0e2151c..869063e 100644 --- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts @@ -1,4 +1,15 @@ import { Component, OnInit } from '@angular/core'; +import {switchMap} from "rxjs/operators"; +import {ActivatedRoute, ParamMap, Router} from "@angular/router"; +import {Param} from "../../../pojo/Param"; +import {AppDefineService} from "../../../service/app-define.service"; +import {MonitorService} from "../../../service/monitor.service"; +import {NzNotificationService} from "ng-zorro-antd/notification"; +import {ParamDefine} from "../../../pojo/ParamDefine"; +import {Monitor} from "../../../pojo/Monitor"; +import {FormGroup} from "@angular/forms"; +import {Message} from "../../../pojo/Message"; +import {throwError} from "rxjs"; @Component({ selector: 'app-monitor-modify', @@ -8,9 +19,124 @@ import { Component, OnInit } from '@angular/core'; }) export class MonitorEditComponent implements OnInit { - constructor() { } + constructor(private appDefineSvc: AppDefineService, + private monitorSvc: MonitorService, + private route: ActivatedRoute, + private router: Router, + private notifySvc: NzNotificationService,) { } + + paramDefines!: ParamDefine[]; + params!: Param[]; + paramValueMap = new Map(); + monitor = new Monitor(); + profileForm: FormGroup = new FormGroup({}); + detected: boolean = true; + passwordVisible: boolean = false; + isSpinning:boolean = false ngOnInit(): void { + this.route.paramMap.pipe( + switchMap((paramMap: ParamMap) => { + let id = paramMap.get("monitorId"); + this.monitor.id = Number(id); + // 查询监控信息 + return this.monitorSvc.getMonitor(this.monitor.id); + }) + ).pipe(switchMap((message: Message) => { + if (message.code === 0) { + this.monitor = message.data.monitor; + if (message.data.params != null) { + message.data.params.forEach((item: Param) => { + this.paramValueMap.set(item.field, item) + }); + } + this.params = message.data.params; + } else { + console.warn(message.msg); + this.notifySvc.error("查询此监控异常", message.msg); + return throwError("查询此监控异常"); + } + return this.appDefineSvc.getAppParamsDefine(this.monitor.app); + })).subscribe(message => { + if (message.code === 0) { + this.paramDefines = message.data; + this.params = []; + this.paramDefines.forEach(define => { + let param = this.paramValueMap.get(define.field); + if (param === undefined) { + param = new Param(); + param.type = define.type === "number" ? 0 : 1; + if (define.type === "boolean") { + param.value = false; + } + if (param.field === "host") { + param.value = this.monitor.host; + } + } + this.params.push(param); + }) + } else { + console.warn(message.msg); + } + }); + } + + onSubmit() { + // todo 暂时单独设置host属性值 + this.params.forEach(param => { + if (param.field === "host") { + param.value = this.monitor.host; + } + }); + let addMonitor = { + "detected": this.detected, + "monitor": this.monitor, + "params": this.params + }; + this.isSpinning = true; + this.monitorSvc.newMonitor(addMonitor) + .subscribe(message => { + this.isSpinning = false; + if (message.code === 0) { + this.notifySvc.success("新增监控成功", ""); + this.router.navigateByUrl("/monitors") + } else { + this.notifySvc.error("新增监控失败", message.msg); + }}, + error => { + this.isSpinning = false + } + ) + } + + onDetect() { + // todo 暂时单独设置host属性值 + this.params.forEach(param => { + if (param.field === "host") { + param.value = this.monitor.host; + } + }); + let detectMonitor = { + "detected": this.detected, + "monitor": this.monitor, + "params": this.params + }; + this.isSpinning = true; + this.monitorSvc.newMonitor(detectMonitor) + .subscribe(message => { + this.isSpinning = false; + if (message.code === 0) { + this.notifySvc.success("探测成功", ""); + } else { + this.notifySvc.error("探测失败", message.msg); + } + }) + } + + onCancel() { + let app = this.monitor.app; + app = app ? app : ''; + this.router.navigateByUrl(`/monitors?app=${app}`) } } diff --git a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html index b077d09..4d45890 100644 --- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html +++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html @@ -1,2 +1,85 @@ - -

monitor-list works!

+ + + + + + + + + + 监控列表 {{app?app.toUpperCase() : ""}} + + + + + + + + + + + + + + + 监控名称 + 监控状态 + 监控主机Host + 监控类型 + 最新修改时间 + 操作 + + + + + + {{ data.name }} + {{ data.status }} + {{ data.host }} + {{ data.app }} + {{ data.gmtUpdate? data.gmtUpdate : data.gmtCreate }} + + + + + + + + + + + + 总量 {{ pageTotal }} + diff --git a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts index 94c3810..21770b1 100644 --- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts +++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts @@ -1,4 +1,10 @@ import { Component, OnInit } from '@angular/core'; +import {ActivatedRoute, Router} from "@angular/router"; +import {MonitorService} from "../../../service/monitor.service"; +import {Monitor} from "../../../pojo/Monitor"; +import {Page} from "../../../pojo/Page"; +import {NzModalService} from "ng-zorro-antd/modal"; +import {NzNotificationService} from "ng-zorro-antd/notification"; @Component({ selector: 'app-monitor-list', @@ -8,9 +14,143 @@ import { Component, OnInit } from '@angular/core'; }) export class MonitorListComponent implements OnInit { - constructor() { } + constructor(private route: ActivatedRoute, + private router: Router, + private modal: NzModalService, + private notifySvc: NzNotificationService, + private monitorSvc: MonitorService) { } + + app!: string; + pageIndex: number = 1; + pageSize: number = 8; + pageTotal: number = 0; + monitors!: Monitor[]; + pageMonitors!: Page; + tableLoading: boolean = true; + checkedMonitorIds = new Set(); ngOnInit(): void { + this.route.queryParamMap + .subscribe(paramMap => { + this.app = paramMap.get("app") || ''; + this.initMonitorTable(); + }); } + initMonitorTable() { + let monitorInit$ = this.monitorSvc.getMonitors(this.app, this.pageIndex - 1, this.pageSize) + .subscribe(message => { + this.tableLoading = false; + if (message.code === 0) { + this.pageMonitors = message.data; + this.monitors = this.pageMonitors.content; + this.pageIndex = this.pageMonitors.number + 1; + this.pageTotal = this.pageMonitors.totalElements; + } else { + console.warn(message.msg); + } + monitorInit$.unsubscribe(); + }, + error => { + this.tableLoading = false; + monitorInit$.unsubscribe(); + }); + } + + onEditOneMonitor(monitorId: number) { + if (monitorId == null) { + this.notifySvc.warning("未选中任何待编辑项!",""); + return; + } + this.router.navigateByUrl(`/monitors/${monitorId}/edit`); + // 参数样例 + // this.router.navigate(['/monitors/new'],{queryParams: {app: "linux"}}); + } + + onEditMonitor() { + // 编辑时只能选中一个监控 + if (this.checkedMonitorIds == null || this.checkedMonitorIds.size === 0) { + this.notifySvc.warning("未选中任何待编辑项!",""); + return; + } + if (this.checkedMonitorIds.size > 1) { + this.notifySvc.warning("只能对一个选中项进行编辑!",""); + return; + } + let monitorId = 0; + this.checkedMonitorIds.forEach(item => monitorId = item); + this.router.navigateByUrl(`/monitors/${monitorId}/edit`); + } + + onDeleteOneMonitor(monitorId: number) { + let monitors = new Set(); + monitors.add(monitorId); + this.modal.confirm({ + nzTitle: '请确认是否删除!', + nzOkText: '确定', + nzCancelText: '取消', + nzOkDanger: true, + nzOkType: "primary", + nzOnOk: () => this.deleteMonitors(monitors) + }); + } + + onDeleteMonitors() { + if (this.checkedMonitorIds == null || this.checkedMonitorIds.size === 0) { + this.notifySvc.warning("未选中任何待删除项!",""); + return; + } + this.modal.confirm({ + nzTitle: '请确认是否批量删除!', + nzOkText: '确定', + nzCancelText: '取消', + nzOkDanger: true, + nzOkType: "primary", + nzOnOk: () => this.deleteMonitors(this.checkedMonitorIds) + }); + } + + + deleteMonitors(monitors: Set) { + if (monitors == null || monitors.size == 0) { + this.notifySvc.warning("未选中任何待删除项!",""); + return; + } + const deleteMonitors$ = this.monitorSvc.deleteMonitors(monitors) + .subscribe(message => { + deleteMonitors$.unsubscribe(); + if (message.code === 0) { + this.notifySvc.success("删除成功!", ""); + this.initMonitorTable(); + } else { + this.notifySvc.error("删除失败!", message.msg); + } + }, + error => { + deleteMonitors$.unsubscribe(); + this.notifySvc.error("删除失败!", error.msg) + } + ); + } + + + // begin: 列表多选逻辑 + checkedAll: boolean = false; + onAllChecked(checked: boolean) { + if (checked) { + this.monitors.forEach(monitor => this.checkedMonitorIds.add(monitor.id)); + } else { + this.checkedMonitorIds.clear(); + } + } + onItemChecked(monitorId: number, checked: boolean) { + if (checked) { + this.checkedMonitorIds.add(monitorId); + } else { + this.checkedMonitorIds.delete(monitorId); + } + } + // end: 列表多选逻辑 + + } diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html index 2c613ad..107d785 100644 --- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html @@ -18,119 +18,124 @@ -
-
- - 监控Host - - - - - - 监控名称 - - - - + +
+ + + 监控Host + + + + + + 监控名称 + + + + - + - - {{paramDefine.name}} - - - - + + {{paramDefine.name}} + + + + - {{paramDefine.name}} - - - - {{paramDefine.name}} + + + + + + + + + + + + {{paramDefine.name}} + + + - - - - - + [nzMin]="-1000" + [nzMax]="65535" + [nzStep]="1" + [nzPlaceHolder]="paramDefine.name" + [name]="paramDefine.field" [id]="paramDefine.field" + > + + {{paramDefine.name}} + + + + - {{paramDefine.name}} - - - - + - {{paramDefine.name}} - - - - + - + + 采集间隔 + + + + + - + + 启动探测 + + + + - - 采集间隔 - - - - + + 描述备注 + + + + + + - - 启动探测 - - - - - - - 描述备注 - - - - - - - -
-
- - - +
+
+ + + +
-
- -
+ +
+
+ diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts index 69b80a4..c9251fc 100644 --- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts @@ -23,7 +23,8 @@ export class MonitorNewComponent implements OnInit { monitor!: Monitor; profileForm: FormGroup = new FormGroup({}); detected: boolean = true; - passwordVisible!: boolean; + passwordVisible: boolean = false; + isSpinning:boolean = false constructor(private appDefineSvc: AppDefineService, private monitorSvc: MonitorService, private route: ActivatedRoute, @@ -48,6 +49,9 @@ export class MonitorNewComponent implements OnInit { let param = new Param(); param.field = define.field; param.type = define.type === "number" ? 0 : 1; + if (define.type === "boolean") { + param.value = false; + } this.params.push(param); }) } else { @@ -58,20 +62,61 @@ export class MonitorNewComponent implements OnInit { } onSubmit() { + // todo 暂时单独设置host属性值 + this.params.forEach(param => { + if (param.field === "host") { + param.value = this.monitor.host; + } + }); let addMonitor = { "detected": this.detected, "monitor": this.monitor, "params": this.params }; + this.isSpinning = true; this.monitorSvc.newMonitor(addMonitor) .subscribe(message => { + this.isSpinning = false; if (message.code === 0) { this.notifySvc.success("新增监控成功", ""); this.router.navigateByUrl("/monitors") } else { this.notifySvc.error("新增监控失败", message.msg); + }}, + error => { + this.isSpinning = false } - }) + ) + } + + onDetect() { + // todo 暂时单独设置host属性值 + this.params.forEach(param => { + if (param.field === "host") { + param.value = this.monitor.host; + } + }); + let detectMonitor = { + "detected": this.detected, + "monitor": this.monitor, + "params": this.params + }; + this.isSpinning = true; + this.monitorSvc.newMonitor(detectMonitor) + .subscribe(message => { + this.isSpinning = false; + if (message.code === 0) { + this.notifySvc.success("探测成功", ""); + } else { + this.notifySvc.error("探测失败", message.msg); + } + }) + } + + onCancel() { + let app = this.monitor.app; + app = app ? app : ''; + this.router.navigateByUrl(`/monitors?app=${app}`) } } diff --git a/web-app/src/app/routes/monitor/monitor-routing.module.ts b/web-app/src/app/routes/monitor/monitor-routing.module.ts index 9fb67f9..9479a30 100644 --- a/web-app/src/app/routes/monitor/monitor-routing.module.ts +++ b/web-app/src/app/routes/monitor/monitor-routing.module.ts @@ -6,7 +6,7 @@ import {MonitorEditComponent} from "./monitor-edit/monitor-edit.component"; import {MonitorDetailComponent} from "./monitor-detail/monitor-detail.component"; const routes: Routes = [ - { path: '', component: MonitorNewComponent }, + { path: '', component: MonitorListComponent }, { path: 'new', component: MonitorNewComponent }, { path: ':monitorId/edit', component: MonitorEditComponent }, { path: ':monitorId', component: MonitorDetailComponent }, diff --git a/web-app/src/app/service/app-define.service.ts b/web-app/src/app/service/app-define.service.ts index 0d229ba..9bc4272 100644 --- a/web-app/src/app/service/app-define.service.ts +++ b/web-app/src/app/service/app-define.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import {Message} from "../pojo/Message"; import {Observable} from "rxjs"; +import {ParamDefine} from "../pojo/ParamDefine"; @Injectable({ @@ -11,12 +12,12 @@ export class AppDefineService { constructor(private http : HttpClient) { } - public getAppParamsDefine(app: string | undefined | null) : Observable { + public getAppParamsDefine(app: string | undefined | null) : Observable> { if (app === null || app === undefined) { console.log("getAppParamsDefine app can not null"); } const paramDefineUri = `/apps/${app}/params`; - return this.http.get(paramDefineUri); + return this.http.get>(paramDefineUri); } } diff --git a/web-app/src/app/service/monitor.service.ts b/web-app/src/app/service/monitor.service.ts index 2d75a54..faa57cc 100644 --- a/web-app/src/app/service/monitor.service.ts +++ b/web-app/src/app/service/monitor.service.ts @@ -1,9 +1,13 @@ import { Injectable } from '@angular/core'; -import {HttpClient} from "@angular/common/http"; +import {HttpClient, HttpParams} from "@angular/common/http"; import {Observable} from "rxjs"; import {Message} from "../pojo/Message"; +import {Page} from "../pojo/Page"; +import {Monitor} from "../pojo/Monitor"; const monitor_uri = "/monitor"; +const monitors_uri = "/monitors"; +const detect_monitor_uri = "/monitor/detect" @Injectable({ providedIn: 'root' @@ -12,8 +16,49 @@ export class MonitorService { constructor(private http : HttpClient) { } - public newMonitor(body: any) : Observable { - return this.http.post(monitor_uri, body); + public newMonitor(body: any) : Observable> { + return this.http.post>(monitor_uri, body); + } + + public editMonitor(body: any) : Observable> { + return this.http.put>(monitor_uri, body); + } + + public deleteMonitor(monitorId: number) : Observable> { + return this.http.delete>(`${monitor_uri}/${monitorId}`); + } + + public deleteMonitors(monitorIds: Set) : Observable> { + let httpParams = new HttpParams(); + monitorIds.forEach(monitorId => { + // 注意HttpParams是不可变对象 需要保存set后返回的对象为最新对象 + httpParams = httpParams.set('ids', monitorId); + }) + const options = { params: httpParams }; + return this.http.delete>(monitors_uri, options); + } + + public detectMonitor(body: any) : Observable> { + return this.http.post>(detect_monitor_uri, body); + } + + public getMonitor(monitorId: number) : Observable> { + return this.http.get>(`${monitor_uri}/${monitorId}`); + } + + public getMonitors(app: string, pageIndex: number, pageSize: number) : Observable>> { + app = app.trim(); + pageIndex = pageIndex ? 1 : pageIndex; + pageSize = pageSize ? 10 : pageSize; + // 注意HttpParams是不可变对象 需要保存set后返回的对象为最新对象 + let httpParams = new HttpParams(); + httpParams = httpParams.appendAll({ + 'app': app, + 'pageIndex': pageIndex, + 'pageSize': pageSize + }); + const options = { params: httpParams }; + return this.http.get>>(monitors_uri, options); } } diff --git a/web-app/src/assets/tmp/app-data.json b/web-app/src/assets/tmp/app-data.json index b66ef59..b6e53d5 100644 --- a/web-app/src/assets/tmp/app-data.json +++ b/web-app/src/assets/tmp/app-data.json @@ -36,12 +36,12 @@ "children": [ { "text": "http", - "link": "/monitors", + "link": "/monitors?app=http", "i18n": "monitor.app.http" }, { "text": "ping", - "link": "/monitors", + "link": "/monitors?app=ping", "i18n": "monitor.app.ping" }, {