package com.iot.gateway.webhook; import com.iot.gateway.entity.OpenWebhook; import com.iot.gateway.mapper.OpenWebhookMapper; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service public class WebhookDispatchService { @Autowired private OpenWebhookMapper webhookMapper; private final HttpClient httpClient = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(5)) .build(); public void dispatchAlarm(Long tenantId, String title, String content) { List channels = webhookMapper.selectList(null).stream() .filter(w -> w.getStatus() == 1 && (w.getTenantId().equals(tenantId) || tenantId == null)) .filter(w -> w.getEvents() != null && w.getEvents().contains("device.alarm")) .toList(); for (OpenWebhook ch : channels) { sendAsync(ch, title, content); } } public boolean sendDirect(String channelType, String webhookUrl, String title, String content) { String payload = formatMessage(channelType, title, content); try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(webhookUrl)) .header("Content-Type", "application/json; charset=utf-8") .timeout(Duration.ofSeconds(5)) .POST(HttpRequest.BodyPublishers.ofString(payload)) .build(); HttpResponse resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); return resp.statusCode() >= 200 && resp.statusCode() < 300; } catch (Exception e) { log.warn("发送 Webhook 消息失败: url={}, err={}", webhookUrl, e.getMessage()); return false; } } private void sendAsync(OpenWebhook webhook, String title, String content) { new Thread(() -> sendDirect(webhook.getChannelType(), webhook.getWebhookUrl(), title, content)).start(); } private String formatMessage(String type, String title, String content) { String safeTitle = title != null ? title.replace("\"", "\\\"") : "IoT 平台通知"; String safeContent = content != null ? content.replace("\"", "\\\"") : ""; if ("wechat_work".equalsIgnoreCase(type) || "dingtalk".equalsIgnoreCase(type)) { return "{\"msgtype\":\"text\",\"text\":{\"content\":\"" + safeTitle + "\\n" + safeContent + "\"}}"; } else if ("feishu".equalsIgnoreCase(type)) { return "{\"msg_type\":\"text\",\"content\":{\"text\":\"" + safeTitle + "\\n" + safeContent + "\"}}"; } else { return "{\"title\":\"" + safeTitle + "\",\"content\":\"" + safeContent + "\",\"time\":" + System.currentTimeMillis() + "}"; } } }