thread-master/apps/backend/internal/module/appnotif/usecase/service.go

124 lines
3.4 KiB
Go
Raw Permalink Normal View History

2026-07-13 01:15:30 +00:00
package usecase
import (
"context"
"fmt"
"strings"
"apps/backend/internal/module/appnotif/domain"
"github.com/google/uuid"
)
type Service struct {
Repo domain.Repository
}
func New(repo domain.Repository) *Service {
return &Service{Repo: repo}
}
func (s *Service) List(ctx context.Context, ownerUID int64) ([]*domain.Notification, error) {
return s.Repo.ListByOwner(ctx, ownerUID)
}
func (s *Service) UnreadCount(ctx context.Context, ownerUID int64) (int64, error) {
return s.Repo.UnreadCount(ctx, ownerUID)
}
func (s *Service) MarkRead(ctx context.Context, ownerUID int64, id string) error {
return s.Repo.MarkRead(ctx, ownerUID, id)
}
func (s *Service) MarkAllRead(ctx context.Context, ownerUID int64) error {
return s.Repo.MarkAllRead(ctx, ownerUID)
}
// NotifyJobTerminal — 相容舊介面;改走 NotifyJobStateupsert 同一 job 一則)
func (s *Service) NotifyJobTerminal(ctx context.Context, ownerUID int64, jobID, status, summary string) error {
return s.NotifyJobState(ctx, ownerUID, jobID, "", status, summary, 100)
}
// NotifyJobState — 同一 job 只保留一則通知,進度更新時改寫 title/body鈴鐺即時有進度
func (s *Service) NotifyJobState(ctx context.Context, ownerUID int64, jobID, templateType, status, summary string, percent int) error {
if ownerUID <= 0 || jobID == "" {
return nil
}
title := jobNotifyTitle(templateType, status, percent)
body := strings.TrimSpace(summary)
if body == "" {
body = fmt.Sprintf("%s · %s", title, jobID)
}
// 進度摘要裡若已含 % 就不再重複;否則附上 percent
if percent > 0 && percent < 100 && !strings.Contains(body, "%") {
body = fmt.Sprintf("%s · %d%%", body, percent)
}
existing, err := s.Repo.FindLatestByJobRef(ctx, ownerUID, jobID)
now := domain.NowNano()
if err == nil && existing != nil {
existing.Title = title
existing.Body = body
existing.Kind = domain.KindJob
existing.RefType = domain.RefJob
existing.RefID = jobID
// 每次有進度/終態都標未讀,鈴鐺才會跳
existing.ReadAt = 0
// 用 created_at 排序時把最新活動頂到前面
existing.CreatedAt = now
return s.Repo.Replace(ctx, existing)
}
n := &domain.Notification{
ID: uuid.NewString(), OwnerUID: ownerUID,
Title: title, Body: body, Kind: domain.KindJob,
RefType: domain.RefJob, RefID: jobID,
CreatedAt: now,
}
return s.Repo.Insert(ctx, n)
}
func jobNotifyTitle(templateType, status string, percent int) string {
name := templateLabelZH(templateType)
switch status {
case "succeeded":
return name + " · 已完成"
case "failed":
return name + " · 失敗"
case "cancelled":
return name + " · 已取消"
case "running":
if percent > 0 {
return fmt.Sprintf("%s · 進行中 %d%%", name, percent)
}
return name + " · 進行中"
case "queued", "pending":
return name + " · 已排程"
default:
if percent > 0 {
return fmt.Sprintf("%s · %d%%", name, percent)
}
return name + " · 更新"
}
}
func templateLabelZH(templateType string) string {
switch templateType {
case "persona_analyze_account":
return "人設分析(爬公開貼文)"
case "persona_analyze_text":
return "人設分析(文字)"
case "compose_mimic":
return "仿寫貼文"
case "threads_token_renew":
return "Threads Token 延長"
case "demo":
return "Demo 測試任務"
default:
if strings.TrimSpace(templateType) == "" {
return "背景任務"
}
return "背景任務"
}
}