All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m26s
207 lines
6.2 KiB
Go
207 lines
6.2 KiB
Go
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
|
|
}
|