# V1 API 文档 V1 API 提供基于任务的消息推送接口,支持多渠道、多格式发送。 ::: tip 💡 推荐使用 V2 API(模板) 对于**所有新项目**,我们强烈推荐使用 [V2 API(模板)](/api/v2): - ✅ **内容复用** - 模板可以在多个场景中复用,无需每次传递完整内容 - ✅ **完全动态** - 通过占位符可以实现完全动态内容(甚至可以只用一个占位符) - ✅ **统一管理** - 在管理后台统一管理消息模板,便于维护 - ✅ **版本控制** - 模板内容修改不影响 API 调用代码 - ✅ **更安全** - 使用加密 Token,不暴露模板 ID ::: warning 关于 V1 API V1 API 仅为兼容历史数据而保留,不推荐在新项目中使用。后续的功能优化和维护重点都在 V2 API(模板)上。 ::: ## 接口地址 ``` POST /api/v1/message/send ``` ## 请求参数 ### 基本参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | token | string | 是 | 推送令牌(加密),在管理后台查看 | | task_id | string | 否 | 任务ID(明文),与 token 二选一 | | title | string | 是 | 消息标题 | | text | string | 否 | 纯文本格式内容 | | html | string | 否 | HTML 格式内容 | | markdown | string | 否 | Markdown 格式内容 | ::: tip 提示 - `token` 和 `task_id` 二选一,推荐使用加密的 `token` - `text`、`html`、`markdown` 至少提供一种格式 - 多种格式可以同时提供,系统会根据渠道自动选择 ::: ### @提醒参数(可选) | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | at_mobiles | array | 否 | @手机号列表,如 `["13800138000", "13900139000"]` | | at_userids | array | 否 | @用户ID列表,如 `["user001", "user002"]` | | at_all | boolean | 否 | 是否@所有人,默认 `false` | ::: warning 注意 @提醒功能仅在支持的渠道(钉钉、企业微信)中生效。 ::: ### 动态接收者参数(可选)🆕 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | recipients | array | 否 | 动态接收者列表,如 `["user1@example.com", "user2@example.com"]` | ::: tip 群发模式 动态接收者功能允许在 API 调用时指定接收者列表,实现群发功能。 **支持的渠道:** - ✅ **邮件** - 支持多个收件人,实现邮件群发 - ✅ **微信公众号** - 支持多个 OpenID,实现公众号群发 **使用条件:** 1. 任务实例必须配置为"动态接收者模式" 2. 一个任务只能配置一个动态接收实例 3. 动态接收实例不能与固定接收实例混合使用 **配置方式:** 在任务编辑页面,添加实例时勾选"动态接收者模式",此时无需配置固定接收者。 **注意事项:** - 如果任务配置了动态接收实例,`recipients` 参数为必填 - 不支持动态接收的渠道会忽略此参数 - 建议控制接收者数量,避免触发渠道限流 ::: ## 请求示例 ### 基本示例(纯文本) ```json { "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "系统通知", "text": "Hello World!" } ``` ### 多格式示例 ```json { "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "系统通知", "text": "您好,系统检测到异常登录。", "html": "

系统通知

您好,系统检测到异常登录

", "markdown": "## 系统通知\n\n您好,系统检测到**异常登录**。" } ``` ### 带@提醒示例 ```json { "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "紧急告警", "text": "服务器CPU使用率超过90%,请及时处理!", "markdown": "## 紧急告警\n\n服务器CPU使用率超过**90%**,请及时处理!", "at_mobiles": ["13800138000", "13900139000"], "at_all": false } ``` ### 动态接收者示例(群发)🆕 ```json { "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "系统维护通知", "text": "系统将于今晚22:00进行维护,预计持续2小时。", "html": "

系统维护通知

系统将于今晚22:00进行维护,预计持续2小时

", "recipients": [ "user1@example.com", "user2@example.com", "user3@example.com" ] } ``` ### cURL 示例 ```bash curl -X POST http://your-domain/api/v1/message/send \ -H "Content-Type: application/json" \ -d '{ "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "系统通知", "text": "Hello World!" }' ``` ## 响应格式 ### 成功响应 ```json { "code": 200, "msg": "success", "data": { "status": "sent" } } ``` ### 失败响应 ```json { "code": 400, "msg": "error message", "data": null } ``` ## 获取 Token 1. 登录 Message Nest 管理后台 2. 进入"发送任务"页面 3. 创建新的发送任务 4. 配置推送渠道(邮件、钉钉、企业微信等) 5. 保存后获得推送令牌(Token) ## 消息格式优先级 V1 API 支持三种消息格式,系统会根据任务实例配置的格式类型自动选择对应的内容。 ### 格式选择规则 当任务实例配置了特定格式类型时,系统按以下优先级选择内容: | 实例配置格式 | 优先级顺序 | 说明 | |------------|-----------|------| | **HTML** | html → markdown → text | 优先使用 HTML,其次 Markdown,最后纯文本 | | **Markdown** | markdown → text → html | 优先使用 Markdown,其次纯文本,最后 HTML | | **Text** | text → markdown → html | 优先使用纯文本,其次 Markdown,最后 HTML | ### 示例说明 **场景 1:邮件渠道(配置为 HTML 格式)** ```json { "text": "纯文本内容", "html": "

HTML内容

", "markdown": "# Markdown内容" } ``` 发送结果:使用 `html` 内容 **场景 2:钉钉渠道(配置为 Markdown 格式)** ```json { "text": "纯文本内容", "markdown": "# Markdown内容" } ``` 发送结果:使用 `markdown` 内容 **场景 3:只提供纯文本** ```json { "text": "纯文本内容" } ``` 发送结果:所有渠道都使用 `text` 内容(兼容性最好) ::: tip 最佳实践 - **邮件渠道**:推荐提供 `html` 格式,视觉效果更好 - **钉钉/企业微信**:推荐提供 `markdown` 格式,支持富文本 - **通用场景**:至少提供 `text` 格式,确保所有渠道都能正常发送 - **多渠道任务**:同时提供多种格式,让系统自动选择最佳格式 ::: ## @提醒功能 @提醒功能允许在钉钉、企业微信等支持的渠道中@特定用户或所有人。 ### 支持的渠道 | 渠道 | @手机号 | @用户ID | @所有人 | |------|--------|--------|--------| | 钉钉 | ✅ | ✅ | ✅ | | 企业微信 | ✅ | ✅ | ✅ | | 邮件 | ❌ | ❌ | ❌ | | 其他 | ❌ | ❌ | ❌ | ### 使用示例 #### @指定手机号 ```json { "token": "...", "title": "系统告警", "text": "服务器异常,请及时处理", "at_mobiles": ["13800138000", "13900139000"] } ``` #### @指定用户ID ```json { "token": "...", "title": "任务通知", "text": "您的任务已完成", "at_userids": ["user001", "user002"] } ``` #### @所有人 ```json { "token": "...", "title": "重要通知", "text": "系统将于今晚22:00进行维护", "at_all": true } ``` #### 组合使用 ```json { "token": "...", "title": "紧急告警", "markdown": "## 紧急告警\n\n生产环境出现严重问题!", "at_mobiles": ["13800138000"], "at_userids": ["admin"], "at_all": false } ``` ::: warning 注意事项 1. @提醒只在支持的渠道中生效,其他渠道会忽略这些参数 2. 钉钉机器人需要配置相应的权限才能使用@功能 3. @所有人功能需谨慎使用,避免打扰群成员 4. 手机号和用户ID格式需符合对应平台的要求 ::: ## 支持的推送渠道 | 渠道 | 支持格式 | @提醒 | 动态接收者 | 说明 | |------|---------|------|-----------|------| | **邮件** | Text, HTML | ❌ | ✅ | SMTP邮件发送,支持群发多个收件人 | | **钉钉** | Text, Markdown | ✅ | ❌ | 钉钉机器人,支持 Markdown 富文本 | | **企业微信** | Text, Markdown | ✅ | ❌ | 企业微信机器人,支持 Markdown | | **微信公众号** | Text | ❌ | ✅ | 微信测试公众号,支持多个 OpenID 群发 | | **自定义Webhook** | Text, HTML, Markdown | ❌ | ❌ | 自定义HTTP请求,格式取决于配置 | | **自托管消息** | Text, HTML, Markdown | ❌ | ❌ | 站内消息,支持多种格式 | ## 使用流程 1. **创建推送渠道** - 在管理后台配置各种推送渠道 - 填写相应的配置信息(如邮箱、Webhook地址等) 2. **创建发送任务** - 选择要使用的推送渠道 - 可以选择多个渠道同时推送 - 获得唯一的推送令牌(Token) 3. **调用API发送消息** - 使用获得的 Token - 发送标题和内容 - 消息会自动推送到配置的所有渠道 ## 工作流程 1. **Token 解析** - 解密 Token 获取任务 ID(或直接使用 task_id) 2. **任务查询** - 根据任务 ID 查询任务信息 3. **实例遍历** - 获取任务关联的所有启用实例 4. **格式选择** - 根据实例配置的格式类型选择对应内容 5. **@提醒处理** - 如果提供了@参数且渠道支持,添加@提醒 6. **消息发送** - 向每个实例发送消息 7. **返回结果** - 返回发送状态 ## 注意事项 ::: warning 重要 - **Token 安全**:Token 是唯一的,请妥善保管,不要在公开代码中硬编码 - **格式兼容**:至少提供一种格式(text/html/markdown),推荐提供多种格式 - **异步调用**:建议使用异步方式调用 API,避免阻塞主流程 - **@提醒限制**:@功能仅在钉钉、企业微信等支持的渠道中生效 - **格式优先级**:系统会根据实例配置自动选择最合适的格式 ::: ## 最佳实践 ### 1. 多格式支持 为了确保消息在不同渠道都有良好的展示效果,建议同时提供多种格式: ```json { "token": "...", "title": "订单通知", "text": "您的订单已发货,订单号:20241206001", "html": "

订单通知

您的订单已发货

订单号:20241206001

", "markdown": "## 订单通知\n\n您的订单已发货\n\n订单号:**20241206001**" } ``` ### 2. 合理使用@提醒 只在需要紧急通知时使用@提醒: ```json { "token": "...", "title": "紧急告警", "text": "生产环境出现严重问题", "at_mobiles": ["13800138000"], // 只@相关负责人 "at_all": false // 避免@所有人 } ``` ### 3. 错误处理 ```python import requests import json def send_message(token, title, text): url = "http://your-domain/api/v1/message/send" data = { "token": token, "title": title, "text": text } try: response = requests.post(url, json=data, timeout=10) result = response.json() if result['code'] == 200: print("发送成功") else: print(f"发送失败:{result['msg']}") except Exception as e: print(f"请求异常:{e}") ``` ## 错误码说明 | 错误码 | 说明 | |--------|------| | 200 | 成功 | | 400 | 请求参数错误 | | 401 | 未授权 | | 404 | Token不存在 | | 500 | 服务器内部错误 | ## 常见问题 ### Q: V1 和 V2 API 有什么区别?应该选择哪个? **A:** | 特性 | V1 API(任务) | V2 API(模板)⭐ 推荐 | |------|--------------|-------------------| | **内容定义** | API 调用时传递 | 预定义在模板中 | | **动态内容** | 完全动态 | 通过占位符替换 | | **内容复用** | 每次都要传递完整内容 | 模板可复用 | | **维护成本** | 修改内容需要改代码 | 只需修改模板 | | **适用场景** | 完全动态、不重复的内容 | 有固定格式的通知消息 | **推荐使用 V2 API(模板)的原因:** 1. **提高开发效率** - 一次定义模板,多处使用 2. **降低维护成本** - 内容修改无需改代码 3. **统一管理** - 所有消息模板集中管理 4. **更好的协作** - 运营人员可以直接修改模板内容 5. **版本控制** - 模板支持启用/禁用,方便灰度发布 6. **完全动态** - 通过占位符同样可以实现完全动态内容 ::: warning V1 API 的定位 V1 API 仅为兼容历史数据而保留,**不推荐在任何新项目中使用**。即使是完全动态的内容,也可以通过模板 + 占位符的方式实现,且更易于后期维护。 后续的功能优化和维护重点都在 V2 API(模板)上。 ::: ### Q: 如何选择使用哪种格式? **A:** 根据渠道特性选择: - **邮件**:推荐 HTML,视觉效果好 - **钉钉/企业微信**:推荐 Markdown,支持富文本 - **通用**:使用 Text,兼容性最好 - **多渠道**:同时提供多种格式,让系统自动选择 ### Q: @提醒不生效怎么办? **A:** 检查以下几点: 1. 渠道是否支持@提醒(仅钉钉、企业微信支持) 2. 机器人是否有@权限 3. 手机号或用户ID格式是否正确 4. 参数名称是否正确(`at_mobiles`、`at_userids`、`at_all`) ### Q: 可以只发送 HTML 格式吗? **A:** 可以,但建议同时提供 text 格式作为备选,确保不支持 HTML 的渠道也能正常发送。 ### Q: Token 和 task_id 有什么区别? **A:** - **token**:加密的任务标识,更安全,推荐使用 - **task_id**:明文的任务ID,不推荐在生产环境使用 ## 各语言调用示例 本节提供各种编程语言的完整调用示例代码。 ### cURL ```bash curl -X POST --location 'http://127.0.0.1:8000/api/v1/message/send' \ --header 'Content-Type: application/json' \ --data '{ "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "message title", "text": "Hello World!" }' ``` ### Python ```python import requests headers = { 'Content-Type': 'application/json', } json_data = { "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "message title", "text": "Hello World!" } response = requests.post( 'http://127.0.0.1:8000/api/v1/message/send', headers=headers, json=json_data ) print("response:", response.json()) ``` **安装依赖:** ```bash pip install requests ``` ### Go ```go package main import ( "fmt" "io" "log" "net/http" "strings" ) func main() { client := &http.Client{} var data = strings.NewReader(`{ "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "message title", "text": "Hello World!" }`) req, err := http.NewRequest("POST", "http://127.0.0.1:8000/api/v1/message/send", data) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() bodyText, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", bodyText) } ``` ### Java ```java import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; public class MessageNestExample { public static void main(String[] args) throws IOException, InterruptedException { HttpClient client = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) .build(); String jsonData = """ { "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "message title", "text": "Hello World!" } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://127.0.0.1:8000/api/v1/message/send")) .POST(BodyPublishers.ofString(jsonData)) .setHeader("Content-Type", "application/json") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ### Node.js #### 使用 request 库 ```javascript var request = require('request'); var headers = { 'Content-Type': 'application/json' }; var dataString = JSON.stringify({ "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title": "message title", "text": "Hello World!" }); var options = { url: 'http://127.0.0.1:8000/api/v1/message/send', method: 'POST', headers: headers, body: dataString }; function callback(error, response, body) { if (!error && response.statusCode == 200) { console.log(body); } } request(options, callback); ``` #### 使用 axios 库 ```javascript const axios = require('axios'); const data = { token: "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", title: "message title", text: "Hello World!" }; axios.post('http://127.0.0.1:8000/api/v1/message/send', data, { headers: { 'Content-Type': 'application/json' } }) .then(response => { console.log('response:', response.data); }) .catch(error => { console.error('error:', error); }); ``` #### 使用 fetch (Node.js 18+) ```javascript const data = { token: "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", title: "message title", text: "Hello World!" }; fetch('http://127.0.0.1:8000/api/v1/message/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => response.json()) .then(data => { console.log('response:', data); }) .catch(error => { console.error('error:', error); }); ``` ### PHP ```php "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", "title" => "message title", "text" => "Hello World!" ); curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:8000/api/v1/message/send'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } else { echo $response; } curl_close($ch); ?> ``` ### C# ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { using var client = new HttpClient(); var data = new { token = "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e", title = "message title", text = "Hello World!" }; var json = JsonSerializer.Serialize(data); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync( "http://127.0.0.1:8000/api/v1/message/send", content ); var responseString = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseString); } } ``` ### Ruby ```ruby require 'net/http' require 'json' require 'uri' uri = URI('http://127.0.0.1:8000/api/v1/message/send') http = Net::HTTP.new(uri.host, uri.port) request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' }) request.body = { token: 'a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e', title: 'message title', text: 'Hello World!' }.to_json response = http.request(request) puts response.body ``` ### 示例说明 ::: tip 提示 - 将示例中的 `http://127.0.0.1:8000` 替换为你的实际服务地址 - 将 `a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e` 替换为你在管理后台创建的实际 Token - 建议在生产环境中使用 HTTPS - 所有示例都使用基本的纯文本格式,实际使用时可以添加 `html`、`markdown` 等参数 ::: ## 下一步 - 查看 [V2 API 文档](/api/v2) 了解基于模板的发送方式 - 查看 [消息模板](/guide/template) 了解如何使用模板功能