feat: 实现IP白名单严格拦截、三方Webhook多租户事件订阅与MCP专线业务调用
This commit is contained in:
@@ -9,6 +9,8 @@ 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;
|
||||
@@ -24,7 +26,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@Tag(name = "开放平台管理 - Webhook 通道管理")
|
||||
@Tag(name = "开放平台管理 - Webhook 事件订阅与通道管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/open/webhook")
|
||||
public class OpenWebhookAdminController {
|
||||
@@ -35,12 +37,65 @@ public class OpenWebhookAdminController {
|
||||
@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);
|
||||
@@ -48,21 +103,73 @@ public class OpenWebhookAdminController {
|
||||
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);
|
||||
return Result.success(webhookMapper.selectPage(page, qw));
|
||||
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("device.alarm");
|
||||
if (req.getStatus() == null) req.setStatus(1);
|
||||
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);
|
||||
@@ -72,14 +179,24 @@ public class OpenWebhookAdminController {
|
||||
@PutMapping("/{id}")
|
||||
public Result<Void> update(@PathVariable Long id, @RequestBody OpenWebhook req) {
|
||||
OpenWebhook item = webhookMapper.selectById(id);
|
||||
if (item == null) throw new BizException("通道不存在");
|
||||
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());
|
||||
if (req.getEvents() != null) {
|
||||
item.setEvents(req.getEvents());
|
||||
}
|
||||
if (req.getStatus() != null) {
|
||||
item.setStatus(req.getStatus());
|
||||
}
|
||||
item.setRemark(req.getRemark());
|
||||
|
||||
webhookMapper.updateById(item);
|
||||
@@ -97,12 +214,14 @@ public class OpenWebhookAdminController {
|
||||
@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("通道不存在");
|
||||
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") : "这是一条来自开放平台的连通性测试消息。";
|
||||
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);
|
||||
return Result.success(Map.of("success", ok, "message", ok ? "测试消息已成功送达" : "发送失败,请检查 URL 与网络配置"));
|
||||
boolean ok = dispatchService.sendDirect(item.getChannelType(), item.getWebhookUrl(), title, content, item.getSecret());
|
||||
return Result.success(Map.of("success", ok, "message", ok ? "测试事件已成功投递" : "投递失败,请检查回调 URL 连通性或服务响应状态"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user