如何实现微信发消息通知及MCP服务开发

前言

本文完整演示如何在 Spring Boot + Spring AI MCP(Model Context Protocol)框架下,接入微信公众号模板消息通知能力,并将其以“AI 工具”MCP服务的形式对外提供,供 IDE/智能体直接调用。你将看到从架构分层、代码实现、配置运行到 MCP 客户端集成的全链路实践。

目标读者

  • 需要在业务中实现“通过微信公众号发送通知”的后端开发
  • 希望将传统 HTTP 能力封装为 AI 工具(MCP)并在 IDE/Agent 中一键调用的开发者
  • 关注 DDD 分层与可维护性架构实践的工程师

核心能力一览

  • 微信模板消息发送:构造模板数据、调用微信接口推送
  • AccessToken 缓存:Guava Cache 本地缓存,避免频繁获取
  • MCP 工具暴露:用 @Tool 将能力注册为模型可调用工具
  • DDD 分层:领域服务/端口、基础设施适配器、网关隔离

架构设计与目录概览

采用 DDD 分层,清晰划分领域与基础设施职责:

src/main/java/org/cheese/mcp/server/weixin/
├── McpServerApplication.java           # 启动类与Bean装配
├── domain/                             # 领域层
│   ├── adapter/IWeiXiPort.java         # 端口接口(面向领域)
│   ├── model/
│   │   ├── WeiXinNoticeFunctionRequest.java
│   │   └── WeiXinNoticeFunctionResponse.java
│   └── service/WeiXinNoticeService.java# 领域服务,暴露@Tool
├── infrastructure/                     # 基础设施层
│   ├── adapter/WeiXiPort.java          # 端口实现,调微信网关
│   └── gateway/
│       ├── IWeixinApiService.java      # Retrofit2 微信API定义
│       └── dto/
│           ├── WeixinTemplateMessageDTO.java
│           └── WeixinTokenResponseDTO.java
└── types/properties/WeiXinApiProperties.java # 配置属性

这种分层好处是:

  • 领域层通过端口抽象依赖外部实现,内聚业务语义,降低耦合;
  • 基础设施层专注对外部系统(微信)的集成,变更可控、可替换;
  • 工具注册放在领域服务,使 AI 侧调用语义直观清晰。

业务调用链与关键代码

1) 领域服务:将能力注册为 MCP 工具

引入SpringAI框架的MCP服务依赖

 <dependency>
	 <groupId>org.springframework.ai</groupId>
	 <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
 </dependency>

下面是 WeiXinNoticeService 的核心逻辑,@Tool 注解会把方法暴露给 MCP 框架,供智能体直接调用:

@Tool(description = "微信公众号消息通知")
public WeiXinNoticeFunctionResponse weixinNotice(WeiXinNoticeFunctionRequest request) throws IOException {
    log.info("微信消息通知,平台:{} 主题:{} 描述:{}", request.getPlatform(), request.getSubject(), request.getDescription());
    return weiXiPort.weixinNotice(request);
}

调用链路:AI → MCP 工具 weixinNotice → 端口接口 IWeiXiPort → 端口实现 WeiXiPort

2) 基础设施适配器:获取 Token 并发送模板消息

获取一个测试公众号:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
如下图,获取自己的wxid参数
获得自己的id
关注自己的测试公众号,获得touser。新增一个测试模版,格式如图所示:
在这里插入图片描述

WeiXiPort 是对外系统(微信API)的适配层,实现了获取 AccessToken、组装模板数据、发送消息的完整流程:

@Override
public WeiXinNoticeFunctionResponse weixinNotice(WeiXinNoticeFunctionRequest request) throws IOException {
    // 1. 获取 accessToken
    String accessToken = weixinAccessToken.getIfPresent(properties.getAppid());
    if (null == accessToken) {
        Call<WeixinTokenResponseDTO> call = weixinApiService.getToken("client_credential", properties.getAppid(), properties.getAppsecret());
        WeixinTokenResponseDTO weixinTokenResponseDTO = call.execute().body();
        assert weixinTokenResponseDTO != null;
        accessToken = weixinTokenResponseDTO.getAccess_token();
        weixinAccessToken.put(properties.getAppid(), accessToken);
    }

    // 2. 发送模板消息
    Map<String, Map<String, String>> data = new HashMap<>();
    WeixinTemplateMessageDTO.put(data, WeixinTemplateMessageDTO.TemplateKey.platform, request.getPlatform());
    WeixinTemplateMessageDTO.put(data, WeixinTemplateMessageDTO.TemplateKey.subject, request.getSubject());
    WeixinTemplateMessageDTO.put(data, WeixinTemplateMessageDTO.TemplateKey.description, request.getDescription());

    WeixinTemplateMessageDTO templateMessageDTO = new WeixinTemplateMessageDTO(properties.getTouser(), properties.getTemplate_id());
    templateMessageDTO.setUrl(request.getJumpUrl());
    templateMessageDTO.setData(data);

    Call<Void> call = weixinApiService.sendMessage(accessToken, templateMessageDTO);
    call.execute();

    WeiXinNoticeFunctionResponse weiXinNoticeFunctionResponse = new WeiXinNoticeFunctionResponse();
    weiXinNoticeFunctionResponse.setSuccess(true);

    return weiXinNoticeFunctionResponse;
}

要点:

  • 本地 Cache<String, String> weixinAccessToken 缓存 Token,按 appId 维度复用;
  • 通过 WeixinTemplateMessageDTO 按模板键 platform/subject/description 组装数据;
  • 最终调用 IWeixinApiService.sendMessage 将模板消息推送出去。

3) 微信网关接口:Retrofit2 定义

public interface IWeixinApiService {

    /**
     * 获取 Access token
     * 文档:<a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html">Get_access_token</a>
     *
     * @param grantType 获取access_token填写client_credential
     * @param appId     第三方用户唯一凭证
     * @param appSecret 第三方用户唯一凭证密钥,即appsecret
     * @return 响应结果
     */
    @GET("cgi-bin/token")
    Call<WeixinTokenResponseDTO> getToken(
            @Query("grant_type") String grantType,
            @Query("appid") String appId,
            @Query("secret") String appSecret
    );

    /**
     * 发送微信公众号模板消息
     * 文档:<a href="https://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl">https://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl</a>
     *
     * @param accessToken              getToken 获取的 token 信息
     * @param weixinTemplateMessageDTO 入参对象
     * @return 应答结果
     */
    @POST("cgi-bin/message/template/send")
    Call<Void> sendMessage(@Query("access_token") String accessToken, @Body WeixinTemplateMessageDTO weixinTemplateMessageDTO);
}

4) 模板消息 DTO:统一键定义与便捷 put

public class WeixinTemplateMessageDTO {
    private String touser = "";
    private String template_id = "";
    private String url = "https://weixin.qq.com";
    private Map<String, Map<String, String>> data = new HashMap<>();

    public static void put(Map<String, Map<String, String>> data, TemplateKey key, String value) {
        data.put(key.getCode(), new HashMap<>() {{ put("value", value); }});
    }

    public enum TemplateKey {
        platform("platform_name","平台"),
        subject("article_name","主题"),
        description("date_name","简述"),
        ;
        // ... getter/setter 省略
    }
}

生产环境请将默认字段改为由配置注入,避免硬编码。


配置与运行

application.yml 关键项

  api:
    original_id: gh_a551e78c8
    app-id: wx08140ecee889
    app-secret: 9360a58ed62642
    template_id: PhDCRzPTLJCCCFGF4cTM
    touser: olI3g16yOowoXDxU

建议通过环境变量或启动参数覆盖以上敏感配置,避免明文入库。

以 Jar 方式运行

mvn clean package
java -jar target/mcp-server-weixin-1.0.0.jar \
  --weixin.api.original_id=gh_xxx \
  --weixin.api.app-id=wx_xxx \
  --weixin.api.app-secret=xxx \
  --weixin.api.template_id=TEMPLATE_ID \
  --weixin.api.touser=OPEN_ID

在 MCP 客户端(IDE/Agent)中启用

将服务注册为 MCP Server(示例片段,按你的客户端要求适配):

{
  "mcpServers": {
    "mcp-server-weixin": {
      "command": "java",
      "args": [
        "-Dspring.ai.mcp.server.stdio=true",
        "-jar",
        "/Users/mumu/Applications/apache-maven-3.8.4/repository/org/cheese/mcp/mcp-server-weixin/1.0.0/mcp-server-weixin-1.0.0.jar",
        "--weixin.api.original_id=gh_e06e056",
        "--weixin.api.app-id=wx5a8a91f",
        "--weixin.api.app-secret=0bea03e79dd8703928",
        "--weixin.api.template_id=O8qI6gy7aMff9WSBb16cFk",
        "--weixin.api.touser=or0Ab6ivw2T6SvU"
      ]
    }
  }
}

上述为演示配置,请替换为你的 Jar 路径与安全参数。


端到端调用示例(从 AI 到微信)

当 IDE/Agent 侧需要发送通知时,直接调用 weixinNotice 工具,传入领域请求:

{
  "platform": "GitHub",
  "subject": "新的 Pull Request",
  "description": "有代码更新,请及时 Review",
  "jumpUrl": "https://github.com/your/repo/pull/123"
}

服务会:

  • 记录日志并进入端口实现;
  • 拿到/刷新 AccessToken;
  • 组合模板数据并调用微信 API 发送。

测试一下

    @org.junit.Test
    public void test_weixinNotice() {
        String userInput = """
                我需要你帮我生成一篇文章,要求如下;
                                
                1. 场景为互联网大厂java求职者面试
                2. 面试管提问 Java 核心知识、JUC、JVM、多线程、线程池、HashMap、ArrayList、Spring、SpringBoot、MyBatis、Dubbo、RabbitMQ、xxl-job、Redis、MySQL、Linux、Docker、设计模式、DDD等不限于此的各项技术问题。
                3. 按照故事场景,以严肃的面试官和搞笑的水货程序员谢飞机进行提问,谢飞机对简单问题可以回答,回答好了面试官还会夸赞。复杂问题胡乱回答,回答的不清晰。
                4. 每次进行3轮提问,每轮可以有3-5个问题。这些问题要有技术业务场景上的衔接性,循序渐进引导提问。最后是面试官让程序员回家等通知类似的话术。
                5. 提问后把问题的答案,写到文章最后,最后的答案要详细讲述出技术点,让小白可以学习下来。
                                                                
                调用工具,将以上内容发布文章到CSDN。     
                                
                并进行微信公众号消息通知,调用通知工具,参数信息获取如下,平台:CSDN、文章:为文章标题 、简述:为文章简述、跳转地址:从发布文章的回复中获取 url
                
                """;

        System.out.println("\n>>> QUESTION: " + userInput);
        ChatResponse response = chatClient.prompt().user(userInput).call().chatResponse();
        System.out.println(response); 
    }

在这里插入图片描述

  • 敏感信息管理:统一使用启动参数/环境变量/密钥管理服务注入,不要写死在 application.yml
  • 可观察性:为 Token 拉取、消息请求/响应增加结构化日志与告警;必要时落盘审计(见 data/log/)。有时AI调用失败,可以打日志,debug模式排查。

结语

通过将微信公众号通知能力以 MCP 工具的方式对外暴露,我们把“传统通知渠道”无缝接入到 AI 工作流中。DDD 分层保证了良好的可维护性与扩展性,MCP 则提供了模型可调用的标准协议。你可以按相同模式扩展到企业微信、钉钉、短信/邮件等渠道,逐步沉淀“通知中台”并赋能智能体。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值