基于责任链模式构建可扩展的微信群发消息风控过滤器(Java实现)

基于责任链模式构建可扩展的微信群发消息风控过滤器(Java实现)

1. 风控场景与设计目标

在企业级微信群发系统中,需对每条待发送消息进行多维度风险检测,包括敏感词、频率限制、内容合规性、接收人白名单等。若采用硬编码 if-else 逻辑,将导致代码耦合度高、难以维护。责任链模式(Chain of Responsibility)允许将多个风控处理器串联,每个处理器独立决策是否放行或拦截,具备高内聚、低耦合、易扩展的特性。

2. 核心接口定义

首先定义风控上下文与处理器接口:

java 复制代码
package wlkankan.cn.risk;

public class MessageContext {
    private String content;
    private String senderId;
    private String[] receiverIds;
    private boolean blocked = false;
    private String blockReason;

    // getters and setters
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public String getSenderId() { return senderId; }
    public void setSenderId(String senderId) { this.senderId = senderId; }
    public String[] getReceiverIds() { return receiverIds; }
    public void setReceiverIds(String[] receiverIds) { this.receiverIds = receiverIds; }
    public boolean isBlocked() { return blocked; }
    public void setBlocked(boolean blocked) { this.blocked = blocked; }
    public String getBlockReason() { return blockReason; }
    public void setBlockReason(String reason) { this.blockReason = reason; }
}

public abstract class RiskFilter {
    protected RiskFilter next;

    public void setNext(RiskFilter next) {
        this.next = next;
    }

    public abstract void doFilter(MessageContext ctx);
}

3. 敏感词过滤器实现

使用AC自动机加载敏感词库,高效匹配:

java 复制代码
package wlkankan.cn.risk.filter;

import wlkankan.cn.risk.MessageContext;
import wlkankan.cn.risk.RiskFilter;
import wlkankan.cn.util.AhoCorasickDoubleArrayTrie;

import java.util.Map;
import java.util.Set;

public class SensitiveWordFilter extends RiskFilter {
    private final AhoCorasickDoubleArrayTrie<String> acdat;

    public SensitiveWordFilter(Set<String> words) {
        this.acdat = new AhoCorasickDoubleArrayTrie<>();
        acdat.build(words);
    }

    @Override
    public void doFilter(MessageContext ctx) {
        if (ctx.isBlocked()) return;

        String content = ctx.getContent();
        Map<Integer, String> matches = acdat.parseText(content);
        if (!matches.isEmpty()) {
            ctx.setBlocked(true);
            ctx.setBlockReason("包含敏感词: " + matches.values().iterator().next());
            return;
        }

        if (next != null) {
            next.doFilter(ctx);
        }
    }
}

4. 发送频率限流过滤器

基于令牌桶算法控制单位时间发送次数:

java 复制代码
package wlkankan.cn.risk.filter;

import com.google.common.util.concurrent.RateLimiter;
import wlkankan.cn.risk.MessageContext;
import wlkankan.cn.risk.RiskFilter;

import java.util.concurrent.ConcurrentHashMap;

public class RateLimitFilter extends RiskFilter {
    private final ConcurrentHashMap<String, RateLimiter> limiters = new ConcurrentHashMap<>();
    private final double permitsPerSecond;

    public RateLimitFilter(double permitsPerSecond) {
        this.permitsPerSecond = permitsPerSecond;
    }

    @Override
    public void doFilter(MessageContext ctx) {
        if (ctx.isBlocked()) return;

        String sender = ctx.getSenderId();
        RateLimiter limiter = limiters.computeIfAbsent(sender,
            k -> RateLimiter.create(permitsPerSecond));
        
        if (!limiter.tryAcquire()) {
            ctx.setBlocked(true);
            ctx.setBlockReason("发送频率超限");
            return;
        }

        if (next != null) {
            next.doFilter(ctx);
        }
    }
}

5. 接收人白名单过滤器

确保仅向授权用户发送:

java 复制代码
package wlkankan.cn.risk.filter;

import wlkankan.cn.risk.MessageContext;
import wlkankan.cn.risk.RiskFilter;

import java.util.Set;

public class WhitelistFilter extends RiskFilter {
    private final Set<String> whitelist;

    public WhitelistFilter(Set<String> whitelist) {
        this.whitelist = whitelist;
    }

    @Override
    public void doFilter(MessageContext ctx) {
        if (ctx.isBlocked()) return;

        for (String receiver : ctx.getReceiverIds()) {
            if (!whitelist.contains(receiver)) {
                ctx.setBlocked(true);
                ctx.setBlockReason("接收人不在白名单: " + receiver);
                return;
            }
        }

        if (next != null) {
            next.doFilter(ctx);
        }
    }
}

6. 链构建与调用入口

提供工厂类动态组装过滤器链:

java 复制代码
package wlkankan.cn.risk;

import wlkankan.cn.risk.filter.RateLimitFilter;
import wlkankan.cn.risk.filter.SensitiveWordFilter;
import wlkankan.cn.risk.filter.WhitelistFilter;

import java.util.Set;

public class RiskFilterChainBuilder {
    public static RiskFilter buildChain(Set<String> sensitiveWords,
                                        Set<String> whitelist,
                                        double rateLimit) {
        RiskFilter wordFilter = new SensitiveWordFilter(sensitiveWords);
        RiskFilter rateFilter = new RateLimitFilter(rateLimit);
        RiskFilter whiteFilter = new WhitelistFilter(whitelist);

        wordFilter.setNext(rateFilter);
        rateFilter.setNext(whiteFilter);
        return wordFilter;
    }
}

业务层调用示例:

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

import wlkankan.cn.risk.MessageContext;
import wlkankan.cn.risk.RiskFilter;
import wlkankan.cn.risk.RiskFilterChainBuilder;

public class MassMessageService {
    private final RiskFilter filterChain;

    public MassMessageService() {
        Set<String> words = loadSensitiveWords(); // 从配置中心加载
        Set<String> whitelist = loadWhitelist();
        this.filterChain = RiskFilterChainBuilder.buildChain(words, whitelist, 5.0);
    }

    public boolean canSend(String content, String sender, String[] receivers) {
        MessageContext ctx = new MessageContext();
        ctx.setContent(content);
        ctx.setSenderId(sender);
        ctx.setReceiverIds(receivers);

        filterChain.doFilter(ctx);
        return !ctx.isBlocked();
    }

    private Set<String> loadSensitiveWords() { /* ... */ return Set.of(); }
    private Set<String> loadWhitelist() { /* ... */ return Set.of(); }
}

7. 动态扩展与热插拔

新增过滤器只需继承RiskFilter并插入链中。例如添加"图片违规检测":

java 复制代码
public class ImageRiskFilter extends RiskFilter {
    @Override
    public void doFilter(MessageContext ctx) {
        // 检查 ctx 中是否包含违规图片URL
        if (next != null) next.doFilter(ctx);
    }
}

通过配置化方式决定链顺序:

java 复制代码
// 伪代码:从JSON配置读取 filterOrder = ["word", "image", "rate", "white"]
List<RiskFilter> filters = configOrder.stream()
    .map(type -> createFilterByType(type))
    .collect(Collectors.toList());
for (int i = 0; i < filters.size() - 1; i++) {
    filters.get(i).setNext(filters.get(i + 1));
}

8. 性能与监控

  • 所有过滤器应为无状态,支持并发;
  • 记录每个过滤器耗时,用于性能分析;
  • 被拦截消息写入审计日志,包含blockReason和上下文摘要。

责任链模式使风控逻辑模块化,新规则可独立开发、测试、部署,极大提升系统可维护性与合规响应速度。

相关推荐
2301_780789667 小时前
跨平台适配!SDK 游戏盾兼容全终端游戏场景
java·网络·ddos
学长毕设8 小时前
基于springboot的食品仓库管理系统
java·spring boot·后端
步行cgn8 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
龙亘川8 小时前
构建城市公共安全数字底座:亘川智城智慧安防系统全维度解读
java·大数据·开发语言·人工智能·信息可视化
金銀銅鐵8 小时前
[Java] 用 CLI 展示 EnumMap 增删键值对时的内部变化
java·源码·ai编程
秋天的怀念_David9 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
Raas1009 小时前
AI网关部署方案?从部署到上线全流程
java·开发语言·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品
niucloud-admin10 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
霸道流氓气质10 小时前
智能客服组件:分层意图识别、RAG Advisor 与人工转接状态机的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
captain37611 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis