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 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().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 records = loadQueueRecords(notifyType, device); List 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 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 logPage = httpRequestLogService.selectPage(new Page<>(curr, limit), wrapper); Page resultPage = new Page<>(curr, limit); resultPage.setTotal(logPage.getTotal()); List 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> 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 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 detail = resendLog(logId, notifyUri, notifyUriPath); details.add(detail); if (Boolean.TRUE.equals(detail.get("success"))) { successCount++; } else { failCount++; } } } else { return R.error("不支持的补发来源"); } Map 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 buildPage(Integer curr, Integer limit, List records) { Page 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 loadQueueRecords(String notifyType, Integer device) { Set keys = new LinkedHashSet<>(); List 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 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 resendQueue(String redisKey, String notifyUri, String notifyUriPath) { Map 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 resendLog(Long logId, String notifyUri, String notifyUriPath) { Map 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", "日志请求报文无法解析为通知对象"); 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().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; } }