一、第三方接口调用(统一认证+统一入库+Beetl格式转换)
核心逻辑:统一认证抽象 + 统一消息待办入库 + Beetl仅做格式转换,全程不侵入业务,兼容多第三方系统(ABC),保证入库格式一致、认证逻辑统一、转换逻辑解耦,完全贴合生产落地需求。
一、方案整体架构(分层清晰,职责单一)
业务层
↓
统一请求/认证/消息待办对象(内部标准)
↓
1. 统一入库(msg_task表,格式固定)
↓
2. 统一认证(多方式适配,不侵入业务)
↓
3. Beetl格式转换(统一结构 → 第三方格式,仅做转换)
↓
4. 第三方接口调用
↓
5. 统一结果处理 + 入库状态更新各层核心职责
认证层:统一封装所有第三方认证方式(API-KEY/OAuth2/用户名密码),提供统一认证入口;
数据层:统一消息+待办入库结构,不管对接多少系统,库表和内部对象不变;
转换层:Beetl仅负责“统一结构 ↔ 第三方结构”的格式转换,不做任何业务逻辑;
调用层:统一客户端接口,根据渠道路由,自动完成认证、转换、调用全流程。
二、完整Java代码(可直接复制落地)
第一部分:统一认证抽象(复用之前,适配全渠道)
1. 认证类型枚举(AuthType.java)
public enum AuthType {
API_KEY, // API密钥认证
BASIC_AUTH, // 基础账号密码认证
OAUTH2, // OAuth2.0认证
SIGN // 签名认证
}2. 统一认证对象(ThirdAuth.java)
import lombok.Data;
/**
* 统一认证对象:兼容所有第三方系统的认证参数,入库不存此对象,仅用于接口调用认证
*/
@Data
public class ThirdAuth {
private Long id;
private String channel; // 对应第三方渠道:abc/xyz(和消息待办的channel一致)
private AuthType authType; // 认证方式
private String appId; // 应用ID(通用)
private String apiKey; // API密钥
private String secret; // 密钥(用于签名/OAuth2)
private String username; // 账号(基础认证)
private String password; // 密码(基础认证)
private String tokenUrl; // OAuth2令牌地址(按需)
}3. 认证器接口(Authenticator.java)
import com.xxx.common.auth.AuthToken;
/**
* 统一认证器接口:不同认证方式实现不同,由工厂路由
*/
public interface Authenticator {
// 执行认证,返回可用于接口调用的凭证(token/请求头)
AuthToken authenticate(ThirdAuth auth) throws Exception;
// 支持的认证类型
AuthType supportType();
}4. 认证凭证(AuthToken.java)
import lombok.Data;
import java.util.Map;
/**
* 认证后的凭证:统一封装,供后续接口调用使用
*/
@Data
public class AuthToken {
private String accessToken; // 令牌(OAuth2/API-KEY)
private Long expireTime; // 过期时间(毫秒)
private Map<String, String> headers; // 认证请求头(如签名头、Authorization)
}5. 认证工厂(AuthFactory.java)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 认证工厂:根据认证类型,路由到对应认证器
*/
@Component
public class AuthFactory {
private final Map<AuthType, Authenticator> authMap;
// Spring自动注入所有Authenticator实现
@Autowired
public AuthFactory(List<Authenticator> authenticators) {
this.authMap = authenticators.stream()
.collect(Collectors.toMap(Authenticator::supportType, a -> a));
}
// 根据认证对象,获取对应认证器并执行认证
public AuthToken doAuth(ThirdAuth auth) throws Exception {
Authenticator authenticator = authMap.get(auth.getAuthType());
if (authenticator == null) {
throw new RuntimeException("不支持的认证方式:" + auth.getAuthType());
}
return authenticator.authenticate(auth);
}
}6. 示例认证实现(API-KEY认证,可扩展其他方式)
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* API-KEY认证实现(ABC系统常用)
*/
@Component
public class ApiKeyAuthenticator implements Authenticator {
@Override
public AuthToken authenticate(ThirdAuth auth) {
AuthToken token = new AuthToken();
token.setAccessToken(auth.getApiKey());
// 构造请求头(ABC系统要求ApiKey放在请求头)
Map<String, String> headers = new HashMap<>();
headers.put("ApiKey", auth.getApiKey());
headers.put("AppId", auth.getAppId());
token.setHeaders(headers);
return token;
}
@Override
public AuthType supportType() {
return AuthType.API_KEY;
}
}第二部分:统一消息待办(入库格式固定)
1. 统一消息待办实体(MsgTask.java,入库实体)
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
/**
* 统一消息待办任务表:入库格式固定,不管对接多少第三方,字段不变
*/
@Data
@TableName("msg_task")
public class MsgTask {
@TableId(type = IdType.AUTO)
private Long id;
private String channel; // 第三方渠道:abc/xyz(和ThirdAuth的channel对应)
private String msgType; // 类型:MESSAGE(纯消息)/ TODO(待办+消息)
private String title; // 消息标题(统一)
private String content; // 消息内容(统一)
private String receiver; // 消息接收人(统一)
private String templateCode; // 模板编码(内部统一,和第三方无关)
// 待办相关(统一字段)
private String todoCode; // 待办编码(内部统一)
private String executor; // 待办执行人(统一)
private Integer priority; // 优先级:1(高)/2(中)/3(低)
private Date expireTime; // 待办过期时间(统一)
// 任务状态(统一)
private Integer status; // 0-待发送 1-已发送 2-发送失败 3-已取消
private String resultMsg; // 发送结果描述
private Date createTime; // 创建时间(入库自动填充)
private Date updateTime; // 更新时间(入库自动填充)
}2. 统一业务请求对象(UnifiedSendRequest.java,业务层构造)
import lombok.Data;
import java.util.Date;
import java.util.Map;
/**
* 业务层统一请求:用于构造消息待办,最终转换为MsgTask入库
*/
@Data
public class UnifiedSendRequest {
private String channel; // 第三方渠道(abc/xyz)
private String msgType; // MESSAGE/TODO
private String title;
private String content;
private String receiver;
private String templateCode;
// 待办字段
private String todoCode;
private String executor;
private Integer priority;
private Date expireTime;
// 扩展参数(用于Beetl转换,不入库)
private Map<String, Object> extraParams;
}3. Mapper(MsgTaskMapper.java,入库操作)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MsgTaskMapper extends BaseMapper<MsgTask> {
}第三部分:Beetl配置 + 格式转换(仅做转换,不做业务)
1. Beetl配置(BeetlConfig.java)
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Beetl配置:仅用于数据格式转换,不做其他逻辑
*/
@Configuration
public class BeetlConfig {
// 初始化Beetl模板引擎,模板根路径:resources/templates/convert
@Bean
public GroupTemplate groupTemplate() {
ClasspathResourceLoader loader = new ClasspathResourceLoader("templates/");
org.beetl.core.Configuration cfg = org.beetl.core.Configuration.defaultConfiguration();
return new GroupTemplate(loader, cfg);
}
// 转换工具类:对外提供统一转换方法
@Bean
public BeetlConvertUtil beetlConvertUtil(GroupTemplate groupTemplate) {
return new BeetlConvertUtil(groupTemplate);
}
// 转换工具类(内部使用,仅做渲染转换)
public static class BeetlConvertUtil {
private final GroupTemplate groupTemplate;
public BeetlConvertUtil(GroupTemplate groupTemplate) {
this.groupTemplate = groupTemplate;
}
/**
* 统一转换方法:传入统一数据 + 模板路径,输出第三方需要的格式字符串
* @param templatePath 模板路径(如:convert/abc_request.btl)
* @param data 统一数据(MsgTask/ThirdAuth等)
* @return 第三方请求格式(JSON/XML)
*/
public String transform(String templatePath, Object data) {
Template template = groupTemplate.getTemplate(templatePath);
template.binding("data", data); // 模板中统一用data.xxx取值
return template.render();
}
}
}2. Beetl转换模板(关键:仅做字段映射)
(1)ABC系统请求模板(convert/abc_request.btl)
{
"user_account": "${data.task.receiver}",
"title": "${data.task.title}",
"content": "${data.task.content}",
"todo_info": {
"todo_id": "${data.task.todoCode}",
"executor": "${data.task.executor}",
"expire_time": "${data.task.expireTime}",
"priority": "${data.task.priority}"
},
"app_id": "${data.auth.appId}"
}(2)ABC系统响应转换模板(convert/abc_response.btl)
{
"success": "${data.raw.code == '0000'}",
"code": "${data.raw.code}",
"message": "${data.raw.msg}",
"data": {
"thirdTaskId": "${data.raw.task_id}"
}
}第四部分:统一第三方客户端(认证+转换+调用)
1. 统一客户端接口(ThirdClient.java)
import com.xxx.common.response.CommonResponse;
import com.xxx.data.entity.MsgTask;
import com.xxx.common.auth.ThirdAuth;
/**
* 第三方客户端统一接口:所有第三方系统(ABC/XYZ)都实现此接口
*/
public interface ThirdClient {
// 渠道编码(和MsgTask、ThirdAuth的channel一致)
String getChannel();
// 统一调用入口:传入入库后的任务 + 对应渠道的认证信息
CommonResponse<?> send(MsgTask task, ThirdAuth auth);
}2. ABC系统客户端实现(AbcThirdClient.java)
import com.alibaba.fastjson2.JSON;
import com.xxx.common.auth.AuthFactory;
import com.xxx.common.auth.AuthToken;
import com.xxx.common.config.BeetlConfig;
import com.xxx.common.response.CommonResponse;
import com.xxx.data.entity.MsgTask;
import com.xxx.common.auth.ThirdAuth;
import com.xxx.client.ThirdClient;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* ABC系统客户端:整合认证、Beetl转换、接口调用
*/
@Component
@RequiredArgsConstructor
public class AbcThirdClient implements ThirdClient {
private final AuthFactory authFactory;
private final BeetlConfig.BeetlConvertUtil beetlConvertUtil;
private final HttpUtil httpUtil; // 自定义HTTP工具类(下文提供)
// ABC系统接口地址(可配置在Nacos/Apollo)
private static final String ABC_SEND_URL = "https://api.abc.com/message/send";
@Override
public String getChannel() {
return "abc"; // 渠道编码,和MsgTask、ThirdAuth的channel对应
}
@Override
public CommonResponse<?> send(MsgTask task, ThirdAuth auth) {
try {
// 第一步:执行统一认证,获取认证凭证
AuthToken authToken = authFactory.doAuth(auth);
// 第二步:Beetl转换:统一MsgTask → ABC系统请求格式
Map<String, Object> convertData = new HashMap<>();
convertData.put("task", task); // 统一入库任务
convertData.put("auth", auth); // 认证信息(按需取值)
String abcRequestJson = beetlConvertUtil.transform(
"convert/abc_request.btl", convertData
);
// 第三步:调用ABC系统接口(携带认证头)
String abcResponseJson = httpUtil.postJson(
ABC_SEND_URL,
abcRequestJson,
authToken.getHeaders() // 认证请求头
);
// 第四步:Beetl转换:ABC响应 → 统一响应格式
Map<String, Object> respConvertData = new HashMap<>();
respConvertData.put("raw", JSON.parseObject(abcResponseJson));
String commonRespJson = beetlConvertUtil.transform(
"convert/abc_response.btl", respConvertData
);
// 转换为统一返回对象
return JSON.parseObject(commonRespJson, CommonResponse.class);
} catch (Exception e) {
// 统一异常处理,返回失败结果
CommonResponse<Object> failResp = new CommonResponse<>();
failResp.setSuccess(false);
failResp.setMessage("ABC系统调用失败:" + e.getMessage());
return failResp;
}
}
}3. 客户端工厂(ThirdClientFactory.java,路由渠道)
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 第三方客户端工厂:根据渠道编码,路由到对应客户端
*/
@Component
@RequiredArgsConstructor
public class ThirdClientFactory {
private final List<ThirdClient> clientList;
// 根据渠道编码,获取对应第三方客户端
public ThirdClient getClient(String channel) {
Map<String, ThirdClient> clientMap = clientList.stream()
.collect(Collectors.toMap(ThirdClient::getChannel, c -> c));
ThirdClient client = clientMap.get(channel);
if (client == null) {
throw new RuntimeException("未找到对应渠道的客户端:" + channel);
}
return client;
}
}第五部分:业务层(统一入库+统一发送)
1. 业务服务(MessageService.java)
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxx.client.ThirdClientFactory;
import com.xxx.common.auth.ThirdAuth;
import com.xxx.common.response.CommonResponse;
import com.xxx.data.entity.MsgTask;
import com.xxx.data.mapper.MsgTaskMapper;
import com.xxx.data.request.UnifiedSendRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* 业务服务:统一构造消息待办、入库、发送,全程不关心第三方差异
*/
@Service
@RequiredArgsConstructor
public class MessageService {
private final MsgTaskMapper msgTaskMapper;
private final ThirdClientFactory clientFactory;
private final ThirdAuthService thirdAuthService; // 自定义服务:根据渠道获取认证信息
/**
* 第一步:构造统一消息待办 → 入库(格式固定)
*/
@Transactional(rollbackFor = Exception.class)
public Long createAndSaveTask(UnifiedSendRequest request) {
// 统一转换为入库实体(MsgTask结构固定,不管对接多少系统)
MsgTask task = new MsgTask();
task.setChannel(request.getChannel());
task.setMsgType(request.getMsgType());
task.setTitle(request.getTitle());
task.setContent(request.getContent());
task.setReceiver(request.getReceiver());
task.setTemplateCode(request.getTemplateCode());
task.setTodoCode(request.getTodoCode());
task.setExecutor(request.getExecutor());
task.setPriority(request.getPriority());
task.setExpireTime(request.getExpireTime());
task.setStatus(0); // 初始状态:待发送
task.setCreateTime(new Date());
task.setUpdateTime(new Date());
// 统一入库(核心:入库格式永远不变)
msgTaskMapper.insert(task);
return task.getId();
}
/**
* 第二步:统一发送(自动路由渠道、认证、转换)
*/
@Transactional(rollbackFor = Exception.class)
public void sendTask(Long taskId) {
// 1. 查询入库的统一任务
MsgTask task = msgTaskMapper.selectById(taskId);
if (task == null || task.getStatus() != 0) {
throw new RuntimeException("任务不存在或非待发送状态");
}
// 2. 根据渠道,获取对应第三方的认证信息(从数据库/配置中心获取)
ThirdAuth auth = thirdAuthService.getAuthByChannel(task.getChannel());
// 3. 路由到对应第三方客户端
ThirdClient client = clientFactory.getClient(task.getChannel());
// 4. 执行发送(自动完成认证、Beetl转换、接口调用)
CommonResponse<?> response = client.send(task, auth);
// 5. 更新任务状态(统一更新,和第三方无关)
task.setStatus(response.isSuccess() ? 1 : 2);
task.setResultMsg(response.getMessage());
task.setUpdateTime(new Date());
msgTaskMapper.updateById(task);
}
}第六部分:辅助工具类(HTTP工具类)
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 统一HTTP工具类:所有第三方接口调用都用这个,统一处理请求/响应
*/
@Component
@Slf4j
public class HttpUtil {
/**
* 发送JSON格式POST请求(携带认证头)
*/
public String postJson(String url, String jsonBody, Map<String, String> headers) {
log.info("发送HTTP请求:url={}, headers={}, body={}", url, headers, jsonBody);
try (HttpResponse response = HttpRequest.post(url)
.addHeaders(headers)
.body(jsonBody)
.timeout(5000)
.execute()) {
String respBody = response.body();
log.info("HTTP响应:url={}, status={}, body={}", url, response.getStatus(), respBody);
return respBody;
} catch (Exception e) {
log.error("HTTP请求失败:url={}", url, e);
throw new RuntimeException("HTTP请求异常:" + e.getMessage());
}
}
}第七部分:统一返回对象(CommonResponse.java)
import lombok.Data;
/**
* 统一返回对象:业务层、客户端调用都用这个,格式固定
*/
@Data
public class CommonResponse<T> {
private boolean success = true;
private String code = "0000";
private String message = "操作成功";
private T data;
// 失败静态方法
public static <T> CommonResponse<T> fail(String message) {
CommonResponse<T> response = new CommonResponse<>();
response.setSuccess(false);
response.setCode("9999");
response.setMessage(message);
return response;
}
}三、关键说明(贴合你的核心需求)
认证系统:统一抽象,支持多认证方式,新增认证方式只需加
Authenticator实现,不改动原有代码;认证信息和渠道绑定,可从配置中心/数据库获取,灵活配置。入库格式:
MsgTask表和UnifiedSendRequest结构固定,不管对接ABC多少系统,入库数据格式完全一致,后续新增第三方无需修改库表。Beetl转换:仅负责“统一结构 ↔ 第三方结构”的字段映射、格式转换,不做任何业务逻辑(不处理消息文案、不处理待办状态),完全解耦第三方差异。
扩展性:新增第三方系统(如XYZ),只需做3件事:
新增对应
ThirdClient实现(如XyzThirdClient);新增2个Beetl模板(xyz_request.btl、xyz_response.btl);
配置对应渠道的认证信息;
无需修改业务层、数据层代码,完全符合开闭原则。
四、补充:建表SQL(MsgTask表)
CREATE TABLE `msg_task` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`channel` varchar(32) NOT NULL COMMENT '第三方渠道(abc/xyz)',
`msg_type` varchar(32) NOT NULL COMMENT '消息类型(MESSAGE/TODO)',
`title` varchar(255) NOT NULL COMMENT '消息标题',
`content` text NOT NULL COMMENT '消息内容',
`receiver` varchar(64) NOT NULL COMMENT '消息接收人',
`template_code` varchar(64) DEFAULT NULL COMMENT '内部模板编码',
`todo_code` varchar(64) DEFAULT NULL COMMENT '待办编码',
`executor` varchar(64) DEFAULT NULL COMMENT '待办执行人',
`priority` tinyint(4) DEFAULT NULL COMMENT '优先级(1高/2中/3低)',
`expire_time` datetime DEFAULT NULL COMMENT '待办过期时间',
`status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '状态(0待发送/1已发送/2发送失败/3已取消)',
`result_msg` varchar(512) DEFAULT NULL COMMENT '发送结果描述',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_channel` (`channel`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一消息待办任务表(格式固定)';五、可直接补充的扩展(按需选择)
如果需要,我可以继续生成:
OAuth2/SIGN签名的
Authenticator实现(完整代码);ThirdAuthService实现(从数据库获取认证信息);异步发送+重试机制(基于Spring的@Async+重试注解);
XYZ系统的客户端+Beetl模板(参考ABC系统,直接复用架构)。
需要我先补充 OAuth2认证实现 还是 XYZ系统的完整客户端+模板?
二、第三方接口调用(统一认证+统一入库+Beetl格式转换)
核心逻辑:统一认证抽象 + 统一消息待办入库 + Beetl仅做格式转换,全程不侵入业务,兼容多第三方系统(ABC),保证入库格式一致、认证逻辑统一、转换逻辑解耦,完全贴合生产落地需求。
一、方案整体架构(分层清晰,职责单一)
业务层
↓
统一请求/认证/消息待办对象(内部标准)
↓
1. 统一入库(msg_task表,格式固定)
↓
2. 统一认证(多方式适配,不侵入业务)
↓
3. Beetl格式转换(统一结构 → 第三方格式,仅做转换)
↓
4. 第三方接口调用
↓
5. 统一结果处理 + 入库状态更新各层核心职责
认证层:统一封装所有第三方认证方式(API-KEY/OAuth2/用户名密码),提供统一认证入口;
数据层:统一消息+待办入库结构,不管对接多少系统,库表和内部对象不变;
转换层:Beetl仅负责“统一结构 ↔ 第三方结构”的格式转换,不做任何业务逻辑;
调用层:统一客户端接口,根据渠道路由,自动完成认证、转换、调用全流程。
二、完整Java代码(可直接复制落地)
第一部分:统一认证抽象(复用之前,适配全渠道)
1. 认证类型枚举(AuthType.java)
public enum AuthType {
API_KEY, // API密钥认证
BASIC_AUTH, // 基础账号密码认证
OAUTH2, // OAuth2.0认证
SIGN // 签名认证
}2. 统一认证对象(ThirdAuth.java)
import lombok.Data;
/**
* 统一认证对象:兼容所有第三方系统的认证参数,入库不存此对象,仅用于接口调用认证
*/
@Data
public class ThirdAuth {
private Long id;
private String channel; // 对应第三方渠道:abc/xyz(和消息待办的channel一致)
private AuthType authType; // 认证方式
private String appId; // 应用ID(通用)
private String apiKey; // API密钥
private String secret; // 密钥(用于签名/OAuth2)
private String username; // 账号(基础认证)
private String password; // 密码(基础认证)
private String tokenUrl; // OAuth2令牌地址(按需)
}3. 认证器接口(Authenticator.java)
import com.xxx.common.auth.AuthToken;
/**
* 统一认证器接口:不同认证方式实现不同,由工厂路由
*/
public interface Authenticator {
// 执行认证,返回可用于接口调用的凭证(token/请求头)
AuthToken authenticate(ThirdAuth auth) throws Exception;
// 支持的认证类型
AuthType supportType();
}4. 认证凭证(AuthToken.java)
import lombok.Data;
import java.util.Map;
/**
* 认证后的凭证:统一封装,供后续接口调用使用
*/
@Data
public class AuthToken {
private String accessToken; // 令牌(OAuth2/API-KEY)
private Long expireTime; // 过期时间(毫秒)
private Map<String, String> headers; // 认证请求头(如签名头、Authorization)
}5. 认证工厂(AuthFactory.java)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 认证工厂:根据认证类型,路由到对应认证器
*/
@Component
public class AuthFactory {
private final Map<AuthType, Authenticator> authMap;
// Spring自动注入所有Authenticator实现
@Autowired
public AuthFactory(List<Authenticator> authenticators) {
this.authMap = authenticators.stream()
.collect(Collectors.toMap(Authenticator::supportType, a -> a));
}
// 根据认证对象,获取对应认证器并执行认证
public AuthToken doAuth(ThirdAuth auth) throws Exception {
Authenticator authenticator = authMap.get(auth.getAuthType());
if (authenticator == null) {
throw new RuntimeException("不支持的认证方式:" + auth.getAuthType());
}
return authenticator.authenticate(auth);
}
}6. 示例认证实现(API-KEY认证,可扩展其他方式)
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* API-KEY认证实现(ABC系统常用)
*/
@Component
public class ApiKeyAuthenticator implements Authenticator {
@Override
public AuthToken authenticate(ThirdAuth auth) {
AuthToken token = new AuthToken();
token.setAccessToken(auth.getApiKey());
// 构造请求头(ABC系统要求ApiKey放在请求头)
Map<String, String> headers = new HashMap<>();
headers.put("ApiKey", auth.getApiKey());
headers.put("AppId", auth.getAppId());
token.setHeaders(headers);
return token;
}
@Override
public AuthType supportType() {
return AuthType.API_KEY;
}
}第二部分:统一消息待办(入库格式固定)
1. 统一消息待办实体(MsgTask.java,入库实体)
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
/**
* 统一消息待办任务表:入库格式固定,不管对接多少第三方,字段不变
*/
@Data
@TableName("msg_task")
public class MsgTask {
@TableId(type = IdType.AUTO)
private Long id;
private String channel; // 第三方渠道:abc/xyz(和ThirdAuth的channel对应)
private String msgType; // 类型:MESSAGE(纯消息)/ TODO(待办+消息)
private String title; // 消息标题(统一)
private String content; // 消息内容(统一)
private String receiver; // 消息接收人(统一)
private String templateCode; // 模板编码(内部统一,和第三方无关)
// 待办相关(统一字段)
private String todoCode; // 待办编码(内部统一)
private String executor; // 待办执行人(统一)
private Integer priority; // 优先级:1(高)/2(中)/3(低)
private Date expireTime; // 待办过期时间(统一)
// 任务状态(统一)
private Integer status; // 0-待发送 1-已发送 2-发送失败 3-已取消
private String resultMsg; // 发送结果描述
private Date createTime; // 创建时间(入库自动填充)
private Date updateTime; // 更新时间(入库自动填充)
}2. 统一业务请求对象(UnifiedSendRequest.java,业务层构造)
import lombok.Data;
import java.util.Date;
import java.util.Map;
/**
* 业务层统一请求:用于构造消息待办,最终转换为MsgTask入库
*/
@Data
public class UnifiedSendRequest {
private String channel; // 第三方渠道(abc/xyz)
private String msgType; // MESSAGE/TODO
private String title;
private String content;
private String receiver;
private String templateCode;
// 待办字段
private String todoCode;
private String executor;
private Integer priority;
private Date expireTime;
// 扩展参数(用于Beetl转换,不入库)
private Map<String, Object> extraParams;
}3. Mapper(MsgTaskMapper.java,入库操作)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MsgTaskMapper extends BaseMapper<MsgTask> {
}第三部分:Beetl配置 + 格式转换(仅做转换,不做业务)
1. Beetl配置(BeetlConfig.java)
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Beetl配置:仅用于数据格式转换,不做其他逻辑
*/
@Configuration
public class BeetlConfig {
// 初始化Beetl模板引擎,模板根路径:resources/templates/convert
@Bean
public GroupTemplate groupTemplate() {
ClasspathResourceLoader loader = new ClasspathResourceLoader("templates/");
org.beetl.core.Configuration cfg = org.beetl.core.Configuration.defaultConfiguration();
return new GroupTemplate(loader, cfg);
}
// 转换工具类:对外提供统一转换方法
@Bean
public BeetlConvertUtil beetlConvertUtil(GroupTemplate groupTemplate) {
return new BeetlConvertUtil(groupTemplate);
}
// 转换工具类(内部使用,仅做渲染转换)
public static class BeetlConvertUtil {
private final GroupTemplate groupTemplate;
public BeetlConvertUtil(GroupTemplate groupTemplate) {
this.groupTemplate = groupTemplate;
}
/**
* 统一转换方法:传入统一数据 + 模板路径,输出第三方需要的格式字符串
* @param templatePath 模板路径(如:convert/abc_request.btl)
* @param data 统一数据(MsgTask/ThirdAuth等)
* @return 第三方请求格式(JSON/XML)
*/
public String transform(String templatePath, Object data) {
Template template = groupTemplate.getTemplate(templatePath);
template.binding("data", data); // 模板中统一用data.xxx取值
return template.render();
}
}
}2. Beetl转换模板(关键:仅做字段映射)
(1)ABC系统请求模板(convert/abc_request.btl)
{
"user_account": "${data.task.receiver}",
"title": "${data.task.title}",
"content": "${data.task.content}",
"todo_info": {
"todo_id": "${data.task.todoCode}",
"executor": "${data.task.executor}",
"expire_time": "${data.task.expireTime}",
"priority": "${data.task.priority}"
},
"app_id": "${data.auth.appId}"
}(2)ABC系统响应转换模板(convert/abc_response.btl)
{
"success": "${data.raw.code == '0000'}",
"code": "${data.raw.code}",
"message": "${data.raw.msg}",
"data": {
"thirdTaskId": "${data.raw.task_id}"
}
}第四部分:统一第三方客户端(认证+转换+调用)
1. 统一客户端接口(ThirdClient.java)
import com.xxx.common.response.CommonResponse;
import com.xxx.data.entity.MsgTask;
import com.xxx.common.auth.ThirdAuth;
/**
* 第三方客户端统一接口:所有第三方系统(ABC/XYZ)都实现此接口
*/
public interface ThirdClient {
// 渠道编码(和MsgTask、ThirdAuth的channel一致)
String getChannel();
// 统一调用入口:传入入库后的任务 + 对应渠道的认证信息
CommonResponse<?> send(MsgTask task, ThirdAuth auth);
}2. ABC系统客户端实现(AbcThirdClient.java)
import com.alibaba.fastjson2.JSON;
import com.xxx.common.auth.AuthFactory;
import com.xxx.common.auth.AuthToken;
import com.xxx.common.config.BeetlConfig;
import com.xxx.common.response.CommonResponse;
import com.xxx.data.entity.MsgTask;
import com.xxx.common.auth.ThirdAuth;
import com.xxx.client.ThirdClient;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* ABC系统客户端:整合认证、Beetl转换、接口调用
*/
@Component
@RequiredArgsConstructor
public class AbcThirdClient implements ThirdClient {
private final AuthFactory authFactory;
private final BeetlConfig.BeetlConvertUtil beetlConvertUtil;
private final HttpUtil httpUtil; // 自定义HTTP工具类(下文提供)
// ABC系统接口地址(可配置在Nacos/Apollo)
private static final String ABC_SEND_URL = "https://api.abc.com/message/send";
@Override
public String getChannel() {
return "abc"; // 渠道编码,和MsgTask、ThirdAuth的channel对应
}
@Override
public CommonResponse<?> send(MsgTask task, ThirdAuth auth) {
try {
// 第一步:执行统一认证,获取认证凭证
AuthToken authToken = authFactory.doAuth(auth);
// 第二步:Beetl转换:统一MsgTask → ABC系统请求格式
Map<String, Object> convertData = new HashMap<>();
convertData.put("task", task); // 统一入库任务
convertData.put("auth", auth); // 认证信息(按需取值)
String abcRequestJson = beetlConvertUtil.transform(
"convert/abc_request.btl", convertData
);
// 第三步:调用ABC系统接口(携带认证头)
String abcResponseJson = httpUtil.postJson(
ABC_SEND_URL,
abcRequestJson,
authToken.getHeaders() // 认证请求头
);
// 第四步:Beetl转换:ABC响应 → 统一响应格式
Map<String, Object> respConvertData = new HashMap<>();
respConvertData.put("raw", JSON.parseObject(abcResponseJson));
String commonRespJson = beetlConvertUtil.transform(
"convert/abc_response.btl", respConvertData
);
// 转换为统一返回对象
return JSON.parseObject(commonRespJson, CommonResponse.class);
} catch (Exception e) {
// 统一异常处理,返回失败结果
CommonResponse<Object> failResp = new CommonResponse<>();
failResp.setSuccess(false);
failResp.setMessage("ABC系统调用失败:" + e.getMessage());
return failResp;
}
}
}3. 客户端工厂(ThirdClientFactory.java,路由渠道)
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 第三方客户端工厂:根据渠道编码,路由到对应客户端
*/
@Component
@RequiredArgsConstructor
public class ThirdClientFactory {
private final List<ThirdClient> clientList;
// 根据渠道编码,获取对应第三方客户端
public ThirdClient getClient(String channel) {
Map<String, ThirdClient> clientMap = clientList.stream()
.collect(Collectors.toMap(ThirdClient::getChannel, c -> c));
ThirdClient client = clientMap.get(channel);
if (client == null) {
throw new RuntimeException("未找到对应渠道的客户端:" + channel);
}
return client;
}
}第五部分:业务层(统一入库+统一发送)
1. 业务服务(MessageService.java)
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxx.client.ThirdClientFactory;
import com.xxx.common.auth.ThirdAuth;
import com.xxx.common.response.CommonResponse;
import com.xxx.data.entity.MsgTask;
import com.xxx.data.mapper.MsgTaskMapper;
import com.xxx.data.request.UnifiedSendRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* 业务服务:统一构造消息待办、入库、发送,全程不关心第三方差异
*/
@Service
@RequiredArgsConstructor
public class MessageService {
private final MsgTaskMapper msgTaskMapper;
private final ThirdClientFactory clientFactory;
private final ThirdAuthService thirdAuthService; // 自定义服务:根据渠道获取认证信息
/**
* 第一步:构造统一消息待办 → 入库(格式固定)
*/
@Transactional(rollbackFor = Exception.class)
public Long createAndSaveTask(UnifiedSendRequest request) {
// 统一转换为入库实体(MsgTask结构固定,不管对接多少系统)
MsgTask task = new MsgTask();
task.setChannel(request.getChannel());
task.setMsgType(request.getMsgType());
task.setTitle(request.getTitle());
task.setContent(request.getContent());
task.setReceiver(request.getReceiver());
task.setTemplateCode(request.getTemplateCode());
task.setTodoCode(request.getTodoCode());
task.setExecutor(request.getExecutor());
task.setPriority(request.getPriority());
task.setExpireTime(request.getExpireTime());
task.setStatus(0); // 初始状态:待发送
task.setCreateTime(new Date());
task.setUpdateTime(new Date());
// 统一入库(核心:入库格式永远不变)
msgTaskMapper.insert(task);
return task.getId();
}
/**
* 第二步:统一发送(自动路由渠道、认证、转换)
*/
@Transactional(rollbackFor = Exception.class)
public void sendTask(Long taskId) {
// 1. 查询入库的统一任务
MsgTask task = msgTaskMapper.selectById(taskId);
if (task == null || task.getStatus() != 0) {
throw new RuntimeException("任务不存在或非待发送状态");
}
// 2. 根据渠道,获取对应第三方的认证信息(从数据库/配置中心获取)
ThirdAuth auth = thirdAuthService.getAuthByChannel(task.getChannel());
// 3. 路由到对应第三方客户端
ThirdClient client = clientFactory.getClient(task.getChannel());
// 4. 执行发送(自动完成认证、Beetl转换、接口调用)
CommonResponse<?> response = client.send(task, auth);
// 5. 更新任务状态(统一更新,和第三方无关)
task.setStatus(response.isSuccess() ? 1 : 2);
task.setResultMsg(response.getMessage());
task.setUpdateTime(new Date());
msgTaskMapper.updateById(task);
}
}第六部分:辅助工具类(HTTP工具类)
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 统一HTTP工具类:所有第三方接口调用都用这个,统一处理请求/响应
*/
@Component
@Slf4j
public class HttpUtil {
/**
* 发送JSON格式POST请求(携带认证头)
*/
public String postJson(String url, String jsonBody, Map<String, String> headers) {
log.info("发送HTTP请求:url={}, headers={}, body={}", url, headers, jsonBody);
try (HttpResponse response = HttpRequest.post(url)
.addHeaders(headers)
.body(jsonBody)
.timeout(5000)
.execute()) {
String respBody = response.body();
log.info("HTTP响应:url={}, status={}, body={}", url, response.getStatus(), respBody);
return respBody;
} catch (Exception e) {
log.error("HTTP请求失败:url={}", url, e);
throw new RuntimeException("HTTP请求异常:" + e.getMessage());
}
}
}第七部分:统一返回对象(CommonResponse.java)
import lombok.Data;
/**
* 统一返回对象:业务层、客户端调用都用这个,格式固定
*/
@Data
public class CommonResponse<T> {
private boolean success = true;
private String code = "0000";
private String message = "操作成功";
private T data;
// 失败静态方法
public static <T> CommonResponse<T> fail(String message) {
CommonResponse<T> response = new CommonResponse<>();
response.setSuccess(false);
response.setCode("9999");
response.setMessage(message);
return response;
}
}三、关键说明(贴合你的核心需求)
认证系统:统一抽象,支持多认证方式,新增认证方式只需加
Authenticator实现,不改动原有代码;认证信息和渠道绑定,可从配置中心/数据库获取,灵活配置。入库格式:
MsgTask表和UnifiedSendRequest结构固定,不管对接ABC多少系统,入库数据格式完全一致,后续新增第三方无需修改库表。Beetl转换:仅负责“统一结构 ↔ 第三方结构”的字段映射、格式转换,不做任何业务逻辑(不处理消息文案、不处理待办状态),完全解耦第三方差异。
扩展性:新增第三方系统(如XYZ),只需做3件事:
新增对应
ThirdClient实现(如XyzThirdClient);新增2个Beetl模板(xyz_request.btl、xyz_response.btl);
配置对应渠道的认证信息;
无需修改业务层、数据层代码,完全符合开闭原则。
四、补充:建表SQL(MsgTask表)
CREATE TABLE `msg_task` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`channel` varchar(32) NOT NULL COMMENT '第三方渠道(abc/xyz)',
`msg_type` varchar(32) NOT NULL COMMENT '消息类型(MESSAGE/TODO)',
`title` varchar(255) NOT NULL COMMENT '消息标题',
`content` text NOT NULL COMMENT '消息内容',
`receiver` varchar(64) NOT NULL COMMENT '消息接收人',
`template_code` varchar(64) DEFAULT NULL COMMENT '内部模板编码',
`todo_code` varchar(64) DEFAULT NULL COMMENT '待办编码',
`executor` varchar(64) DEFAULT NULL COMMENT '待办执行人',
`priority` tinyint(4) DEFAULT NULL COMMENT '优先级(1高/2中/3低)',
`expire_time` datetime DEFAULT NULL COMMENT '待办过期时间',
`status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '状态(0待发送/1已发送/2发送失败/3已取消)',
`result_msg` varchar(512) DEFAULT NULL COMMENT '发送结果描述',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_channel` (`channel`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一消息待办任务表(格式固定)';五、可直接补充的扩展(按需选择)
如果需要,我可以继续生成:
OAuth2/SIGN签名的
Authenticator实现(完整代码);ThirdAuthService实现(从数据库获取认证信息);异步发送+重试机制(基于Spring的@Async+重试注解);
XYZ系统的客户端+Beetl模板(参考ABC系统,直接复用架构)。