# Notify 系统通知

MornBoot提供通用的注解,用于声明系统通知,并提供灵活的分发、处理机制。适用于:

  • 消息提醒(短信、邮件等)
  • 消息推送(Web推送、手机推送等)
  • 告警推送
  • 第三方系统通知

Since:v1.2.1

# 必要配置

SpringBootApplication

@EnableNotify // 开启通知

# Notify Annotation

使用@Notify注解声明通知的类型、名称,使用@NotifyReceiver注解声明接收人的类型、唯一标识。

@Slf4j
@Component
public class TestNotifyBean {
  /**
   * 向角色发送邮箱验证码
   */
  @Notify(type = "email", name = "captcha")
  @NotifyReceiver(type = "role", value = {"r1", "r2"})
  public void captchaToRole() {
    log.info("Do some thing and Notify to role.");
  }
}

# Notify Processor

实现NotifyProcessor接口,以处理系统通知。使用@NotifyType标识可处理的通知类型。MornBoot会自动将NotifyMeta分发给符合规则的NotifyProcessor

@NotifyType默认为*代表可处理所有类型的系统通知。

@Slf4j
@NotifyType("email")
public class EmailNotifyProcessor implements NotifyProcessor {

  @Override
  public void handle(NotifyMeta meta) {
    log.info("Notify|{}", meta);
  }
}

系统通知的处理会在方法执行完毕后触发。captchaToRole的日志:

Do some thing and Notify to role.
Notify|NotifyMeta(source=null, notifyType=email, notifyName=captcha, receiverType=role, receiverValues=[r1, r2])

# Notify Arguments

使用NotifyArgs可以动态指定接收人。

由于NotifyArguments基于线程变量ThreadLocal实现。 在一个线程中,触发多次Notify时,需要使用NotifyArguments.getNotifyArgs(String notifyType, String notifyName)指明所属Notify

@Slf4j
@Component
public class TestNotifyBean {
  /**
   * 向角色发送邮箱验证码
   */
  @Notify(type = "email", name = "captcha")
  @NotifyReceiver(type = "role" /*, value = {"r1", "r2"}*/)
  public void captchaToRole() {
    log.info("Do some thing and Notify to role.");
    // 设置通知参数
    NotifyArgs notifyArgs = NotifyArguments.getNotifyArgs();
    notifyArgs.addReceiverValue("r1");
    notifyArgs.addReceiverValue("r2");
  }
}

# Template

通知内容通常比较固定,可以使用内容模板进行管理。

SpringBootApplication启动类配置

@EnableTemplate // 开启模板渲染

国际化资源文件:

tpl.email.captcha=尊敬的${role}您好,您的验证码是${captcha}。

使用@Template标注模板类型,与@Notify配合使用时,MornBoot会根据通知类型和名称自动填充模板名称@Template#name

  1. 默认的填充规则为tpl.[notifyType].[notifyName]
  2. 配置morn.template.prefix属性可以修改前缀。
  3. 使用NotifyArguments指定模板变量。
  4. resource表示使用国际化资源文件管理模板内容。
@Slf4j
@Component
public class TestNotifyBean {
  /**
   * 向角色发送邮箱验证码
   */
  @Template(RESOURCE)
  @Notify(type = "email", name = "captcha")
  @NotifyReceiver(type = "role", value = {"r1", "r2"})
  public void captchaToRole() {
    // 设置模板参数
    CriteriaMap templateArgs = NotifyArguments.getTemplateArgs();
    templateArgs.put("role", "管理员");
    templateArgs.put("captcha", "a1b2c3");
  }
}

实现TemplateNotifyProcessor<T>处理模板系统通知。MornBoot会将模板变量填充进模板内容。

@Slf4j
@NotifyType
public class EmailNotifyProcessor extends AbstractTemplateNotifyProcessor<String> {

  @Override
  public void handle(NotifyMeta meta) {
    log.info("Content:{}", meta, getTemplateContent());
  }
}

captchaToRole的日志:

尊敬的管理员您好,您的验证码是a1b2c3。

实现新的模板解析器TemplateResolver,并指定新的@TemplateType,可以自定义模板。参考源码:

@TemplateType(RESOURCE)
public class ResourceTemplateResolver extends AbstractTemplateResolver<String> {

  /**
   * 国际化翻译器
   */
  private final Translator translator;

  public ResourceTemplateResolver(TemplateProperties templateProperties, Translator translator) {
    super(templateProperties);
    this.translator = translator;
  }

  @Override
  public String resolve() {
    String templatePath = getTemplatePath();
    String templateContent = translator.translate(templatePath);
    CriteriaMap args = getTemplateMeta().getArgs();
    for (Entry<String, Object> entry : args.entrySet()) {
      String name = entry.getKey();
      String value = String.valueOf(entry.getValue());
      templateContent = StringUtils.replace(templateContent, getPlaceholder(name), value);
    }
    return templateContent;
  }

  /**
   * 生成占位符
   *
   * @param name 属性名称
   * @return 占位符
   */
  private String getPlaceholder(String name) {
    return String.format("${%s}", name);
  }
}

# 其它用法

使用NotifyDispatcher可以直接分发系统通知,而不需要通过注解驱动。

public class NotifyTrigger {

  private final NotifyDispatcher notifyDispatcher;

  public NotifyAspect(NotifyDispatcher notifyDispatcher) {
    this.notifyDispatcher = notifyDispatcher;
  }

  public void triggering() {
    NotifyMeta notifyMeta = new NotifyMeta();
    notifyDispatcher.handle(notifyMeta);
  }
}