Files
hfb_sys/backend/internal/modules/notification/repository_test.go
T
yml2213 4461e7929e feat(distribution): 站内信与通知体系多租户独立分支隔离
- 新增 000071 数据库迁移:notifications 表支持 distribution_site_id 并自动回填历史分销订单通知
- 通知自动归属:在事务内根据订单或申诉自动关联当前分销站点 ID
- 隔离查询与已读控制:列表、未读统计与批量已读均严格按站点隔离,防止主站与分站交叉泄漏
- 前台界面联动:消息中心通知 Tab 及移动端/PC端站内信页面展示分站专属标识与精准过滤
2026-09-07 19:10:02 +08:00

194 lines
5.6 KiB
Go

package notification
import (
"context"
"errors"
"testing"
"hfb_sys/backend/internal/database"
"hfb_sys/backend/internal/model"
)
func TestRepositoryUnreadCountAndMarkRead(t *testing.T) {
db := database.NewTestDB()
if err := db.AutoMigrate(&model.Notification{}); err != nil {
t.Fatalf("AutoMigrate() error = %v", err)
}
repo := NewRepository(db)
ctx := context.Background()
if err := db.Create(&model.Notification{
UserID: 10,
Type: "order",
Title: "订单通知",
Content: "请处理订单",
}).Error; err != nil {
t.Fatalf("Create() error = %v", err)
}
count, err := repo.UnreadCount(ctx, 10)
if err != nil {
t.Fatalf("UnreadCount() error = %v", err)
}
if count != 1 {
t.Fatalf("UnreadCount() = %d, want 1", count)
}
if err := repo.MarkRead(ctx, 10, 1); err != nil {
t.Fatalf("MarkRead() error = %v", err)
}
// 重复标记应保持幂等,不应误报不存在。
if err := repo.MarkRead(ctx, 10, 1); err != nil {
t.Fatalf("MarkRead() repeat error = %v", err)
}
count, err = repo.UnreadCount(ctx, 10)
if err != nil {
t.Fatalf("UnreadCount() after read error = %v", err)
}
if count != 0 {
t.Fatalf("UnreadCount() after read = %d, want 0", count)
}
}
func TestRepositoryMarkReadNotFound(t *testing.T) {
db := database.NewTestDB()
if err := db.AutoMigrate(&model.Notification{}); err != nil {
t.Fatalf("AutoMigrate() error = %v", err)
}
repo := NewRepository(db)
err := repo.MarkRead(context.Background(), 10, 99)
if !errors.Is(err, ErrNotificationNotFound) {
t.Fatalf("MarkRead() error = %v, want ErrNotificationNotFound", err)
}
}
func TestNotificationTenantIsolation(t *testing.T) {
db := database.NewTestDB()
if err := db.AutoMigrate(&model.Notification{}, &model.DistributionSite{}, &model.RentalOrder{}); err != nil {
t.Fatalf("AutoMigrate() error = %v", err)
}
repo := NewRepository(db)
ctx := context.Background()
site := model.DistributionSite{
ID: 101,
SiteCode: "branch_a",
SiteName: "分站A",
Status: "active",
}
if err := db.Create(&site).Error; err != nil {
t.Fatalf("create distribution site error: %v", err)
}
siteID := site.ID
// 1. 创建主站通知
mainNotif := model.Notification{
UserID: 20,
Type: "order",
Title: "主站订单通知",
Content: "这是主站的订单通知",
BizType: "order",
DistributionSiteID: nil,
}
if err := db.Create(&mainNotif).Error; err != nil {
t.Fatalf("create main notif error: %v", err)
}
// 2. 创建分站通知
branchNotif := model.Notification{
UserID: 20,
Type: "order",
Title: "分站A订单通知",
Content: "这是分站A的订单通知",
BizType: "order",
DistributionSiteID: &siteID,
}
if err := db.Create(&branchNotif).Error; err != nil {
t.Fatalf("create branch notif error: %v", err)
}
// 3. 验证未读数隔离
mainUnread, err := repo.UnreadCount(ctx, 20)
if err != nil || mainUnread != 1 {
t.Fatalf("mainUnread = %d, err = %v, want 1", mainUnread, err)
}
branchUnread, err := repo.UnreadCount(ctx, 20, "branch_a")
if err != nil || branchUnread != 1 {
t.Fatalf("branchUnread = %d, err = %v, want 1", branchUnread, err)
}
// 4. 验证列表查询隔离
mainList, err := repo.List(ctx, 20, 1, 10)
if err != nil || mainList.Total != 1 {
t.Fatalf("mainList total = %d, err = %v, want 1", mainList.Total, err)
}
items := mainList.Items.([]NotificationDTO)
if items[0].Title != "主站订单通知" {
t.Fatalf("mainList item title = %s, want 主站订单通知", items[0].Title)
}
branchList, err := repo.List(ctx, 20, 1, 10, "branch_a")
if err != nil || branchList.Total != 1 {
t.Fatalf("branchList total = %d, err = %v, want 1", branchList.Total, err)
}
branchItems := branchList.Items.([]NotificationDTO)
if branchItems[0].Title != "分站A订单通知" {
t.Fatalf("branchList item title = %s, want 分站A订单通知", branchItems[0].Title)
}
// 5. 跨站点标记单条已读阻断
err = repo.MarkRead(ctx, 20, mainNotif.ID, "branch_a")
if !errors.Is(err, ErrNotificationNotFound) {
t.Fatalf("cross-site MarkRead error = %v, want ErrNotificationNotFound", err)
}
// 6. 分站全部已读不影响主站
affected, err := repo.MarkAllRead(ctx, 20, "branch_a")
if err != nil || affected != 1 {
t.Fatalf("MarkAllRead affected = %d, err = %v, want 1", affected, err)
}
branchUnreadAfter, err := repo.UnreadCount(ctx, 20, "branch_a")
if err != nil || branchUnreadAfter != 0 {
t.Fatalf("branchUnreadAfter = %d, want 0", branchUnreadAfter)
}
mainUnreadAfter, err := repo.UnreadCount(ctx, 20)
if err != nil || mainUnreadAfter != 1 {
t.Fatalf("mainUnreadAfter = %d, want 1 (should remain unread)", mainUnreadAfter)
}
// 7. 测试 Append 自动关联订单 distribution_site_id
order := model.RentalOrder{
ID: 3001,
DistributionSiteID: &siteID,
OrderNo: "ORD3001",
}
if err := db.Create(&order).Error; err != nil {
t.Fatalf("create order error: %v", err)
}
orderID := order.ID
err = Append(db, Entry{
UserID: 20,
Type: "order",
Title: "订单自动关联分站",
Content: "自动读取订单 site ID",
BizType: "order",
BizID: &orderID,
})
if err != nil {
t.Fatalf("Append with auto lookup error: %v", err)
}
var autoNotif model.Notification
if err := db.Where("biz_id = ? AND biz_type = 'order'", orderID).First(&autoNotif).Error; err != nil {
t.Fatalf("find autoNotif error: %v", err)
}
if autoNotif.DistributionSiteID == nil || *autoNotif.DistributionSiteID != siteID {
t.Fatalf("autoNotif.DistributionSiteID = %v, want %d", autoNotif.DistributionSiteID, siteID)
}
}