企业微信第三方应用API对接的Java后端架构设计:解耦与可扩展性实践

企业微信第三方应用API对接的Java后端架构设计:解耦与可扩展性实践

企业微信第三方应用需处理多租户(不同企业)、动态凭证(suite_access_token、provider_access_token、access_token)及事件回调。若将逻辑硬编码在Controller中,将导致代码臃肿、难以维护。本文基于wlkankan.cn.wecom.thirdparty包,通过策略模式、事件驱动与配置中心实现高内聚低耦合架构。

核心凭证分层模型

java 复制代码
package wlkankan.cn.wecom.thirdparty.model;

public class SuiteConfig {
    private String suiteId;
    private String suiteSecret;
    private String token; // 回调token
    private String aesKey;
}

public class CorpAuthInfo {
    private String corpId;
    private String permanentCode;
    private String suiteId;
}

多级Token管理器

java 复制代码
package wlkankan.cn.wecom.thirdparty.token;

import wlkankan.cn.wecom.thirdparty.model.SuiteConfig;
import wlkankan.cn.wecom.thirdparty.model.CorpAuthInfo;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class ThirdPartyTokenManager {
    private final ConcurrentHashMap<String, String> suiteTokenCache = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, String> corpTokenCache = new ConcurrentHashMap<>();

    public String getSuiteAccessToken(SuiteConfig config) {
        return suiteTokenCache.computeIfAbsent(config.getSuiteId(), k -> fetchSuiteToken(config));
    }

    public String getCorpAccessToken(CorpAuthInfo authInfo, SuiteConfig suiteConfig) {
        String key = authInfo.getCorpId() + ":" + authInfo.getSuiteId();
        return corpTokenCache.computeIfAbsent(key, k -> fetchCorpToken(authInfo, suiteConfig));
    }

    private String fetchSuiteToken(SuiteConfig config) {
        // 调用 /cgi-bin/service/get_suite_token
        return "mock_suite_token";
    }

    private String fetchCorpToken(CorpAuthInfo authInfo, SuiteConfig suiteConfig) {
        // 调用 /cgi-bin/service/get_corp_token
        return "mock_corp_token";
    }
}

事件回调处理器抽象

java 复制代码
package wlkankan.cn.wecom.thirdparty.event;

import wlkankan.cn.wecom.thirdparty.model.CorpAuthInfo;

public interface WeComEventHandler {
    boolean supports(String msgType);
    void handle(CorpAuthInfo corpInfo, Object eventPayload);
}

具体事件实现:通讯录变更

java 复制代码
package wlkankan.cn.wecom.thirdparty.event.impl;

import wlkankan.cn.wecom.thirdparty.event.WeComEventHandler;
import wlkankan.cn.wecom.thirdparty.model.CorpAuthInfo;
import org.springframework.stereotype.Component;

@Component
public class UserUpdateEventHandler implements WeComEventHandler {

    @Override
    public boolean supports(String msgType) {
        return "change_user".equals(msgType);
    }

    @Override
    public void handle(CorpAuthInfo corpInfo, Object eventPayload) {
        // 解析 eventPayload 为 ChangeUserEvent
        // 同步至本地数据库
        System.out.println("Handling user update for " + corpInfo.getCorpId());
    }
}

回调入口统一路由

java 复制代码
package wlkankan.cn.wecom.thirdparty.controller;

import wlkankan.cn.wecom.thirdparty.event.WeComEventHandler;
import wlkankan.cn.wecom.thirdparty.service.CallbackService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/wecom/callback")
public class WeComCallbackController {

    @Resource
    private CallbackService callbackService;

    @PostMapping("/{suiteId}")
    public String handleCallback(@PathVariable String suiteId,
                                 @RequestParam String msg_signature,
                                 @RequestParam String timestamp,
                                 @RequestParam String nonce,
                                 @RequestBody String encryptedXml) {
        return callbackService.process(suiteId, msg_signature, timestamp, nonce, encryptedXml);
    }
}

回调服务解耦逻辑

java 复制代码
package wlkankan.cn.wecom.thirdparty.service;

import wlkankan.cn.wecom.thirdparty.event.WeComEventHandler;
import wlkankan.cn.wecom.thirdparty.model.CorpAuthInfo;
import wlkankan.cn.wecom.thirdparty.model.SuiteConfig;
import wlkankan.cn.wecom.thirdparty.token.ThirdPartyTokenManager;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Service
public class CallbackService {
    @Resource
    private ThirdPartyTokenManager tokenManager;

    @Resource
    private List<WeComEventHandler> handlers;

    @Resource
    private SuiteConfigRepository suiteConfigRepo;

    @Resource
    private CorpAuthRepository corpAuthRepo;

    public String process(String suiteId, String signature, String ts, String nonce, String xml) {
        SuiteConfig suiteConfig = suiteConfigRepo.findBySuiteId(suiteId);
        // 1. 验签(略)
        // 2. 解密XML获取 ToUserName (corpId) 和 MsgType
        String corpId = extractCorpId(xml);
        String msgType = extractMsgType(xml);

        CorpAuthInfo corpInfo = corpAuthRepo.findByCorpIdAndSuiteId(corpId, suiteId);
        Object payload = parsePayload(xml);

        // 3. 路由到对应处理器
        handlers.stream()
            .filter(h -> h.supports(msgType))
            .findFirst()
            .ifPresent(h -> h.handle(corpInfo, payload));

        return "success";
    }

    // XML解析方法略
    private String extractCorpId(String xml) { return "mock_corp"; }
    private String extractMsgType(String xml) { return "change_user"; }
    private Object parsePayload(String xml) { return new Object(); }
}

API调用代理:按租户自动注入Token

java 复制代码
package wlkankan.cn.wecom.thirdparty.api;

import wlkankan.cn.wecom.thirdparty.model.CorpAuthInfo;
import wlkankan.cn.wecom.thirdparty.token.ThirdPartyTokenManager;
import org.springframework.web.reactive.function.client.WebClient;

public class TenantAwareApiClient {
    private final WebClient webClient = WebClient.create();
    private final ThirdPartyTokenManager tokenManager;

    public TenantAwareApiClient(ThirdPartyTokenManager tokenManager) {
        this.tokenManager = tokenManager;
    }

    public String sendTextMessage(CorpAuthInfo corpInfo, String content, String toUser) {
        String accessToken = tokenManager.getCorpAccessToken(corpInfo, null);
        String json = String.format("""
            {"touser":"%s","msgtype":"text","text":{"content":"%s"}}
            """, toUser, content);
        return webClient.post()
            .uri("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken)
            .bodyValue(json)
            .retrieve()
            .bodyToMono(String.class)
            .block();
    }
}

通过wlkankan.cn.wecom.thirdparty模块构建的分层凭证管理、事件处理器注册机制与租户感知API客户端,系统在新增事件类型或对接新第三方应用时,仅需扩展接口实现,无需修改核心流程,显著提升可维护性与可扩展性。

相关推荐
sheji34162 小时前
【开题答辩全过程】以 基于Java的智慧党建管理系统的设计与实现为例,包含答辩的问题和答案
java·开发语言
ascarl20102 小时前
Oracle 12c 官方卸载工具 (Deinstall Tool) 标准流程
数据库·oracle
百炼成神 LV@菜哥2 小时前
记GaussDB(for PostgreSQL)入门SQL操作
数据库·postgresql·gaussdb
之歆2 小时前
Agent:链式工作流模式
数据库
冰冰菜的扣jio2 小时前
理解RocketMQ的消息模型
java·rocketmq·java-rocketmq
很搞笑的在打麻将2 小时前
Java集合线程安全实践:从ArrayList数据迁移问题到synchronizedList解决方案
java·jvm·算法
坚持学习前端日记2 小时前
微服务模块化项目结构
java·jvm·微服务
烤麻辣烫2 小时前
java进阶--刷题与详解-1
java·开发语言·学习·intellij-idea
cypking2 小时前
一、Mac 下 JDK + Maven 安装配置文档(Bash 终端 / Source 生效)
java·macos·maven