Skip to content

全局拦截(PostDataProxy)

INFO

PostDataProxy 是全局 DataProxy 拦截器,对所有 Erupt 实体类的数据操作统一生效,无需在每个 @Erupt 注解上单独配置。

1.13.1 及以上版本支持

执行顺序

每次触发 DataProxy 回调时,框架按以下顺序依次执行:

1. 注册容器中的 @PreDataProxy(通过 registerAnnotationContainer 注册的注解)
2. 父类 / 接口上的 @PreDataProxy(沿继承链向上遍历)
3. 当前类及其接口上的 @PreDataProxy
4. @Erupt(dataProxy = ...) 指定的 DataProxy
5. PostDataProxy(全局注册,最后执行)

PostDataProxy 始终在所有局部 DataProxy 之后触发,适合做跨切面的全局处理。

注册方法

实现 DataProxy<Object> 接口,在 @PostConstruct 中调用 PostDataProxy.register(...) 完成注册:

java
@Component
public class GlobalAuditProxy implements DataProxy<Object> {

    @PostConstruct
    public void init() {
        PostDataProxy.register(GlobalAuditProxy.class);
    }

}

可注册多个 PostDataProxy,按注册顺序依次执行。

识别当前操作的实体类

在任意回调方法内,通过 DataProxyContext 获取当前正在操作的 Erupt 类信息:

java
@Component
public class GlobalAuditProxy implements DataProxy<Object> {

    @PostConstruct
    public void init() {
        PostDataProxy.register(GlobalAuditProxy.class);
    }

    @Override
    public void afterAdd(Object model) {
        EruptModel eruptModel = DataProxyContext.get().getEruptModel();
        Class<?> clazz = eruptModel.getClazz(); // 实际操作的实体类,如 Order.class
        String eruptName = eruptModel.getEruptName(); // 如 "Order"
        // 记录审计日志
        auditLog("ADD", clazz.getSimpleName(), model);
    }

    @Override
    public void afterUpdate(Object model) {
        EruptModel eruptModel = DataProxyContext.get().getEruptModel();
        auditLog("UPDATE", eruptModel.getClazz().getSimpleName(), model);
    }

    @Override
    public void afterDelete(Object model) {
        EruptModel eruptModel = DataProxyContext.get().getEruptModel();
        auditLog("DELETE", eruptModel.getClazz().getSimpleName(), model);
    }

}

按注解条件过滤

结合 eruptModel.getClazz().getAnnotation(...) 可以只对带有特定注解的实体类生效:

java
@Component
public class FlowPostDataProxy implements DataProxy<Object> {

    @PostConstruct
    public void init() {
        PostDataProxy.register(FlowPostDataProxy.class);
    }

    @Override
    public void afterFetch(Collection<Map<String, Object>> list) {
        EruptModel eruptModel = DataProxyContext.get().getEruptModel();
        // 只对标注了 @EruptFlow 的实体类做额外处理
        EruptFlow eruptFlow = eruptModel.getClazz().getAnnotation(EruptFlow.class);
        if (eruptFlow == null) return;
        // 注入流程状态信息
        for (Map<String, Object> row : list) {
            row.put("flowStatus", queryFlowStatus(row.get("id")));
        }
    }

}

常见应用场景

全局操作审计

java
@Component
@Slf4j
public class AuditDataProxy implements DataProxy<Object> {

    @Autowired
    private AuditLogService auditLogService;

    @PostConstruct
    public void init() {
        PostDataProxy.register(AuditDataProxy.class);
    }

    @Override
    public void afterAdd(Object model) {
        log("新增", model);
    }

    @Override
    public void afterUpdate(Object model) {
        log("修改", model);
    }

    @Override
    public void afterDelete(Object model) {
        log("删除", model);
    }

    private void log(String action, Object model) {
        String entity = DataProxyContext.get().getEruptModel().getClazz().getSimpleName();
        auditLogService.save(action, entity, model.toString());
    }

}

全局数据脱敏

java
@Component
public class MaskDataProxy implements DataProxy<Object> {

    private static final String PHONE_FIELD = "phone";

    @PostConstruct
    public void init() {
        PostDataProxy.register(MaskDataProxy.class);
    }

    @Override
    public void afterFetch(Collection<Map<String, Object>> list) {
        for (Map<String, Object> row : list) {
            if (row.containsKey(PHONE_FIELD) && row.get(PHONE_FIELD) instanceof String phone) {
                // 保留前三后四,中间替换为 ****
                row.put(PHONE_FIELD, phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
            }
        }
    }

}

全局行级权限过滤

java
@Component
public class TenantDataProxy implements DataProxy<Object> {

    @PostConstruct
    public void init() {
        PostDataProxy.register(TenantDataProxy.class);
    }

    @Override
    public String beforeFetch(List<Condition> conditions) {
        // 注入租户隔离条件(追加在所有局部 DataProxy 条件之后)
        Long tenantId = SecurityContext.currentTenantId();
        return "tenantId = " + tenantId;
    }

}

WARNING

当多个 PostDataProxy 都重写了 beforeFetch 时,每个返回的条件字符串都会被框架独立拼接为 AND 条件,互不覆盖。

与局部 DataProxy 的区别

@Erupt(dataProxy = ...)PostDataProxy
作用范围单个实体类全部实体类
配置方式@Erupt 注解上声明调用 PostDataProxy.register(...)
执行顺序第 4 步第 5 步(最后)
典型用途业务逻辑、字段填充、数据校验审计日志、数据脱敏、租户隔离

所有 DataProxy 回调方法均可在 PostDataProxy 中使用,完整方法列表参考 DataProxy 接口定义

贡献者

The avatar of contributor named as YuePeng YuePeng
The avatar of contributor named as Claude Opus 5 (1M context) Claude Opus 5 (1M context)

页面历史

Released under the Apache-2.0 License.