package user import ( "context" "fmt" "strings" "time" logicCommon "github.com/perfect-panel/server/internal/logic/common" "github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/phone" "github.com/perfect-panel/server/pkg/tool" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" ) type GetUserListLogic struct { ctx context.Context svcCtx *svc.ServiceContext logger.Logger } func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserListLogic { return &GetUserListLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logger.WithContext(ctx), } } func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (*types.GetUserListResponse, error) { list, total, err := l.svcCtx.UserModel.QueryPageList(l.ctx, req.Page, req.Size, &user.UserFilterParams{ UserId: req.UserId, Search: req.Search, Unscoped: req.Unscoped, SubscribeId: req.SubscribeId, UserSubscribeId: req.UserSubscribeId, ShortCode: req.ShortCode, DeviceId: req.DeviceId, FamilyJoined: req.FamilyJoined, FamilyStatus: req.FamilyStatus, FamilyOwnerUserId: req.FamilyOwnerUserId, FamilyId: req.FamilyId, Order: req.SortOrder, }) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetUserListLogic failed: %v", err.Error()) } referCodes := make([]string, 0, len(list)) for _, item := range list { referCode := strings.TrimSpace(item.ReferCode) if referCode == "" { continue } referCodes = append(referCodes, referCode) } inviteLinkMap := logicCommon.NewInviteLinkResolver(l.ctx, l.svcCtx).ResolveInviteLinksBatch(referCodes, 8, 3, 1500*time.Millisecond) // Batch fetch active subscriptions userIds := make([]int64, 0, len(list)) for _, u := range list { userIds = append(userIds, u.Id) } activeSubs, err := l.svcCtx.UserModel.FindActiveSubscribesByUserIds(l.ctx, userIds) if err != nil { // Log error but continue l.Logger.Error("FindActiveSubscribesByUserIds failed", logger.Field("error", err.Error())) } type familyRelation struct { UserId int64 FamilyId int64 Role uint8 FamilyStatus uint8 OwnerUserId int64 MaxMembers int64 } relationMap := map[int64]familyRelation{} familyIds := make([]int64, 0) if len(userIds) > 0 { var relations []familyRelation relationErr := l.svcCtx.DB.WithContext(l.ctx). Table("user_family_member"). Select("user_family_member.user_id, user_family_member.family_id, user_family_member.role, user_family.status as family_status, user_family.owner_user_id, user_family.max_members"). Joins("JOIN user_family ON user_family.id = user_family_member.family_id AND user_family.deleted_at IS NULL"). Where("user_family_member.user_id IN ? AND user_family_member.deleted_at IS NULL AND user_family_member.status = ?", userIds, user.FamilyMemberActive). Scan(&relations).Error if relationErr != nil { l.Logger.Error("query family relations failed", logger.Field("error", relationErr.Error())) } familyIdSet := make(map[int64]struct{}, len(relations)) for _, relation := range relations { if _, exists := relationMap[relation.UserId]; !exists { relationMap[relation.UserId] = relation } if _, ok := familyIdSet[relation.FamilyId]; ok { continue } familyIdSet[relation.FamilyId] = struct{}{} familyIds = append(familyIds, relation.FamilyId) } } type familyCount struct { FamilyId int64 Count int64 } familyCountMap := map[int64]int64{} if len(familyIds) > 0 { var counts []familyCount countErr := l.svcCtx.DB.WithContext(l.ctx). Table("user_family_member"). Select("family_id, COUNT(1) as count"). Where("family_id IN ? AND status = ? AND deleted_at IS NULL", familyIds, user.FamilyMemberActive). Group("family_id"). Scan(&counts).Error if countErr != nil { l.Logger.Error("query family member count failed", logger.Field("error", countErr.Error())) } for _, count := range counts { familyCountMap[count.FamilyId] = count.Count } } userRespList := make([]types.User, 0, len(list)) for _, item := range list { var u types.User tool.DeepCopy(&u, item) // Set LastLoginTime if item.LastLoginTime != nil { u.LastLoginTime = item.LastLoginTime.Unix() } // Set MemberStatus and update LastLoginTime from traffic if activeSubs != nil { if info, ok := activeSubs[item.Id]; ok { u.MemberStatus = info.MemberStatus u.PurchasedPackage = info.PurchasedPackage if info.LastTrafficAt != nil { trafficTime := info.LastTrafficAt.Unix() if trafficTime > u.LastLoginTime { u.LastLoginTime = trafficTime } } } } // 处理 AuthMethods authMethods := make([]types.UserAuthMethod, len(u.AuthMethods)) // 直接创建目标 slice for i, method := range u.AuthMethods { tool.DeepCopy(&authMethods[i], method) if method.AuthType == "mobile" { authMethods[i].AuthIdentifier = phone.FormatToInternational(method.AuthIdentifier) } } u.AuthMethods = authMethods // 填充 DeviceID for i, d := range item.UserDevices { if i < len(u.UserDevices) { u.UserDevices[i].DeviceNo = tool.DeviceIdToHash(d.Id) } } if relation, ok := relationMap[item.Id]; ok { u.FamilyJoined = true u.FamilyId = relation.FamilyId u.FamilyRole = relation.Role u.FamilyOwnerUserId = relation.OwnerUserId u.FamilyMaxMembers = relation.MaxMembers u.FamilyMemberCount = familyCountMap[relation.FamilyId] switch relation.FamilyStatus { case user.FamilyStatusActive: u.FamilyStatus = "active" default: u.FamilyStatus = "disabled" } switch relation.Role { case user.FamilyRoleOwner: u.FamilyRoleName = "owner" case user.FamilyRoleMember: u.FamilyRoleName = "member" default: u.FamilyRoleName = fmt.Sprintf("role_%d", relation.Role) } } if referCode := strings.TrimSpace(item.ReferCode); referCode != "" { u.ShareLink = inviteLinkMap[referCode] } userRespList = append(userRespList, u) } return &types.GetUserListResponse{ Total: total, List: userRespList, }, nil }