Files
IoT-OpenApi/src/main/java/com/iot/gateway/admin/OpenWebhookAdminController.java
T

228 lines
9.5 KiB
Java

package com.iot.gateway.admin;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.iot.gateway.common.BizException;
import com.iot.gateway.common.Result;
import com.iot.gateway.entity.OpenWebhook;
import com.iot.gateway.mapper.OpenWebhookMapper;
import com.iot.gateway.webhook.WebhookDispatchService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
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;
@Slf4j
@Tag(name = "开放平台管理 - Webhook 事件订阅与通道管理")
@RestController
@RequestMapping("/api/open/webhook")
public class OpenWebhookAdminController {
@Autowired
private OpenWebhookMapper webhookMapper;
@Autowired
private WebhookDispatchService dispatchService;
@Autowired(required = false)
private org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
@Operation(summary = "获取所有可订阅的标准开放事件 (Events) 枚举字典")
@GetMapping("/events")
public Result<List<Map<String, Object>>> listEvents() {
return Result.success(List.of(
Map.of(
"code", "alarm.triggered",
"name", "设备告警触发",
"category", "ALARM",
"desc", "现场物理注塑机发生工艺超温、高压超标、停机等严重报警",
"level", "CRITICAL"
),
Map.of(
"code", "alarm.recovered",
"name", "设备告警消除",
"category", "ALARM",
"desc", "报警条件解除,设备故障自动或手动复位消除",
"level", "INFO"
),
Map.of(
"code", "alarm.acknowledged",
"name", "告警已被确认",
"category", "ALARM",
"desc", "现场操作人员或值班工程师已在平台确认该告警",
"level", "INFO"
),
Map.of(
"code", "device.online",
"name", "设备上线与重连",
"category", "DEVICE",
"desc", "边缘智能网关或现场物理注塑机通过 MQTT 成功建立连接",
"level", "INFO"
),
Map.of(
"code", "device.offline",
"name", "设备失联与掉线",
"category", "DEVICE",
"desc", "设备失联或心跳超时断开,可能存在断电或网络故障",
"level", "WARNING"
),
Map.of(
"code", "device.status_changed",
"name", "设备运行状态切换",
"category", "DEVICE",
"desc", "注塑机在运行 (RUNNING)、待机 (STANDBY)、停机 (STOPPED) 间切换",
"level", "INFO"
)
));
}
@Operation(summary = "Webhook 通道分页列表")
@GetMapping("/page")
public Result<Page<OpenWebhook>> page(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Long tenantId,
@RequestParam(required = false) String clientId,
@RequestParam(required = false) String keyword) {
Page<OpenWebhook> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<OpenWebhook> qw = new LambdaQueryWrapper<>();
if (tenantId != null) {
qw.eq(OpenWebhook::getTenantId, tenantId);
}
if (StringUtils.hasText(clientId)) {
qw.eq(OpenWebhook::getClientId, clientId);
}
if (StringUtils.hasText(keyword)) {
qw.and(w -> w.like(OpenWebhook::getChannelName, keyword)
.or().like(OpenWebhook::getWebhookUrl, keyword));
}
qw.orderByDesc(OpenWebhook::getCreateTime);
Page<OpenWebhook> resPage = webhookMapper.selectPage(page, qw);
populateTenantAndClientNames(resPage.getRecords());
return Result.success(resPage);
}
private void populateTenantAndClientNames(List<OpenWebhook> webhooks) {
if (webhooks == null || webhooks.isEmpty() || jdbcTemplate == null) {
return;
}
try {
Map<Long, String> tenantNameMap = new HashMap<>();
List<Map<String, Object>> tenantRows = jdbcTemplate.queryForList("SELECT id, tenant_name FROM sys_tenant");
for (Map<String, Object> r : tenantRows) {
Object idObj = r.get("id");
Object nameObj = r.get("tenant_name");
if (idObj instanceof Number && nameObj != null) {
tenantNameMap.put(((Number) idObj).longValue(), nameObj.toString());
}
}
Map<String, String> clientNameMap = new HashMap<>();
List<Map<String, Object>> clientRows = jdbcTemplate.queryForList("SELECT client_id, client_name FROM open_client");
for (Map<String, Object> r : clientRows) {
Object cId = r.get("client_id");
Object cName = r.get("client_name");
if (cId != null && cName != null) {
clientNameMap.put(cId.toString(), cName.toString());
}
}
for (OpenWebhook w : webhooks) {
if (w.getTenantId() != null) {
w.setTenantName(tenantNameMap.getOrDefault(w.getTenantId(), "租户 #" + w.getTenantId()));
}
if (StringUtils.hasText(w.getClientId())) {
w.setClientName(clientNameMap.getOrDefault(w.getClientId(), w.getClientId()));
}
}
} catch (Exception e) {
log.warn("填充 Webhook 关联租户与应用信息异常: {}", e.getMessage());
}
}
@Operation(summary = "新建 Webhook 通道")
@PostMapping
public Result<OpenWebhook> create(@RequestBody OpenWebhook req) {
req.setId(null);
if (req.getTenantId() == null) {
req.setTenantId(1L);
}
if (!StringUtils.hasText(req.getEvents())) {
req.setEvents("alarm.triggered");
}
if (req.getStatus() == null) {
req.setStatus(1);
}
if (!StringUtils.hasText(req.getChannelType())) {
req.setChannelType("http");
}
webhookMapper.insert(req);
return Result.success(req);
}
@Operation(summary = "修改 Webhook 通道")
@PutMapping("/{id}")
public Result<Void> update(@PathVariable Long id, @RequestBody OpenWebhook req) {
OpenWebhook item = webhookMapper.selectById(id);
if (item == null) {
throw new BizException("通道不存在");
}
if (req.getTenantId() != null) {
item.setTenantId(req.getTenantId());
}
item.setClientId(req.getClientId());
item.setChannelName(req.getChannelName());
item.setChannelType(req.getChannelType());
item.setWebhookUrl(req.getWebhookUrl());
item.setSecret(req.getSecret());
if (req.getEvents() != null) {
item.setEvents(req.getEvents());
}
if (req.getStatus() != null) {
item.setStatus(req.getStatus());
}
item.setRemark(req.getRemark());
webhookMapper.updateById(item);
return Result.success();
}
@Operation(summary = "删除 Webhook 通道")
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable Long id) {
webhookMapper.deleteById(id);
return Result.success();
}
@Operation(summary = "测试 Webhook 推送连通性")
@PostMapping("/{id}/test")
public Result<Map<String, Object>> testSend(@PathVariable Long id, @RequestBody(required = false) Map<String, String> body) {
OpenWebhook item = webhookMapper.selectById(id);
if (item == null) {
throw new BizException("通道不存在");
}
String title = body != null && body.containsKey("title") ? body.get("title") : "【IoT开放平台测试】三方事件回调连通性验证";
String content = body != null && body.containsKey("content") ? body.get("content") : "这是一条来自物联网开放平台的模拟业务事件通知,包含签名验证与数据包投递。";
boolean ok = dispatchService.sendDirect(item.getChannelType(), item.getWebhookUrl(), title, content, item.getSecret());
return Result.success(Map.of("success", ok, "message", ok ? "测试事件已成功投递" : "投递失败,请检查回调 URL 连通性或服务响应状态"));
}
}