83 lines
3.2 KiB
Go
83 lines
3.2 KiB
Go
|
|
package repository
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"backend/pkg/notification/domain/entity"
|
|||
|
|
"context"
|
|||
|
|
|
|||
|
|
"github.com/gocql/gocql"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type NotificationRepository interface {
|
|||
|
|
NotificationEventRepository
|
|||
|
|
UserNotificationRepository
|
|||
|
|
NotificationCursorRepository
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---- 1. Event ----
|
|||
|
|
// 專心管「事件本體」,fan-out 前先寫這張。
|
|||
|
|
// 通常由上游 domain event consumer 呼叫 Create。
|
|||
|
|
|
|||
|
|
type QueryNotificationEventParam struct {
|
|||
|
|
ObjectID *string
|
|||
|
|
ObjectType *string
|
|||
|
|
Limit *int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type NotificationEventRepository interface {
|
|||
|
|
// Create 建立一筆新的 NotificationEvent。
|
|||
|
|
Create(ctx context.Context, e *entity.NotificationEvent) error
|
|||
|
|
// GetByID 依 EventID 取得事件。
|
|||
|
|
GetByID(ctx context.Context, id string) (*entity.NotificationEvent, error)
|
|||
|
|
// ListByObject 依 object_type + object_id 查詢相關事件(選用,debug / 後台用)。
|
|||
|
|
ListByObject(ctx context.Context, param QueryNotificationEventParam) ([]*entity.NotificationEvent, error)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---- 2. 使用者通知:user_notification ----
|
|||
|
|
// 管使用者的小鈴鐺 row,fan-out 之後用這個寫入。
|
|||
|
|
|
|||
|
|
// ListLatestOptions 查列表用的參數
|
|||
|
|
type ListLatestOptions struct {
|
|||
|
|
UserID string
|
|||
|
|
Buckets []string // e.g. []string{"202511", "202510"}
|
|||
|
|
Limit int // 建議在 service 層限制最大值,例如 <= 100
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type UserNotificationRepository interface {
|
|||
|
|
// CreateUserNotification 建立單一通知(針對某一個 user)。
|
|||
|
|
// 由呼叫端決定 bucket 與 TTL 秒數。
|
|||
|
|
CreateUserNotification(ctx context.Context, n *entity.UserNotification, ttlSeconds int) error
|
|||
|
|
|
|||
|
|
// BulkCreate 批次建立多筆通知(fan-out worker 使用)。
|
|||
|
|
// 一般期望要嘛全部成功要嘛全部失敗。
|
|||
|
|
BulkCreate(ctx context.Context, list []*entity.UserNotification, ttlSeconds int) error
|
|||
|
|
|
|||
|
|
// ListLatest 取得某 user 最新的通知列表(小鈴鐺拉下來用)。
|
|||
|
|
ListLatest(ctx context.Context, opt ListLatestOptions) ([]*entity.UserNotification, error)
|
|||
|
|
|
|||
|
|
// MarkRead 將單一通知設為已讀。
|
|||
|
|
// 用 (user_id, bucket, ts) 精準定位那一筆資料。
|
|||
|
|
MarkRead(ctx context.Context, userID, bucket string, ts gocql.UUID) error
|
|||
|
|
|
|||
|
|
// MarkAllRead 將指定 buckets 範圍內的通知設為已讀。
|
|||
|
|
// 常見用法:最近幾個 bucket(例如最近 30 天)全部標為已讀。
|
|||
|
|
// Cassandra 不適合全表掃描,實作時可分批 select 再 update。
|
|||
|
|
MarkAllRead(ctx context.Context, userID string, buckets []string) error
|
|||
|
|
|
|||
|
|
// CountUnreadApprox 回傳未讀數(允許是近似值)。
|
|||
|
|
// 實作方式可以是:
|
|||
|
|
// - 掃少量 buckets 中 status='UNREAD' 的 row,然後在應用端計算
|
|||
|
|
// - 或讀取外部 counter(Redis / 另一張 counter table)
|
|||
|
|
CountUnreadApprox(ctx context.Context, userID string, buckets []string) (int64, error)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---- 3. NotificationCursorRepository ----
|
|||
|
|
// 管 last_seen 光標,用來減少大量「每一筆更新已讀」的成本。
|
|||
|
|
|
|||
|
|
type NotificationCursorRepository interface {
|
|||
|
|
// GetCursor 取得某 user 的光標,如果不存在可以回傳 (nil, nil)。
|
|||
|
|
GetCursor(ctx context.Context, userID string) (*entity.NotificationCursor, error)
|
|||
|
|
// UpsertCursor 新增或更新光標。
|
|||
|
|
// 一般在使用者打開通知列表、或捲到最上面時更新。
|
|||
|
|
UpsertCursor(ctx context.Context, cursor *entity.NotificationCursor) error
|
|||
|
|
}
|