diff --git a/internal/logic/admin/invite/benefits.go b/internal/logic/admin/invite/benefits.go index 69076b7..7c0fe66 100644 --- a/internal/logic/admin/invite/benefits.go +++ b/internal/logic/admin/invite/benefits.go @@ -23,8 +23,9 @@ type InviteRelation struct { } type paidOrderRow struct { - UserId int64 `gorm:"column:user_id"` - OrderNo string `gorm:"column:order_no"` + UserId int64 `gorm:"column:user_id"` + SubscriptionUserId int64 `gorm:"column:subscription_user_id"` + OrderNo string `gorm:"column:order_no"` } type systemLogRow struct { @@ -81,7 +82,7 @@ func QueryBenefits(ctx context.Context, db *gorm.DB, relations []InviteRelation) var paidOrders []paidOrderRow if err := db.WithContext(ctx). Table("`order`"). - Select("user_id, order_no"). + Select("user_id, subscription_user_id, order_no"). Where("user_id IN ? AND status IN ?", inviteeIds, []int{2, 5}). Scan(&paidOrders).Error; err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invitee paid orders failed: %v", err) @@ -92,11 +93,16 @@ func QueryBenefits(ctx context.Context, db *gorm.DB, relations []InviteRelation) orderNos := make([]string, 0, len(paidOrders)) orderToInvitee := make(map[string]int64, len(paidOrders)) + orderToSubscriptionUser := make(map[string]int64, len(paidOrders)) inviterIds := make([]int64, 0, len(relations)) inviteeAndInviterSet := make(map[int64]struct{}, len(relations)*2) for _, order := range paidOrders { orderNos = append(orderNos, order.OrderNo) orderToInvitee[order.OrderNo] = order.UserId + if order.SubscriptionUserId > 0 && order.SubscriptionUserId != order.UserId { + orderToSubscriptionUser[order.OrderNo] = order.SubscriptionUserId + inviteeAndInviterSet[order.SubscriptionUserId] = struct{}{} + } } for _, relation := range relations { inviterIds = append(inviterIds, relation.InviterId) @@ -111,7 +117,7 @@ func QueryBenefits(ctx context.Context, db *gorm.DB, relations []InviteRelation) if err := fillCommissionBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderNos, inviterIds); err != nil { return nil, err } - if err := fillGiftBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderNos, inviteeAndInviterIds); err != nil { + if err := fillGiftBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderToSubscriptionUser, orderNos, inviteeAndInviterIds); err != nil { return nil, err } @@ -143,7 +149,7 @@ func fillCommissionBenefits(ctx context.Context, db *gorm.DB, benefits map[int64 return nil } -func fillGiftBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benefits, orderToInvitee map[string]int64, inviteeToInviter map[int64]int64, orderNos []string, userIds []int64) error { +func fillGiftBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benefits, orderToInvitee map[string]int64, inviteeToInviter map[int64]int64, orderToSubscriptionUser map[string]int64, orderNos []string, userIds []int64) error { var rows []systemLogRow if err := db.WithContext(ctx). Table("system_logs"). @@ -170,6 +176,8 @@ func fillGiftBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benef benefit.InviterGiftDays += content.Amount case inviteeId: benefit.InviteeGiftDays += content.Amount + case orderToSubscriptionUser[content.OrderNo]: + benefit.InviteeGiftDays += content.Amount default: continue } diff --git a/internal/logic/admin/invite/benefits_test.go b/internal/logic/admin/invite/benefits_test.go new file mode 100644 index 0000000..322490c --- /dev/null +++ b/internal/logic/admin/invite/benefits_test.go @@ -0,0 +1,105 @@ +package invite + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func TestQueryBenefitsCountsFamilyOwnerGiftAsInviteeGift(t *testing.T) { + db, mock, cleanup := newBenefitsTestDB(t) + defer cleanup() + + mock.ExpectQuery("COUNT(*) as cnt"). + WithArgs(int64(200), 2, 5). + WillReturnRows(sqlmock.NewRows([]string{"user_id", "cnt"}).AddRow(200, 1)) + mock.ExpectQuery("SELECT user_id, subscription_user_id, order_no FROM `order`"). + WithArgs(int64(200), 2, 5). + WillReturnRows(sqlmock.NewRows([]string{"user_id", "subscription_user_id", "order_no"}).AddRow(200, 900, "family-order")) + mock.ExpectQuery("object_id IN"). + WithArgs(33, int64(100), "family-order", 331, 332). + WillReturnRows(sqlmock.NewRows([]string{"object_id", "content"})) + mock.ExpectQuery("object_id IN"). + WithArgs(34, int64(900), int64(200), int64(100), "family-order"). + WillReturnRows(sqlmock.NewRows([]string{"object_id", "content"}). + AddRow(900, `{"type":341,"order_no":"family-order","amount":7,"balance":7,"remark":"邀请赠送"}`)) + + benefits, err := QueryBenefits(context.Background(), db, []InviteRelation{{InviteeId: 200, InviterId: 100}}) + if err != nil { + t.Fatalf("QueryBenefits returned error: %v", err) + } + + benefit := benefits[200] + if benefit.InviteeGiftDays != 7 { + t.Fatalf("InviteeGiftDays = %d, want 7", benefit.InviteeGiftDays) + } + if benefit.InviterGiftDays != 0 { + t.Fatalf("InviterGiftDays = %d, want 0", benefit.InviterGiftDays) + } + assertBenefitsExpectations(t, mock) +} + +func TestQueryBenefitsKeepsDirectInviteeGift(t *testing.T) { + db, mock, cleanup := newBenefitsTestDB(t) + defer cleanup() + + mock.ExpectQuery("COUNT(*) as cnt"). + WithArgs(int64(200), 2, 5). + WillReturnRows(sqlmock.NewRows([]string{"user_id", "cnt"}).AddRow(200, 1)) + mock.ExpectQuery("SELECT user_id, subscription_user_id, order_no FROM `order`"). + WithArgs(int64(200), 2, 5). + WillReturnRows(sqlmock.NewRows([]string{"user_id", "subscription_user_id", "order_no"}).AddRow(200, 0, "direct-order")) + mock.ExpectQuery("object_id IN"). + WithArgs(33, int64(100), "direct-order", 331, 332). + WillReturnRows(sqlmock.NewRows([]string{"object_id", "content"})) + mock.ExpectQuery("object_id IN"). + WithArgs(34, int64(200), int64(100), "direct-order"). + WillReturnRows(sqlmock.NewRows([]string{"object_id", "content"}). + AddRow(200, `{"type":341,"order_no":"direct-order","amount":5,"balance":5,"remark":"邀请赠送"}`)) + + benefits, err := QueryBenefits(context.Background(), db, []InviteRelation{{InviteeId: 200, InviterId: 100}}) + if err != nil { + t.Fatalf("QueryBenefits returned error: %v", err) + } + + if got := benefits[200].InviteeGiftDays; got != 5 { + t.Fatalf("InviteeGiftDays = %d, want 5", got) + } + assertBenefitsExpectations(t, mock) +} + +func newBenefitsTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock, func()) { + t.Helper() + + sqlDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherFunc(func(expectedSQL, actualSQL string) error { + if strings.Contains(actualSQL, expectedSQL) { + return nil + } + return fmt.Errorf("actual sql %q does not contain %q", actualSQL, expectedSQL) + }))) + if err != nil { + t.Fatalf("create sqlmock: %v", err) + } + + db, err := gorm.Open(mysql.New(mysql.Config{Conn: sqlDB, SkipInitializeWithVersion: true}), &gorm.Config{}) + if err != nil { + _ = sqlDB.Close() + t.Fatalf("open gorm db: %v", err) + } + + return db, mock, func() { + _ = sqlDB.Close() + } +} + +func assertBenefitsExpectations(t *testing.T, mock sqlmock.Sqlmock) { + t.Helper() + if err := mock.ExpectationsWereMet(); err != nil { + t.Fatalf("unmet sql expectations: %v", err) + } +} diff --git a/internal/logic/public/user/getInviteRecordsLogic.go b/internal/logic/public/user/getInviteRecordsLogic.go index 5238928..4e7a40a 100644 --- a/internal/logic/public/user/getInviteRecordsLogic.go +++ b/internal/logic/public/user/getInviteRecordsLogic.go @@ -5,7 +5,6 @@ import ( "encoding/json" logmodel "github.com/perfect-panel/server/internal/model/log" - ordermodel "github.com/perfect-panel/server/internal/model/order" "github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" @@ -14,6 +13,7 @@ import ( "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" + "gorm.io/gorm" ) const ( @@ -45,8 +45,10 @@ type parsedInviteRecordLog struct { } type inviteOrderUser struct { - OrderNo string `gorm:"column:order_no"` - UserId int64 `gorm:"column:user_id"` + OrderNo string `gorm:"column:order_no"` + UserId int64 `gorm:"column:user_id"` + SubscriptionUserId int64 `gorm:"column:subscription_user_id"` + RefererId int64 `gorm:"column:referer_id"` } // Get invite gift records @@ -67,9 +69,17 @@ func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequ normalizeInviteRecordsPagination(req) + visibleUserIds, err := l.resolveInviteRecordVisibleUserIds(u.Id) + if err != nil { + l.Errorw("[GetInviteRecords] resolve visible users failed", + logger.Field("error", err.Error()), + logger.Field("user_id", u.Id)) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "resolve visible users failed: %v", err.Error()) + } + query := l.svcCtx.DB.WithContext(l.ctx). Table("system_logs"). - Where("type = ? AND object_id = ?", logmodel.TypeGift.Uint8(), u.Id). + Where("type = ? AND object_id IN ?", logmodel.TypeGift.Uint8(), visibleUserIds). Where("JSON_VALID(content) = 1"). Where("JSON_UNQUOTE(JSON_EXTRACT(content, '$.remark')) = ?", "邀请赠送") if req.StartTime > 0 { @@ -79,20 +89,10 @@ func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequ query = query.Where("created_at <= FROM_UNIXTIME(?)", req.EndTime) } - var total int64 - if err = query.Count(&total).Error; err != nil { - l.Errorw("[GetInviteRecords] count logs failed", - logger.Field("error", err.Error()), - logger.Field("user_id", u.Id)) - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count logs failed: %v", err.Error()) - } - var logs []inviteRecordLog if err = query. Select("id, object_id, content, CAST(UNIX_TIMESTAMP(created_at) * 1000 AS SIGNED) AS created_at"). Order("created_at DESC, id DESC"). - Limit(req.Size). - Offset((req.Page - 1) * req.Size). Scan(&logs).Error; err != nil { l.Errorw("[GetInviteRecords] query logs failed", logger.Field("error", err.Error()), @@ -102,7 +102,7 @@ func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequ parsedLogs, orderNos := l.parseInviteRecordContents(logs) if len(logs) == 0 || len(parsedLogs) == 0 { - return &types.GetInviteRecordsResponse{Total: total, List: []types.InviteRecord{}}, nil + return &types.GetInviteRecordsResponse{Total: 0, List: []types.InviteRecord{}}, nil } orders, err := l.queryInviteRecordOrders(orderNos) @@ -113,10 +113,15 @@ func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequ return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query orders failed: %v", err.Error()) } - list := make([]types.InviteRecord, 0, len(parsedLogs)) + allRecords := make([]types.InviteRecord, 0, len(parsedLogs)) for _, parsed := range parsedLogs { content := parsed.content logItem := parsed.log + orderInfo, hasOrder := orders[content.OrderNo] + if !l.canViewInviteRecord(u.Id, logItem.ObjectId, hasOrder, orderInfo) { + continue + } + record := types.InviteRecord{ Role: inviteRecordRoleInviter, GiftDays: content.Amount, @@ -124,7 +129,7 @@ func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequ CreatedAt: logItem.CreatedAt, } - if orderInfo, ok := orders[content.OrderNo]; ok { + if hasOrder { peerId := orderInfo.UserId if orderInfo.UserId == u.Id { record.Role = inviteRecordRoleInvitee @@ -135,15 +140,56 @@ func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequ } } - list = append(list, record) + allRecords = append(allRecords, record) } + total := int64(len(allRecords)) + list := paginateInviteRecords(allRecords, req.Page, req.Size) return &types.GetInviteRecordsResponse{ Total: total, List: list, }, nil } +func (l *GetInviteRecordsLogic) resolveInviteRecordVisibleUserIds(currentUserId int64) ([]int64, error) { + visibleUserIds := []int64{currentUserId} + + var relation struct { + FamilyId int64 `gorm:"column:family_id"` + Role uint8 `gorm:"column:role"` + OwnerUserId int64 `gorm:"column:owner_user_id"` + } + err := l.svcCtx.DB.WithContext(l.ctx). + Model(&user.UserFamilyMember{}). + Select("user_family_member.family_id, user_family_member.role, user_family.owner_user_id"). + Joins("JOIN user_family ON user_family.id = user_family_member.family_id AND user_family.deleted_at IS NULL AND user_family.status = ?", user.FamilyStatusActive). + Where("user_family_member.user_id = ? AND user_family_member.status = ? AND user_family_member.deleted_at IS NULL", currentUserId, user.FamilyMemberActive). + First(&relation).Error + if err == nil { + if relation.Role != user.FamilyRoleOwner && relation.OwnerUserId > 0 && relation.OwnerUserId != currentUserId { + visibleUserIds = append(visibleUserIds, relation.OwnerUserId) + } + return visibleUserIds, nil + } + if errors.Is(err, gorm.ErrRecordNotFound) { + return visibleUserIds, nil + } + return nil, err +} + +func (l *GetInviteRecordsLogic) canViewInviteRecord(currentUserId, logObjectId int64, hasOrder bool, orderInfo inviteOrderUser) bool { + if logObjectId == currentUserId { + if hasOrder { + return orderInfo.UserId == currentUserId || orderInfo.RefererId == currentUserId + } + return true + } + if !hasOrder { + return false + } + return orderInfo.UserId == currentUserId && orderInfo.SubscriptionUserId == logObjectId +} + func normalizeInviteRecordsPagination(req *types.GetInviteRecordsRequest) { if req.Page < 1 { req.Page = 1 @@ -156,6 +202,18 @@ func normalizeInviteRecordsPagination(req *types.GetInviteRecordsRequest) { } } +func paginateInviteRecords(records []types.InviteRecord, page, size int) []types.InviteRecord { + start := (page - 1) * size + if start >= len(records) { + return []types.InviteRecord{} + } + end := start + size + if end > len(records) { + end = len(records) + } + return records[start:end] +} + func (l *GetInviteRecordsLogic) parseInviteRecordContents(logs []inviteRecordLog) ([]parsedInviteRecordLog, []string) { parsedLogs := make([]parsedInviteRecordLog, 0, len(logs)) orderNos := make([]string, 0, len(logs)) @@ -183,9 +241,10 @@ func (l *GetInviteRecordsLogic) queryInviteRecordOrders(orderNos []string) (map[ var orderData []inviteOrderUser err := l.svcCtx.DB.WithContext(l.ctx). - Model(&ordermodel.Order{}). - Select("order_no, user_id"). - Where("order_no IN ?", orderNos). + Table("`order`"). + Select("`order`.order_no, `order`.user_id, `order`.subscription_user_id, invitee.referer_id"). + Joins("LEFT JOIN user invitee ON invitee.id = `order`.user_id"). + Where("`order`.order_no IN ?", orderNos). Scan(&orderData).Error if err != nil { return nil, err diff --git a/internal/logic/public/user/getInviteRecordsLogic_test.go b/internal/logic/public/user/getInviteRecordsLogic_test.go index 8c46cb3..2b509e6 100644 --- a/internal/logic/public/user/getInviteRecordsLogic_test.go +++ b/internal/logic/public/user/getInviteRecordsLogic_test.go @@ -20,16 +20,14 @@ func TestGetInviteRecordsInviter(t *testing.T) { svcCtx, mock, cleanup := newInviteRecordsTestSvc(t) defer cleanup() - mock.ExpectQuery("count(*)"). - WithArgs(34, int64(100), "邀请赠送"). - WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) + expectNoInviteRecordsFamily(t, mock, 100) mock.ExpectQuery("SELECT id, object_id, content"). - WithArgs(34, int64(100), "邀请赠送", 10). + WithArgs(34, int64(100), "邀请赠送"). WillReturnRows(sqlmock.NewRows([]string{"id", "object_id", "content", "created_at"}). AddRow(1, 100, `{"order_no":"order-1","amount":7,"remark":"邀请赠送"}`, 1779934580000)) - mock.ExpectQuery("SELECT order_no, user_id FROM `order`"). + mock.ExpectQuery("SELECT `order`.order_no, `order`.user_id, `order`.subscription_user_id, invitee.referer_id FROM `order`"). WithArgs("order-1"). - WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id"}).AddRow("order-1", 200)) + WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id", "subscription_user_id", "referer_id"}).AddRow("order-1", 200, 200, 100)) resp, err := NewGetInviteRecordsLogic(inviteRecordsContext(100, 0), svcCtx).GetInviteRecords(&types.GetInviteRecordsRequest{Page: 1, Size: 10}) if err != nil { @@ -49,16 +47,14 @@ func TestGetInviteRecordsInvitee(t *testing.T) { svcCtx, mock, cleanup := newInviteRecordsTestSvc(t) defer cleanup() - mock.ExpectQuery("count(*)"). - WithArgs(34, int64(200), "邀请赠送"). - WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) + expectNoInviteRecordsFamily(t, mock, 200) mock.ExpectQuery("SELECT id, object_id, content"). - WithArgs(34, int64(200), "邀请赠送", 10). + WithArgs(34, int64(200), "邀请赠送"). WillReturnRows(sqlmock.NewRows([]string{"id", "object_id", "content", "created_at"}). AddRow(2, 200, `{"order_no":"order-2","amount":7,"remark":"邀请赠送"}`, 1779934590000)) - mock.ExpectQuery("SELECT order_no, user_id FROM `order`"). + mock.ExpectQuery("SELECT `order`.order_no, `order`.user_id, `order`.subscription_user_id, invitee.referer_id FROM `order`"). WithArgs("order-2"). - WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id"}).AddRow("order-2", 200)) + WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id", "subscription_user_id", "referer_id"}).AddRow("order-2", 200, 200, 100)) resp, err := NewGetInviteRecordsLogic(inviteRecordsContext(200, 100), svcCtx).GetInviteRecords(&types.GetInviteRecordsRequest{Page: 1, Size: 10}) if err != nil { @@ -78,16 +74,14 @@ func TestGetInviteRecordsMissingOrderReturnsDirtyRecord(t *testing.T) { svcCtx, mock, cleanup := newInviteRecordsTestSvc(t) defer cleanup() - mock.ExpectQuery("count(*)"). - WithArgs(34, int64(100), "邀请赠送"). - WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) + expectNoInviteRecordsFamily(t, mock, 100) mock.ExpectQuery("SELECT id, object_id, content"). - WithArgs(34, int64(100), "邀请赠送", 10). + WithArgs(34, int64(100), "邀请赠送"). WillReturnRows(sqlmock.NewRows([]string{"id", "object_id", "content", "created_at"}). AddRow(3, 100, `{"order_no":"missing-order","amount":7,"remark":"邀请赠送"}`, 1779934600000)) - mock.ExpectQuery("SELECT order_no, user_id FROM `order`"). + mock.ExpectQuery("SELECT `order`.order_no, `order`.user_id, `order`.subscription_user_id, invitee.referer_id FROM `order`"). WithArgs("missing-order"). - WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id"})) + WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id", "subscription_user_id", "referer_id"})) resp, err := NewGetInviteRecordsLogic(inviteRecordsContext(100, 0), svcCtx).GetInviteRecords(&types.GetInviteRecordsRequest{Page: 1, Size: 10}) if err != nil { @@ -102,6 +96,62 @@ func TestGetInviteRecordsMissingOrderReturnsDirtyRecord(t *testing.T) { assertInviteRecordsExpectations(t, mock) } +func TestGetInviteRecordsFamilyMemberSeesOwnerGiftLog(t *testing.T) { + svcCtx, mock, cleanup := newInviteRecordsTestSvc(t) + defer cleanup() + + expectInviteRecordsFamilyMember(t, mock, 200, 900) + mock.ExpectQuery("SELECT id, object_id, content"). + WithArgs(34, int64(200), int64(900), "邀请赠送"). + WillReturnRows(sqlmock.NewRows([]string{"id", "object_id", "content", "created_at"}). + AddRow(4, 900, `{"order_no":"family-order","amount":7,"remark":"邀请赠送"}`, 1779934610000)) + mock.ExpectQuery("SELECT `order`.order_no, `order`.user_id, `order`.subscription_user_id, invitee.referer_id FROM `order`"). + WithArgs("family-order"). + WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id", "subscription_user_id", "referer_id"}).AddRow("family-order", 200, 900, 100)) + + resp, err := NewGetInviteRecordsLogic(inviteRecordsContext(200, 100), svcCtx).GetInviteRecords(&types.GetInviteRecordsRequest{Page: 1, Size: 10}) + if err != nil { + t.Fatalf("GetInviteRecords returned error: %v", err) + } + assertInviteRecordResponse(t, resp, types.InviteRecord{ + Role: inviteRecordRoleInvitee, + PeerHash: hash.InvitePeerHash(100), + GiftDays: 7, + OrderNo: "family-order", + CreatedAt: 1779934610000, + }) + assertInviteRecordsExpectations(t, mock) +} + +func TestGetInviteRecordsOwnerDoesNotSeeMemberGiftLog(t *testing.T) { + svcCtx, mock, cleanup := newInviteRecordsTestSvc(t) + defer cleanup() + + expectInviteRecordsFamilyOwner(t, mock, 900) + mock.ExpectQuery("SELECT id, object_id, content"). + WithArgs(34, int64(900), "邀请赠送"). + WillReturnRows(sqlmock.NewRows([]string{"id", "object_id", "content", "created_at"}). + AddRow(5, 900, `{"order_no":"member-order","amount":7,"remark":"邀请赠送"}`, 1779934620000)) + mock.ExpectQuery("SELECT `order`.order_no, `order`.user_id, `order`.subscription_user_id, invitee.referer_id FROM `order`"). + WithArgs("member-order"). + WillReturnRows(sqlmock.NewRows([]string{"order_no", "user_id", "subscription_user_id", "referer_id"}).AddRow("member-order", 200, 900, 100)) + + resp, err := NewGetInviteRecordsLogic(inviteRecordsContext(900, 0), svcCtx).GetInviteRecords(&types.GetInviteRecordsRequest{Page: 1, Size: 10}) + if err != nil { + t.Fatalf("GetInviteRecords returned error: %v", err) + } + if resp == nil { + t.Fatal("response is nil") + } + if resp.Total != 0 { + t.Fatalf("Total = %d, want 0", resp.Total) + } + if len(resp.List) != 0 { + t.Fatalf("len(List) = %d, want 0", len(resp.List)) + } + assertInviteRecordsExpectations(t, mock) +} + func newInviteRecordsTestSvc(t *testing.T) (*svc.ServiceContext, sqlmock.Sqlmock, func()) { t.Helper() @@ -126,6 +176,27 @@ func newInviteRecordsTestSvc(t *testing.T) (*svc.ServiceContext, sqlmock.Sqlmock } } +func expectNoInviteRecordsFamily(t *testing.T, mock sqlmock.Sqlmock, userId int64) { + t.Helper() + mock.ExpectQuery("FROM `user_family_member` JOIN user_family"). + WithArgs(1, userId, 1, 1). + WillReturnError(gorm.ErrRecordNotFound) +} + +func expectInviteRecordsFamilyMember(t *testing.T, mock sqlmock.Sqlmock, userId, ownerUserId int64) { + t.Helper() + mock.ExpectQuery("FROM `user_family_member` JOIN user_family"). + WithArgs(1, userId, 1, 1). + WillReturnRows(sqlmock.NewRows([]string{"family_id", "role", "owner_user_id"}).AddRow(800, 2, ownerUserId)) +} + +func expectInviteRecordsFamilyOwner(t *testing.T, mock sqlmock.Sqlmock, userId int64) { + t.Helper() + mock.ExpectQuery("FROM `user_family_member` JOIN user_family"). + WithArgs(1, userId, 1, 1). + WillReturnRows(sqlmock.NewRows([]string{"family_id", "role", "owner_user_id"}).AddRow(800, 1, userId)) +} + func inviteRecordsContext(userId, refererId int64) context.Context { return context.WithValue(context.Background(), constant.CtxKeyUser, &modeluser.User{ Id: userId,