mirror of
https://codeup.aliyun.com/64f7d6b8ce01efaafef1e678/coal/coal.git
synced 2026-07-27 07:55:37 +08:00
feat(DingtalkBotService): remove YAML Front Matter from message content and add utility method
This commit is contained in:
157
doc/notifyClient-debugPush-api.md
Normal file
157
doc/notifyClient-debugPush-api.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# 通知客户端调试接口文档
|
||||
|
||||
## 接口说明
|
||||
|
||||
手动发送通知给指定的客户端,用于测试和调试通知功能。
|
||||
|
||||
## 接口地址
|
||||
|
||||
```
|
||||
POST /notifyClient/debugPush
|
||||
```
|
||||
|
||||
## 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"targetAccessKey": "ak-xxxx-xxxx-xxxx", // 必填:目标客户端的 AccessKey
|
||||
"content": "这是一条测试消息", // 必填:消息内容
|
||||
"metadata": { // 可选:元数据
|
||||
"title": "测试通知",
|
||||
"priority": "high",
|
||||
"category": "system"
|
||||
},
|
||||
"deadline": 1737859200 // 可选:截止时间(Unix时间戳,秒),不填默认为1小时后
|
||||
}
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| targetAccessKey | String | 是 | 目标客户端的 AccessKey,可以从客户端列表中获取 |
|
||||
| content | String | 是 | 消息内容,支持纯文本或 Markdown 格式 |
|
||||
| metadata | Object | 否 | 元数据键值对,用于传递额外信息 |
|
||||
| deadline | Long | 否 | 消息截止时间(Unix时间戳,秒),超过这个时间的消息不会被投递。不填默认为当前时间+1小时 |
|
||||
|
||||
## 响应结果
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"messageId": "msg-xxxx-xxxx-xxxx",
|
||||
"targetAccessKey": "ak-xxxx-xxxx-xxxx",
|
||||
"targetClientName": "测试客户端",
|
||||
"serverUrl": "grpc://localhost:50051",
|
||||
"timestamp": 1737859200000
|
||||
}
|
||||
```
|
||||
|
||||
### 失败响应
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "目标客户端不存在: ak-xxxx-xxxx-xxxx",
|
||||
"targetAccessKey": "ak-xxxx-xxxx-xxxx",
|
||||
"timestamp": 1737859200000
|
||||
}
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### cURL 示例
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/notifyClient/debugPush \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"targetAccessKey": "ak-123e4567-e89b-12d3-a456-426614174000",
|
||||
"content": "这是一条测试消息",
|
||||
"metadata": {
|
||||
"title": "系统通知",
|
||||
"priority": "high"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### JavaScript 示例
|
||||
|
||||
```javascript
|
||||
fetch('http://localhost:8080/notifyClient/debugPush', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
targetAccessKey: 'ak-123e4567-e89b-12d3-a456-426614174000',
|
||||
content: '这是一条测试消息',
|
||||
metadata: {
|
||||
title: '系统通知',
|
||||
priority: 'high'
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => console.log('推送结果:', data))
|
||||
.catch(error => console.error('推送失败:', error));
|
||||
```
|
||||
|
||||
### Python 示例
|
||||
|
||||
```python
|
||||
import requests
|
||||
import time
|
||||
|
||||
url = 'http://localhost:8080/notifyClient/debugPush'
|
||||
payload = {
|
||||
'targetAccessKey': 'ak-123e4567-e89b-12d3-a456-426614174000',
|
||||
'content': '这是一条测试消息',
|
||||
'metadata': {
|
||||
'title': '系统通知',
|
||||
'priority': 'high'
|
||||
},
|
||||
'deadline': int(time.time()) + 3600 # 1小时后过期
|
||||
}
|
||||
|
||||
response = requests.post(url, json=payload)
|
||||
print('推送结果:', response.json())
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 如何获取客户端的 AccessKey?
|
||||
|
||||
A: 通过 `/notifyClient/list` 接口查询客户端列表,返回结果中包含每个客户端的 `ak` 字段。
|
||||
|
||||
### Q2: 消息推送失败怎么办?
|
||||
|
||||
A: 检查以下几点:
|
||||
1. 目标客户端的 AccessKey 是否正确
|
||||
2. 通知服务器地址是否正确配置
|
||||
3. 目标客户端是否在线并已连接到通知服务器
|
||||
4. 查看服务器日志获取详细错误信息
|
||||
|
||||
### Q3: deadline 参数应该填什么值?
|
||||
|
||||
A: deadline 是 Unix 时间戳(秒),表示消息的截止时间。可以使用:
|
||||
- JavaScript: `Math.floor(Date.now() / 1000) + 3600` // 1小时后
|
||||
- Python: `int(time.time()) + 3600` // 1小时后
|
||||
- 不填则默认为当前时间+1小时
|
||||
|
||||
### Q4: metadata 可以放什么数据?
|
||||
|
||||
A: metadata 是一个键值对对象,所有的 key 和 value 都必须是字符串类型。常用的字段有:
|
||||
- `title`: 消息标题
|
||||
- `priority`: 优先级(low/normal/high)
|
||||
- `category`: 分类(system/business/alert等)
|
||||
- 其他自定义字段
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 此接口仅用于调试和测试,生产环境中应该通过正常的业务流程发送通知
|
||||
2. 消息内容建议不超过 10KB
|
||||
3. 如果目标客户端不在线,消息会在客户端下次连接时投递(deadline 未过期的情况下)
|
||||
4. 建议在调用前先通过 `/notifyClient/getById` 接口确认目标客户端存在
|
||||
@@ -160,12 +160,15 @@ public class DingtalkBotService extends BaseService<DingtalkBotEntity, DingtalkB
|
||||
|
||||
body.put("msgtype", "markdown");
|
||||
|
||||
// 移除 YAML Front Matter
|
||||
String cleanedContent = removeYamlFrontMatter(content);
|
||||
|
||||
body.put(
|
||||
"markdown",
|
||||
new HashMap<String, Object>() {
|
||||
{
|
||||
put("title", title);
|
||||
put("text", content);
|
||||
put("text", cleanedContent);
|
||||
}
|
||||
});
|
||||
try {
|
||||
@@ -185,6 +188,38 @@ public class DingtalkBotService extends BaseService<DingtalkBotEntity, DingtalkB
|
||||
return sendMsgResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 YAML Front Matter
|
||||
* YAML Front Matter 格式为文件开头的 ---...--- 包围的内容
|
||||
* @param content 原始内容
|
||||
* @return 移除 YAML Front Matter 后的内容
|
||||
*/
|
||||
private String removeYamlFrontMatter(String content) {
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// YAML Front Matter 必须从文件开头开始
|
||||
String trimmed = content.trim();
|
||||
if (!trimmed.startsWith("---")) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// 查找第二个 --- 的位置
|
||||
int endIndex = trimmed.indexOf("---", 3);
|
||||
if (endIndex == -1) {
|
||||
// 如果没有找到结束标记,返回原内容
|
||||
return content;
|
||||
}
|
||||
|
||||
// 移除 YAML Front Matter 并去除前后空白
|
||||
String result = trimmed.substring(endIndex + 3).trim();
|
||||
|
||||
log.debug("移除 YAML Front Matter: 原长度={}, 新长度={}", content.length(), result.length());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送钉钉消息(兼容旧接口,内部进行模板渲染)
|
||||
* @param dingtalkBotEntity 钉钉机器人实体
|
||||
|
||||
@@ -93,14 +93,13 @@ public class NotifyClientService extends BaseService<NotifyClientEntity, NotifyC
|
||||
*
|
||||
* 注意:
|
||||
* 1. title 会放到 metadata 中
|
||||
* 2. content 应该已包含完整的 YAML Front Matter 格式(由调用方在模板中处理)
|
||||
* 3. 本方法不处理 YAML Front Matter 的特殊字段
|
||||
* 4. 服务器地址从配置文件读取(notify.server-url),支持环境变量 NOTIFY_SERVER_URL
|
||||
* 5. 所有推送都是 push 模式(推送到指定 AK 的客户端)
|
||||
* 2. content 保留完整内容(包括 YAML Front Matter,通知客户端支持解析)
|
||||
* 3. 服务器地址从配置文件读取(notify.server-url),支持环境变量 NOTIFY_SERVER_URL
|
||||
* 4. 所有推送都是 push 模式(推送到指定 AK 的客户端)
|
||||
*
|
||||
* @param client 通知客户端实体(使用其 AK 作为目标客户端标识)
|
||||
* @param title 已渲染的标题(放入 metadata)
|
||||
* @param content 已渲染的内容(应包含完整的 YAML Front Matter)
|
||||
* @param content 已渲染的内容(保留完整的 YAML Front Matter)
|
||||
* @param params 原始参数(保留用于扩展)
|
||||
*/
|
||||
public void doSend(NotifyClientEntity client, String title, String content, Map<String, Object> params) {
|
||||
@@ -121,7 +120,7 @@ public class NotifyClientService extends BaseService<NotifyClientEntity, NotifyC
|
||||
// 计算截止时间(默认当前时间 + 30分钟)
|
||||
long deadline = calculateDeadline(params);
|
||||
|
||||
// 推送模式:发送到指定 AK 的客户端
|
||||
// 推送模式:发送到指定 AK 的客户端(保留完整内容,包括 YAML Front Matter)
|
||||
log.debug("推送到客户端: serverUrl={}, targetAK={}", serverUrl, client.getAk());
|
||||
String messageId = NotifyGrpcClient.pushMessage(
|
||||
serverUrl,
|
||||
|
||||
@@ -4,8 +4,8 @@ import cn.lihongjie.coal.base.dto.CommonQuery;
|
||||
import cn.lihongjie.coal.base.dto.IdRequest;
|
||||
import cn.lihongjie.coal.base.service.BaseService;
|
||||
import cn.lihongjie.coal.dbFunctions.DbFunctionService;
|
||||
import cn.lihongjie.coal.dingtalkBot.entity.DingtalkBotEntity;
|
||||
import cn.lihongjie.coal.dingtalkBot.service.DingtalkBotService;
|
||||
import cn.lihongjie.coal.dingtalkBotScene.service.DingtalkBotSceneService;
|
||||
import cn.lihongjie.coal.exception.BizException;
|
||||
import cn.lihongjie.coal.weightDeviceData.entity.WeightDeviceDataEntity;
|
||||
import cn.lihongjie.coal.weightDeviceData.repository.WeightDeviceDataRepository;
|
||||
@@ -56,6 +56,8 @@ public class WeightDeviceDataAlertService
|
||||
|
||||
@Autowired private DingtalkBotService dingtalkBotService;
|
||||
|
||||
@Autowired private DingtalkBotSceneService dingtalkBotSceneService;
|
||||
|
||||
@Autowired private ObjectMapper objectMapper;
|
||||
|
||||
public WeightDeviceDataAlertDto create(CreateWeightDeviceDataAlertDto request) {
|
||||
@@ -420,26 +422,18 @@ public class WeightDeviceDataAlertService
|
||||
notificationData.put("queryJson", valueObject);
|
||||
|
||||
log.info("notificationData: {}", objectMapper.writeValueAsString(notificationData));
|
||||
// 如果配置了钉钉场景,发送钉钉通知
|
||||
|
||||
// 如果配置了场景,使用统一的场景服务发送通知(支持钉钉机器人和通知客户端)
|
||||
if (alert.getScene() != null) {
|
||||
List<DingtalkBotEntity> bots = alert.getScene().getDingtalkBots();
|
||||
|
||||
if (bots != null && !bots.isEmpty()) {
|
||||
for (DingtalkBotEntity bot : bots) {
|
||||
try {
|
||||
dingtalkBotService.doSend(
|
||||
bot,
|
||||
alert.getScene().getDingtalkBotTemplate(),
|
||||
notificationData);
|
||||
|
||||
log.info("钉钉通知发送成功: 规则={}, Bot={}", alert.getName(), bot.getName());
|
||||
} catch (Exception e) {
|
||||
log.error("发送钉钉通知失败: Bot={}, 错误={}", bot.getName(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("规则 {} 配置了场景但没有可用的钉钉机器人", alert.getName());
|
||||
try {
|
||||
dingtalkBotSceneService.sendMessage(alert.getScene().getId(), notificationData);
|
||||
log.info("场景通知发送成功: 规则={}, 场景={}", alert.getName(), alert.getScene().getName());
|
||||
} catch (Exception e) {
|
||||
log.error("发送场景通知失败: 规则={}, 场景={}, 错误={}",
|
||||
alert.getName(), alert.getScene().getName(), e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
log.debug("规则 {} 未配置场景,跳过通知发送", alert.getName());
|
||||
}
|
||||
|
||||
log.info("发送告警通知: 规则={}, 实际吨数={}, 进度={}%, 记录数={}",
|
||||
|
||||
Reference in New Issue
Block a user