各种配置项修复,优化到后台管理端配置
Build docker and publish / build (20.15.1) (push) Has been cancelled

This commit is contained in:
2026-03-04 17:58:40 -08:00
parent 149dfe1ac3
commit 4752f844ef
31 changed files with 1467 additions and 148 deletions
@@ -3,11 +3,13 @@ package user
import (
"context"
userModel "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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type DeleteUserSubscribeLogic struct {
@@ -27,13 +29,17 @@ func NewDeleteUserSubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext
func (l *DeleteUserSubscribeLogic) DeleteUserSubscribe(req *types.DeleteUserSubscribeRequest) error {
// find user subscribe by ID
userSubscribe, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId)
userSubscribe := &userModel.Subscribe{}
err := l.svcCtx.DB.WithContext(l.ctx).Model(&userModel.Subscribe{}).Where("id = ?", req.UserSubscribeId).First(userSubscribe).Error
if err != nil {
l.Errorw("failed to find user subscribe", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId))
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "user subscribe not found"), "failed to find user subscribe: %v", err.Error())
}
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to find user subscribe: %v", err.Error())
}
err = l.svcCtx.UserModel.DeleteSubscribeById(l.ctx, req.UserSubscribeId)
err = l.svcCtx.DB.WithContext(l.ctx).Where("id = ?", req.UserSubscribeId).Delete(&userModel.Subscribe{}).Error
if err != nil {
l.Errorw("failed to delete user subscribe", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "failed to delete user subscribe: %v", err.Error())
@@ -0,0 +1,68 @@
package user
import (
"context"
"time"
modelUser "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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type DissolveFamilyLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logger.Logger
}
func NewDissolveFamilyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DissolveFamilyLogic {
return &DissolveFamilyLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logger.WithContext(ctx),
}
}
func (l *DissolveFamilyLogic) DissolveFamily(req *types.DissolveFamilyRequest) error {
var family modelUser.UserFamily
err := l.svcCtx.DB.WithContext(l.ctx).
Where("id = ? AND deleted_at IS NULL", req.FamilyId).
First(&family).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyNotExist), "family does not exist")
}
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family failed")
}
if family.Status != modelUser.FamilyStatusActive {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyStatusInvalid), "family status is invalid")
}
now := time.Now()
transactionErr := l.svcCtx.DB.WithContext(l.ctx).Transaction(func(tx *gorm.DB) error {
if err = tx.Model(&modelUser.UserFamilyMember{}).
Where("family_id = ? AND deleted_at IS NULL AND status = ?", req.FamilyId, modelUser.FamilyMemberActive).
Updates(map[string]interface{}{
"status": modelUser.FamilyMemberRemoved,
"left_at": now,
}).Error; err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "remove family members failed")
}
if err = tx.Model(&modelUser.UserFamily{}).
Where("id = ?", req.FamilyId).
Update("status", 0).Error; err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "disable family failed")
}
return nil
})
if transactionErr != nil {
return transactionErr
}
return nil
}
+99
View File
@@ -0,0 +1,99 @@
package user
import (
"context"
"fmt"
"strings"
"github.com/perfect-panel/server/internal/model/user"
"gorm.io/gorm"
)
func mapFamilyStatus(status uint8) string {
if status == user.FamilyStatusActive {
return "active"
}
return "disabled"
}
func mapFamilyRoleName(role uint8) string {
switch role {
case user.FamilyRoleOwner:
return "owner"
case user.FamilyRoleMember:
return "member"
default:
return fmt.Sprintf("role_%d", role)
}
}
func mapFamilyMemberStatusName(status uint8) string {
switch status {
case user.FamilyMemberActive:
return "active"
case user.FamilyMemberLeft:
return "left"
case user.FamilyMemberRemoved:
return "removed"
default:
return fmt.Sprintf("status_%d", status)
}
}
func normalizeFamilyStatusInput(status string) (uint8, bool) {
switch strings.ToLower(strings.TrimSpace(status)) {
case "", "all":
return 0, false
case "active", "1":
return user.FamilyStatusActive, true
case "disabled", "0":
return 0, true
default:
return 0, false
}
}
func findUserIdentifiers(ctx context.Context, db *gorm.DB, userIDs []int64) (map[int64]string, error) {
identifierMap := make(map[int64]string)
if len(userIDs) == 0 {
return identifierMap, nil
}
type authRow struct {
UserId int64
AuthType string
AuthIdentifier string
}
var rows []authRow
err := db.WithContext(ctx).
Table("user_auth_methods").
Select("user_id, auth_type, auth_identifier").
Where("user_id IN ? AND deleted_at IS NULL", userIDs).
Scan(&rows).Error
if err != nil {
return nil, err
}
priority := map[string]int{
"email": 1,
"mobile": 2,
"telegram": 3,
"device": 4,
}
selectedPriority := make(map[int64]int, len(userIDs))
for _, row := range rows {
currentPriority := priority[row.AuthType]
if currentPriority == 0 {
currentPriority = 100
}
if previous, exists := selectedPriority[row.UserId]; exists && previous <= currentPriority {
continue
}
selectedPriority[row.UserId] = currentPriority
identifierMap[row.UserId] = row.AuthIdentifier
}
return identifierMap, nil
}
@@ -0,0 +1,106 @@
package user
import (
"context"
"strconv"
modelUser "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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type GetFamilyDetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logger.Logger
}
func NewGetFamilyDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFamilyDetailLogic {
return &GetFamilyDetailLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logger.WithContext(ctx),
}
}
func (l *GetFamilyDetailLogic) GetFamilyDetail(req *types.GetFamilyDetailRequest) (*types.FamilyDetail, error) {
var family modelUser.UserFamily
err := l.svcCtx.DB.WithContext(l.ctx).
Where("id = ? AND deleted_at IS NULL", req.Id).
First(&family).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.FamilyNotExist), "family does not exist")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family detail failed")
}
var members []modelUser.UserFamilyMember
if err = l.svcCtx.DB.WithContext(l.ctx).
Where("family_id = ? AND deleted_at IS NULL", family.Id).
Order("joined_at DESC").
Find(&members).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family members failed")
}
userIDs := make([]int64, 0, len(members)+1)
userIDs = append(userIDs, family.OwnerUserId)
activeMemberCount := int64(0)
for _, member := range members {
userIDs = append(userIDs, member.UserId)
if member.Status == modelUser.FamilyMemberActive {
activeMemberCount++
}
}
identifierMap, identifierErr := findUserIdentifiers(l.ctx, l.svcCtx.DB, userIDs)
if identifierErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family member identifiers failed")
}
memberItems := make([]types.FamilyMemberItem, 0, len(members))
for _, member := range members {
identifier := identifierMap[member.UserId]
if identifier == "" {
identifier = strconv.FormatInt(member.UserId, 10)
}
memberItem := types.FamilyMemberItem{
UserId: member.UserId,
Identifier: identifier,
Role: member.Role,
RoleName: mapFamilyRoleName(member.Role),
Status: member.Status,
StatusName: mapFamilyMemberStatusName(member.Status),
JoinSource: member.JoinSource,
JoinedAt: member.JoinedAt.Unix(),
}
if member.LeftAt != nil {
memberItem.LeftAt = member.LeftAt.Unix()
}
memberItems = append(memberItems, memberItem)
}
ownerIdentifier := identifierMap[family.OwnerUserId]
if ownerIdentifier == "" {
ownerIdentifier = strconv.FormatInt(family.OwnerUserId, 10)
}
return &types.FamilyDetail{
Summary: types.FamilySummary{
FamilyId: family.Id,
OwnerUserId: family.OwnerUserId,
OwnerIdentifier: ownerIdentifier,
Status: mapFamilyStatus(family.Status),
ActiveMemberCount: activeMemberCount,
MaxMembers: family.MaxMembers,
CreatedAt: family.CreatedAt.Unix(),
UpdatedAt: family.UpdatedAt.Unix(),
},
Members: memberItems,
}, nil
}
@@ -0,0 +1,147 @@
package user
import (
"context"
"strconv"
"strings"
modelUser "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/xerr"
"github.com/pkg/errors"
)
type GetFamilyListLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logger.Logger
}
func NewGetFamilyListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFamilyListLogic {
return &GetFamilyListLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logger.WithContext(ctx),
}
}
func (l *GetFamilyListLogic) GetFamilyList(req *types.GetFamilyListRequest) (*types.GetFamilyListResponse, error) {
page := req.Page
size := req.Size
if page <= 0 {
page = 1
}
if size <= 0 {
size = 20
}
if size > 200 {
size = 200
}
query := l.svcCtx.DB.WithContext(l.ctx).
Model(&modelUser.UserFamily{}).
Where("user_family.deleted_at IS NULL")
if req.OwnerUserId != nil {
query = query.Where("user_family.owner_user_id = ?", *req.OwnerUserId)
}
if req.FamilyId != nil {
query = query.Where("user_family.id = ?", *req.FamilyId)
}
if req.UserId != nil {
query = query.Where(
"EXISTS (SELECT 1 FROM user_family_member ufm WHERE ufm.family_id = user_family.id AND ufm.deleted_at IS NULL AND ufm.status = ? AND ufm.user_id = ?)",
modelUser.FamilyMemberActive, *req.UserId,
)
}
if statusValue, ok := normalizeFamilyStatusInput(req.Status); ok {
query = query.Where("user_family.status = ?", statusValue)
}
keyword := strings.TrimSpace(req.Keyword)
if keyword != "" {
keywordLike := "%" + keyword + "%"
query = query.Where(
"(CAST(user_family.id AS CHAR) LIKE ? OR CAST(user_family.owner_user_id AS CHAR) LIKE ? OR "+
"EXISTS (SELECT 1 FROM user_auth_methods owner_auth WHERE owner_auth.user_id = user_family.owner_user_id AND owner_auth.deleted_at IS NULL AND owner_auth.auth_identifier LIKE ?) OR "+
"EXISTS (SELECT 1 FROM user_family_member keyword_member JOIN user_auth_methods keyword_auth ON keyword_auth.user_id = keyword_member.user_id AND keyword_auth.deleted_at IS NULL WHERE keyword_member.family_id = user_family.id AND keyword_member.deleted_at IS NULL AND keyword_auth.auth_identifier LIKE ?))",
keywordLike, keywordLike, keywordLike, keywordLike,
)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count family list failed")
}
var families []modelUser.UserFamily
if err := query.Order("user_family.id DESC").
Limit(size).
Offset((page - 1) * size).
Find(&families).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family list failed")
}
if len(families) == 0 {
return &types.GetFamilyListResponse{
Total: total,
List: []types.FamilySummary{},
}, nil
}
ownerIDs := make([]int64, 0, len(families))
familyIDs := make([]int64, 0, len(families))
for _, family := range families {
ownerIDs = append(ownerIDs, family.OwnerUserId)
familyIDs = append(familyIDs, family.Id)
}
identifierMap, identifierErr := findUserIdentifiers(l.ctx, l.svcCtx.DB, ownerIDs)
if identifierErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family owner identifiers failed")
}
type familyCount struct {
FamilyId int64
Count int64
}
var counts []familyCount
if err := l.svcCtx.DB.WithContext(l.ctx).
Table("user_family_member").
Select("family_id, COUNT(1) as count").
Where("family_id IN ? AND deleted_at IS NULL AND status = ?", familyIDs, modelUser.FamilyMemberActive).
Group("family_id").
Scan(&counts).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family member counts failed")
}
countMap := make(map[int64]int64, len(counts))
for _, count := range counts {
countMap[count.FamilyId] = count.Count
}
list := make([]types.FamilySummary, 0, len(families))
for _, family := range families {
ownerIdentifier := identifierMap[family.OwnerUserId]
if ownerIdentifier == "" {
ownerIdentifier = strconv.FormatInt(family.OwnerUserId, 10)
}
list = append(list, types.FamilySummary{
FamilyId: family.Id,
OwnerUserId: family.OwnerUserId,
OwnerIdentifier: ownerIdentifier,
Status: mapFamilyStatus(family.Status),
ActiveMemberCount: countMap[family.Id],
MaxMembers: family.MaxMembers,
CreatedAt: family.CreatedAt.Unix(),
UpdatedAt: family.UpdatedAt.Unix(),
})
}
return &types.GetFamilyListResponse{
Total: total,
List: list,
}, nil
}
@@ -3,6 +3,7 @@ package user
import (
"context"
"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"
@@ -32,5 +33,51 @@ func (l *GetUserDetailLogic) GetUserDetail(req *types.GetDetailRequest) (*types.
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get user detail error: %v", err.Error())
}
tool.DeepCopy(&resp, userInfo)
type familyRelation struct {
FamilyId int64
Role uint8
FamilyStatus uint8
OwnerUserId int64
MaxMembers int64
}
var relation familyRelation
relationErr := l.svcCtx.DB.WithContext(l.ctx).
Table("user_family_member").
Select("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 = ? AND user_family_member.deleted_at IS NULL AND user_family_member.status = ?", req.Id, user.FamilyMemberActive).
First(&relation).Error
if relationErr == nil {
resp.FamilyJoined = true
resp.FamilyId = relation.FamilyId
resp.FamilyRole = relation.Role
resp.FamilyOwnerUserId = relation.OwnerUserId
resp.FamilyMaxMembers = relation.MaxMembers
if relation.FamilyStatus == user.FamilyStatusActive {
resp.FamilyStatus = "active"
} else {
resp.FamilyStatus = "disabled"
}
if relation.Role == user.FamilyRoleOwner {
resp.FamilyRoleName = "owner"
} else if relation.Role == user.FamilyRoleMember {
resp.FamilyRoleName = "member"
}
type familyCount struct {
Count int64
}
var count familyCount
if countErr := l.svcCtx.DB.WithContext(l.ctx).
Table("user_family_member").
Select("COUNT(1) as count").
Where("family_id = ? AND status = ? AND deleted_at IS NULL", relation.FamilyId, user.FamilyMemberActive).
Scan(&count).Error; countErr == nil {
resp.FamilyMemberCount = count.Count
}
}
return &resp, nil
}
+94 -7
View File
@@ -2,6 +2,7 @@ package user
import (
"context"
"fmt"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
@@ -28,13 +29,17 @@ func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUs
}
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,
Order: "DESC",
UserId: req.UserId,
Search: req.Search,
Unscoped: req.Unscoped,
SubscribeId: req.SubscribeId,
UserSubscribeId: req.UserSubscribeId,
ShortCode: req.ShortCode,
FamilyJoined: req.FamilyJoined,
FamilyStatus: req.FamilyStatus,
FamilyOwnerUserId: req.FamilyOwnerUserId,
FamilyId: req.FamilyId,
Order: "DESC",
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetUserListLogic failed: %v", err.Error())
@@ -51,6 +56,64 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (*types.Ge
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 {
@@ -66,6 +129,7 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (*types.Ge
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 {
@@ -85,6 +149,29 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (*types.Ge
}
u.AuthMethods = authMethods
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)
}
}
userRespList = append(userRespList, u)
}
@@ -0,0 +1,71 @@
package user
import (
"context"
"time"
modelUser "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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type RemoveFamilyMemberLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logger.Logger
}
func NewRemoveFamilyMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RemoveFamilyMemberLogic {
return &RemoveFamilyMemberLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logger.WithContext(ctx),
}
}
func (l *RemoveFamilyMemberLogic) RemoveFamilyMember(req *types.RemoveFamilyMemberRequest) error {
var family modelUser.UserFamily
err := l.svcCtx.DB.WithContext(l.ctx).
Where("id = ? AND deleted_at IS NULL", req.FamilyId).
First(&family).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyNotExist), "family does not exist")
}
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family failed")
}
if family.Status != modelUser.FamilyStatusActive {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyStatusInvalid), "family status is invalid")
}
var member modelUser.UserFamilyMember
err = l.svcCtx.DB.WithContext(l.ctx).
Where("family_id = ? AND user_id = ? AND deleted_at IS NULL AND status = ?", req.FamilyId, req.UserId, modelUser.FamilyMemberActive).
First(&member).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyNotExist), "active family member does not exist")
}
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family member failed")
}
if member.Role == modelUser.FamilyRoleOwner {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyOwnerOperationForbidden), "cannot remove family owner")
}
now := time.Now()
if err = l.svcCtx.DB.WithContext(l.ctx).
Model(&modelUser.UserFamilyMember{}).
Where("id = ?", member.Id).
Updates(map[string]interface{}{
"status": modelUser.FamilyMemberRemoved,
"left_at": now,
}).Error; err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "remove family member failed")
}
return nil
}
@@ -0,0 +1,60 @@
package user
import (
"context"
modelUser "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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type UpdateFamilyMaxMembersLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logger.Logger
}
func NewUpdateFamilyMaxMembersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateFamilyMaxMembersLogic {
return &UpdateFamilyMaxMembersLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logger.WithContext(ctx),
}
}
func (l *UpdateFamilyMaxMembersLogic) UpdateFamilyMaxMembers(req *types.UpdateFamilyMaxMembersRequest) error {
var family modelUser.UserFamily
err := l.svcCtx.DB.WithContext(l.ctx).
Where("id = ? AND deleted_at IS NULL", req.FamilyId).
First(&family).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyNotExist), "family does not exist")
}
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query family failed")
}
var activeCount int64
if err = l.svcCtx.DB.WithContext(l.ctx).
Table("user_family_member").
Where("family_id = ? AND deleted_at IS NULL AND status = ?", req.FamilyId, modelUser.FamilyMemberActive).
Count(&activeCount).Error; err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count active members failed")
}
if req.MaxMembers < activeCount {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyMemberLimitExceeded), "max members below active member count")
}
if err = l.svcCtx.DB.WithContext(l.ctx).
Model(&modelUser.UserFamily{}).
Where("id = ?", req.FamilyId).
Update("max_members", req.MaxMembers).Error; err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update family max members failed")
}
return nil
}