各种配置项修复,优化到后台管理端配置

This commit is contained in:
shanshanzhong 2026-03-04 19:42:21 -08:00
parent 3415262017
commit 2215df8c0b
6 changed files with 157 additions and 20 deletions

View File

@ -0,0 +1,4 @@
DROP INDEX `idx_user_deleted_at` ON `user_auth_methods`;
ALTER TABLE `user_auth_methods`
DROP COLUMN `deleted_at`;

View File

@ -0,0 +1,4 @@
ALTER TABLE `user_auth_methods`
ADD COLUMN `deleted_at` DATETIME(3) DEFAULT NULL COMMENT 'Deletion Time';
CREATE INDEX `idx_user_deleted_at` ON `user_auth_methods` (`user_id`, `deleted_at`);

View File

@ -40,6 +40,25 @@ func (h *familyBindingHelper) getUserEmailMethod(userId int64) (*user.AuthMethod
return method, nil return method, nil
} }
func validateMemberJoinConflict(ownerFamilyId int64, memberRecord *user.UserFamilyMember) error {
if memberRecord == nil {
return nil
}
if ownerFamilyId != 0 && memberRecord.FamilyId == ownerFamilyId {
if memberRecord.Status == user.FamilyMemberActive {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already in this family")
}
return nil
}
if memberRecord.Status == user.FamilyMemberActive {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyCrossBindForbidden), "user already belongs to another family")
}
return nil
}
func (h *familyBindingHelper) validateJoinFamily(ownerUserId, memberUserId int64) error { func (h *familyBindingHelper) validateJoinFamily(ownerUserId, memberUserId int64) error {
if ownerUserId == memberUserId { if ownerUserId == memberUserId {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already bound to this family") return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already bound to this family")
@ -49,13 +68,14 @@ func (h *familyBindingHelper) validateJoinFamily(ownerUserId, memberUserId int64
err := h.svcCtx.DB.WithContext(h.ctx). err := h.svcCtx.DB.WithContext(h.ctx).
Unscoped(). Unscoped().
Model(&user.UserFamily{}). Model(&user.UserFamily{}).
Where("owner_user_id = ? AND status = ?", ownerUserId, user.FamilyStatusActive). Where("owner_user_id = ?", ownerUserId).
First(&ownerFamily).Error First(&ownerFamily).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query owner family failed") return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query owner family failed")
} }
var memberRecord user.UserFamilyMember var memberRecord user.UserFamilyMember
memberExists := false
err = h.svcCtx.DB.WithContext(h.ctx). err = h.svcCtx.DB.WithContext(h.ctx).
Unscoped(). Unscoped().
Model(&user.UserFamilyMember{}). Model(&user.UserFamilyMember{}).
@ -65,10 +85,12 @@ func (h *familyBindingHelper) validateJoinFamily(ownerUserId, memberUserId int64
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member family relation failed") return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member family relation failed")
} }
if err == nil { if err == nil {
if ownerFamily.Id != 0 && memberRecord.FamilyId == ownerFamily.Id { memberExists = true
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already in this family") }
if memberExists {
if err = validateMemberJoinConflict(ownerFamily.Id, &memberRecord); err != nil {
return err
} }
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyCrossBindForbidden), "user already belongs to another family")
} }
if ownerFamily.Id == 0 { if ownerFamily.Id == 0 {
@ -116,12 +138,8 @@ func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source
memberExists := err == nil memberExists := err == nil
if memberExists { if memberExists {
if memberRecord.FamilyId == ownerFamily.Id { if err = validateMemberJoinConflict(ownerFamily.Id, &memberRecord); err != nil {
if memberRecord.Status == user.FamilyMemberActive { return err
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already in this family")
}
} else {
return errors.Wrapf(xerr.NewErrCode(xerr.FamilyCrossBindForbidden), "user already belongs to another family")
} }
} }
@ -151,12 +169,16 @@ func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source
return nil return nil
} }
if memberRecord.FamilyId != ownerFamily.Id {
memberRecord.FamilyId = ownerFamily.Id
}
memberRecord.Status = user.FamilyMemberActive memberRecord.Status = user.FamilyMemberActive
memberRecord.Role = user.FamilyRoleMember memberRecord.Role = user.FamilyRoleMember
memberRecord.JoinSource = source memberRecord.JoinSource = source
memberRecord.JoinedAt = now memberRecord.JoinedAt = now
memberRecord.LeftAt = nil memberRecord.LeftAt = nil
if err = tx.Save(&memberRecord).Error; err != nil { memberRecord.DeletedAt = gorm.DeletedAt{}
if err = tx.Unscoped().Save(&memberRecord).Error; err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update family member failed") return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update family member failed")
} }
return nil return nil

View File

@ -0,0 +1,107 @@
package user
import (
stderrors "errors"
"testing"
modelUser "github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/pkg/xerr"
pkgerrors "github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
func extractFamilyJoinCode(err error) uint32 {
if err == nil {
return 0
}
var codeErr *xerr.CodeError
if stderrors.As(pkgerrors.Cause(err), &codeErr) {
return codeErr.GetErrCode()
}
return 0
}
func TestValidateMemberJoinConflict(t *testing.T) {
ownerFamilyID := int64(11)
testCases := []struct {
name string
ownerFamily int64
memberRecord *modelUser.UserFamilyMember
wantCode uint32
}{
{
name: "no member record",
ownerFamily: ownerFamilyID,
wantCode: 0,
},
{
name: "same family active member",
ownerFamily: ownerFamilyID,
memberRecord: &modelUser.UserFamilyMember{
FamilyId: ownerFamilyID,
Status: modelUser.FamilyMemberActive,
},
wantCode: xerr.FamilyAlreadyBound,
},
{
name: "same family left member",
ownerFamily: ownerFamilyID,
memberRecord: &modelUser.UserFamilyMember{
FamilyId: ownerFamilyID,
Status: modelUser.FamilyMemberLeft,
},
wantCode: 0,
},
{
name: "same family removed member",
ownerFamily: ownerFamilyID,
memberRecord: &modelUser.UserFamilyMember{
FamilyId: ownerFamilyID,
Status: modelUser.FamilyMemberRemoved,
},
wantCode: 0,
},
{
name: "cross family active member",
ownerFamily: ownerFamilyID,
memberRecord: &modelUser.UserFamilyMember{
FamilyId: ownerFamilyID + 1,
Status: modelUser.FamilyMemberActive,
},
wantCode: xerr.FamilyCrossBindForbidden,
},
{
name: "cross family left member",
ownerFamily: ownerFamilyID,
memberRecord: &modelUser.UserFamilyMember{
FamilyId: ownerFamilyID + 1,
Status: modelUser.FamilyMemberLeft,
},
wantCode: 0,
},
{
name: "cross family removed member",
ownerFamily: ownerFamilyID,
memberRecord: &modelUser.UserFamilyMember{
FamilyId: ownerFamilyID + 1,
Status: modelUser.FamilyMemberRemoved,
},
wantCode: 0,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
err := validateMemberJoinConflict(testCase.ownerFamily, testCase.memberRecord)
if testCase.wantCode == 0 {
require.NoError(t, err)
return
}
require.Error(t, err)
require.Equal(t, testCase.wantCode, extractFamilyJoinCode(err))
})
}
}

View File

@ -36,12 +36,12 @@ func init() {
RegisterIPLimit: "Too many registrations", RegisterIPLimit: "Too many registrations",
EmailBindError: "Email already bound", EmailBindError: "Email already bound",
UserBindInviteCodeExist: "Invite code already bound", UserBindInviteCodeExist: "Invite code already bound",
FamilyMemberLimitExceeded: "Family member limit exceeded", FamilyMemberLimitExceeded: "家庭成员数量已达上限",
FamilyAlreadyBound: "Family already bound", FamilyAlreadyBound: "已绑定家庭组",
FamilyCrossBindForbidden: "Cross-family binding is forbidden", FamilyCrossBindForbidden: "禁止跨家庭组绑定",
FamilyNotExist: "Family does not exist", FamilyNotExist: "家庭组不存在",
FamilyStatusInvalid: "Family status is invalid", FamilyStatusInvalid: "家庭组状态无效",
FamilyOwnerOperationForbidden: "Owner operation is forbidden", FamilyOwnerOperationForbidden: "家庭组所有者不允许此操作",
// Node error // Node error
NodeExist: "Node already exists", NodeExist: "Node already exists",

View File

@ -7,9 +7,9 @@ import (
) )
func TestFamilyErrorCodeMessages(t *testing.T) { func TestFamilyErrorCodeMessages(t *testing.T) {
require.Equal(t, "Family member limit exceeded", MapErrMsg(FamilyMemberLimitExceeded)) require.Equal(t, "家庭成员数量已达上限", MapErrMsg(FamilyMemberLimitExceeded))
require.Equal(t, "Family already bound", MapErrMsg(FamilyAlreadyBound)) require.Equal(t, "已绑定家庭组", MapErrMsg(FamilyAlreadyBound))
require.Equal(t, "Cross-family binding is forbidden", MapErrMsg(FamilyCrossBindForbidden)) require.Equal(t, "禁止跨家庭组绑定", MapErrMsg(FamilyCrossBindForbidden))
} }
func TestFamilyErrorCodeIsRegistered(t *testing.T) { func TestFamilyErrorCodeIsRegistered(t *testing.T) {