From 86e79681da9c98bd08bd1f2be0c6dbd3f3dd3159 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期六, 07 三月 2026 13:47:31 +0800
Subject: [PATCH] #
---
src/main/java/com/zy/asrs/domain/NotifySendResult.java | 13
src/main/java/com/zy/asrs/domain/param/NotifyResendParam.java | 15
src/main/webapp/views/notifyReport/notifyReport.html | 839 ++++++++++++++++++++++++++++++++
src/main/java/com/zy/asrs/service/NotifyAsyncService.java | 32
src/main/java/com/zy/asrs/utils/NotifyUtils.java | 52 +
src/main/java/com/zy/asrs/domain/vo/NotifyReportVo.java | 109 ++++
src/main/resources/sql/20260307_add_notify_report_menu.sql | 51 +
src/main/java/com/zy/asrs/controller/NotifyReportController.java | 414 +++++++++++++++
src/main/resources/application.yml | 2
9 files changed, 1,505 insertions(+), 22 deletions(-)
diff --git a/src/main/java/com/zy/asrs/controller/NotifyReportController.java b/src/main/java/com/zy/asrs/controller/NotifyReportController.java
new file mode 100644
index 0000000..017c759
--- /dev/null
+++ b/src/main/java/com/zy/asrs/controller/NotifyReportController.java
@@ -0,0 +1,414 @@
+package com.zy.asrs.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.baomidou.mybatisplus.plugins.Page;
+import com.core.annotations.ManagerAuth;
+import com.core.common.Cools;
+import com.core.common.R;
+import com.zy.asrs.domain.NotifyDto;
+import com.zy.asrs.domain.NotifySendResult;
+import com.zy.asrs.domain.param.NotifyResendParam;
+import com.zy.asrs.domain.vo.NotifyReportVo;
+import com.zy.asrs.entity.HttpRequestLog;
+import com.zy.asrs.service.HttpRequestLogService;
+import com.zy.asrs.service.NotifyAsyncService;
+import com.zy.asrs.utils.NotifyUtils;
+import com.zy.common.utils.RedisUtil;
+import com.zy.common.web.BaseController;
+import com.zy.system.entity.Config;
+import com.zy.system.service.ConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@RestController
+public class NotifyReportController extends BaseController {
+
+ @Autowired
+ private RedisUtil redisUtil;
+ @Autowired
+ private NotifyUtils notifyUtils;
+ @Autowired
+ private NotifyAsyncService notifyAsyncService;
+ @Autowired
+ private HttpRequestLogService httpRequestLogService;
+ @Autowired
+ private ConfigService configService;
+
+ @RequestMapping(value = "/notifyReport/summary/auth")
+ @ManagerAuth
+ public R summary() {
+ Map<String, Object> result = new HashMap<>();
+ String notifyEnable = getConfigValue("notifyEnable");
+ String notifyUri = getConfigValue("notifyUri");
+ String notifyUriPath = getConfigValue("notifyUriPath");
+ String endpoint = buildNotifyEndpoint(notifyUri, notifyUriPath);
+
+ result.put("notifyEnable", notifyEnable);
+ result.put("notifyEnable$", "Y".equalsIgnoreCase(notifyEnable) ? "宸插紑鍚�" : "宸插叧闂�");
+ result.put("notifyUri", notifyUri);
+ result.put("notifyUriPath", notifyUriPath);
+ result.put("notifyEndpoint", endpoint);
+ result.put("queueCount", loadQueueRecords(null, null).size());
+ if (!Cools.isEmpty(endpoint)) {
+ result.put("logCount", httpRequestLogService.selectCount(new EntityWrapper<HttpRequestLog>().eq("name", endpoint)));
+ } else {
+ result.put("logCount", 0);
+ }
+ return R.ok(result);
+ }
+
+ @RequestMapping(value = "/notifyReport/queue/list/auth")
+ @ManagerAuth
+ public R queueList(@RequestParam(defaultValue = "1") Integer curr,
+ @RequestParam(defaultValue = "15") Integer limit,
+ @RequestParam(required = false) String notifyType,
+ @RequestParam(required = false) Integer device,
+ @RequestParam(required = false) String taskNo,
+ @RequestParam(required = false) String superTaskNo,
+ @RequestParam(required = false) String msgType,
+ @RequestParam(required = false) String condition) {
+ List<NotifyReportVo> records = loadQueueRecords(notifyType, device);
+ List<NotifyReportVo> filtered = new ArrayList<>();
+ for (NotifyReportVo record : records) {
+ if (!matchesQueue(record, notifyType, device, taskNo, superTaskNo, msgType, condition)) {
+ continue;
+ }
+ filtered.add(record);
+ }
+ filtered.sort(Comparator.comparing(NotifyReportVo::getId, Comparator.nullsLast(Long::compareTo)).reversed());
+ return R.ok(buildPage(curr, limit, filtered));
+ }
+
+ @RequestMapping(value = "/notifyReport/log/list/auth")
+ @ManagerAuth
+ public R logList(@RequestParam(defaultValue = "1") Integer curr,
+ @RequestParam(defaultValue = "15") Integer limit,
+ @RequestParam(required = false) String notifyType,
+ @RequestParam(required = false) Integer device,
+ @RequestParam(required = false) String taskNo,
+ @RequestParam(required = false) String superTaskNo,
+ @RequestParam(required = false) String msgType,
+ @RequestParam(required = false) Integer result,
+ @RequestParam(required = false) String condition) {
+ String endpoint = buildNotifyEndpoint(getConfigValue("notifyUri"), getConfigValue("notifyUriPath"));
+ EntityWrapper<HttpRequestLog> wrapper = new EntityWrapper<>();
+ if (!Cools.isEmpty(endpoint)) {
+ wrapper.eq("name", endpoint);
+ } else {
+ wrapper.like("request", "\"notifyType\"");
+ }
+ if (!Cools.isEmpty(taskNo)) {
+ wrapper.like("request", taskNo);
+ }
+ if (!Cools.isEmpty(superTaskNo)) {
+ wrapper.like("request", superTaskNo);
+ }
+ if (!Cools.isEmpty(msgType)) {
+ wrapper.like("request", msgType);
+ }
+ if (!Cools.isEmpty(notifyType)) {
+ wrapper.like("request", "\"notifyType\":\"" + notifyType + "\"");
+ }
+ if (device != null) {
+ wrapper.like("request", "\"device\":" + device);
+ }
+ if (!Cools.isEmpty(condition)) {
+ wrapper.andNew().like("request", condition).or().like("response", condition);
+ }
+ if (result != null) {
+ wrapper.eq("result", result);
+ }
+ wrapper.orderBy("create_time", false);
+
+ Page<HttpRequestLog> logPage = httpRequestLogService.selectPage(new Page<>(curr, limit), wrapper);
+ Page<NotifyReportVo> resultPage = new Page<>(curr, limit);
+ resultPage.setTotal(logPage.getTotal());
+
+ List<NotifyReportVo> rows = new ArrayList<>();
+ for (HttpRequestLog log : logPage.getRecords()) {
+ rows.add(buildLogVo(log));
+ }
+ resultPage.setRecords(rows);
+ return R.ok(resultPage);
+ }
+
+ @RequestMapping(value = "/notifyReport/resend/auth")
+ @ManagerAuth
+ public R resend(@RequestBody NotifyResendParam param) {
+ if (param == null || Cools.isEmpty(param.getSourceType())) {
+ return R.error("琛ュ彂鍙傛暟涓嶈兘涓虹┖");
+ }
+
+ String notifyUri = getConfigValue("notifyUri");
+ String notifyUriPath = getConfigValue("notifyUriPath");
+ if (Cools.isEmpty(notifyUri) || Cools.isEmpty(notifyUriPath)) {
+ return R.error("璇峰厛閰嶇疆 notifyUri 鍜� notifyUriPath");
+ }
+
+ List<Map<String, Object>> details = new ArrayList<>();
+ int successCount = 0;
+ int failCount = 0;
+
+ if ("queue".equalsIgnoreCase(param.getSourceType())) {
+ if (Cools.isEmpty(param.getRedisKeys())) {
+ return R.error("璇烽�夋嫨瑕佽ˉ鍙戠殑闃熷垪閫氱煡");
+ }
+ for (String redisKey : param.getRedisKeys()) {
+ Map<String, Object> detail = resendQueue(redisKey, notifyUri, notifyUriPath);
+ details.add(detail);
+ if (Boolean.TRUE.equals(detail.get("success"))) {
+ successCount++;
+ } else {
+ failCount++;
+ }
+ }
+ } else if ("log".equalsIgnoreCase(param.getSourceType())) {
+ if (Cools.isEmpty(param.getLogIds())) {
+ return R.error("璇烽�夋嫨瑕佽ˉ鍙戠殑閫氱煡鏃ュ織");
+ }
+ for (Long logId : param.getLogIds()) {
+ Map<String, Object> detail = resendLog(logId, notifyUri, notifyUriPath);
+ details.add(detail);
+ if (Boolean.TRUE.equals(detail.get("success"))) {
+ successCount++;
+ } else {
+ failCount++;
+ }
+ }
+ } else {
+ return R.error("涓嶆敮鎸佺殑琛ュ彂鏉ユ簮");
+ }
+
+ Map<String, Object> result = new HashMap<>();
+ result.put("successCount", successCount);
+ result.put("failCount", failCount);
+ result.put("allSuccess", failCount == 0);
+ result.put("details", details);
+ return R.ok(result);
+ }
+
+ private Page<NotifyReportVo> buildPage(Integer curr, Integer limit, List<NotifyReportVo> records) {
+ Page<NotifyReportVo> page = new Page<>(curr, limit);
+ page.setTotal(records.size());
+
+ int fromIndex = Math.max((curr - 1) * limit, 0);
+ if (fromIndex >= records.size()) {
+ page.setRecords(new ArrayList<>());
+ return page;
+ }
+ int toIndex = Math.min(fromIndex + limit, records.size());
+ page.setRecords(new ArrayList<>(records.subList(fromIndex, toIndex)));
+ return page;
+ }
+
+ private List<NotifyReportVo> loadQueueRecords(String notifyType, Integer device) {
+ Set<String> keys = new LinkedHashSet<>();
+ List<String> notifyTypes = new ArrayList<>();
+ if (!Cools.isEmpty(notifyType)) {
+ notifyTypes.add(notifyType);
+ } else {
+ notifyTypes.addAll(notifyUtils.getSupportedNotifyTypes());
+ }
+
+ for (String item : notifyTypes) {
+ String prefix = notifyUtils.getKeyPrefix(item);
+ if (Cools.isEmpty(prefix)) {
+ continue;
+ }
+
+ String pattern = device == null ? prefix + "*" : prefix + device + "_*";
+ Set matched = redisUtil.keys(pattern);
+ if (matched == null) {
+ continue;
+ }
+ for (Object key : matched) {
+ if (key != null) {
+ keys.add(String.valueOf(key));
+ }
+ }
+ }
+
+ List<NotifyReportVo> rows = new ArrayList<>();
+ for (String key : keys) {
+ Object object = redisUtil.get(key);
+ if (!(object instanceof NotifyDto)) {
+ continue;
+ }
+ rows.add(buildQueueVo(key, (NotifyDto) object));
+ }
+ return rows;
+ }
+
+ private NotifyReportVo buildQueueVo(String redisKey, NotifyDto dto) {
+ NotifyReportVo vo = new NotifyReportVo();
+ vo.setSourceType("queue");
+ vo.setRedisKey(redisKey);
+ vo.setId(dto.getId());
+ vo.setNotifyType(dto.getNotifyType());
+ vo.setDevice(dto.getDevice());
+ vo.setTaskNo(dto.getTaskNo());
+ vo.setSuperTaskNo(dto.getSuperTaskNo());
+ vo.setMsgType(dto.getMsgType());
+ vo.setMsgDesc(dto.getMsgDesc());
+ vo.setData(dto.getData());
+ vo.setFailTimes(dto.getFailTimes());
+ vo.setRetryTimes(dto.getRetryTimes());
+ vo.setRetryTime(dto.getRetryTime());
+ vo.setLastRetryTime(dto.getLastRetryTime());
+ vo.setRequestPayload(JSON.toJSONString(dto));
+ return vo;
+ }
+
+ private NotifyReportVo buildLogVo(HttpRequestLog log) {
+ NotifyReportVo vo = new NotifyReportVo();
+ vo.setSourceType("log");
+ vo.setLogId(log.getId());
+ vo.setCreateTime(log.getCreateTime());
+ vo.setResult(log.getResult());
+ vo.setResponse(log.getResponse());
+ vo.setRequestPayload(log.getRequest());
+
+ NotifyDto dto = parseNotifyDto(log.getRequest());
+ if (dto != null) {
+ vo.setId(dto.getId());
+ vo.setNotifyType(dto.getNotifyType());
+ vo.setDevice(dto.getDevice());
+ vo.setTaskNo(dto.getTaskNo());
+ vo.setSuperTaskNo(dto.getSuperTaskNo());
+ vo.setMsgType(dto.getMsgType());
+ vo.setMsgDesc(dto.getMsgDesc());
+ vo.setData(dto.getData());
+ vo.setFailTimes(dto.getFailTimes());
+ vo.setRetryTimes(dto.getRetryTimes());
+ vo.setRetryTime(dto.getRetryTime());
+ vo.setLastRetryTime(dto.getLastRetryTime());
+ } else {
+ vo.setData(log.getRequest());
+ }
+ return vo;
+ }
+
+ private boolean matchesQueue(NotifyReportVo record, String notifyType, Integer device, String taskNo,
+ String superTaskNo, String msgType, String condition) {
+ if (!Cools.isEmpty(notifyType) && !notifyType.equals(record.getNotifyType())) {
+ return false;
+ }
+ if (device != null && !device.equals(record.getDevice())) {
+ return false;
+ }
+ if (!containsValue(record.getTaskNo(), taskNo)) {
+ return false;
+ }
+ if (!containsValue(record.getSuperTaskNo(), superTaskNo)) {
+ return false;
+ }
+ if (!containsValue(record.getMsgType(), msgType) && !containsValue(record.getMsgDesc(), msgType)) {
+ return false;
+ }
+ if (Cools.isEmpty(condition)) {
+ return true;
+ }
+ return containsValue(record.getTaskNo(), condition)
+ || containsValue(record.getSuperTaskNo(), condition)
+ || containsValue(record.getMsgType(), condition)
+ || containsValue(record.getMsgDesc(), condition)
+ || containsValue(record.getData(), condition)
+ || containsValue(record.getNotifyType$(), condition)
+ || containsValue(record.getRedisKey(), condition);
+ }
+
+ private boolean containsValue(String source, String target) {
+ if (Cools.isEmpty(target)) {
+ return true;
+ }
+ if (Cools.isEmpty(source)) {
+ return false;
+ }
+ return source.contains(target);
+ }
+
+ private Map<String, Object> resendQueue(String redisKey, String notifyUri, String notifyUriPath) {
+ Map<String, Object> detail = new HashMap<>();
+ detail.put("sourceType", "queue");
+ detail.put("redisKey", redisKey);
+
+ Object object = redisUtil.get(redisKey);
+ if (!(object instanceof NotifyDto)) {
+ detail.put("success", false);
+ detail.put("message", "闃熷垪閫氱煡涓嶅瓨鍦ㄦ垨宸茶娑堣垂");
+ return detail;
+ }
+
+ NotifyDto notifyDto = (NotifyDto) object;
+ NotifySendResult result = notifyAsyncService.sendNotifyNow(notifyUri, notifyUriPath, redisKey, notifyDto, true, false);
+ detail.put("success", result.isSuccess());
+ detail.put("message", result.getMessage());
+ detail.put("taskNo", notifyDto.getTaskNo());
+ detail.put("superTaskNo", notifyDto.getSuperTaskNo());
+ detail.put("response", result.getResponse());
+ return detail;
+ }
+
+ private Map<String, Object> resendLog(Long logId, String notifyUri, String notifyUriPath) {
+ Map<String, Object> detail = new HashMap<>();
+ detail.put("sourceType", "log");
+ detail.put("logId", logId);
+
+ HttpRequestLog log = httpRequestLogService.selectById(logId);
+ if (log == null) {
+ detail.put("success", false);
+ detail.put("message", "閫氱煡鏃ュ織涓嶅瓨鍦�");
+ return detail;
+ }
+
+ NotifyDto notifyDto = parseNotifyDto(log.getRequest());
+ if (notifyDto == null) {
+ detail.put("success", false);
+ detail.put("message", "鏃ュ織璇锋眰鎶ユ枃鏃犳硶瑙f瀽涓洪�氱煡瀵硅薄");
+ return detail;
+ }
+
+ NotifySendResult result = notifyAsyncService.sendNotifyNow(notifyUri, notifyUriPath, null, notifyDto, false, false);
+ detail.put("success", result.isSuccess());
+ detail.put("message", result.getMessage());
+ detail.put("taskNo", notifyDto.getTaskNo());
+ detail.put("superTaskNo", notifyDto.getSuperTaskNo());
+ detail.put("response", result.getResponse());
+ return detail;
+ }
+
+ private NotifyDto parseNotifyDto(String request) {
+ if (Cools.isEmpty(request)) {
+ return null;
+ }
+ try {
+ return JSON.parseObject(request, NotifyDto.class);
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
+
+ private String getConfigValue(String code) {
+ Config config = configService.selectOne(new EntityWrapper<Config>().eq("code", code));
+ return config == null ? null : config.getValue();
+ }
+
+ private String buildNotifyEndpoint(String notifyUri, String notifyUriPath) {
+ if (Cools.isEmpty(notifyUri) || Cools.isEmpty(notifyUriPath)) {
+ return null;
+ }
+ return notifyUri + notifyUriPath;
+ }
+}
diff --git a/src/main/java/com/zy/asrs/domain/NotifySendResult.java b/src/main/java/com/zy/asrs/domain/NotifySendResult.java
new file mode 100644
index 0000000..c92adec
--- /dev/null
+++ b/src/main/java/com/zy/asrs/domain/NotifySendResult.java
@@ -0,0 +1,13 @@
+package com.zy.asrs.domain;
+
+import lombok.Data;
+
+@Data
+public class NotifySendResult {
+
+ private boolean success;
+
+ private String response;
+
+ private String message;
+}
diff --git a/src/main/java/com/zy/asrs/domain/param/NotifyResendParam.java b/src/main/java/com/zy/asrs/domain/param/NotifyResendParam.java
new file mode 100644
index 0000000..7c41c13
--- /dev/null
+++ b/src/main/java/com/zy/asrs/domain/param/NotifyResendParam.java
@@ -0,0 +1,15 @@
+package com.zy.asrs.domain.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class NotifyResendParam {
+
+ private String sourceType;
+
+ private List<String> redisKeys;
+
+ private List<Long> logIds;
+}
diff --git a/src/main/java/com/zy/asrs/domain/vo/NotifyReportVo.java b/src/main/java/com/zy/asrs/domain/vo/NotifyReportVo.java
new file mode 100644
index 0000000..1e62426
--- /dev/null
+++ b/src/main/java/com/zy/asrs/domain/vo/NotifyReportVo.java
@@ -0,0 +1,109 @@
+package com.zy.asrs.domain.vo;
+
+import com.core.common.Cools;
+import lombok.Data;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Data
+public class NotifyReportVo {
+
+ private String sourceType;
+
+ private String redisKey;
+
+ private Long logId;
+
+ private Long id;
+
+ private String notifyType;
+
+ private Integer device;
+
+ private String taskNo;
+
+ private String superTaskNo;
+
+ private String msgType;
+
+ private String msgDesc;
+
+ private String data;
+
+ private String requestPayload;
+
+ private Integer failTimes;
+
+ private Integer retryTimes;
+
+ private Integer retryTime;
+
+ private Long lastRetryTime;
+
+ private Date createTime;
+
+ private Integer result;
+
+ private String response;
+
+ public String getNotifyType$() {
+ if (Cools.isEmpty(this.notifyType)) {
+ return "";
+ }
+ if ("Crn".equals(this.notifyType)) {
+ return "鍫嗗灈鏈�";
+ }
+ if ("Devp".equals(this.notifyType)) {
+ return "杈撻�佺嚎";
+ }
+ if ("DualCrn".equals(this.notifyType)) {
+ return "鍙屼几浣嶅爢鍨涙満";
+ }
+ if ("Rgv".equals(this.notifyType)) {
+ return "RGV";
+ }
+ if ("task".equals(this.notifyType)) {
+ return "浠诲姟";
+ }
+ return this.notifyType;
+ }
+
+ public String getRetryProgress$() {
+ if (this.retryTimes == null && this.failTimes == null) {
+ return "";
+ }
+ return (this.retryTimes == null ? 0 : this.retryTimes) + "/" + (this.failTimes == null ? 0 : this.failTimes);
+ }
+
+ public String getQueueStatus$() {
+ if (!"queue".equals(this.sourceType)) {
+ return "";
+ }
+ if (this.retryTimes == null || this.retryTimes == 0) {
+ return "寰呭彂閫�";
+ }
+ return "閲嶈瘯涓�";
+ }
+
+ public String getResult$() {
+ if (this.result == null) {
+ return "";
+ }
+ return this.result == 1 ? "鎴愬姛" : "澶辫触";
+ }
+
+ public String getLastRetryTime$() {
+ if (this.lastRetryTime == null || this.lastRetryTime <= 0L) {
+ return "";
+ }
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(this.lastRetryTime));
+ }
+
+ public String getCreateTime$() {
+ if (this.createTime == null) {
+ return "";
+ }
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
+ }
+}
diff --git a/src/main/java/com/zy/asrs/service/NotifyAsyncService.java b/src/main/java/com/zy/asrs/service/NotifyAsyncService.java
index e2ac52c..7bde6e2 100644
--- a/src/main/java/com/zy/asrs/service/NotifyAsyncService.java
+++ b/src/main/java/com/zy/asrs/service/NotifyAsyncService.java
@@ -3,6 +3,7 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zy.asrs.domain.NotifyDto;
+import com.zy.asrs.domain.NotifySendResult;
import com.zy.asrs.entity.HttpRequestLog;
import com.zy.common.utils.HttpHandler;
import com.zy.common.utils.RedisUtil;
@@ -36,40 +37,51 @@
*/
@Async
public void sendNotifyAsync(String notifyUri, String notifyUriPath, String key, NotifyDto notifyDto) {
+ sendNotifyNow(notifyUri, notifyUriPath, key, notifyDto, true, true);
+ }
+
+ public NotifySendResult sendNotifyNow(String notifyUri, String notifyUriPath, String key, NotifyDto notifyDto,
+ boolean deleteOnSuccess, boolean updateRetryState) {
HttpRequestLog httpRequestLog = new HttpRequestLog();
httpRequestLog.setName(notifyUri + notifyUriPath);
httpRequestLog.setRequest(JSON.toJSONString(notifyDto));
httpRequestLog.setCreateTime(new Date());
- boolean success = false;
+ NotifySendResult result = new NotifySendResult();
+ result.setSuccess(false);
try {
- // 瑙﹀彂閫氱煡
String response = new HttpHandler.Builder()
.setUri(notifyUri)
.setPath(notifyUriPath)
.setJson(JSON.toJSONString(notifyDto))
.build()
.doPost();
+ result.setResponse(response);
httpRequestLog.setResponse(response);
JSONObject jsonObject = JSON.parseObject(response);
- Integer code = jsonObject.getInteger("code");
- if (code == 200) {
- // 閫氱煡鎴愬姛
- redisUtil.del(key);
- success = true;
+ Integer code = jsonObject == null ? null : jsonObject.getInteger("code");
+ if (code != null && code == 200) {
+ if (deleteOnSuccess && key != null) {
+ redisUtil.del(key);
+ }
+ result.setSuccess(true);
+ result.setMessage("閫氱煡鎴愬姛");
+ } else {
+ result.setMessage("閫氱煡鎺ュ彛杩斿洖澶辫触");
}
} catch (Exception e) {
log.error("寮傛閫氱煡澶辫触, key={}", key, e);
+ result.setMessage("閫氱煡寮傚父: " + e.getMessage());
} finally {
- // 淇濆瓨璁板綍
+ httpRequestLog.setResult(result.isSuccess() ? 1 : 0);
httpRequestLogService.insert(httpRequestLog);
}
- if (!success) {
- // 閫氱煡澶辫触锛屾洿鏂伴噸璇曟鏁�
+ if (!result.isSuccess() && updateRetryState && key != null) {
handleNotifyFailure(key, notifyDto);
}
+ return result;
}
/**
diff --git a/src/main/java/com/zy/asrs/utils/NotifyUtils.java b/src/main/java/com/zy/asrs/utils/NotifyUtils.java
index c573ccf..4bda147 100644
--- a/src/main/java/com/zy/asrs/utils/NotifyUtils.java
+++ b/src/main/java/com/zy/asrs/utils/NotifyUtils.java
@@ -13,6 +13,7 @@
import org.springframework.stereotype.Component;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -40,7 +41,7 @@
return null;
}
- Set keys = redisUtil.keys(key + "*");
+ Set keys = redisUtil.keys(key + "_*");
if (keys == null) {
return null;
}
@@ -52,23 +53,52 @@
return list;
}
- public String getKey(String notifyType, Integer device) {
- String key = null;
+ public List<String> getSupportedNotifyTypes() {
+ return Arrays.asList(
+ String.valueOf(SlaveType.Crn),
+ String.valueOf(SlaveType.Devp),
+ String.valueOf(SlaveType.DualCrn),
+ String.valueOf(SlaveType.Rgv),
+ "task"
+ );
+ }
+
+ public String getKeyPrefix(String notifyType) {
if (notifyType.equals(String.valueOf(SlaveType.Crn))) {
- key = RedisKeyType.QUEUE_CRN.key + device;
+ return RedisKeyType.QUEUE_CRN.key;
} else if (notifyType.equals(String.valueOf(SlaveType.Devp))) {
- key = RedisKeyType.QUEUE_DEVP.key + device;
+ return RedisKeyType.QUEUE_DEVP.key;
} else if (notifyType.equals(String.valueOf(SlaveType.DualCrn))) {
- key = RedisKeyType.QUEUE_DUAL_CRN.key + device;
+ return RedisKeyType.QUEUE_DUAL_CRN.key;
} else if (notifyType.equals(String.valueOf(SlaveType.Rgv))) {
- key = RedisKeyType.QUEUE_RGV.key + device;
+ return RedisKeyType.QUEUE_RGV.key;
} else if (notifyType.equals("task")) {
- key = RedisKeyType.QUEUE_TASK.key + device;
- } else {
+ return RedisKeyType.QUEUE_TASK.key;
+ }
+ return null;
+ }
+
+ public String getNotifyTypeDesc(String notifyType) {
+ if (notifyType.equals(String.valueOf(SlaveType.Crn))) {
+ return "鍫嗗灈鏈�";
+ } else if (notifyType.equals(String.valueOf(SlaveType.Devp))) {
+ return "杈撻�佺嚎";
+ } else if (notifyType.equals(String.valueOf(SlaveType.DualCrn))) {
+ return "鍙屼几浣嶅爢鍨涙満";
+ } else if (notifyType.equals(String.valueOf(SlaveType.Rgv))) {
+ return "RGV";
+ } else if (notifyType.equals("task")) {
+ return "浠诲姟";
+ }
+ return notifyType;
+ }
+
+ public String getKey(String notifyType, Integer device) {
+ String prefix = getKeyPrefix(notifyType);
+ if (prefix == null) {
return null;
}
-
- return key;
+ return prefix + device;
}
private boolean append(String notifyType, Integer device, String taskNo, String superTaskNo, NotifyMsgType msgType, String data) {
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index f809816..87e4b79 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 绯荤粺鐗堟湰淇℃伅
app:
- version: 1.0.5.0
+ version: 1.0.5.1
version-type: dev # prd 鎴� dev
server:
diff --git a/src/main/resources/sql/20260307_add_notify_report_menu.sql b/src/main/resources/sql/20260307_add_notify_report_menu.sql
new file mode 100644
index 0000000..a83ae50
--- /dev/null
+++ b/src/main/resources/sql/20260307_add_notify_report_menu.sql
@@ -0,0 +1,51 @@
+-- 灏� 閫氱煡涓婃姤 鑿滃崟鎸傝浇鍒帮細鏃ュ織鎶ヨ〃锛堜紭鍏堬級鎴栧紑鍙戜笓鐢�
+-- 璇存槑锛氭墽琛屾湰鑴氭湰鍚庯紝璇峰湪鈥滆鑹叉巿鏉冣�濋噷缁欏搴旇鑹插嬀閫夋柊鑿滃崟鍜屸�滄煡鐪嬧�濇潈闄愩��
+
+SET @notify_parent_id := COALESCE(
+ (
+ SELECT id
+ FROM sys_resource
+ WHERE code = 'logReport' AND level = 1
+ ORDER BY id
+ LIMIT 1
+ ),
+ (
+ SELECT id
+ FROM sys_resource
+ WHERE code = 'develop' AND level = 1
+ ORDER BY id
+ LIMIT 1
+ )
+);
+
+INSERT INTO sys_resource(code, name, resource_id, level, sort, status)
+SELECT 'notifyReport/notifyReport.html', '閫氱煡涓婃姤', @notify_parent_id, 2, 998, 1
+FROM dual
+WHERE @notify_parent_id IS NOT NULL
+ AND NOT EXISTS (
+ SELECT 1
+ FROM sys_resource
+ WHERE code = 'notifyReport/notifyReport.html' AND level = 2
+ );
+
+SET @notify_report_id := (
+ SELECT id
+ FROM sys_resource
+ WHERE code = 'notifyReport/notifyReport.html' AND level = 2
+ ORDER BY id
+ LIMIT 1
+);
+
+INSERT INTO sys_resource(code, name, resource_id, level, sort, status)
+SELECT 'notifyReport/notifyReport.html#view', '鏌ョ湅', @notify_report_id, 3, 1, 1
+FROM dual
+WHERE @notify_report_id IS NOT NULL
+ AND NOT EXISTS (
+ SELECT 1
+ FROM sys_resource
+ WHERE code = 'notifyReport/notifyReport.html#view' AND level = 3
+ );
+
+SELECT id, code, name, resource_id, level, sort, status
+FROM sys_resource
+WHERE code IN ('notifyReport/notifyReport.html', 'notifyReport/notifyReport.html#view');
diff --git a/src/main/webapp/views/notifyReport/notifyReport.html b/src/main/webapp/views/notifyReport/notifyReport.html
new file mode 100644
index 0000000..b5cbb67
--- /dev/null
+++ b/src/main/webapp/views/notifyReport/notifyReport.html
@@ -0,0 +1,839 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>閫氱煡涓婃姤</title>
+ <link rel="stylesheet" href="../../static/vue/element/element.css" />
+ <style>
+ [v-cloak] {
+ display: none;
+ }
+
+ html,
+ body {
+ margin: 0;
+ min-height: 100%;
+ font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
+ background:
+ radial-gradient(1200px 420px at 0% -5%, rgba(36, 91, 160, 0.14), transparent 55%),
+ radial-gradient(1000px 500px at 100% 0%, rgba(35, 147, 120, 0.11), transparent 60%),
+ linear-gradient(180deg, #f2f6fb 0%, #edf2f7 100%);
+ color: #243447;
+ }
+
+ .page-shell {
+ max-width: 1680px;
+ margin: 0 auto;
+ padding: 16px;
+ box-sizing: border-box;
+ }
+
+ .hero {
+ border-radius: 18px;
+ padding: 18px 20px 16px;
+ color: #fff;
+ background: linear-gradient(135deg, #114a7a 0%, #226ca1 44%, #1f9c8c 100%);
+ box-shadow: 0 18px 36px rgba(17, 74, 122, 0.22);
+ margin-bottom: 14px;
+ overflow: hidden;
+ position: relative;
+ }
+
+ .hero::after {
+ content: "";
+ position: absolute;
+ right: -60px;
+ top: -90px;
+ width: 280px;
+ height: 280px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.08);
+ filter: blur(2px);
+ }
+
+ .hero-head {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+ flex-wrap: wrap;
+ }
+
+ .hero-title {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ }
+
+ .hero-title-main {
+ font-size: 22px;
+ font-weight: 700;
+ letter-spacing: 0.4px;
+ }
+
+ .hero-title-sub {
+ font-size: 13px;
+ opacity: 0.9;
+ max-width: 760px;
+ line-height: 1.6;
+ }
+
+ .hero-actions {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+
+ .summary-grid {
+ position: relative;
+ z-index: 1;
+ margin-top: 16px;
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 10px;
+ }
+
+ .summary-card {
+ min-height: 78px;
+ border-radius: 14px;
+ padding: 12px 14px;
+ background: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ backdrop-filter: blur(3px);
+ }
+
+ .summary-card .label {
+ font-size: 12px;
+ opacity: 0.86;
+ }
+
+ .summary-card .value {
+ margin-top: 7px;
+ font-size: 24px;
+ font-weight: 700;
+ line-height: 1.15;
+ word-break: break-all;
+ }
+
+ .summary-card .sub {
+ margin-top: 6px;
+ font-size: 12px;
+ opacity: 0.88;
+ word-break: break-all;
+ }
+
+ .panel {
+ border-radius: 18px;
+ border: 1px solid #dbe4ef;
+ background:
+ radial-gradient(700px 220px at -10% 0%, rgba(41, 112, 196, 0.05), transparent 55%),
+ radial-gradient(860px 260px at 110% 16%, rgba(31, 156, 140, 0.06), transparent 58%),
+ rgba(250, 252, 255, 0.88);
+ box-shadow: 0 16px 32px rgba(39, 63, 92, 0.08);
+ overflow: hidden;
+ }
+
+ .toolbar-card {
+ padding: 16px 16px 6px;
+ margin-bottom: 14px;
+ }
+
+ .toolbar-title {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-bottom: 14px;
+ flex-wrap: wrap;
+ }
+
+ .toolbar-title .main {
+ font-size: 16px;
+ font-weight: 700;
+ color: #243447;
+ }
+
+ .toolbar-title .sub {
+ font-size: 12px;
+ color: #7b8ba1;
+ }
+
+ .filter-grid {
+ display: grid;
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+ gap: 12px;
+ }
+
+ .filter-item.full {
+ grid-column: span 2;
+ }
+
+ .filter-actions {
+ margin-top: 14px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ flex-wrap: wrap;
+ }
+
+ .filter-actions-left,
+ .filter-actions-right {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+
+ .board {
+ padding: 14px;
+ }
+
+ .board-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-bottom: 12px;
+ flex-wrap: wrap;
+ }
+
+ .board-head .title {
+ font-size: 15px;
+ font-weight: 700;
+ color: #26384d;
+ }
+
+ .board-head .desc {
+ font-size: 12px;
+ color: #8594a8;
+ }
+
+ .board .el-tabs__nav-wrap::after {
+ background: rgba(222, 230, 238, 0.86);
+ }
+
+ .table-shell {
+ border-radius: 16px;
+ overflow: hidden;
+ border: 1px solid #e0e8f2;
+ background: rgba(255, 255, 255, 0.9);
+ }
+
+ .table-toolbar {
+ padding: 12px 12px 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+
+ .table-toolbar-left,
+ .table-toolbar-right {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+
+ .table-note {
+ font-size: 12px;
+ color: #8090a4;
+ }
+
+ .code-text {
+ font-family: Menlo, Monaco, Consolas, "Liberation Mono", monospace;
+ font-size: 12px;
+ }
+
+ .payload-box {
+ max-height: 62vh;
+ overflow: auto;
+ border-radius: 12px;
+ border: 1px solid #d9e4f0;
+ background: linear-gradient(180deg, #fbfdff 0%, #f4f8fc 100%);
+ padding: 14px;
+ margin: 0;
+ white-space: pre-wrap;
+ word-break: break-word;
+ line-height: 1.65;
+ color: #28405a;
+ font-family: Menlo, Monaco, Consolas, "Liberation Mono", monospace;
+ font-size: 12px;
+ }
+
+ .pagination-wrap {
+ padding: 12px 14px 14px;
+ display: flex;
+ justify-content: flex-end;
+ background: rgba(255, 255, 255, 0.72);
+ }
+
+ .empty-hint {
+ padding: 34px 12px;
+ text-align: center;
+ color: #8c9aae;
+ font-size: 13px;
+ }
+
+ @media (max-width: 1360px) {
+ .summary-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .filter-grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+
+ .filter-item.full {
+ grid-column: span 1;
+ }
+ }
+
+ @media (max-width: 900px) {
+ .page-shell {
+ padding: 10px;
+ }
+
+ .summary-grid,
+ .filter-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .hero-title-main {
+ font-size: 18px;
+ }
+
+ .table-toolbar,
+ .filter-actions {
+ align-items: flex-start;
+ }
+
+ .pagination-wrap {
+ justify-content: center;
+ }
+ }
+ </style>
+</head>
+<body>
+<div id="app" class="page-shell" v-cloak>
+ <section class="hero">
+ <div class="hero-head">
+ <div class="hero-title">
+ <div class="hero-title-main">閫氱煡涓婃姤涓績</div>
+ <div class="hero-title-sub">鏌ョ湅褰撳墠寰呴�氱煡闃熷垪銆佹帴鍙e彂閫佹棩蹇楋紝鏀寔鎸変换鍔�/璁惧蹇�熺瓫閫夛紝骞跺澶辫触鎴栧緟鍙戦�侀�氱煡鎵ц鎵嬪姩琛ュ彂銆�</div>
+ </div>
+ <div class="hero-actions">
+ <el-button type="primary" icon="el-icon-refresh" :loading="summaryLoading" @click="refreshAll">鍒锋柊鍏ㄥ眬</el-button>
+ </div>
+ </div>
+ <div class="summary-grid">
+ <div class="summary-card">
+ <div class="label">涓婃姤寮�鍏�</div>
+ <div class="value">{{ summary['notifyEnable$'] || '-' }}</div>
+ <div class="sub">`notifyEnable` = {{ summary.notifyEnable || '-' }}</div>
+ </div>
+ <div class="summary-card">
+ <div class="label">褰撳墠闃熷垪鏁�</div>
+ <div class="value">{{ summary.queueCount || 0 }}</div>
+ <div class="sub">Redis 涓緟涓婃姤鎴栧緟閲嶈瘯閫氱煡</div>
+ </div>
+ <div class="summary-card">
+ <div class="label">閫氱煡鏃ュ織鏁�</div>
+ <div class="value">{{ summary.logCount || 0 }}</div>
+ <div class="sub">閫氱煡鎺ュ彛鍘嗗彶璋冪敤璁板綍</div>
+ </div>
+ <div class="summary-card">
+ <div class="label">閫氱煡鍦板潃</div>
+ <div class="value" style="font-size: 16px;">{{ summary.notifyEndpoint || '-' }}</div>
+ <div class="sub">鏉ユ簮锛歯otifyUri + notifyUriPath</div>
+ </div>
+ </div>
+ </section>
+
+ <section class="panel toolbar-card">
+ <div class="toolbar-title">
+ <div>
+ <div class="main">绛涢�夋潯浠�</div>
+ <div class="sub">闃熷垪涓庢棩蹇楀叡鐢ㄥ悓涓�缁勬煡璇㈡潯浠讹紝鍒囨崲椤电鏃朵繚鎸佷竴鑷淬��</div>
+ </div>
+ </div>
+
+ <div class="filter-grid">
+ <div class="filter-item">
+ <el-select v-model="filters.notifyType" clearable placeholder="閫氱煡绫诲瀷" size="medium">
+ <el-option label="鍫嗗灈鏈�" value="Crn"></el-option>
+ <el-option label="鍙屼几浣嶅爢鍨涙満" value="DualCrn"></el-option>
+ <el-option label="杈撻�佺嚎" value="Devp"></el-option>
+ <el-option label="RGV" value="Rgv"></el-option>
+ <el-option label="浠诲姟" value="task"></el-option>
+ </el-select>
+ </div>
+ <div class="filter-item">
+ <el-input v-model.trim="filters.device" placeholder="璁惧鍙�" clearable></el-input>
+ </div>
+ <div class="filter-item">
+ <el-input v-model.trim="filters.taskNo" placeholder="浠诲姟鍙�" clearable></el-input>
+ </div>
+ <div class="filter-item">
+ <el-input v-model.trim="filters.superTaskNo" placeholder="涓婄骇浠诲姟鍙�" clearable></el-input>
+ </div>
+ <div class="filter-item">
+ <el-input v-model.trim="filters.msgType" placeholder="娑堟伅绫诲瀷/鎻忚堪" clearable></el-input>
+ </div>
+ <div class="filter-item">
+ <el-select v-model="filters.result" clearable placeholder="鏃ュ織缁撴灉">
+ <el-option label="鎴愬姛" :value="1"></el-option>
+ <el-option label="澶辫触" :value="0"></el-option>
+ </el-select>
+ </div>
+ <div class="filter-item full">
+ <el-input v-model.trim="filters.condition" placeholder="閫氱敤鎼滅储锛氫换鍔″彿銆佹秷鎭弿杩般�佹姤鏂囧叧閿瓧銆丷edis Key" clearable></el-input>
+ </div>
+ </div>
+
+ <div class="filter-actions">
+ <div class="filter-actions-left">
+ <el-button type="primary" icon="el-icon-search" :loading="queue.loading || log.loading" @click="handleSearch">鏌ヨ</el-button>
+ <el-button icon="el-icon-refresh-left" @click="handleReset">閲嶇疆</el-button>
+ </div>
+ <div class="filter-actions-right">
+ <span class="table-note">褰撳墠椤电锛歿{ activeTab === 'queue' ? '閫氱煡闃熷垪' : '鍙戦�佹棩蹇�' }}</span>
+ </div>
+ </div>
+ </section>
+
+ <section class="panel board">
+ <div class="board-head">
+ <div>
+ <div class="title">閫氱煡鏌ョ湅涓庤ˉ鍙�</div>
+ <div class="desc">褰撳墠闃熷垪鏄剧ず Redis 瀹炴椂鏁版嵁锛屽彂閫佹棩蹇楁樉绀哄巻鍙叉帴鍙h皟鐢ㄧ粨鏋溿��</div>
+ </div>
+ </div>
+
+ <el-tabs v-model="activeTab" @tab-click="handleTabChange">
+ <el-tab-pane label="褰撳墠閫氱煡闃熷垪" name="queue">
+ <div class="table-shell">
+ <div class="table-toolbar">
+ <div class="table-toolbar-left">
+ <el-button type="primary" size="mini" icon="el-icon-position" :disabled="queue.selection.length === 0" :loading="resendLoading" @click="batchResendQueue">鎵归噺琛ュ彂</el-button>
+ <span class="table-note">宸查�� {{ queue.selection.length }} 鏉�</span>
+ </div>
+ <div class="table-toolbar-right">
+ <span class="table-note">灞曠ず寰呭彂閫佸拰寰呴噸璇曢�氱煡</span>
+ </div>
+ </div>
+ <el-table
+ :data="queue.records"
+ border
+ stripe
+ height="520"
+ v-loading="queue.loading"
+ @selection-change="queue.selection = $event"
+ :header-cell-style="headerCellStyle">
+ <el-table-column type="selection" width="48"></el-table-column>
+ <el-table-column prop="id" label="閫氱煡ID" min-width="180" show-overflow-tooltip>
+ <template slot-scope="scope">
+ <span class="code-text">{{ scope.row.id }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="notifyType$" label="閫氱煡绫诲瀷" width="120"></el-table-column>
+ <el-table-column prop="device" label="璁惧鍙�" width="88"></el-table-column>
+ <el-table-column prop="taskNo" label="浠诲姟鍙�" min-width="120" show-overflow-tooltip></el-table-column>
+ <el-table-column prop="superTaskNo" label="涓婄骇浠诲姟鍙�" min-width="130" show-overflow-tooltip></el-table-column>
+ <el-table-column prop="msgDesc" label="娑堟伅鎻忚堪" min-width="180" show-overflow-tooltip></el-table-column>
+ <el-table-column prop="retryProgress$" label="閲嶈瘯娆℃暟" width="88"></el-table-column>
+ <el-table-column prop="retryTime" label="闂撮殧(s)" width="84"></el-table-column>
+ <el-table-column prop="lastRetryTime$" label="涓婃閲嶈瘯鏃堕棿" width="168"></el-table-column>
+ <el-table-column label="闃熷垪鐘舵��" width="104">
+ <template slot-scope="scope">
+ <el-tag size="mini" :type="scope.row.queueStatus$ === '寰呭彂閫�' ? 'success' : 'warning'">{{ scope.row.queueStatus$ || '-' }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="170" fixed="right">
+ <template slot-scope="scope">
+ <el-button type="text" size="mini" @click="openPayload('閫氱煡鎶ユ枃', scope.row.requestPayload)">鏌ョ湅鎶ユ枃</el-button>
+ <el-button type="text" size="mini" @click="resendQueueRow(scope.row)">琛ュ彂</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <div v-if="!queue.loading && queue.records.length === 0" class="empty-hint">褰撳墠娌℃湁寰呭彂閫侀�氱煡</div>
+ <div class="pagination-wrap">
+ <el-pagination
+ background
+ layout="total, sizes, prev, pager, next, jumper"
+ :current-page="queue.page.curr"
+ :page-sizes="[15, 30, 50, 100, 200]"
+ :page-size="queue.page.limit"
+ :total="queue.page.total"
+ @size-change="handleQueueSizeChange"
+ @current-change="handleQueuePageChange">
+ </el-pagination>
+ </div>
+ </div>
+ </el-tab-pane>
+
+ <el-tab-pane label="閫氱煡鍙戦�佹棩蹇�" name="log">
+ <div class="table-shell">
+ <div class="table-toolbar">
+ <div class="table-toolbar-left">
+ <el-button type="primary" size="mini" icon="el-icon-position" :disabled="log.selection.length === 0" :loading="resendLoading" @click="batchResendLog">鎵归噺琛ュ彂</el-button>
+ <span class="table-note">宸查�� {{ log.selection.length }} 鏉�</span>
+ </div>
+ <div class="table-toolbar-right">
+ <span class="table-note">鏀寔浠庡巻鍙叉棩蹇楅噸鏂板彂閫侀�氱煡</span>
+ </div>
+ </div>
+ <el-table
+ :data="log.records"
+ border
+ stripe
+ height="520"
+ v-loading="log.loading"
+ @selection-change="log.selection = $event"
+ :header-cell-style="headerCellStyle">
+ <el-table-column type="selection" width="48"></el-table-column>
+ <el-table-column prop="createTime$" label="鍙戦�佹椂闂�" width="166"></el-table-column>
+ <el-table-column label="缁撴灉" width="84">
+ <template slot-scope="scope">
+ <el-tag v-if="scope.row.result === 1" size="mini" type="success">鎴愬姛</el-tag>
+ <el-tag v-else-if="scope.row.result === 0" size="mini" type="danger">澶辫触</el-tag>
+ <span v-else>-</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="notifyType$" label="閫氱煡绫诲瀷" width="120"></el-table-column>
+ <el-table-column prop="device" label="璁惧鍙�" width="88"></el-table-column>
+ <el-table-column prop="taskNo" label="浠诲姟鍙�" min-width="120" show-overflow-tooltip></el-table-column>
+ <el-table-column prop="superTaskNo" label="涓婄骇浠诲姟鍙�" min-width="130" show-overflow-tooltip></el-table-column>
+ <el-table-column prop="msgDesc" label="娑堟伅鎻忚堪" min-width="180" show-overflow-tooltip></el-table-column>
+ <el-table-column label="鎿嶄綔" width="250" fixed="right">
+ <template slot-scope="scope">
+ <el-button type="text" size="mini" @click="openPayload('閫氱煡鎶ユ枃', scope.row.requestPayload)">鏌ョ湅鎶ユ枃</el-button>
+ <el-button type="text" size="mini" @click="openPayload('鎺ュ彛鍝嶅簲', scope.row.response)">鏌ョ湅鍝嶅簲</el-button>
+ <el-button type="text" size="mini" @click="resendLogRow(scope.row)">琛ュ彂</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <div v-if="!log.loading && log.records.length === 0" class="empty-hint">褰撳墠绛涢�夋潯浠朵笅娌℃湁閫氱煡鏃ュ織</div>
+ <div class="pagination-wrap">
+ <el-pagination
+ background
+ layout="total, sizes, prev, pager, next, jumper"
+ :current-page="log.page.curr"
+ :page-sizes="[15, 30, 50, 100, 200]"
+ :page-size="log.page.limit"
+ :total="log.page.total"
+ @size-change="handleLogSizeChange"
+ @current-change="handleLogPageChange">
+ </el-pagination>
+ </div>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+ </section>
+
+ <el-dialog :title="dialog.title" :visible.sync="dialog.visible" width="72%" :close-on-click-modal="false">
+ <pre class="payload-box">{{ dialog.content || '-' }}</pre>
+ </el-dialog>
+</div>
+
+<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
+<script type="text/javascript" src="../../static/vue/element/element.js"></script>
+<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
+<script>
+ new Vue({
+ el: '#app',
+ data: function () {
+ return {
+ activeTab: 'queue',
+ summaryLoading: false,
+ resendLoading: false,
+ summary: {},
+ filters: {
+ notifyType: '',
+ device: '',
+ taskNo: '',
+ superTaskNo: '',
+ msgType: '',
+ result: null,
+ condition: ''
+ },
+ queue: {
+ loading: false,
+ records: [],
+ selection: [],
+ page: {
+ curr: 1,
+ limit: 15,
+ total: 0
+ }
+ },
+ log: {
+ loading: false,
+ records: [],
+ selection: [],
+ page: {
+ curr: 1,
+ limit: 15,
+ total: 0
+ }
+ },
+ dialog: {
+ visible: false,
+ title: '',
+ content: ''
+ },
+ headerCellStyle: {
+ background: '#f7f9fc',
+ color: '#2d4058',
+ fontWeight: 600
+ }
+ };
+ },
+ created: function () {
+ this.refreshAll();
+ },
+ methods: {
+ buildFilterParams: function (includeResult) {
+ var params = {};
+ var filters = this.filters;
+ if (filters.notifyType) params.notifyType = filters.notifyType;
+ if (filters.device !== '' && filters.device !== null && filters.device !== undefined) {
+ params.device = filters.device;
+ }
+ if (filters.taskNo) params.taskNo = filters.taskNo;
+ if (filters.superTaskNo) params.superTaskNo = filters.superTaskNo;
+ if (filters.msgType) params.msgType = filters.msgType;
+ if (filters.condition) params.condition = filters.condition;
+ if (includeResult && filters.result !== '' && filters.result !== null && filters.result !== undefined) {
+ params.result = filters.result;
+ }
+ return params;
+ },
+ buildQueryString: function (params) {
+ var search = [];
+ Object.keys(params).forEach(function (key) {
+ if (params[key] === '' || params[key] === null || params[key] === undefined) {
+ return;
+ }
+ search.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
+ });
+ return search.join('&');
+ },
+ fetchJson: function (url, options) {
+ var requestOptions = options || {};
+ requestOptions.headers = requestOptions.headers || {};
+ requestOptions.headers.token = localStorage.getItem('token');
+ if (requestOptions.body && !requestOptions.headers['Content-Type']) {
+ requestOptions.headers['Content-Type'] = 'application/json;charset=UTF-8';
+ }
+ return fetch(url, requestOptions).then(function (response) {
+ return response.json();
+ });
+ },
+ ensureSuccess: function (res, fallback) {
+ if (res && res.code === 403) {
+ top.location.href = baseUrl + '/';
+ return false;
+ }
+ if (!res || res.code !== 200) {
+ this.$message.error((res && res.msg) || fallback || '璇锋眰澶辫触');
+ return false;
+ }
+ return true;
+ },
+ refreshAll: function () {
+ this.loadSummary();
+ this.loadQueue();
+ this.loadLog();
+ },
+ loadSummary: function () {
+ var self = this;
+ self.summaryLoading = true;
+ self.fetchJson(baseUrl + '/notifyReport/summary/auth')
+ .then(function (res) {
+ if (!self.ensureSuccess(res, '鑾峰彇閫氱煡姒傝澶辫触')) {
+ return;
+ }
+ self.summary = res.data || {};
+ })
+ .finally(function () {
+ self.summaryLoading = false;
+ });
+ },
+ loadQueue: function () {
+ var self = this;
+ self.queue.loading = true;
+ var params = self.buildFilterParams(false);
+ params.curr = self.queue.page.curr;
+ params.limit = self.queue.page.limit;
+ self.fetchJson(baseUrl + '/notifyReport/queue/list/auth?' + self.buildQueryString(params))
+ .then(function (res) {
+ if (!self.ensureSuccess(res, '鑾峰彇閫氱煡闃熷垪澶辫触')) {
+ return;
+ }
+ self.queue.records = (res.data && res.data.records) || [];
+ self.queue.page.total = (res.data && res.data.total) || 0;
+ self.queue.selection = [];
+ })
+ .finally(function () {
+ self.queue.loading = false;
+ });
+ },
+ loadLog: function () {
+ var self = this;
+ self.log.loading = true;
+ var params = self.buildFilterParams(true);
+ params.curr = self.log.page.curr;
+ params.limit = self.log.page.limit;
+ self.fetchJson(baseUrl + '/notifyReport/log/list/auth?' + self.buildQueryString(params))
+ .then(function (res) {
+ if (!self.ensureSuccess(res, '鑾峰彇閫氱煡鏃ュ織澶辫触')) {
+ return;
+ }
+ self.log.records = (res.data && res.data.records) || [];
+ self.log.page.total = (res.data && res.data.total) || 0;
+ self.log.selection = [];
+ })
+ .finally(function () {
+ self.log.loading = false;
+ });
+ },
+ handleSearch: function () {
+ this.queue.page.curr = 1;
+ this.log.page.curr = 1;
+ this.refreshAll();
+ },
+ handleReset: function () {
+ this.filters = {
+ notifyType: '',
+ device: '',
+ taskNo: '',
+ superTaskNo: '',
+ msgType: '',
+ result: null,
+ condition: ''
+ };
+ this.queue.page.curr = 1;
+ this.log.page.curr = 1;
+ this.refreshAll();
+ },
+ handleTabChange: function () {
+ if (this.activeTab === 'queue' && this.queue.records.length === 0) {
+ this.loadQueue();
+ return;
+ }
+ if (this.activeTab === 'log' && this.log.records.length === 0) {
+ this.loadLog();
+ }
+ },
+ handleQueuePageChange: function (page) {
+ this.queue.page.curr = page;
+ this.loadQueue();
+ },
+ handleQueueSizeChange: function (size) {
+ this.queue.page.limit = size;
+ this.queue.page.curr = 1;
+ this.loadQueue();
+ },
+ handleLogPageChange: function (page) {
+ this.log.page.curr = page;
+ this.loadLog();
+ },
+ handleLogSizeChange: function (size) {
+ this.log.page.limit = size;
+ this.log.page.curr = 1;
+ this.loadLog();
+ },
+ openPayload: function (title, content) {
+ this.dialog.title = title;
+ this.dialog.content = content || '';
+ this.dialog.visible = true;
+ },
+ resendRequest: function (payload) {
+ var self = this;
+ self.resendLoading = true;
+ return self.fetchJson(baseUrl + '/notifyReport/resend/auth', {
+ method: 'POST',
+ body: JSON.stringify(payload)
+ }).then(function (res) {
+ if (!self.ensureSuccess(res, '琛ュ彂澶辫触')) {
+ return;
+ }
+ var data = res.data || {};
+ var message = '鎴愬姛 ' + (data.successCount || 0) + ' 鏉�';
+ if ((data.failCount || 0) > 0) {
+ message += '锛屽け璐� ' + data.failCount + ' 鏉�';
+ }
+ self.$message({
+ type: (data.failCount || 0) > 0 ? 'warning' : 'success',
+ message: message
+ });
+ self.loadSummary();
+ self.loadQueue();
+ self.loadLog();
+ if ((data.failCount || 0) > 0) {
+ self.openPayload('琛ュ彂缁撴灉', JSON.stringify(data.details || [], null, 2));
+ }
+ }).finally(function () {
+ self.resendLoading = false;
+ });
+ },
+ confirmResend: function (payload, title) {
+ var self = this;
+ self.$confirm(title || '纭畾鎵ц鎵嬪姩琛ュ彂鍚楋紵', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(function () {
+ self.resendRequest(payload);
+ }).catch(function () {});
+ },
+ resendQueueRow: function (row) {
+ this.confirmResend({
+ sourceType: 'queue',
+ redisKeys: [row.redisKey]
+ }, '纭畾琛ュ彂璇ラ槦鍒楅�氱煡鍚楋紵');
+ },
+ resendLogRow: function (row) {
+ this.confirmResend({
+ sourceType: 'log',
+ logIds: [row.logId]
+ }, '纭畾鎸夎鏃ュ織閲嶆柊琛ュ彂閫氱煡鍚楋紵');
+ },
+ batchResendQueue: function () {
+ var keys = this.queue.selection.map(function (item) {
+ return item.redisKey;
+ });
+ if (keys.length === 0) {
+ this.$message.warning('璇烽�夋嫨瑕佽ˉ鍙戠殑闃熷垪閫氱煡');
+ return;
+ }
+ this.confirmResend({
+ sourceType: 'queue',
+ redisKeys: keys
+ }, '纭畾鎵归噺琛ュ彂閫変腑鐨勯槦鍒楅�氱煡鍚楋紵');
+ },
+ batchResendLog: function () {
+ var ids = this.log.selection.map(function (item) {
+ return item.logId;
+ });
+ if (ids.length === 0) {
+ this.$message.warning('璇烽�夋嫨瑕佽ˉ鍙戠殑閫氱煡鏃ュ織');
+ return;
+ }
+ this.confirmResend({
+ sourceType: 'log',
+ logIds: ids
+ }, '纭畾鎵归噺琛ュ彂閫変腑鐨勯�氱煡鏃ュ織鍚楋紵');
+ }
+ }
+ });
+</script>
+</body>
+</html>
--
Gitblit v1.9.1