Class NotificationOutboxBase

java.lang.Object
io.leandev.appfuse.notification.outbox.NotificationOutboxBase
All Implemented Interfaces:
OutboxView

@MappedSuperclass public abstract class NotificationOutboxBase extends Object implements OutboxView

通知遞送 Outbox 列的欄位基類(每收件人 × 每通道一列)

是 transactional outbox 與遞送稽核的載體:NotificationService 渲染後寫入 PENDING 列,fast-path 與輪詢器經 NotificationOutboxDispatcher 遞送並更新狀態。 本列即遞送稽核(狀態、嘗試次數、最後錯誤、時間)。

ADR-017:框架 jar 不擁有 @Entity——本類為 @MappedSuperclass(欄位 + 狀態機邏輯),具體 @Entity(表名、唯一 約束、租戶性)由應用宣告。租戶欄位不在此(決策二):tenant 專案的應用 entity 以 @TenantId 宣告——請求緒寫入時 Hibernate 自動注入當前租戶、去重查詢自動限縮該租戶;排程/輪詢緒(root)不受 filter、天然跨租戶撈取(決策三的推導)。ownership 專案無租戶欄位。跨 async 邊界重建租戶 context 時,dispatcher 以 TenantAware 探詢本列租戶(見 NotificationOutboxDispatcher)。

dedupeKey 在租戶內唯一(冪等去重):同鍵不重複建列,避免同一業務事件重送。呼叫端未提供去重鍵時, 該列取得每列自成一鍵的唯一值(=不參與去重),而非 null——見 dedupeKey 欄位說明與 ADR-017 決策五。

Since:
4.0.0
  • Field Details

    • MAX_ERROR_LENGTH

      public static final int MAX_ERROR_LENGTH
      最後錯誤訊息保留的最大長度(超出截斷)
      See Also:
  • Constructor Details

    • NotificationOutboxBase

      public NotificationOutboxBase()
  • Method Details

    • getCcRecipientAddresses

      public List<String> getCcRecipientAddresses()
      取得 EMAIL CC 位址;非 EMAIL 或舊資料列回空清單。
      Specified by:
      getCcRecipientAddresses in interface OutboxView
    • setCcRecipientAddresses

      public void setCcRecipientAddresses(List<String> addresses)
      設定 EMAIL CC 位址並序列化保存。
    • initializeNextAttemptAt

      protected void initializeNextAttemptAt()
      讓直接建立的待送列沿用 auditing 所寫入的建立時間,避免 nextAttemptAt=null 導致輪詢器永遠撈不到。Entity callback 於 entity listener 之後執行,因此可安全使用 createdDate;若應用未啟用 Spring Data JPA auditing,則明確失敗而非靜默漏送。
    • markSent

      public void markSent(Instant sentAt)
      標記遞送成功(終態)。
      Parameters:
      sentAt - 遞送成功時間點;由應用層的 Clock 提供
    • markSuppressed

      public void markSuppressed(String error)
      標記因政策被抑制(終態,不重試)。用於確定性封鎖,如郵件防火牆全擋——重試無益, 故不增 attempts、不排重試,直接轉 SUPPRESSED(與 DEAD 重試耗盡區分)。
      Parameters:
      error - 抑制原因(自動截斷至 MAX_ERROR_LENGTH)
    • recordFailure

      public void recordFailure(String error, Instant nextAttemptTime)
      記錄一次遞送失敗;依是否逾重試上限轉 FAILED(排定重試)或 DEAD(放棄)
      Parameters:
      error - 失敗原因(自動截斷至 MAX_ERROR_LENGTH)
      nextAttemptTime - 下次重試時間(轉 DEAD 時忽略)
    • requeue

      public void requeue(Instant nextAttemptAt)

      重排此列供重送(人工補救入口):把終態 / SENT / FAILED 列重置為 PENDING 並排定即刻遞送 ——status=PENDINGattempts=0nextAttemptAt=now、清除 lastErrorsentAt; maxAttempts 不變。重排後回歸正常遞送與退避路徑,可再成功(SENT)或再次耗盡 maxAttemptsDEAD(非無限重試),SUPPRESSED 政策仍成立則再 SUPPRESSED

      重排是重排既有列、非新建列:不動 dedupeKey、不涉唯一約束,故不製造重複列。呼叫端於 重排後另行觸發遞送(fast-path NotificationOutboxDispatcher#dispatchAsync),遞送仍走原子認領、 叢集安全(人工重送與輪詢並發不重複送出)。

      狀態守衛:

      • SENDING(認領中):拋 ConflictException(對映 HTTP 409)——人工重送不得與進行中遞送競爭; 滯留的 SENDING(遞送節點崩潰)由輪詢逾認領逾時後自然重新認領,非本方法路徑。
      • PENDING:no-op(本就待送,不改任何欄位,冪等)。
      • DEAD / FAILED / SUPPRESSED / SENT:重置為 PENDING
      Parameters:
      nextAttemptAt - 重新排定的下次嘗試時間點;由應用層的 Clock 提供
      Throws:
      ConflictException - 若此列目前為 SENDING(認領中)