228 lines
9.5 KiB
Java
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 连通性或服务响应状态"));
|
|
}
|
|
}
|