package user import ( "context" "fmt" "strings" "time" "gorm.io/gorm" ) // FindActiveSubscribesByUserIds Find active subscriptions for multiple users func (m *customUserModel) FindActiveSubscribesByUserIds(ctx context.Context, userIds []int64) (map[int64]*UserStatusInfo, error) { if len(userIds) == 0 { return map[int64]*UserStatusInfo{}, nil } type Result struct { UserId int64 Name string Quantity int64 UpdatedAt *time.Time } var results []Result // Query latest active subscription for each user err := m.QueryNoCacheCtx(ctx, &results, func(conn *gorm.DB, v interface{}) error { return conn.Table("user_subscribe us"). Select("us.user_id, subscribe.name, COALESCE(o.quantity, 1) AS quantity, us.updated_at"). Joins("LEFT JOIN subscribe ON us.subscribe_id = subscribe.id"). Joins("LEFT JOIN `order` o ON o.id = (SELECT MAX(o2.id) FROM `order` o2 WHERE o2.user_id = us.user_id AND o2.subscribe_id = us.subscribe_id AND o2.status IN (2, 5))"). Where("us.user_id IN ? AND us.status IN (0, 1) AND us.expire_time > ?", userIds, time.Now()). Order("us.created_at ASC, us.id ASC"). // Ascending so we can overwrite in map to get the latest Scan(v).Error }) if err != nil { return nil, err } userMap := make(map[int64]*UserStatusInfo) packageTotals := make(map[int64]map[string]int64) packageOrder := make(map[int64][]string) for _, r := range results { name := strings.TrimSpace(r.Name) if name == "" { name = "Unknown" } quantity := r.Quantity if quantity <= 0 { quantity = 1 } if _, ok := packageTotals[r.UserId]; !ok { packageTotals[r.UserId] = make(map[string]int64) } if _, exists := packageTotals[r.UserId][name]; !exists { packageOrder[r.UserId] = append(packageOrder[r.UserId], name) } packageTotals[r.UserId][name] += quantity info, ok := userMap[r.UserId] if !ok { info = &UserStatusInfo{} userMap[r.UserId] = info } info.MemberStatus = formatPackageDisplay(name, quantity) if r.UpdatedAt != nil && (info.LastTrafficAt == nil || r.UpdatedAt.After(*info.LastTrafficAt)) { info.LastTrafficAt = r.UpdatedAt } } for userID, info := range userMap { orderedNames := packageOrder[userID] if len(orderedNames) == 0 { continue } parts := make([]string, 0, len(orderedNames)) for _, name := range orderedNames { parts = append(parts, formatPackageDisplay(name, packageTotals[userID][name])) } info.PurchasedPackage = strings.Join(parts, ", ") } return userMap, nil } func formatPackageDisplay(name string, quantity int64) string { if quantity <= 1 { return name } return fmt.Sprintf("%s*%d", name, quantity) }