Files
Message-Push-Nest/docs/api/v1.md
T

20 KiB
Raw Blame History

V1 API 文档

V1 API 提供基于任务的消息推送接口,支持多渠道、多格式发送。

::: tip 💡 推荐使用 V2 API(模板) 对于所有新项目,我们强烈推荐使用 V2 API(模板)

  • 内容复用 - 模板可以在多个场景中复用,无需每次传递完整内容
  • 完全动态 - 通过占位符可以实现完全动态内容(甚至可以只用一个占位符)
  • 统一管理 - 在管理后台统一管理消息模板,便于维护
  • 版本控制 - 模板内容修改不影响 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 提示

  • tokentask_id 二选一,推荐使用加密的 token
  • texthtmlmarkdown 至少提供一种格式
  • 多种格式可以同时提供,系统会根据渠道自动选择 :::

@提醒参数(可选)

参数 类型 必填 说明
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 参数为必填
  • 不支持动态接收的渠道会忽略此参数
  • 建议控制接收者数量,避免触发渠道限流 :::

请求示例

基本示例(纯文本)

{
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e",
    "title": "系统通知",
    "text": "Hello World!"
}

多格式示例

{
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e",
    "title": "系统通知",
    "text": "您好,系统检测到异常登录。",
    "html": "<h2>系统通知</h2><p>您好,系统检测到<strong>异常登录</strong>。</p>",
    "markdown": "## 系统通知\n\n您好,系统检测到**异常登录**。"
}

带@提醒示例

{
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e",
    "title": "紧急告警",
    "text": "服务器CPU使用率超过90%,请及时处理!",
    "markdown": "## 紧急告警\n\n服务器CPU使用率超过**90%**,请及时处理!",
    "at_mobiles": ["13800138000", "13900139000"],
    "at_all": false
}

动态接收者示例(群发)🆕

{
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e",
    "title": "系统维护通知",
    "text": "系统将于今晚22:00进行维护,预计持续2小时。",
    "html": "<h2>系统维护通知</h2><p>系统将于今晚<strong>22:00</strong>进行维护,预计持续<strong>2小时</strong>。</p>",
    "recipients": [
        "user1@example.com",
        "user2@example.com",
        "user3@example.com"
    ]
}

cURL 示例

curl -X POST http://your-domain/api/v1/message/send \
  -H "Content-Type: application/json" \
  -d '{
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e",
    "title": "系统通知",
    "text": "Hello World!"
  }'

响应格式

成功响应

{
    "code": 200,
    "msg": "success",
    "data": {
        "status": "sent"
    }
}

失败响应

{
    "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 格式)

{
  "text": "纯文本内容",
  "html": "<h1>HTML内容</h1>",
  "markdown": "# Markdown内容"
}

发送结果:使用 html 内容

场景 2:钉钉渠道(配置为 Markdown 格式)

{
  "text": "纯文本内容",
  "markdown": "# Markdown内容"
}

发送结果:使用 markdown 内容

场景 3:只提供纯文本

{
  "text": "纯文本内容"
}

发送结果:所有渠道都使用 text 内容(兼容性最好)

::: tip 最佳实践

  • 邮件渠道:推荐提供 html 格式,视觉效果更好
  • 钉钉/企业微信:推荐提供 markdown 格式,支持富文本
  • 通用场景:至少提供 text 格式,确保所有渠道都能正常发送
  • 多渠道任务:同时提供多种格式,让系统自动选择最佳格式 :::

@提醒功能

@提醒功能允许在钉钉、企业微信等支持的渠道中@特定用户或所有人。

支持的渠道

渠道 @手机号 @用户ID @所有人
钉钉
企业微信
邮件
其他

使用示例

@指定手机号

{
  "token": "...",
  "title": "系统告警",
  "text": "服务器异常,请及时处理",
  "at_mobiles": ["13800138000", "13900139000"]
}

@指定用户ID

{
  "token": "...",
  "title": "任务通知",
  "text": "您的任务已完成",
  "at_userids": ["user001", "user002"]
}

@所有人

{
  "token": "...",
  "title": "重要通知",
  "text": "系统将于今晚22:00进行维护",
  "at_all": true
}

组合使用

{
  "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. 多格式支持

为了确保消息在不同渠道都有良好的展示效果,建议同时提供多种格式:

{
  "token": "...",
  "title": "订单通知",
  "text": "您的订单已发货,订单号:20241206001",
  "html": "<h2>订单通知</h2><p>您的订单已发货</p><p>订单号:<strong>20241206001</strong></p>",
  "markdown": "## 订单通知\n\n您的订单已发货\n\n订单号:**20241206001**"
}

2. 合理使用@提醒

只在需要紧急通知时使用@提醒:

{
  "token": "...",
  "title": "紧急告警",
  "text": "生产环境出现严重问题",
  "at_mobiles": ["13800138000"],  // 只@相关负责人
  "at_all": false  // 避免@所有人
}

3. 错误处理

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_mobilesat_useridsat_all

Q: 可以只发送 HTML 格式吗?

A: 可以,但建议同时提供 text 格式作为备选,确保不支持 HTML 的渠道也能正常发送。

Q: Token 和 task_id 有什么区别?

A:

  • token:加密的任务标识,更安全,推荐使用
  • task_id:明文的任务ID,不推荐在生产环境使用

各语言调用示例

本节提供各种编程语言的完整调用示例代码。

cURL

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

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())

安装依赖:

pip install requests

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

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<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        
        System.out.println(response.body());
    }
}

Node.js

使用 request 库

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 库

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+)

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
$ch = curl_init();

$data = array(
    "token" => "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#

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

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
  • 所有示例都使用基本的纯文本格式,实际使用时可以添加 htmlmarkdown 等参数 :::

下一步