Files

566 lines
14 KiB
Markdown
Raw Permalink Normal View History

2025-12-06 13:07:16 +08:00
# V2 API 文档
V2 API 提供基于消息模板的发送接口,支持占位符替换和多实例发送。
::: tip ⭐ 推荐使用
V2 API(模板)是我们推荐的消息发送方式,相比 V1 API 具有以下优势:
-**内容复用** - 一次定义,多处使用,大幅提高开发效率
-**灵活性** - 通过占位符实现动态内容,兼顾固定格式和动态数据
-**易维护** - 修改消息内容无需改代码,运营人员可直接操作
-**版本控制** - 支持模板启用/禁用,便于灰度发布和回滚
-**团队协作** - 开发和运营分工明确,提高协作效率
适用于 90% 的消息发送场景,特别是有固定格式的通知类消息。
:::
## 接口概述
V2 API 与 V1 API 的主要区别:
| 特性 | V1 API | V2 API |
|------|--------|--------|
| 发送方式 | 基于任务 | 基于模板 |
| 内容定义 | API 调用时传递 | 模板预定义 |
| 动态内容 | 不支持 | 支持占位符 |
| 多格式 | 单一格式 | Text/HTML/Markdown |
| 安全性 | Token 加密 | Token 加密 |
## 接口地址
```
POST /api/v2/message/send
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| token | string | 是 | 加密的模板 Token |
| title | string | 是 | 消息标题 |
| placeholders | object | 否 | 占位符键值对 |
2025-12-14 12:43:09 +08:00
| recipients | array | 条件必填 | 动态接收者列表(群发模式)🆕 |
2025-12-06 13:07:16 +08:00
### 参数说明
#### token
- 模板的加密 Token,在管理后台的"消息模板"页面获取
- **注意**V2 API 只支持加密 Token,不支持明文模板 ID
- Token 使用对称加密算法生成,确保安全性
#### title
- 消息标题,会传递给所有支持标题的渠道(如邮件)
- 必填参数,不能为空
#### placeholders
- 占位符的键值对,用于替换模板中的 `{{key}}`
- 格式为 JSON 对象:`{"key": "value"}`
- 如果模板中定义了占位符但未传递,将使用默认值
- 如果既未传递也无默认值,占位符将保持原样
2025-12-14 12:43:09 +08:00
#### recipients 🆕
- 动态接收者列表,用于群发场景
- 格式为字符串数组:`["user1@example.com", "user2@example.com"]`
- **条件必填**:如果模板配置了动态接收实例,此参数为必填
- **支持的渠道**
- ✅ 邮件 - 多个收件人邮箱地址
- ✅ 微信公众号 - 多个用户 OpenID
- **使用限制**
- 一个模板只能配置一个动态接收实例
- 动态接收实例不能与固定接收实例混合使用
- 建议控制接收者数量,避免触发渠道限流
2025-12-06 13:07:16 +08:00
## 请求示例
### 基本示例
```bash
curl -X POST http://your-domain/api/v2/message/send \
-H "Content-Type: application/json" \
-d '{
"token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"title": "系统通知",
"placeholders": {
"username": "张三",
"action": "登录",
"time": "2024-12-06 12:00:00"
}
}'
```
### Python 示例
```python
import requests
import json
url = "http://your-domain/api/v2/message/send"
headers = {"Content-Type": "application/json"}
data = {
"token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"title": "系统通知",
"placeholders": {
"username": "张三",
"action": "登录",
"time": "2024-12-06 12:00:00"
}
}
response = requests.post(url, headers=headers, data=json.dumps(data))
print(response.json())
```
### Go 示例
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := "http://your-domain/api/v2/message/send"
data := map[string]interface{}{
"token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"title": "系统通知",
"placeholders": map[string]string{
"username": "张三",
"action": "登录",
"time": "2024-12-06 12:00:00",
},
}
jsonData, _ := json.Marshal(data)
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
```
### JavaScript/Node.js 示例
```javascript
const axios = require('axios');
const url = 'http://your-domain/api/v2/message/send';
const data = {
token: 'a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b',
title: '系统通知',
placeholders: {
username: '张三',
action: '登录',
time: '2024-12-06 12:00:00'
}
};
axios.post(url, data)
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error:', error);
});
```
2025-12-14 12:43:09 +08:00
### 动态接收者示例(群发)🆕
```javascript
const axios = require('axios');
const url = 'http://your-domain/api/v2/message/send';
const data = {
token: 'a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b',
title: '活动通知',
placeholders: {
activity_name: '双十二大促',
start_time: '2024-12-12 00:00:00',
discount: '全场8折'
},
recipients: [
'user1@example.com',
'user2@example.com',
'user3@example.com'
]
};
axios.post(url, data)
.then(response => {
console.log(response.data);
console.log(`成功发送给 ${response.data.data.count} 个接收者`);
})
.catch(error => {
console.error('Error:', error);
});
```
2025-12-06 13:07:16 +08:00
### Java 示例
```java
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class MessageSender {
public static void main(String[] args) throws Exception {
String url = "http://your-domain/api/v2/message/send";
String jsonData = """
{
"token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"title": "系统通知",
"placeholders": {
"username": "张三",
"action": "登录",
"time": "2024-12-06 12:00:00"
}
}
""";
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = jsonData.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
int responseCode = conn.getResponseCode();
System.out.println("Response Code: " + responseCode);
}
}
```
## 响应格式
### 成功响应
```json
{
"code": 200,
"msg": "success",
"data": {
"token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"count": 3
}
}
```
**响应字段说明:**
- `code` - 状态码,200 表示成功
- `msg` - 响应消息
- `data.token` - 使用的模板 Token
- `data.count` - 成功发送的实例数量
### 失败响应
```json
{
"code": 400,
"msg": "token解析失败:invalid token format",
"data": null
}
```
## 工作流程
1. **Token 解析** - 解密 Token 获取模板 ID
2. **模板查询** - 根据模板 ID 查询模板信息
3. **状态检查** - 检查模板是否启用
4. **占位符替换** - 使用传入的 placeholders 替换模板中的占位符
5. **实例遍历** - 获取模板关联的所有启用实例
6. **格式匹配** - 根据实例的 ContentType 选择对应格式的内容
7. **消息发送** - 向每个实例发送消息
8. **返回结果** - 返回发送成功的实例数量
## 占位符替换规则
### 基本规则
模板中使用 `{{key}}` 定义占位符,API 调用时通过 `placeholders` 参数传递替换值。
**模板内容:**
```text
您好,{{username}}
您的订单 {{order_id}} 已经 {{status}}。
```
**API 调用:**
```json
{
"placeholders": {
"username": "张三",
"order_id": "20241206001",
"status": "发货"
}
}
```
**替换结果:**
```text
您好,张三!
您的订单 20241206001 已经发货。
```
### 默认值处理
如果占位符定义了默认值,未传递时使用默认值:
**占位符定义:**
```json
[
{
"key": "username",
"label": "用户名",
"default": "Guest"
}
]
```
**API 调用(未传递 username):**
```json
{
"placeholders": {}
}
```
**替换结果:**
```text
您好,Guest
```
### 未定义占位符
如果占位符既未传递也无默认值,将保持原样:
```text
您好,{{username}} // username 未传递且无默认值
```
## 内容格式选择
V2 API 支持三种内容格式,系统会根据实例配置自动选择:
| 格式 | 适用渠道 | 说明 |
|------|---------|------|
| Text | 所有渠道 | 纯文本格式,兼容性最好 |
| HTML | 邮件 | 富文本格式,支持样式 |
| Markdown | 钉钉、企业微信 | Markdown 格式,支持格式化 |
**示例:**
假设模板定义了三种格式的内容:
- Text: `您好,{{username}}`
- HTML: `<h2>您好,{{username}}</h2>`
- Markdown: `## 您好,{{username}}`
当发送到不同实例时:
- **邮件实例(ContentType=html** → 使用 HTML 格式
- **钉钉实例(ContentType=markdown** → 使用 Markdown 格式
- **其他实例(ContentType=text** → 使用 Text 格式
## @提醒功能
如果模板配置了@提醒,会自动应用到支持的渠道(钉钉、企业微信)。
**模板配置:**
- @手机号`13800138000,13900139000`
- @用户ID`user001,user002`
- @所有人:是
**发送效果:**
- 钉钉/企业微信会@指定的手机号或用户
- 如果启用@所有人,会@群内所有成员
## 错误码说明
| 错误码 | 说明 | 解决方案 |
|--------|------|---------|
| 200 | 成功 | - |
| 400 | 请求参数错误 | 检查请求参数格式 |
| 400 | token解析失败 | 检查 Token 是否正确 |
| 400 | 模板不存在 | 检查模板 ID 是否有效 |
| 400 | 模板已禁用 | 在管理后台启用模板 |
| 400 | 模板没有配置发送实例 | 为模板添加发送实例 |
| 400 | 模板没有启用的发送实例 | 启用至少一个实例 |
| 500 | 服务器内部错误 | 联系管理员 |
## 获取模板 Token
### 方式一:管理后台查看
1. 登录 Message Nest 管理后台
2. 进入"消息模板"页面
3. 点击模板的"接口"按钮
4. 查看并复制加密的 Token
### 方式二:API 代码示例
管理后台提供多种语言的 API 调用示例,包含真实的加密 Token。
## 安全性说明
### Token 加密
- V2 API 使用对称加密算法保护模板 ID
- Token 是确定性加密,相同的模板 ID 生成相同的 Token
- 加密密钥存储在服务器端,客户端无需关心加密细节
### 最佳实践
1. **Token 保护**
- 不要在公开代码中硬编码 Token
- 使用环境变量或配置文件存储 Token
- 定期检查 Token 的使用情况
2. **权限控制**
- 合理设置模板的启用/禁用状态
- 及时禁用不再使用的模板
- 定期审查模板配置
3. **内容安全**
- 注意模板内容的合规性
- 避免在模板中包含敏感信息
- 对用户输入进行验证和过滤
## 使用场景
### 1. 用户通知
```json
{
"token": "...",
"title": "账号安全提醒",
"placeholders": {
"username": "张三",
"action": "登录",
"ip": "192.168.1.100",
"time": "2024-12-06 12:00:00"
}
}
```
### 2. 订单通知
```json
{
"token": "...",
"title": "订单状态更新",
"placeholders": {
"order_id": "20241206001",
"status": "已发货",
"tracking_number": "SF1234567890",
"estimated_delivery": "2024-12-08"
}
}
```
### 3. 系统告警
```json
{
"token": "...",
"title": "系统告警",
"placeholders": {
"service": "API Server",
"level": "严重",
"message": "CPU使用率超过90%",
"time": "2024-12-06 12:00:00"
}
}
```
### 4. 营销推广
```json
{
"token": "...",
"title": "优惠活动通知",
"placeholders": {
"username": "张三",
"product": "VIP会员",
"discount": "8折",
"expire_date": "2024-12-31"
}
}
```
## 性能优化
### 异步发送
V2 API 采用异步发送机制,API 调用立即返回,实际发送在后台进行。
**优点:**
- 快速响应,不阻塞调用方
- 支持批量发送多个实例
- 自动重试失败的发送
### 批量发送
一次 API 调用可以发送到多个实例(渠道),系统自动遍历所有启用的实例。
**示例:**
模板配置了 3 个实例:
- 邮件实例(启用)
- 钉钉实例(启用)
- 企业微信实例(禁用)
调用 API 后,消息会发送到邮件和钉钉,企业微信实例被跳过。
## 常见问题
### Q: V1 和 V2 API 可以同时使用吗?
**A:** 可以。V1 和 V2 API 是独立的,可以根据需求选择使用。
### Q: 如何从 V1 迁移到 V2
**A:**
1. 创建消息模板,定义占位符
2. 为模板配置发送实例
3. 获取模板 Token
4. 修改 API 调用代码,使用 V2 接口
### Q: 占位符可以嵌套吗?
**A:** 不支持。占位符只支持一级替换,不支持嵌套或递归。
### Q: 可以动态添加占位符吗?
**A:** 不可以。占位符必须在模板中预先定义,API 调用时只能传递已定义的占位符。
### Q: 如何调试模板?
**A:** 使用管理后台的"预览"功能,可以填写测试数据查看替换效果。
## 下一步
- 查看 [消息模板文档](/guide/template) 了解如何创建和管理模板
- 查看 [V1 API 文档](/api/usage) 了解传统的发送方式
- 查看 [API 示例](/api/examples) 了解更多调用示例